mollom_test_server.module 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751
  1. <?php
  2. /**
  3. * @file
  4. * Fake Mollom REST Testing server implementation.
  5. *
  6. * This is a simplified re-implementation of Mollom's REST Testing API, which
  7. * is used by functional Drupal module tests only.
  8. *
  9. * While the duplication means additional engineering work, the fake server
  10. * provides unique testing facilities that are not possible to achieve with
  11. * Mollom's official REST Testing API server:
  12. *
  13. * - All requests and request parameters are stored, which enables tests to
  14. * retrieve previously sent request information and assert that the client
  15. * sent the expected parameters and values (and no unexpected parameters).
  16. * This is important since most of the business logic for request parameters
  17. * depends on external user input and lives in form processing code; i.e.,
  18. * the Mollom module's primary functionality is not unit-testable.
  19. * - Special test server variables allow tests to trigger special and unexpected
  20. * service conditions in between requests, such as 404s or service downtimes,
  21. * without having to manipulate the regular request parameters (and thus,
  22. * without having to add testing related code and logic to the regular runtime
  23. * code).
  24. * - The module's behavior and communication can be tested against this fake
  25. * server implementation without having the Testing mode enabled; i.e., this
  26. * fake server can also act as production server.
  27. *
  28. * @see MollomWebTestCase::getServerRecord()
  29. *
  30. * In addition, this fake server implementation allows to double-check whether
  31. * Mollom's API follows the expectations of Mollom client developers, and it
  32. * successfully helped to discover inconsistencies as well as unexpected API
  33. * behaviors in the past.
  34. *
  35. * The module communicates with this fake server when the 'mollom_class'
  36. * variable is set to 'MollomDrupalTestLocal'. The fake test server module is
  37. * automatically enabled for tests when needed.
  38. *
  39. * @see MollomWebTestCase::setUp()
  40. * @see MollomDrupalTestLocal
  41. *
  42. * The fake server architecture is kept as simple as possible:
  43. *
  44. * - Menu router definitions are registering a controller/handler for each main
  45. * API endpoint.
  46. * - The controller callback validates OAuth parameters (when applicable) and
  47. * optionally calls into dedicated functions to handle the request-specific
  48. * logic (e.g., Content API) or performs the logic directly (e.g., Site API).
  49. * - The callback result, i.e. response parameters (or error code), is processed
  50. * by the delivery callback and transformed into an HTTP response following
  51. * the Mollom API format.
  52. *
  53. * @see mollom_test_server_menu()
  54. * @see mollom_test_server_rest_deliver()
  55. *
  56. * Clean URLs must be enabled.
  57. */
  58. /**
  59. * Implements hook_menu().
  60. */
  61. function mollom_test_server_menu() {
  62. // Base path and argument count for all registered routes.
  63. $path = 'mollom-test/rest/v1';
  64. $base_args = count(explode('/', $path)) - 1;
  65. // @todo Consider to use a generic page callback, passing arg(3), the resource
  66. // type, and optionally arg(4), the resource, as argument. This would allow
  67. // us to use PHP Exceptions to throw different status codes and errors. Make
  68. // that page callback dynamically switch the delivery callback (for JSON).
  69. $base = array(
  70. // Access depends on whether OAuth parameters have been sent, and in any
  71. // case, error responses must be in the requested/accepted Content-Type
  72. // passed by the client (as opposed to a 403 HTML page generated by Drupal).
  73. 'access callback' => TRUE,
  74. 'type' => MENU_CALLBACK,
  75. 'delivery callback' => 'mollom_test_server_rest_deliver',
  76. );
  77. $items[$path . '/site'] = $base + array(
  78. 'page callback' => 'mollom_test_server_rest_site',
  79. );
  80. $items[$path . '/content'] = $base + array(
  81. 'page callback' => 'mollom_test_server_rest_content',
  82. );
  83. $items[$path . '/captcha'] = $base + array(
  84. 'page callback' => 'mollom_test_server_rest_captcha',
  85. );
  86. $items[$path . '/feedback'] = $base + array(
  87. 'page callback' => 'mollom_test_server_rest_send_feedback',
  88. );
  89. $items[$path . '/blacklist/%'] = $base + array(
  90. 'page callback' => 'mollom_test_server_rest_blacklist',
  91. 'page arguments' => array($base_args + 2),
  92. );
  93. // @todo Whitelist endpoints.
  94. return $items;
  95. }
  96. /**
  97. * Returns HTTP request query parameters for the current request.
  98. *
  99. * @see Mollom::httpBuildQuery()
  100. * @see http://php.net/manual/en/wrappers.php.php
  101. */
  102. function mollom_test_server_rest_get_parameters() {
  103. $data = &drupal_static(__FUNCTION__);
  104. if (isset($data)) {
  105. return $data;
  106. }
  107. // @todo Replace with Mollom::getServerParameters()
  108. if ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') {
  109. $data = Mollom::httpParseQuery($_SERVER['QUERY_STRING']);
  110. // Remove $_GET['q'].
  111. // @see .htaccess
  112. unset($data['q']);
  113. }
  114. elseif ($_SERVER['REQUEST_METHOD'] == 'POST' || $_SERVER['REQUEST_METHOD'] == 'PUT') {
  115. $data = Mollom::httpParseQuery(file_get_contents('php://input'));
  116. }
  117. return $data;
  118. }
  119. /**
  120. * Returns the parsed HTTP Authorization request header as an array.
  121. *
  122. * @todo Replace with Mollom::getServerAuthentication()
  123. */
  124. function mollom_test_server_rest_get_auth_header() {
  125. $header = &drupal_static(__FUNCTION__);
  126. if (isset($header)) {
  127. return $header;
  128. }
  129. $header = array();
  130. if (function_exists('apache_request_headers')) {
  131. $headers = apache_request_headers();
  132. if (isset($headers['Authorization'])) {
  133. $input = $headers['Authorization'];
  134. }
  135. }
  136. elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
  137. $input = $_SERVER['HTTP_AUTHORIZATION'];
  138. }
  139. if (isset($input)) {
  140. preg_match_all('@([^, =]+)="([^"]*)"@', $input, $header);
  141. $header = array_combine($header[1], $header[2]);
  142. }
  143. return $header;
  144. }
  145. /**
  146. * Delivery callback for REST API endpoints.
  147. */
  148. function mollom_test_server_rest_deliver($page_callback_result) {
  149. // All fake-server responses are not cached.
  150. drupal_page_is_cacheable(FALSE);
  151. drupal_add_http_header('Content-Type', 'application/xml; charset=utf-8');
  152. $xml = new DOMDocument('1.0', 'utf-8');
  153. $element = $xml->createElement('response');
  154. // Append status response parameters.
  155. // @todo Add support for custom codes (redirect/refresh) + error messages.
  156. $code = 200;
  157. if (!is_array($page_callback_result) && $page_callback_result !== TRUE) {
  158. switch ($page_callback_result) {
  159. case MENU_NOT_FOUND:
  160. $code = 404;
  161. $message = 'Not found';
  162. break;
  163. case Mollom::AUTH_ERROR:
  164. $code = 401;
  165. $message = 'Unauthorized';
  166. break;
  167. default:
  168. $code = 400;
  169. $message = 'Bad request';
  170. break;
  171. }
  172. }
  173. $status = array(
  174. 'code' => $code,
  175. );
  176. if (isset($message)) {
  177. $status['message'] = $message;
  178. }
  179. mollom_test_server_rest_add_xml($xml, $element, $status);
  180. // Append other response parameters.
  181. if (is_array($page_callback_result)) {
  182. mollom_test_server_rest_add_xml($xml, $element, $page_callback_result);
  183. }
  184. $xml->appendChild($element);
  185. print $xml->saveXML();
  186. // Perform end-of-request tasks.
  187. drupal_page_footer();
  188. }
  189. function mollom_test_server_rest_add_xml(DOMDocument $doc, DOMNode $parent, $data, $key = NULL) {
  190. if (is_scalar($data)) {
  191. // Mollom REST API always uses integers instead of Booleans due to varying
  192. // implementations of JSON protocol across client platforms/frameworks.
  193. if (is_bool($data)) {
  194. $data = (int) $data;
  195. }
  196. $element = $doc->createTextNode($data);
  197. $parent->appendChild($element);
  198. }
  199. else {
  200. foreach ($data as $property => $value) {
  201. $key = (is_numeric($property) ? 'item' : $property);
  202. $element = $doc->createElement($key);
  203. $parent->appendChild($element);
  204. mollom_test_server_rest_add_xml($doc, $element, $value, $key);
  205. }
  206. }
  207. }
  208. /**
  209. * Returns whether the OAuth request signature is valid.
  210. */
  211. function mollom_test_server_rest_validate_auth() {
  212. $data = mollom_test_server_rest_get_parameters();
  213. $header = mollom_test_server_rest_get_auth_header();
  214. $sites = variable_get('mollom_test_server_site', array());
  215. // Validate the timestamp.
  216. $client_time = $header['oauth_timestamp'];
  217. $time = REQUEST_TIME;
  218. $offset = abs($time - $client_time);
  219. if ($offset > Mollom::TIME_OFFSET_MAX) {
  220. return FALSE;
  221. }
  222. $sent_signature = $header['oauth_signature'];
  223. unset($header['oauth_signature']);
  224. $base_string = implode('&', array(
  225. $_SERVER['REQUEST_METHOD'],
  226. Mollom::rawurlencode($GLOBALS['base_url'] . '/' . $_GET['q']),
  227. Mollom::rawurlencode(Mollom::httpBuildQuery($data + $header)),
  228. ));
  229. $nonce = $header['oauth_nonce'];
  230. if (!isset($sites[$header['oauth_consumer_key']]['privateKey'])) {
  231. return FALSE;
  232. }
  233. $privateKey = $sites[$header['oauth_consumer_key']]['privateKey'];
  234. $key = Mollom::rawurlencode($privateKey) . '&' . '';
  235. $signature = rawurlencode(base64_encode(hash_hmac('sha1', $base_string, $key, TRUE)));
  236. return $signature === $sent_signature;
  237. }
  238. /**
  239. * REST callback for CRUD site operations.
  240. *
  241. * @param $publicKey
  242. * (optional) The public key of a site.
  243. * @param $delete
  244. * (optional) Whether to delete the site with $publicKey.
  245. */
  246. function mollom_test_server_rest_site($publicKey = NULL, $delete = FALSE) {
  247. $data = mollom_test_server_rest_get_parameters();
  248. $bin = 'mollom_test_server_site';
  249. $sites = variable_get($bin, array());
  250. if (isset($publicKey)) {
  251. // Validate authentication.
  252. if (!mollom_test_server_rest_validate_auth()) {
  253. return Mollom::AUTH_ERROR;
  254. }
  255. // Check whether publicKey exists.
  256. if (!isset($sites[$publicKey])) {
  257. return MENU_NOT_FOUND;
  258. }
  259. }
  260. if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  261. // Return existing site.
  262. if (isset($publicKey)) {
  263. $response = $sites[$publicKey];
  264. }
  265. // Return list of existing sites.
  266. else {
  267. $response = array(
  268. 'list' => array_values($sites),
  269. 'listCount' => count($sites),
  270. 'listOffset' => 0,
  271. 'listTotal' => count($sites),
  272. );
  273. return $response;
  274. }
  275. }
  276. else {
  277. // Update site.
  278. if (isset($publicKey) && !$delete) {
  279. $sites[$publicKey] = $data + $sites[$publicKey];
  280. variable_set($bin, $sites);
  281. $response = $sites[$publicKey];
  282. }
  283. // Create new site.
  284. // Authentication is ignored in this case.
  285. elseif (!$delete) {
  286. $data['publicKey'] = $publicKey = md5(rand() . REQUEST_TIME);
  287. $data['privateKey'] = $privateKey = md5(rand() . REQUEST_TIME);
  288. // Apply default values.
  289. $data += array(
  290. 'url' => '',
  291. 'email' => '',
  292. 'expectedLanguages' => array(),
  293. 'subscriptionType' => '',
  294. // Client version info is not defined by default.
  295. );
  296. $sites[$publicKey] = $data;
  297. variable_set($bin, $sites);
  298. $response = $data;
  299. }
  300. // Delete site.
  301. else {
  302. unset($sites[$publicKey]);
  303. variable_set($bin, $sites);
  304. return TRUE;
  305. }
  306. }
  307. return array('site' => $response);
  308. }
  309. /**
  310. * REST callback for mollom.checkContent to perform textual analysis.
  311. */
  312. function mollom_test_server_rest_content($contentId = NULL) {
  313. $data = mollom_test_server_rest_get_parameters();
  314. if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  315. // @todo List/read content.
  316. if (empty($contentId)) {
  317. return FALSE;
  318. }
  319. return FALSE;
  320. }
  321. else {
  322. // Content ID in request parameters must match the one in path.
  323. if (isset($data['id']) && $data['id'] != $contentId) {
  324. return FALSE;
  325. }
  326. if (isset($contentId)) {
  327. $data['id'] = $contentId;
  328. }
  329. }
  330. // Default POST: Create or update content and check it.
  331. return array('content' => mollom_test_server_check_content($data));
  332. }
  333. /**
  334. * REST callback to for CAPTCHAs.
  335. */
  336. function mollom_test_server_rest_captcha($captchaId = NULL) {
  337. $data = mollom_test_server_rest_get_parameters();
  338. if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  339. // There is no GET /captcha[/{captchaId}].
  340. return FALSE;
  341. }
  342. else {
  343. // CAPTCHA ID in request parameters must match the one in path.
  344. if (isset($data['id']) && $data['id'] != $captchaId) {
  345. return FALSE;
  346. }
  347. // Verify CAPTCHA.
  348. if (isset($captchaId)) {
  349. $data['id'] = $captchaId;
  350. $response = mollom_test_server_check_captcha($data);
  351. if (!is_array($response)) {
  352. return $response;
  353. }
  354. return array('captcha' => $response);
  355. }
  356. }
  357. // Create a new CAPTCHA resource.
  358. return array('captcha' => mollom_test_server_get_captcha($data));
  359. }
  360. /**
  361. * REST callback for Blacklist API.
  362. *
  363. * @param $public_key
  364. * The public key of a site.
  365. *
  366. * @todo Abstract actual functionality like other REST handlers.
  367. */
  368. function mollom_test_server_rest_blacklist($public_key, $entryId = NULL, $delete = FALSE) {
  369. if (empty($public_key)) {
  370. return FALSE;
  371. }
  372. $data = mollom_test_server_rest_get_parameters();
  373. // Prepare text value.
  374. if (isset($data['value'])) {
  375. $data['value'] = drupal_strtolower(trim($data['value']));
  376. }
  377. $bin = 'mollom_test_server_blacklist_' . $public_key;
  378. $entries = variable_get($bin, array());
  379. if ($_SERVER['REQUEST_METHOD'] == 'GET') {
  380. // List blacklist entries.
  381. if (empty($entryId)) {
  382. $response = array();
  383. // Remove deleted entries (== FALSE).
  384. $entries = array_filter($entries);
  385. $response['list'] = $entries;
  386. // @todo Not required yet.
  387. $response['listCount'] = count($entries);
  388. $response['listOffset'] = 0;
  389. $response['listTotal'] = count($entries);
  390. return $response;
  391. }
  392. // Read a single entry.
  393. else {
  394. // Check whether the entry exists and was not deleted.
  395. if (!empty($entries[$entryId])) {
  396. return array('entry' => $entries[$entryId]);
  397. }
  398. else {
  399. return MENU_NOT_FOUND;
  400. }
  401. }
  402. }
  403. else {
  404. // Update an existing entry.
  405. if (isset($entryId)) {
  406. // Entry ID must match.
  407. if (isset($data['id']) && $data['id'] != $entryId) {
  408. return FALSE;
  409. }
  410. // Check that the entry was not deleted.
  411. if (empty($entries[$entryId])) {
  412. return MENU_NOT_FOUND;
  413. }
  414. // Entry ID cannot be updated.
  415. unset($data['id']);
  416. $entries[$entryId] = $data;
  417. variable_set($bin, $entries);
  418. $response = $data;
  419. $response['id'] = $entryId;
  420. return array('entry' => $response);
  421. }
  422. // Create a new entry.
  423. elseif (!$delete) {
  424. $entryId = max(array_keys($entries)) + 1;
  425. $data['id'] = $entryId;
  426. $entries[$entryId] = $data;
  427. variable_set($bin, $entries);
  428. $response = $data;
  429. return array('entry' => $response);
  430. }
  431. // Delete an existing entry.
  432. else {
  433. // Check that the entry was not deleted already.
  434. if (!empty($entries[$entryId])) {
  435. $entries[$entryId] = FALSE;
  436. variable_set($bin, $entries);
  437. return TRUE;
  438. }
  439. else {
  440. return MENU_NOT_FOUND;
  441. }
  442. }
  443. }
  444. }
  445. /**
  446. * REST callback for mollom.sendFeedback to send feedback for a moderated post.
  447. */
  448. function mollom_test_server_rest_send_feedback() {
  449. $data = mollom_test_server_rest_get_parameters();
  450. // A resource ID is required.
  451. if (empty($data['contentId']) && empty($data['captchaId'])) {
  452. return 400;
  453. }
  454. // The feedback is valid if the supplied reason is one of the supported
  455. // strings. Otherwise, it's a bad request.
  456. $storage = variable_get('mollom_test_server_feedback', array());
  457. $storage[] = $data;
  458. variable_set('mollom_test_server_feedback', $storage);
  459. // Default value assumed in the API for feedback type is "moderate".
  460. if (empty($data['type'])) {
  461. $data['type'] = 'moderate';
  462. }
  463. $reason_result = in_array($data['reason'], array('spam', 'profanity', 'quality', 'unwanted', 'approve', 'delete'));
  464. $feedback_result = in_array($data['type'], array('flag', 'moderate'));
  465. return $reason_result && $feedback_result ? TRUE : 400;
  466. }
  467. /**
  468. * API callback for mollom.checkContent to perform textual analysis.
  469. *
  470. * @todo Add support for 'redirect' and 'refresh' values.
  471. */
  472. function mollom_test_server_check_content($data) {
  473. $response = array();
  474. // If only a single value for checks is passed, it is a string.
  475. if (isset($data['checks']) && is_string($data['checks'])) {
  476. $data['checks'] = array($data['checks']);
  477. }
  478. $header = mollom_test_server_rest_get_auth_header();
  479. $publicKey = $header['oauth_consumer_key'];
  480. // Fetch blacklist.
  481. $blacklist = variable_get('mollom_test_server_blacklist_' . $publicKey, array());
  482. // Determine content keys to analyze.
  483. $post_keys = array('postTitle' => 1, 'postBody' => 1);
  484. $type = FALSE;
  485. if (isset($data['type']) && in_array($data['type'], array('user'))) {
  486. $type = $data['type'];
  487. if ($type == 'user') {
  488. $post_keys += array('authorName' => 1, 'authorMail' => 1);
  489. }
  490. }
  491. $post = implode('\n', array_intersect_key($data, $post_keys));
  492. $update = isset($data['stored']);
  493. // Spam filter: Check post_title and post_body for ham, spam, or unsure.
  494. if (!$update && (!isset($data['checks']) || in_array('spam', $data['checks']))) {
  495. $spam = FALSE;
  496. $ham = FALSE;
  497. // 'spam' always has precedence.
  498. if (strpos($post, 'spam') !== FALSE) {
  499. $spam = TRUE;
  500. }
  501. // Otherwise, check for 'ham'.
  502. elseif (strpos($post, 'ham') !== FALSE) {
  503. $ham = TRUE;
  504. }
  505. // Lastly, take a forced 'unsure' into account.
  506. elseif (strpos($post, 'unsure') !== FALSE) {
  507. // Enabled unsure mode.
  508. if (!isset($data['unsure']) || $data['unsure']) {
  509. $spam = TRUE;
  510. $ham = TRUE;
  511. }
  512. // Binary mode.
  513. else {
  514. $spam = FALSE;
  515. $ham = TRUE;
  516. }
  517. }
  518. // Check blacklist.
  519. if ($matches = mollom_test_server_check_content_blacklist($post, $blacklist, 'spam')) {
  520. $spam = TRUE;
  521. $ham = FALSE;
  522. $response['reason'] = 'blacklist';
  523. $response['blacklistSpam'] = $matches;
  524. }
  525. if ($spam && $ham) {
  526. $response['spamScore'] = 0.5;
  527. $response['spamClassification'] = 'unsure';
  528. $qualityScore = 0.5;
  529. }
  530. elseif ($spam) {
  531. $response['spamScore'] = 1.0;
  532. $response['spamClassification'] = 'spam';
  533. $qualityScore = 0.0;
  534. }
  535. elseif ($ham) {
  536. $response['spamScore'] = 0.0;
  537. $response['spamClassification'] = 'ham';
  538. $qualityScore = 1.0;
  539. }
  540. else {
  541. $response['spamScore'] = 0.5;
  542. $response['spamClassification'] = 'unsure';
  543. $qualityScore = NULL;
  544. }
  545. // In case a previous spam check was unsure and a CAPTCHA was solved, the
  546. // result is supposed to be ham - unless the new content is spam.
  547. if (!empty($data['id']) && $response['spamClassification'] == 'unsure') {
  548. $content_captchas = variable_get('mollom_test_server_content_captcha', array());
  549. if (!empty($content_captchas[$data['id']])) {
  550. $response['spamScore'] = 0.0;
  551. $response['spamClassification'] = 'ham';
  552. }
  553. }
  554. }
  555. // Quality filter.
  556. if (isset($data['checks']) && in_array('quality', $data['checks'])) {
  557. if (isset($qualityScore)) {
  558. $response['qualityScore'] = $qualityScore;
  559. }
  560. else {
  561. $response['qualityScore'] = 0;
  562. }
  563. }
  564. // Profanity filter.
  565. if (isset($data['checks']) && in_array('profanity', $data['checks'])) {
  566. $profanityScore = 0.0;
  567. if (strpos($post, 'profanity') !== FALSE) {
  568. $profanityScore = 1.0;
  569. }
  570. // Check blacklist.
  571. if ($matches = mollom_test_server_check_content_blacklist($post, $blacklist, 'profanity')) {
  572. $profanityScore = 1.0;
  573. $response['blacklistProfanity'] = $matches;
  574. }
  575. $response['profanityScore'] = $profanityScore;
  576. }
  577. // Language detection.
  578. if (isset($data['checks']) && in_array('language', $data['checks'])) {
  579. $languages = array();
  580. if (stripos($post, 'ist seit der Mitte')) {
  581. $languages[] = array(
  582. 'languageCode' => 'de',
  583. );
  584. }
  585. if (stripos($post, 'it is the most populous city')) {
  586. $languages[] = array(
  587. 'languageCode' => 'en',
  588. );
  589. }
  590. if (count($languages) == 0) {
  591. $languages[] = array(
  592. 'languageCode' => 'zxx',
  593. );
  594. }
  595. $score = 1/count($languages);
  596. foreach($languages as $id => $langObj) {
  597. $languages[$id]['languageScore'] = $score;
  598. }
  599. $response['languages'] = $languages;
  600. }
  601. $storage = variable_get('mollom_test_server_content', array());
  602. $contentId = (!empty($data['id']) ? $data['id'] : md5(mt_rand()));
  603. if (isset($storage[$contentId])) {
  604. $storage[$contentId] = array_merge($storage[$contentId], $data);
  605. }
  606. else {
  607. $storage[$contentId] = $data;
  608. }
  609. if ($update) {
  610. $response = array_merge($storage[$contentId], $response);
  611. }
  612. $response['id'] = $contentId;
  613. variable_set('mollom_test_server_content', $storage);
  614. return $response;
  615. }
  616. /**
  617. * Checks a string against blacklisted terms.
  618. */
  619. function mollom_test_server_check_content_blacklist($string, $blacklist, $reason) {
  620. $terms = array();
  621. foreach ($blacklist as $entry) {
  622. if ($entry['reason'] == $reason) {
  623. $term = preg_quote($entry['value']);
  624. if ($entry['match'] == 'exact') {
  625. $term = '\b' . $term . '\b';
  626. }
  627. $terms[] = $term;
  628. }
  629. }
  630. if (!empty($terms)) {
  631. $terms = '/(' . implode('|', $terms) . ')/';
  632. preg_match_all($terms, strtolower($string), $matches);
  633. return $matches[1];
  634. }
  635. return array();
  636. }
  637. /**
  638. * API callback for mollom.getImageCaptcha to fetch a CAPTCHA image.
  639. */
  640. function mollom_test_server_get_captcha($data) {
  641. $captchaId = (!empty($data['id']) ? $data['id'] : md5(mt_rand()));
  642. $response = array(
  643. 'id' => $captchaId,
  644. );
  645. // Return a HTTPS URL if 'ssl' parameter was passed.
  646. $base_url = $GLOBALS['base_url'];
  647. if (!empty($data['ssl'])) {
  648. $base_url = str_replace('http', 'https', $base_url);
  649. }
  650. $response['url'] = $base_url . '/' . drupal_get_path('module', 'mollom') . '/images/powered-by-mollom-2.gif?captchaId=' . $captchaId;
  651. $storage = variable_get('mollom_test_server_captcha', array());
  652. $storage[$captchaId] = $data;
  653. variable_set('mollom_test_server_captcha', $storage);
  654. return $response;
  655. }
  656. /**
  657. * API callback for mollom.checkCaptcha to validate a CAPTCHA response.
  658. */
  659. function mollom_test_server_check_captcha($data) {
  660. $response = array();
  661. if (isset($data['solution']) && $data['solution'] == 'correct') {
  662. $response['solved'] = TRUE;
  663. }
  664. else {
  665. $response['solved'] = FALSE;
  666. $response['reason'] = '';
  667. }
  668. $storage = variable_get('mollom_test_server_captcha', array());
  669. $captchaId = $data['id'];
  670. if (!isset($storage[$captchaId])) {
  671. return MENU_NOT_FOUND;
  672. }
  673. $storage[$captchaId] = array_merge($storage[$captchaId], $data);
  674. $response['id'] = $captchaId;
  675. variable_set('mollom_test_server_captcha', $storage);
  676. if (isset($storage[$captchaId]['contentId'])) {
  677. $contentId = $storage[$captchaId]['contentId'];
  678. $content_captchas = variable_get('mollom_test_server_content_captcha', array());
  679. $content_captchas[$contentId] = $response['solved'];
  680. variable_set('mollom_test_server_content_captcha', $content_captchas);
  681. }
  682. return $response;
  683. }