The version of vichan running on lainchan.org
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
5.3KB

  1. <?php
  2. require 'inc/functions.php';
  3. if (!$config['search']['enable']) {
  4. die(_("Post search is disabled"));
  5. }
  6. $queries_per_minutes = $config['search']['queries_per_minutes'];
  7. $queries_per_minutes_all = $config['search']['queries_per_minutes_all'];
  8. $search_limit = $config['search']['search_limit'];
  9. if (isset($config['search']['boards'])) {
  10. $boards = $config['search']['boards'];
  11. } else {
  12. $boards = listBoards(TRUE);
  13. }
  14. $body = Element('search_form.html', Array('boards' => $boards, 'b' => isset($_GET['board']) ? $_GET['board'] : false, 'search' => isset($_GET['search']) ? str_replace('"', '&quot;', utf8tohtml($_GET['search'])) : false));
  15. if(isset($_GET['search']) && !empty($_GET['search']) && isset($_GET['board']) && in_array($_GET['board'], $boards)) {
  16. $phrase = $_GET['search'];
  17. $_body = '';
  18. $query = prepare("SELECT COUNT(*) FROM ``search_queries`` WHERE `ip` = :ip AND `time` > :time");
  19. $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
  20. $query->bindValue(':time', time() - ($queries_per_minutes[1] * 60));
  21. $query->execute() or error(db_error($query));
  22. if($query->fetchColumn() > $queries_per_minutes[0])
  23. error(_('Wait a while before searching again, please.'));
  24. $query = prepare("SELECT COUNT(*) FROM ``search_queries`` WHERE `time` > :time");
  25. $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60));
  26. $query->execute() or error(db_error($query));
  27. if($query->fetchColumn() > $queries_per_minutes_all[0])
  28. error(_('Wait a while before searching again, please.'));
  29. $query = prepare("INSERT INTO ``search_queries`` VALUES (:ip, :time, :query)");
  30. $query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
  31. $query->bindValue(':time', time());
  32. $query->bindValue(':query', $phrase);
  33. $query->execute() or error(db_error($query));
  34. _syslog(LOG_NOTICE, 'Searched /' . $_GET['board'] . '/ for "' . $phrase . '"');
  35. // Cleanup search queries table
  36. $query = prepare("DELETE FROM ``search_queries`` WHERE `time` <= :time");
  37. $query->bindValue(':time', time() - ($queries_per_minutes_all[1] * 60));
  38. $query->execute() or error(db_error($query));
  39. openBoard($_GET['board']);
  40. $filters = Array();
  41. function search_filters($m) {
  42. global $filters;
  43. $name = $m[2];
  44. $value = isset($m[4]) ? $m[4] : $m[3];
  45. if(!in_array($name, array('id', 'thread', 'subject', 'name'))) {
  46. // unknown filter
  47. return $m[0];
  48. }
  49. $filters[$name] = $value;
  50. return $m[1];
  51. }
  52. $phrase = trim(preg_replace_callback('/(^|\s)(\w+):("(.*)?"|[^\s]*)/', 'search_filters', $phrase));
  53. if(!preg_match('/[^*^\s]/', $phrase) && empty($filters)) {
  54. _syslog(LOG_WARNING, 'Query too broad.');
  55. $body .= '<p class="unimportant" style="text-align:center">(Query too broad.)</p>';
  56. echo Element('page.html', Array(
  57. 'config'=>$config,
  58. 'title'=>'Search',
  59. 'body'=>$body,
  60. ));
  61. exit;
  62. }
  63. // Escape escape character
  64. $phrase = str_replace('!', '!!', $phrase);
  65. // Remove SQL wildcard
  66. $phrase = str_replace('%', '!%', $phrase);
  67. // Use asterisk as wildcard to suit convention
  68. $phrase = str_replace('*', '%', $phrase);
  69. // Remove `, it's used by table prefix magic
  70. $phrase = str_replace('`', '!`', $phrase);
  71. $like = '';
  72. $match = Array();
  73. // Find exact phrases
  74. if(preg_match_all('/"(.+?)"/', $phrase, $m)) {
  75. foreach($m[1] as &$quote) {
  76. $phrase = str_replace("\"{$quote}\"", '', $phrase);
  77. $match[] = $pdo->quote($quote);
  78. }
  79. }
  80. $words = explode(' ', $phrase);
  81. foreach($words as &$word) {
  82. if(empty($word))
  83. continue;
  84. $match[] = $pdo->quote($word);
  85. }
  86. $like = '';
  87. foreach($match as &$phrase) {
  88. if(!empty($like))
  89. $like .= ' AND ';
  90. $phrase = preg_replace('/^\'(.+)\'$/', '\'%$1%\'', $phrase);
  91. $like .= '`body` LIKE ' . $phrase . ' ESCAPE \'!\'';
  92. }
  93. foreach($filters as $name => $value) {
  94. if(!empty($like))
  95. $like .= ' AND ';
  96. $like .= '`' . $name . '` = '. $pdo->quote($value);
  97. }
  98. $like = str_replace('%', '%%', $like);
  99. $query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE " . $like . " ORDER BY `time` DESC LIMIT :limit", $board['uri']));
  100. $query->bindValue(':limit', $search_limit, PDO::PARAM_INT);
  101. $query->execute() or error(db_error($query));
  102. if($query->rowCount() == $search_limit) {
  103. _syslog(LOG_WARNING, 'Query too broad.');
  104. $body .= '<p class="unimportant" style="text-align:center">('._('Query too broad.').')</p>';
  105. echo Element('page.html', Array(
  106. 'config'=>$config,
  107. 'title'=>'Search',
  108. 'body'=>$body,
  109. ));
  110. exit;
  111. }
  112. $temp = '';
  113. while($post = $query->fetch()) {
  114. if(!$post['thread']) {
  115. $po = new Thread($post);
  116. } else {
  117. $po = new Post($post);
  118. }
  119. $temp .= $po->build(true) . '<hr/>';
  120. }
  121. if(!empty($temp))
  122. $_body .= '<fieldset><legend>' .
  123. sprintf(ngettext('%d result in', '%d results in', $query->rowCount()),
  124. $query->rowCount()) . ' <a href="/' .
  125. sprintf($config['board_path'], $board['uri']) . $config['file_index'] .
  126. '">' .
  127. sprintf($config['board_abbreviation'], $board['uri']) . ' - ' . $board['title'] .
  128. '</a></legend>' . $temp . '</fieldset>';
  129. $body .= '<hr/>';
  130. if(!empty($_body))
  131. $body .= $_body;
  132. else
  133. $body .= '<p style="text-align:center" class="unimportant">('._('No results.').')</p>';
  134. }
  135. echo Element('page.html', Array(
  136. 'config'=>$config,
  137. 'title'=>_('Search'),
  138. 'body'=>'' . $body
  139. ));