Exemplo n.º 1
0
 public function board_statistics($output, $shortname = null)
 {
     $boards = $this->radix_coll->getAll();
     $available = $this->board_stats->getAvailableStats();
     while (true) {
         // Obtain all of the statistics already stored on the database to check for update frequency.
         $stats = $this->dc->qb()->select('board_id, name, timestamp')->from($this->dc->p('plugin_fu_board_statistics'), 'bs')->orderBy('timestamp', 'desc')->execute()->fetchAll();
         // Obtain the list of all statistics enabled.
         $avail = [];
         foreach ($available as $k => $a) {
             // get only the non-realtime ones
             if (isset($available['frequency'])) {
                 $avail[] = $k;
             }
         }
         foreach ($boards as $board) {
             if (!is_null($shortname) && $shortname != $board->shortname) {
                 continue;
             }
             // Update all statistics for the specified board or current board.
             $output->writeln($board->shortname . ' (' . $board->id . ')');
             foreach ($available as $k => $a) {
                 $output->writeln('  ' . $k . ' ');
                 $found = false;
                 $skip = false;
                 foreach ($stats as $r) {
                     // Determine if the statistics already exists or that the information is outdated.
                     if ($r['board_id'] == $board->id && $r['name'] == $k) {
                         // This statistics report has run once already.
                         $found = true;
                         if (!isset($a['frequency'])) {
                             $skip = true;
                             continue;
                         }
                         // This statistics report has not reached its frequency EOL.
                         if (time() - $r['timestamp'] <= $a['frequency']) {
                             $skip = true;
                             continue;
                         }
                         break;
                     }
                 }
                 // racing conditions with our cron.
                 if ($found === false) {
                     $this->board_stats->saveStat($board->id, $k, time() + 600, '');
                 }
                 // We were able to obtain a LOCK on the statistics report and has already reached the
                 // targeted frequency time.
                 if ($skip === false) {
                     $output->writeln('* Processing...');
                     $process = 'process' . $a['function'];
                     $result = $this->board_stats->{$process}($board);
                     // Save the statistics report in a JSON array.
                     $this->board_stats->saveStat($board->id, $k, time(), $result);
                 }
             }
         }
         sleep(10);
     }
 }
Exemplo n.º 2
0
 /**
  * Creates a Report object from an associative array
  *
  * @param   array  $array  An associative array
  * @return  \Foolz\FoolFuuka\Model\Report
  */
 public function fromArray(array $array)
 {
     $new = new Report($this->getContext());
     foreach ($array as $key => $item) {
         $new->{$key} = $item;
     }
     $new->reason_processed = htmlentities(@iconv('UTF-8', 'UTF-8//IGNORE', $new->reason));
     if (!isset($new->radix)) {
         $new->radix = $this->radix_coll->getById($new->board_id);
     }
     return $new;
 }
Exemplo n.º 3
0
 /**
  * Removes the board and renames its dir with a _removed_ + microtime() suffix
  */
 public function remove()
 {
     $this->dc->getConnection()->beginTransaction();
     $this->dc->qb()->delete($this->dc->p('boards_preferences'))->where('board_id = :id')->setParameter(':id', $this->id)->execute();
     $this->dc->qb()->delete($this->dc->p('boards'))->where('id = :id')->setParameter(':id', $this->id)->execute();
     // rename the directory and prevent directory collision
     $base = $this->preferences->get('foolfuuka.boards.directory') . '/' . $this->shortname;
     if (file_exists($base)) {
         $rename_to = $this->preferences->get('foolfuuka.boards.directory') . '/' . $this->shortname . '_removed_' . str_replace('.', '', (string) microtime(true));
         rename($base, $rename_to);
     }
     // for huge boards, this better run via command line
     $this->removeTables();
     $this->dc->getConnection()->commit();
     $this->collection->clearCache();
 }
Exemplo n.º 4
0
 /**
  * Adds a new ban
  *
  * @param   string  $ip_decimal  The IP of the banned user in decimal format
  * @param   string  $reason      The reason for the ban
  * @param   int     $length      The length of the ban in seconds
  * @param   array   $board_ids   The array of board IDs, global ban if left empty
  *
  * @return  \Foolz\Foolfuuka\Model\Ban
  * @throws  \Foolz\Foolfuuka\Model\BanException
  */
 public function add($ip_decimal, $reason, $length, $board_ids = [])
 {
     // 0 is a global ban
     if (empty($board_ids)) {
         $board_ids = [0];
     } else {
         // check that all ids are existing boards
         $valid_board_ids = [];
         foreach ($this->radix_coll->getAll() as $board) {
             $valid_board_ids[] = $board->id;
         }
         foreach ($board_ids as $id) {
             if (!in_array($id, $valid_board_ids)) {
                 throw new BanException(_i('You entered a non-existent board ID.'));
             }
         }
     }
     if (!ctype_digit((string) $ip_decimal)) {
         throw new BanException(_i('You entered an invalid IP.'));
     }
     if (mb_strlen($reason, 'utf-8') > 10000) {
         throw new BanException(_i('You entered a too long reason for the ban.'));
     }
     if (!ctype_digit($length)) {
         throw new BanException(_i('You entered an invalid length for the ban.'));
     }
     $time = time();
     $objects = [];
     try {
         $old = $this->getByIp($ip_decimal);
     } catch (BanNotFoundException $e) {
         $old = false;
     }
     foreach ($board_ids as $board_id) {
         $new = new Ban($this->getContext());
         $new->ip = $ip_decimal;
         $new->reason = $reason;
         $new->start = $time;
         $new->length = $length;
         $new->board_id = $board_id;
         $new->creator_id = $this->getAuth()->getUser()->getId();
         $new->appeal = '';
         if (isset($old[$new->board_id])) {
             if ($new->length < $old[$new->board_id]->length) {
                 $new->length = $old[$new->board_id]->length;
             }
             $this->dc->qb()->update($this->dc->p('banned_posters'))->where('id = :id')->set('start', $new->start)->set('length', $new->length)->set('creator_id', $new->creator_id)->setParameter(':id', $old[$new->board_id]->id)->execute();
         } else {
             $this->dc->getConnection()->insert($this->dc->p('banned_posters'), ['ip' => $new->ip, 'reason' => $new->reason, 'start' => $new->start, 'length' => $new->length, 'board_id' => $new->board_id, 'creator_id' => $new->creator_id, 'appeal' => $new->appeal]);
         }
         $objects[] = $new;
     }
     return $objects;
 }
Exemplo n.º 5
0
 /**
  * Commodity to check that the shortname is not wrong and return a coherent error
  */
 protected function check_board()
 {
     $board = $this->getQuery('board', $this->getPost('board', null));
     if ($board === null) {
         return false;
     }
     if (!($this->radix = $this->radix_coll->getByShortname($board))) {
         return false;
     }
     return true;
 }
Exemplo n.º 6
0
 public function action_sphinx_config()
 {
     $data = [];
     $mysql = $this->preferences->get('foolfuuka.sphinx.listen_mysql', null);
     $data['mysql'] = ['host' => $mysql === null ? '127.0.0.1' : explode(':', $mysql)[0], 'port' => $mysql === null ? '3306' : explode(':', $mysql)[1], 'flag' => $this->preferences->get('foolfuuka.sphinx.connection_flags', '0')];
     $sphinx = $this->preferences->get('foolfuuka.sphinx.listen', null);
     $data['sphinx'] = ['port' => $sphinx === null ? '9306' : explode(':', $sphinx)[1], 'working_directory' => $this->preferences->get('foolfuuka.sphinx.dir', '/usr/local/sphinx'), 'mem_limit' => $this->preferences->get('foolfuuka.sphinx.mem_limit', '1024M'), 'min_word_len' => $this->preferences->get('foolfuuka.sphinx.min_word_len', 1), 'max_children' => $this->preferences->get('foolfuuka.sphinx.max_children', 0), 'max_matches' => $this->preferences->get('foolfuuka.sphinx.max_matches', 5000), 'distributed' => (int) $this->preferences->get('foolfuuka.sphinx.distributed', 0)];
     $data['boards'] = $this->radix_coll->getAll();
     $data['example'] = current($data['boards']);
     $this->param_manager->setParam('method_title', [_i('Search'), 'Sphinx', _i('Configuration File'), _i('Generate')]);
     $this->builder->createPartial('body', $data['sphinx']['distributed'] > 1 ? 'boards/sphinx_dist_config' : 'boards/sphinx_config')->getParamManager()->setParams($data);
     return new Response($this->builder->build());
 }
Exemplo n.º 7
0
 /**
  * Delete the post and eventually the entire thread if it's OP
  * Also deletes the images when it's the only post with that image
  *
  * @param null $password
  * @param bool $force
  * @param bool $thread
  * @throws CommentSendingDatabaseException
  * @throws CommentDeleteWrongPassException
  * @return array|bool
  */
 protected function p_delete($password = null, $force = false, $thread = false)
 {
     if (!$this->getAuth()->hasAccess('comment.passwordless_deletion') && $force !== true) {
         if (!password_verify($password, $this->comment->getDelpass())) {
             throw new CommentDeleteWrongPassException(_i('You did not provide the correct deletion password.'));
         }
     }
     try {
         $this->dc->getConnection()->beginTransaction();
         // check that the post isn't already in deleted
         $has_deleted = $this->dc->qb()->select('COUNT(*) as found')->from($this->radix->getTable('_deleted'), 'd')->where('doc_id = :doc_id')->setParameter(':doc_id', $this->comment->doc_id)->execute()->fetch();
         if (!$has_deleted['found']) {
             // throw into _deleted table
             $this->dc->getConnection()->executeUpdate('INSERT INTO ' . $this->radix->getTable('_deleted') . ' ' . $this->dc->qb()->select('*')->from($this->radix->getTable(), 't')->where('doc_id = ' . $this->dc->getConnection()->quote($this->comment->doc_id))->getSQL());
         }
         // delete post
         $this->dc->qb()->delete($this->radix->getTable())->where('doc_id = :doc_id')->setParameter(':doc_id', $this->comment->doc_id)->execute();
         // purge reports
         $this->dc->qb()->delete($this->dc->p('reports'))->where('board_id = :board_id')->andWhere('doc_id = :doc_id')->setParameter(':board_id', $this->radix->id)->setParameter(':doc_id', $this->comment->doc_id)->execute();
         // clear cache
         $this->radix_coll->clearCache();
         // remove image file
         if (isset($this->media)) {
             $media_sql = $this->dc->qb()->select('COUNT(*)')->from($this->radix->getTable(), 't')->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->getSQL();
             $this->dc->qb()->update($this->radix->getTable('_images'))->set('total', '(' . $media_sql . ')')->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->execute();
             $has_image = $this->dc->qb()->select('total')->from($this->radix->getTable('_images'), 'ti')->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->execute()->fetch();
             if (!$has_image || !$has_image['total']) {
                 $media = new Media($this->getContext(), $this->bulk);
                 $media->delete();
             }
         }
         // if this is OP, delete replies too
         if ($this->comment->op) {
             // delete thread data
             $this->dc->qb()->delete($this->radix->getTable('_threads'))->where('thread_num = :thread_num')->setParameter(':thread_num', $this->comment->thread_num)->execute();
             // process each comment
             $comments = $this->dc->qb()->select('doc_id')->from($this->radix->getTable(), 'b')->where('thread_num = :thread_num')->setParameter(':thread_num', $this->comment->thread_num)->execute()->fetchAll();
             foreach ($comments as $comment) {
                 $post = Board::forge($this->getContext())->getPost()->setOptions('doc_id', $comment['doc_id'])->setRadix($this->radix)->getComments();
                 $post = current($post);
                 $post = new Comment($this->getContext(), $post);
                 $post->delete(null, true, true);
             }
         } else {
             // if this is not triggered by a thread deletion, update the thread table
             if ($thread === false && !$this->radix->archive) {
                 $time_last = '
                 (
                     COALESCE(GREATEST(
                         time_op,
                         (
                             SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr
                             WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum = 0
                         )
                     ), time_op)
                 )';
                 $time_bump = '
                 (
                     COALESCE(GREATEST(
                         time_op,
                         (
                             SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr
                             WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum = 0
                                 AND (email <> \'sage\' OR email IS NULL)
                         )
                     ), time_op)
                 )';
                 $time_ghost = '
                 (
                     SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr
                     WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum <> 0
                 )';
                 $time_ghost_bump = '
                 (
                     SELECT MAX(timestamp) FROM ' . $this->radix->getTable() . ' xr
                     WHERE thread_num = ' . $this->dc->getConnection()->quote($this->comment->thread_num) . ' AND subnum <> 0
                         AND (email <> \'sage\' OR email IS NULL)
                 )';
                 // update thread information
                 $this->dc->qb()->update($this->radix->getTable('_threads'))->set('time_last', $time_last)->set('time_bump', $time_bump)->set('time_ghost', $time_ghost)->set('time_ghost_bump', $time_ghost_bump)->set('time_last_modified', ':time')->set('nreplies', 'nreplies - 1')->set('nimages', $this->media === null ? 'nimages' : 'nimages - 1')->where('thread_num = :thread_num')->setParameter(':time', $this->getRadixTime())->setParameter(':thread_num', $this->comment->thread_num)->execute();
             }
         }
         $this->dc->getConnection()->commit();
         $this->clearCache();
         if ($thread === false) {
             $this->audit->log(Audit::AUDIT_DEL_POST, ['radix' => $this->radix->id, 'doc_id' => $this->comment->doc_id, 'thread_num' => $this->comment->thread_num, 'num' => $this->comment->num, 'subnum' => $this->comment->subnum]);
         }
     } catch (\Doctrine\DBAL\DBALException $e) {
         $this->logger->error('\\Foolz\\FoolFuuka\\Model\\CommentInsert: ' . $e->getMessage());
         $this->dc->getConnection()->rollBack();
         throw new CommentSendingDatabaseException(_i('Something went wrong when deleting the post in the database. Try again.'));
     }
     return $this;
 }
Exemplo n.º 8
0
 /**
  * Bans an image for a board or for all the boards
  *
  * @param  boolean  $global  False if the media should be banned only on current Radix, true if it should be banned on all the Radix and future ones
  */
 public function p_ban($global = false)
 {
     if ($global === false) {
         $this->dc->qb()->update($this->radix->getTable('_images'))->set('banned', 1)->where('media_id = :media_id')->setParameter(':media_id', $this->media->media_id)->execute();
         $this->delete(true, true, true);
         return;
     }
     $result = $this->dc->qb()->select('COUNT(*) as count')->from($this->radix->getTable('_images'), 'ri')->where('media_hash = :md5')->setParameter(':md5', $this->media->media_hash)->execute()->fetch();
     if (!$result['count']) {
         $this->dc->getConnection()->insert('banned_md5', ['md5' => $this->media->media_hash])->execute();
     }
     foreach ($this->radix_coll->getAll() as $radix) {
         try {
             $media = $this->media_factory->getByMediaHash($radix, $this->media->media_hash);
             $media = new Media($this->getContext(), CommentBulk::forge($this->radix, null, $media));
             $this->dc->qb()->update($radix->getTable('_images'))->set('banned', 1)->where('media_id = :media_id')->setParameter(':media_id', $media->media_id)->execute();
             $media->delete(true, true, true);
         } catch (MediaNotFoundException $e) {
             $this->dc->getConnection()->insert($radix->getTable('_images'), ['media_hash' => $this->media->media_hash, 'banned' => 1]);
         }
     }
 }
Exemplo n.º 9
0
 public function radix_search()
 {
     if ($this->getPost('submit_search_global')) {
         $this->radix = null;
     }
     $text = $this->getPost('text');
     if ($this->radix !== null && $this->getPost('submit_post')) {
         return $this->radix_post(str_replace(',', '_', $text));
     }
     $this->response = new StreamedResponse();
     // Check all allowed search modifiers and apply only these
     $modifiers = ['boards', 'subject', 'text', 'username', 'tripcode', 'email', 'filename', 'capcode', 'uid', 'image', 'deleted', 'ghost', 'type', 'filter', 'start', 'end', 'order', 'page'];
     if ($this->getAuth()->hasAccess('comment.see_ip')) {
         $modifiers[] = 'poster_ip';
         $modifiers[] = 'deletion_mode';
     }
     // GET -> URL Redirection to provide URL presentable for sharing links.
     if ($this->getPost()) {
         if ($this->radix !== null) {
             $redirect_url = [$this->radix->shortname, 'search'];
         } else {
             $redirect_url = ['_', 'search'];
         }
         foreach ($modifiers as $modifier) {
             if ($this->getPost($modifier)) {
                 if ($modifier === 'image') {
                     array_push($redirect_url, $modifier);
                     array_push($redirect_url, rawurlencode(Media::urlsafe_b64encode(Media::urlsafe_b64decode($this->getPost($modifier)))));
                 } elseif ($modifier === 'boards') {
                     if ($this->getPost('submit_search_global')) {
                     } elseif (count($this->getPost($modifier)) == 1) {
                         $boards = $this->getPost($modifier);
                         $redirect_url[0] = $boards[0];
                     } elseif (count($this->getPost($modifier)) > 1) {
                         $redirect_url[0] = '_';
                         // avoid setting this if we're just searching on all the boards
                         $sphinx_boards = [];
                         foreach ($this->radix_coll->getAll() as $k => $b) {
                             if ($b->sphinx) {
                                 $sphinx_boards[] = $b;
                             }
                         }
                         if (count($sphinx_boards) !== count($this->getPost($modifier))) {
                             array_push($redirect_url, $modifier);
                             array_push($redirect_url, rawurlencode(implode('.', $this->getPost($modifier))));
                         }
                     }
                 } else {
                     array_push($redirect_url, $modifier);
                     array_push($redirect_url, rawurlencode($this->getPost($modifier)));
                 }
             }
         }
         return new RedirectResponse($this->uri->create($redirect_url), 303);
     }
     $search = $this->uri->uri_to_assoc($this->request->getPathInfo(), 1, $modifiers);
     $this->param_manager->setParam('search', $search);
     // latest searches system
     if (!is_array($cookie_array = @json_decode($this->getCookie('search_latest_5'), true))) {
         $cookie_array = [];
     }
     // sanitize
     foreach ($cookie_array as $item) {
         // all subitems must be array, all must have 'board'
         if (!is_array($item) || !isset($item['board'])) {
             $cookie_array = [];
             break;
         }
     }
     $search_opts = array_filter($search);
     $search_opts['board'] = $this->radix !== null ? $this->radix->shortname : false;
     unset($search_opts['page']);
     // if it's already in the latest searches, remove the previous entry
     foreach ($cookie_array as $key => $item) {
         if ($item === $search_opts) {
             unset($cookie_array[$key]);
             break;
         }
     }
     // we don't want more than 5 entries for latest searches
     if (count($cookie_array) > 4) {
         array_pop($cookie_array);
     }
     array_unshift($cookie_array, $search_opts);
     $this->builder->getPartial('tools_search')->getParamManager()->setParam('latest_searches', $cookie_array);
     $this->response->headers->setCookie(new Cookie($this->getContext(), 'search_latest_5', json_encode($cookie_array), 60 * 60 * 24 * 30));
     foreach ($search as $key => $value) {
         if ($value !== null) {
             $search[$key] = trim(rawurldecode($value));
         }
     }
     if ($search['boards'] !== null) {
         $search['boards'] = explode('.', $search['boards']);
     }
     if ($search['image'] !== null) {
         $search['image'] = base64_encode(Media::urlsafe_b64decode($search['image']));
     }
     if ($this->getAuth()->hasAccess('comment.see_ip') && $search['poster_ip'] !== null) {
         if (!filter_var($search['poster_ip'], FILTER_VALIDATE_IP)) {
             return $this->error(_i('The poster IP you inserted is not a valid IP address.'));
         }
         $search['poster_ip'] = Inet::ptod($search['poster_ip']);
     }
     try {
         $board = Search::forge($this->getContext())->getSearch($search)->setRadix($this->radix)->setPage($search['page'] ? $search['page'] : 1);
         $board->getComments();
     } catch (\Foolz\Foolfuuka\Model\SearchException $e) {
         return $this->error($e->getMessage());
     } catch (\Foolz\Foolfuuka\Model\BoardException $e) {
         return $this->error($e->getMessage());
     } catch (\Foolz\SphinxQL\ConnectionException $e) {
         return $this->error($this->preferences->get('foolfuuka.sphinx.custom_message', 'It appears that the search engine is offline at the moment. Please try again later.'));
     }
     // Generate the $title with all search modifiers enabled.
     $title = [];
     if ($search['text']) {
         array_push($title, sprintf(_i('that contain &lsquo;%s&rsquo;'), e($search['text'])));
     }
     if ($search['subject']) {
         array_push($title, sprintf(_i('with the subject &lsquo;%s&rsquo;'), e($search['subject'])));
     }
     if ($search['username']) {
         array_push($title, sprintf(_i('with the username &lsquo;%s&rsquo;'), e($search['username'])));
     }
     if ($search['tripcode']) {
         array_push($title, sprintf(_i('with the tripcode &lsquo;%s&rsquo;'), e($search['tripcode'])));
     }
     if ($search['filename']) {
         array_push($title, sprintf(_i('with the filename &lsquo;%s&rsquo;'), e($search['filename'])));
     }
     if ($search['image']) {
         array_push($title, sprintf(_i('with the image hash &lsquo;%s&rsquo;'), e($search['image'])));
     }
     if ($search['deleted'] == 'deleted') {
         array_push($title, _i('that have been deleted'));
     }
     if ($search['deleted'] == 'not-deleted') {
         array_push($title, _i('that has not been deleted'));
     }
     if ($search['ghost'] == 'only') {
         array_push($title, _i('that are by ghosts'));
     }
     if ($search['ghost'] == 'none') {
         array_push($title, _i('that are not by ghosts'));
     }
     if ($search['type'] == 'sticky') {
         array_push($title, _i('that were stickied'));
     }
     if ($search['type'] == 'op') {
         array_push($title, _i('that are only OP posts'));
     }
     if ($search['type'] == 'posts') {
         array_push($title, _i('that are only non-OP posts'));
     }
     if ($search['filter'] == 'image') {
         array_push($title, _i('that do not contain images'));
     }
     if ($search['filter'] == 'text') {
         array_push($title, _i('that only contain images'));
     }
     if ($search['capcode'] == 'user') {
         array_push($title, _i('that were made by users'));
     }
     if ($search['capcode'] == 'mod') {
         array_push($title, _i('that were made by mods'));
     }
     if ($search['capcode'] == 'admin') {
         array_push($title, _i('that were made by admins'));
     }
     if ($search['start']) {
         array_push($title, sprintf(_i('posts after %s'), e($search['start'])));
     }
     if ($search['end']) {
         array_push($title, sprintf(_i('posts before %s'), e($search['end'])));
     }
     if ($search['order'] == 'asc') {
         array_push($title, _i('in ascending order'));
     }
     if (!empty($title)) {
         $title = sprintf(_i('Searching for posts %s.'), implode(' ' . _i('and') . ' ', $title));
     } else {
         $title = _i('Displaying all posts with no filters applied.');
     }
     if ($this->radix) {
         $this->builder->getProps()->addTitle($title);
     } else {
         $this->builder->getProps()->addTitle('Global Search &raquo; ' . $title);
     }
     if ($board->getSearchCount() > 5000) {
         $search_title = sprintf(_i('%s <small>Returning only first %d of %d results found.</small>', $title, $this->preferences->get('foolfuuka.sphinx.max_matches', 5000), $board->getSearchCount()));
     } else {
         $search_title = sprintf(_i('%s <small>%d results found.</small>', $title, $board->getSearchCount()));
     }
     $this->param_manager->setParam('section_title', $search_title);
     $main_partial = $this->builder->createPartial('body', 'board');
     $main_partial->getParamManager()->setParam('board', $board->getComments());
     $pagination = $search;
     unset($pagination['page']);
     $pagination_arr = [];
     $pagination_arr[] = $this->radix !== null ? $this->radix->shortname : '_';
     $pagination_arr[] = 'search';
     foreach ($pagination as $key => $item) {
         if ($item || $item === 0) {
             $pagination_arr[] = rawurlencode($key);
             if (is_array($item)) {
                 $item = implode('.', $item);
             }
             if ($key == 'poster_ip') {
                 $item = Inet::dtop($item);
             }
             $pagination_arr[] = rawurlencode($item);
         }
     }
     $pagination_arr[] = 'page';
     $this->param_manager->setParam('pagination', ['base_url' => $this->uri->create($pagination_arr), 'current_page' => $search['page'] ?: 1, 'total' => ceil($board->getCount() / 25)]);
     $this->param_manager->setParam('modifiers', ['post_show_board_name' => $this->radix === null, 'post_show_view_button' => true]);
     $this->profiler->logMem('Controller Chan $this', $this);
     $this->profiler->log('Controller Chan::search End');
     $this->response->setCallback(function () {
         $this->builder->stream();
     });
     return $this->response;
 }
Exemplo n.º 10
0
 /**
  * Returns the Comment by doc_id or the first Comment found with a matching media_id
  *
  * @return  null|\Foolz\FoolFuuka\Model\CommentBulk
  * @throws  \Foolz\FoolFuuka\Model\ReportMediaNotFoundException
  * @throws  \Foolz\FoolFuuka\Model\ReportCommentNotFoundException
  */
 public function p_getComment()
 {
     if ($this->media_id !== null) {
         // custom "get the first doc_id with the media"
         $doc_id_res = $this->dc->qb()->select('doc_id')->from($this->radix_coll->getById($this->board_id)->getTable(), 'a')->where('media_id = :media_id')->orderBy('timestamp', 'desc')->setParameter('media_id', $this->media_id)->execute()->fetch();
         if ($doc_id_res !== null) {
             $this->doc_id = $doc_id_res->doc_id;
         } else {
             $this->report_coll->delete($this->id);
             return null;
         }
     }
     try {
         $comments = Board::forge($this->getContext())->getPost()->setRadix($this->radix)->setOptions('doc_id', $this->doc_id)->getComments();
         $this->comment = current($comments);
     } catch (BoardException $e) {
         $this->report_coll->delete($this->id);
         return null;
     }
     return $this->comment;
 }
Exemplo n.º 11
0
 /**
  * Gets the search results
  *
  * @return  \Foolz\Foolfuuka\Model\Search  The current object
  * @throws  SearchEmptyResultException     If there's no results to display
  * @throws  SearchRequiresSphinxException  If the search submitted requires Sphinx to run
  * @throws  SearchSphinxOfflineException   If the Sphinx server is unreachable
  * @throws  SearchInvalidException         If the values of the search weren't compatible with the domain
  */
 protected function p_getSearchComments()
 {
     $this->profiler->log('Board::getSearchComments Start');
     extract($this->options);
     // set all empty fields to null
     $search_fields = ['boards', 'subject', 'text', 'username', 'tripcode', 'email', 'capcode', 'uid', 'poster_ip', 'filename', 'image', 'deleted', 'ghost', 'filter', 'type', 'start', 'end', 'results', 'order'];
     foreach ($search_fields as $field) {
         if (!isset($args[$field])) {
             $args[$field] = null;
         }
     }
     // populate an array containing all boards that would be searched
     $boards = [];
     if ($args['boards'] !== null) {
         foreach ($args['boards'] as $board) {
             $b = $this->radix_coll->getByShortname($board);
             if ($b) {
                 $boards[] = $b;
             }
         }
     }
     // search all boards if none selected
     if (count($boards) == 0) {
         $boards = $this->radix_coll->getAll();
     }
     // if image is set, get either the media_hash or media_id
     if ($args['image'] !== null) {
         if (substr($args['image'], -2) !== '==') {
             $args['image'] .= '==';
         }
         // if board is set, retrieve media_id
         if ($this->radix !== null) {
             try {
                 $media = $this->media_factory->getByMediaHash($this->radix, $args['image']);
             } catch (MediaNotFoundException $e) {
                 $this->comments_unsorted = [];
                 $this->comments = [];
                 $this->profiler->log('Board::getSearchComments Ended Prematurely');
                 throw new SearchEmptyResultException(_i('No results found.'));
             }
             $args['image'] = $media->media_id;
         }
     }
     if ($this->radix === null && !$this->preferences->get('foolfuuka.sphinx.global')) {
         // global search requires sphinx
         throw new SearchRequiresSphinxException(_i('Sorry, this action requires the Sphinx to be installed and running.'));
     } elseif ($this->radix === null && $this->preferences->get('foolfuuka.sphinx.global') || $this->radix !== null && $this->radix->sphinx) {
         // configure sphinx connection params
         $sphinx = explode(':', $this->preferences->get('foolfuuka.sphinx.listen'));
         $conn = new SphinxConnnection();
         $conn->setParams(['host' => $sphinx[0], 'port' => $sphinx[1], 'options' => [MYSQLI_OPT_CONNECT_TIMEOUT => 5]]);
         $conn->silenceConnectionWarning(true);
         // establish connection
         try {
             SphinxQL::forge($conn);
         } catch (\Foolz\SphinxQL\ConnectionException $e) {
             throw new SearchSphinxOfflineException(_i('The search backend is currently unavailable.'));
         }
         // determine if all boards will be used for search or not
         if ($this->radix == null) {
             $indexes = [];
             foreach ($boards as $radix) {
                 if (!$radix->sphinx) {
                     continue;
                 }
                 $indexes[] = $radix->shortname . '_ancient';
                 $indexes[] = $radix->shortname . '_main';
                 $indexes[] = $radix->shortname . '_delta';
             }
         } else {
             $indexes = [$this->radix->shortname . '_ancient', $this->radix->shortname . '_main', $this->radix->shortname . '_delta'];
         }
         // start search query
         $query = SphinxQL::forge()->select('id', 'board')->from($indexes);
         // parse search params
         if ($args['subject'] !== null) {
             $query->match('title', $args['subject']);
         }
         if ($args['text'] !== null) {
             if (mb_strlen($args['text'], 'utf-8') < 1) {
                 return [];
             }
             $query->match('comment', $args['text'], true);
         }
         if ($args['username'] !== null) {
             $query->match('name', $args['username']);
         }
         if ($args['tripcode'] !== null) {
             $query->match('trip', '"' . $args['tripcode'] . '"');
         }
         if ($args['email'] !== null) {
             $query->match('email', $args['email']);
         }
         if ($args['capcode'] !== null) {
             if ($args['capcode'] === 'user') {
                 $query->where('cap', ord('N'));
             } elseif ($args['capcode'] === 'mod') {
                 $query->where('cap', ord('M'));
             } elseif ($args['capcode'] === 'admin') {
                 $query->where('cap', ord('A'));
             } elseif ($args['capcode'] === 'dev') {
                 $query->where('cap', ord('D'));
             }
         }
         if ($args['uid'] !== null) {
             $query->match('pid', $args['uid']);
         }
         if ($this->getAuth()->hasAccess('comment.see_ip') && $args['poster_ip'] !== null) {
             $query->where('pip', (int) Inet::ptod($args['poster_ip']));
         }
         if ($args['filename'] !== null) {
             $query->match('media_filename', $args['filename']);
         }
         if ($args['image'] !== null) {
             if ($this->radix !== null) {
                 $query->where('mid', (int) $args['image']);
             } else {
                 $query->match('media_hash', '"' . $args['image'] . '"');
             }
         }
         if ($args['deleted'] !== null) {
             if ($args['deleted'] == 'deleted') {
                 $query->where('is_deleted', 1);
             }
             if ($args['deleted'] == 'not-deleted') {
                 $query->where('is_deleted', 0);
             }
         }
         if ($args['ghost'] !== null) {
             if ($args['ghost'] == 'only') {
                 $query->where('is_internal', 1);
             }
             if ($args['ghost'] == 'none') {
                 $query->where('is_internal', 0);
             }
         }
         if ($args['filter'] !== null) {
             if ($args['filter'] == 'image') {
                 $query->where('has_image', 0);
             }
             if ($args['filter'] == 'text') {
                 $query->where('has_image', 1);
             }
         }
         if ($args['type'] !== null) {
             if ($args['type'] == 'sticky') {
                 $query->where('is_sticky', 1);
             }
             if ($args['type'] == 'op') {
                 $query->where('is_op', 1);
             }
             if ($args['type'] == 'posts') {
                 $query->where('is_op', 0);
             }
         }
         if ($args['start'] !== null) {
             $query->where('timestamp', '>=', intval(strtotime($args['start'])));
         }
         if ($args['end'] !== null) {
             $query->where('timestamp', '<=', intval(strtotime($args['end'])));
         }
         if ($args['results'] !== null) {
             if ($args['results'] == 'op') {
                 $query->groupBy('thread_num');
                 $query->withinGroupOrderBy('is_op', 'desc');
             }
             if ($args['results'] == 'posts') {
                 $query->where('is_op', 0);
             }
         }
         if ($args['order'] !== null && $args['order'] == 'asc') {
             $query->orderBy('timestamp', 'ASC');
         } else {
             $query->orderBy('timestamp', 'DESC');
         }
         $max_matches = $this->preferences->get('foolfuuka.sphinx.max_matches', 5000);
         // set sphinx options
         $query->limit($limit)->offset($page * $limit - $limit >= $max_matches ? $max_matches - 1 : $page * $limit - $limit)->option('max_matches', (int) $max_matches)->option('reverse_scan', $args['order'] === 'asc' ? 0 : 1);
         // submit query
         try {
             $search = $query->execute();
         } catch (\Foolz\SphinxQL\DatabaseException $e) {
             $this->logger->error('Search Error: ' . $e->getMessage());
             throw new SearchInvalidException(_i('The search backend returned an error.'));
         }
         // no results found
         if (!count($search)) {
             $this->comments_unsorted = [];
             $this->comments = [];
             throw new SearchEmptyResultException(_i('No results found.'));
         }
         $sphinx_meta = Helper::pairsToAssoc(Helper::create($conn)->showMeta()->execute());
         $this->total_count = $sphinx_meta['total'];
         $this->total_found = $sphinx_meta['total_found'];
         // populate sql array for full records
         $sql = [];
         foreach ($search as $doc => $result) {
             $board = $this->radix_coll->getById($result['board']);
             $sql[] = $this->dc->qb()->select('*, ' . $result['board'] . ' AS board_id')->from($board->getTable(), 'r')->leftJoin('r', $board->getTable('_images'), 'mg', 'mg.media_id = r.media_id')->where('doc_id = ' . $this->dc->getConnection()->quote($result['id']))->getSQL();
         }
         $result = $this->dc->getConnection()->executeQuery(implode(' UNION ', $sql))->fetchAll();
     } else {
         // this is not implemented yet, would require some sort of MySQL search
         throw new SearchRequiresSphinxException(_i('Sorry, this board does not have search enabled.'));
     }
     // no results found IN DATABASE, but we might still get a search count from Sphinx
     if (!count($result)) {
         $this->comments_unsorted = [];
         $this->comments = [];
     } else {
         // process results
         foreach ($result as $key => $row) {
             $board = $this->radix !== null ? $this->radix : $this->radix_coll->getById($row['board_id']);
             $bulk = new CommentBulk();
             $bulk->import($row, $board);
             $this->comments_unsorted[] = $bulk;
             unset($result[$key]);
         }
     }
     $this->comments[0]['posts'] = $this->comments_unsorted;
     return $this;
 }
Exemplo n.º 12
0
 /**
  * Gets the search results
  *
  * @return  \Foolz\FoolFuuka\Model\Search  The current object
  * @throws  SearchEmptyResultException     If there's no results to display
  * @throws  SearchRequiresSphinxException  If the search submitted requires Sphinx to run
  * @throws  SearchSphinxOfflineException   If the Sphinx server is unreachable
  * @throws  SearchInvalidException         If the values of the search weren't compatible with the domain
  */
 protected function p_getResults()
 {
     $this->profiler->log('Search::getResults Start');
     extract($this->options);
     $boards = [];
     $input = $this->getUserInput();
     if ($this->radix !== null) {
         $boards[] = $this->radix;
     } elseif ($input['boards'] !== null) {
         foreach ($input['boards'] as $board) {
             $b = $this->radix_coll->getByShortname($board);
             if ($b) {
                 $boards[] = $b;
             }
         }
     }
     // search all boards if none selected
     if (count($boards) == 0) {
         $boards = $this->radix_coll->getAll();
     }
     // if image is set, get either the media_hash or media_id
     if ($input['image'] !== null && substr($input['image'], -2) !== '==') {
         $input['image'] .= '==';
     }
     if ($this->radix === null && !$this->preferences->get('foolfuuka.sphinx.global')) {
         throw new SearchRequiresSphinxException(_i('Sorry, the global search function has not been enabled.'));
     }
     if ($this->radix !== null && !$this->radix->sphinx) {
         throw new SearchRequiresSphinxException(_i('Sorry, this board does not have search enabled.'));
     }
     $sphinx = explode(':', $this->preferences->get('foolfuuka.sphinx.listen'));
     $conn = new SphinxConnnection();
     $conn->setParams(['host' => $sphinx[0], 'port' => $sphinx[1], 'options' => [MYSQLI_OPT_CONNECT_TIMEOUT => 5]]);
     $indices = [];
     foreach ($boards as $radix) {
         if (!$radix->sphinx) {
             continue;
         }
         $indices[] = $radix->shortname . '_ancient';
         $indices[] = $radix->shortname . '_main';
         $indices[] = $radix->shortname . '_delta';
     }
     // establish connection
     try {
         $query = SphinxQL::create($conn)->select('id', 'board', 'tnum')->from($indices)->setFullEscapeChars(['\\', '(', ')', '|', '-', '!', '@', '%', '~', '"', '&', '/', '^', '$', '='])->setHalfEscapeChars(['\\', '(', ')', '!', '@', '%', '~', '&', '/', '^', '$', '=']);
     } catch (\Foolz\SphinxQL\Exception\ConnectionException $e) {
         throw new SearchSphinxOfflineException($this->preferences->get('foolfuuka.sphinx.custom_message', _i('The search backend is currently unavailable.')));
     }
     // process user input
     if ($input['subject'] !== null) {
         $query->match('title', $input['subject']);
     }
     if ($input['text'] !== null) {
         if (mb_strlen($input['text'], 'utf-8') < 1) {
             return [];
         }
         $query->match('comment', $input['text'], true);
     }
     if ($input['username'] !== null) {
         $query->match('name', $input['username']);
     }
     if ($input['tripcode'] !== null) {
         $query->match('trip', '"' . $input['tripcode'] . '"');
     }
     if ($input['email'] !== null) {
         $query->match('email', $input['email']);
     }
     if ($input['capcode'] !== null) {
         switch ($input['capcode']) {
             case 'user':
                 $query->where('cap', ord('N'));
                 break;
             case 'mod':
                 $query->where('cap', ord('M'));
                 break;
             case 'dev':
                 $query->where('cap', ord('D'));
                 break;
             case 'admin':
                 $query->where('cap', ord('A'));
                 break;
         }
     }
     if ($input['uid'] !== null) {
         $query->match('pid', $input['uid']);
     }
     if ($input['country'] !== null) {
         $query->match('country', $input['country'], true);
     }
     if ($this->getAuth()->hasAccess('comment.see_ip') && $input['poster_ip'] !== null) {
         $query->where('pip', (int) Inet::ptod($input['poster_ip']));
     }
     if ($input['filename'] !== null) {
         $query->match('media_filename', $input['filename']);
     }
     if ($input['image'] !== null) {
         $query->match('media_hash', '"' . $input['image'] . '"');
     }
     if ($input['deleted'] !== null) {
         switch ($input['deleted']) {
             case 'deleted':
                 $query->where('is_deleted', 1);
                 break;
             case 'not-deleted':
                 $query->where('is_deleted', 0);
                 break;
         }
     }
     if ($input['ghost'] !== null) {
         switch ($input['ghost']) {
             case 'only':
                 $query->where('is_internal', 1);
                 break;
             case 'none':
                 $query->where('is_internal', 0);
                 break;
         }
     }
     if ($input['filter'] !== null) {
         switch ($input['filter']) {
             case 'image':
                 $query->where('has_image', 0);
                 break;
             case 'text':
                 $query->where('has_image', 1);
                 break;
         }
     }
     if ($input['type'] !== null) {
         switch ($input['type']) {
             case 'sticky':
                 $query->where('is_sticky', 1);
                 break;
             case 'op':
                 $query->where('is_op', 1);
                 break;
             case 'posts':
                 $query->where('is_op', 0);
                 break;
         }
     }
     if ($input['start'] !== null) {
         $query->where('timestamp', '>=', intval(strtotime($input['start'])));
     }
     if ($input['end'] !== null) {
         $query->where('timestamp', '<=', intval(strtotime($input['end'])));
     }
     if ($input['results'] !== null && $input['results'] == 'thread') {
         $query->groupBy('tnum');
         $query->withinGroupOrderBy('is_op', 'desc');
     }
     if ($input['order'] !== null && $input['order'] == 'asc') {
         $query->orderBy('timestamp', 'ASC');
     } else {
         $query->orderBy('timestamp', 'DESC');
     }
     $max_matches = $this->preferences->get('foolfuuka.sphinx.max_matches', 5000);
     // set sphinx options
     $query->limit($limit)->offset($page * $limit - $limit >= $max_matches ? $max_matches - 1 : $page * $limit - $limit)->option('max_matches', (int) $max_matches)->option('reverse_scan', $input['order'] === 'asc' ? 0 : 1);
     // submit query
     try {
         $this->profiler->log('Start: SphinxQL: ' . $query->compile()->getCompiled());
         $search = $query->execute();
         $this->profiler->log('Stop: SphinxQL');
     } catch (\Foolz\SphinxQL\Exception\DatabaseException $e) {
         $this->logger->error('Search Error: ' . $e->getMessage());
         throw new SearchInvalidException(_i('The search backend returned an error.'));
     }
     // no results found
     if (!count($search)) {
         $this->comments_unsorted = [];
         $this->comments = [];
         throw new SearchEmptyResultException(_i('No results found.'));
     }
     $sphinx_meta = Helper::pairsToAssoc(Helper::create($conn)->showMeta()->execute());
     $this->total_count = $sphinx_meta['total'];
     $this->total_found = $sphinx_meta['total_found'];
     // populate sql array for full records
     $sql = [];
     foreach ($search as $doc => $result) {
         $board = $this->radix_coll->getById($result['board']);
         if ($input['results'] !== null && $input['results'] == 'thread') {
             $post = 'num = ' . $this->dc->getConnection()->quote($result['tnum']) . ' AND subnum = 0';
         } else {
             $post = 'doc_id = ' . $this->dc->getConnection()->quote($result['id']);
         }
         $sql[] = $this->dc->qb()->select('*, ' . $result['board'] . ' AS board_id')->from($board->getTable(), 'r')->leftJoin('r', $board->getTable('_images'), 'mg', 'mg.media_id = r.media_id')->where($post)->getSQL();
     }
     $result = $this->dc->getConnection()->executeQuery(implode(' UNION ', $sql))->fetchAll();
     // no results found IN DATABASE, but we might still get a search count from Sphinx
     if (!count($result)) {
         $this->comments_unsorted = [];
         $this->comments = [];
     } else {
         // process results
         foreach ($result as $key => $row) {
             $board = $this->radix !== null ? $this->radix : $this->radix_coll->getById($row['board_id']);
             $bulk = new CommentBulk();
             $bulk->import($row, $board);
             $this->comments_unsorted[] = $bulk;
             unset($result[$key]);
         }
     }
     $this->comments[0]['posts'] = $this->comments_unsorted;
     return $this;
 }
Exemplo n.º 13
0
 public function getMessage()
 {
     if ($this->board_id == 0) {
         $message = _i('It looks like you were banned on all boards.');
     } else {
         $message = _i('It looks like you were banned on /' . $this->radix_coll->getById($this->board_id)->shortname . '/.');
     }
     if ($this->length) {
         $message .= ' ' . _i('This ban will last until:') . ' ' . date(DATE_COOKIE, $this->start + $this->length) . '.';
     } else {
         $message .= ' ' . _i('This ban will last forever.');
     }
     if ($this->reason) {
         $message .= ' ' . _i('The reason for this ban is:') . ' «' . $this->reason . '».';
     }
     if ($this->appeal_status == Ban::APPEAL_NONE) {
         $message .= ' ' . _i('If you wish to appeal your ban, go to the "%s" page.', '<a href="' . $this->uri->create('_/appeal_ban') . '">' . _i('appeal ban') . '</a>');
     } elseif ($this->appeal_status == Ban::APPEAL_PENDING) {
         $message .= ' ' . _i('Your appeal is pending.');
     }
     return $message;
 }