Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück

Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_admin.php

Zuletzt modifiziert: 09.10.2024, 12:51 - Dateigröße: 87.15 KiB


 0001  <?php
 0002  /**
 0003  *
 0004  * This file is part of the phpBB Forum Software package.
 0005  *
 0006  * @copyright (c) phpBB Limited <https://www.phpbb.com>
 0007  * @license GNU General Public License, version 2 (GPL-2.0)
 0008  *
 0009  * For full copyright and license information, please see
 0010  * the docs/CREDITS.txt file.
 0011  *
 0012  */
 0013   
 0014  /**
 0015  * @ignore
 0016  */
 0017  if (!defined('IN_PHPBB'))
 0018  {
 0019      exit;
 0020  }
 0021   
 0022  /**
 0023  * Recalculate Nested Sets
 0024  *
 0025  * @param int    $new_id    first left_id (should start with 1)
 0026  * @param string    $pkey    primary key-column (containing the id for the parent_id of the children)
 0027  * @param string    $table    constant or fullname of the table
 0028  * @param int    $parent_id parent_id of the current set (default = 0)
 0029  * @param array    $where    contains strings to compare closer on the where statement (additional)
 0030  */
 0031  function recalc_nested_sets(&$new_id, $pkey, $table, $parent_id = 0, $where = array())
 0032  {
 0033      global $db;
 0034   
 0035      $sql = 'SELECT *
 0036          FROM ' . $table . '
 0037          WHERE parent_id = ' . (int) $parent_id .
 0038          ((!empty($where)) ? ' AND ' . implode(' AND ', $where) : '') . '
 0039          ORDER BY left_id ASC';
 0040      $result = $db->sql_query($sql);
 0041      while ($row = $db->sql_fetchrow($result))
 0042      {
 0043          // First we update the left_id for this module
 0044          if ($row['left_id'] != $new_id)
 0045          {
 0046              $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('left_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
 0047          }
 0048          $new_id++;
 0049   
 0050          // Then we go through any children and update their left/right id's
 0051          recalc_nested_sets($new_id, $pkey, $table, $row[$pkey], $where);
 0052   
 0053          // Then we come back and update the right_id for this module
 0054          if ($row['right_id'] != $new_id)
 0055          {
 0056              $db->sql_query('UPDATE ' . $table . ' SET ' . $db->sql_build_array('UPDATE', array('right_id' => $new_id)) . " WHERE $pkey = {$row[$pkey]}");
 0057          }
 0058          $new_id++;
 0059      }
 0060      $db->sql_freeresult($result);
 0061  }
 0062   
 0063  /**
 0064  * Simple version of jumpbox, just lists authed forums
 0065  */
 0066  function make_forum_select($select_id = false, $ignore_id = false, $ignore_acl = false, $ignore_nonpost = false, $ignore_emptycat = true, $only_acl_post = false, $return_array = false)
 0067  {
 0068      global $db, $user, $auth;
 0069   
 0070      // This query is identical to the jumpbox one
 0071      $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, forum_flags, forum_options, left_id, right_id
 0072          FROM ' . FORUMS_TABLE . '
 0073          ORDER BY left_id ASC';
 0074      $result = $db->sql_query($sql, 600);
 0075   
 0076      $right = 0;
 0077      $padding_store = array('0' => '');
 0078      $padding = '';
 0079      $forum_list = ($return_array) ? array() : '';
 0080   
 0081      // Sometimes it could happen that forums will be displayed here not be displayed within the index page
 0082      // This is the result of forums not displayed at index, having list permissions and a parent of a forum with no permissions.
 0083      // If this happens, the padding could be "broken"
 0084   
 0085      while ($row = $db->sql_fetchrow($result))
 0086      {
 0087          if ($row['left_id'] < $right)
 0088          {
 0089              $padding .= '&nbsp; &nbsp;';
 0090              $padding_store[$row['parent_id']] = $padding;
 0091          }
 0092          else if ($row['left_id'] > $right + 1)
 0093          {
 0094              $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : '';
 0095          }
 0096   
 0097          $right = $row['right_id'];
 0098          $disabled = false;
 0099   
 0100          if (!$ignore_acl && $auth->acl_gets(array('f_list', 'a_forum', 'a_forumadd', 'a_forumdel'), $row['forum_id']))
 0101          {
 0102              if ($only_acl_post && !$auth->acl_get('f_post', $row['forum_id']) || (!$auth->acl_get('m_approve', $row['forum_id']) && !$auth->acl_get('f_noapprove', $row['forum_id'])))
 0103              {
 0104                  $disabled = true;
 0105              }
 0106          }
 0107          else if (!$ignore_acl)
 0108          {
 0109              continue;
 0110          }
 0111   
 0112          if (
 0113              ((is_array($ignore_id) && in_array($row['forum_id'], $ignore_id)) || $row['forum_id'] == $ignore_id)
 0114              ||
 0115              // Non-postable forum with no subforums, don't display
 0116              ($row['forum_type'] == FORUM_CAT && ($row['left_id'] + 1 == $row['right_id']) && $ignore_emptycat)
 0117              ||
 0118              ($row['forum_type'] != FORUM_POST && $ignore_nonpost)
 0119              )
 0120          {
 0121              $disabled = true;
 0122          }
 0123   
 0124          if ($return_array)
 0125          {
 0126              // Include some more information...
 0127              $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? true : false) : (($row['forum_id'] == $select_id) ? true : false);
 0128              $forum_list[$row['forum_id']] = array_merge(array('padding' => $padding, 'selected' => ($selected && !$disabled), 'disabled' => $disabled), $row);
 0129          }
 0130          else
 0131          {
 0132              $selected = (is_array($select_id)) ? ((in_array($row['forum_id'], $select_id)) ? ' selected="selected"' : '') : (($row['forum_id'] == $select_id) ? ' selected="selected"' : '');
 0133              $forum_list .= '<option value="' . $row['forum_id'] . '"' . (($disabled) ? ' disabled="disabled" class="disabled-option"' : $selected) . '>' . $padding . $row['forum_name'] . '</option>';
 0134          }
 0135      }
 0136      $db->sql_freeresult($result);
 0137      unset($padding_store);
 0138   
 0139      return $forum_list;
 0140  }
 0141   
 0142  /**
 0143  * Generate size select options
 0144  */
 0145  function size_select_options($size_compare)
 0146  {
 0147      global $user;
 0148   
 0149      $size_types_text = array($user->lang['BYTES'], $user->lang['KIB'], $user->lang['MIB']);
 0150      $size_types = array('b', 'kb', 'mb');
 0151   
 0152      $s_size_options = '';
 0153   
 0154      for ($i = 0, $size = sizeof($size_types_text); $i < $size; $i++)
 0155      {
 0156          $selected = ($size_compare == $size_types[$i]) ? ' selected="selected"' : '';
 0157          $s_size_options .= '<option value="' . $size_types[$i] . '"' . $selected . '>' . $size_types_text[$i] . '</option>';
 0158      }
 0159   
 0160      return $s_size_options;
 0161  }
 0162   
 0163  /**
 0164  * Generate list of groups (option fields without select)
 0165  *
 0166  * @param int $group_id The default group id to mark as selected
 0167  * @param array $exclude_ids The group ids to exclude from the list, false (default) if you whish to exclude no id
 0168  * @param int $manage_founder If set to false (default) all groups are returned, if 0 only those groups returned not being managed by founders only, if 1 only those groups returned managed by founders only.
 0169  *
 0170  * @return string The list of options.
 0171  */
 0172  function group_select_options($group_id, $exclude_ids = false, $manage_founder = false)
 0173  {
 0174      global $db, $user, $config;
 0175   
 0176      $exclude_sql = ($exclude_ids !== false && sizeof($exclude_ids)) ? 'WHERE ' . $db->sql_in_set('group_id', array_map('intval', $exclude_ids), true) : '';
 0177      $sql_and = (!$config['coppa_enable']) ? (($exclude_sql) ? ' AND ' : ' WHERE ') . "group_name <> 'REGISTERED_COPPA'" : '';
 0178      $sql_founder = ($manage_founder !== false) ? (($exclude_sql || $sql_and) ? ' AND ' : ' WHERE ') . 'group_founder_manage = ' . (int) $manage_founder : '';
 0179   
 0180      $sql = 'SELECT group_id, group_name, group_type
 0181          FROM ' . GROUPS_TABLE . "
 0182          $exclude_sql
 0183          $sql_and
 0184          $sql_founder
 0185          ORDER BY group_type DESC, group_name ASC";
 0186      $result = $db->sql_query($sql);
 0187   
 0188      $s_group_options = '';
 0189      while ($row = $db->sql_fetchrow($result))
 0190      {
 0191          $selected = ($row['group_id'] == $group_id) ? ' selected="selected"' : '';
 0192          $s_group_options .= '<option' . (($row['group_type'] == GROUP_SPECIAL) ? ' class="sep"' : '') . ' value="' . $row['group_id'] . '"' . $selected . '>' . (($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name']) . '</option>';
 0193      }
 0194      $db->sql_freeresult($result);
 0195   
 0196      return $s_group_options;
 0197  }
 0198   
 0199  /**
 0200  * Obtain authed forums list
 0201  */
 0202  function get_forum_list($acl_list = 'f_list', $id_only = true, $postable_only = false, $no_cache = false)
 0203  {
 0204      global $db, $auth;
 0205      static $forum_rows;
 0206   
 0207      if (!isset($forum_rows))
 0208      {
 0209          // This query is identical to the jumpbox one
 0210          $expire_time = ($no_cache) ? 0 : 600;
 0211   
 0212          $sql = 'SELECT forum_id, forum_name, parent_id, forum_type, left_id, right_id
 0213              FROM ' . FORUMS_TABLE . '
 0214              ORDER BY left_id ASC';
 0215          $result = $db->sql_query($sql, $expire_time);
 0216   
 0217          $forum_rows = array();
 0218   
 0219          $right = $padding = 0;
 0220          $padding_store = array('0' => 0);
 0221   
 0222          while ($row = $db->sql_fetchrow($result))
 0223          {
 0224              if ($row['left_id'] < $right)
 0225              {
 0226                  $padding++;
 0227                  $padding_store[$row['parent_id']] = $padding;
 0228              }
 0229              else if ($row['left_id'] > $right + 1)
 0230              {
 0231                  // Ok, if the $padding_store for this parent is empty there is something wrong. For now we will skip over it.
 0232                  // @todo digging deep to find out "how" this can happen.
 0233                  $padding = (isset($padding_store[$row['parent_id']])) ? $padding_store[$row['parent_id']] : $padding;
 0234              }
 0235   
 0236              $right = $row['right_id'];
 0237              $row['padding'] = $padding;
 0238   
 0239              $forum_rows[] = $row;
 0240          }
 0241          $db->sql_freeresult($result);
 0242          unset($padding_store);
 0243      }
 0244   
 0245      $rowset = array();
 0246      foreach ($forum_rows as $row)
 0247      {
 0248          if ($postable_only && $row['forum_type'] != FORUM_POST)
 0249          {
 0250              continue;
 0251          }
 0252   
 0253          if ($acl_list == '' || ($acl_list != '' && $auth->acl_gets($acl_list, $row['forum_id'])))
 0254          {
 0255              $rowset[] = ($id_only) ? (int) $row['forum_id'] : $row;
 0256          }
 0257      }
 0258   
 0259      return $rowset;
 0260  }
 0261   
 0262  /**
 0263  * Get forum branch
 0264  */
 0265  function get_forum_branch($forum_id, $type = 'all', $order = 'descending', $include_forum = true)
 0266  {
 0267      global $db;
 0268   
 0269      switch ($type)
 0270      {
 0271          case 'parents':
 0272              $condition = 'f1.left_id BETWEEN f2.left_id AND f2.right_id';
 0273          break;
 0274   
 0275          case 'children':
 0276              $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id';
 0277          break;
 0278   
 0279          default:
 0280              $condition = 'f2.left_id BETWEEN f1.left_id AND f1.right_id OR f1.left_id BETWEEN f2.left_id AND f2.right_id';
 0281          break;
 0282      }
 0283   
 0284      $rows = array();
 0285   
 0286      $sql = 'SELECT f2.*
 0287          FROM ' . FORUMS_TABLE . ' f1
 0288          LEFT JOIN ' . FORUMS_TABLE . " f2 ON ($condition)
 0289          WHERE f1.forum_id = $forum_id
 0290          ORDER BY f2.left_id " . (($order == 'descending') ? 'ASC' : 'DESC');
 0291      $result = $db->sql_query($sql);
 0292   
 0293      while ($row = $db->sql_fetchrow($result))
 0294      {
 0295          if (!$include_forum && $row['forum_id'] == $forum_id)
 0296          {
 0297              continue;
 0298          }
 0299   
 0300          $rows[] = $row;
 0301      }
 0302      $db->sql_freeresult($result);
 0303   
 0304      return $rows;
 0305  }
 0306   
 0307  /**
 0308  * Copies permissions from one forum to others
 0309  *
 0310  * @param int    $src_forum_id        The source forum we want to copy permissions from
 0311  * @param array    $dest_forum_ids        The destination forum(s) we want to copy to
 0312  * @param bool    $clear_dest_perms    True if destination permissions should be deleted
 0313  * @param bool    $add_log            True if log entry should be added
 0314  *
 0315  * @return bool                        False on error
 0316  */
 0317  function copy_forum_permissions($src_forum_id, $dest_forum_ids, $clear_dest_perms = true, $add_log = true)
 0318  {
 0319      global $db;
 0320   
 0321      // Only one forum id specified
 0322      if (!is_array($dest_forum_ids))
 0323      {
 0324          $dest_forum_ids = array($dest_forum_ids);
 0325      }
 0326   
 0327      // Make sure forum ids are integers
 0328      $src_forum_id = (int) $src_forum_id;
 0329      $dest_forum_ids = array_map('intval', $dest_forum_ids);
 0330   
 0331      // No source forum or no destination forums specified
 0332      if (empty($src_forum_id) || empty($dest_forum_ids))
 0333      {
 0334          return false;
 0335      }
 0336   
 0337      // Check if source forum exists
 0338      $sql = 'SELECT forum_name
 0339          FROM ' . FORUMS_TABLE . '
 0340          WHERE forum_id = ' . $src_forum_id;
 0341      $result = $db->sql_query($sql);
 0342      $src_forum_name = $db->sql_fetchfield('forum_name');
 0343      $db->sql_freeresult($result);
 0344   
 0345      // Source forum doesn't exist
 0346      if (empty($src_forum_name))
 0347      {
 0348          return false;
 0349      }
 0350   
 0351      // Check if destination forums exists
 0352      $sql = 'SELECT forum_id, forum_name
 0353          FROM ' . FORUMS_TABLE . '
 0354          WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 0355      $result = $db->sql_query($sql);
 0356   
 0357      $dest_forum_ids = $dest_forum_names = array();
 0358      while ($row = $db->sql_fetchrow($result))
 0359      {
 0360          $dest_forum_ids[]    = (int) $row['forum_id'];
 0361          $dest_forum_names[]    = $row['forum_name'];
 0362      }
 0363      $db->sql_freeresult($result);
 0364   
 0365      // No destination forum exists
 0366      if (empty($dest_forum_ids))
 0367      {
 0368          return false;
 0369      }
 0370   
 0371      // From the mysql documentation:
 0372      // Prior to MySQL 4.0.14, the target table of the INSERT statement cannot appear
 0373      // in the FROM clause of the SELECT part of the query. This limitation is lifted in 4.0.14.
 0374      // Due to this we stay on the safe side if we do the insertion "the manual way"
 0375   
 0376      // Rowsets we're going to insert
 0377      $users_sql_ary = $groups_sql_ary = array();
 0378   
 0379      // Query acl users table for source forum data
 0380      $sql = 'SELECT user_id, auth_option_id, auth_role_id, auth_setting
 0381          FROM ' . ACL_USERS_TABLE . '
 0382          WHERE forum_id = ' . $src_forum_id;
 0383      $result = $db->sql_query($sql);
 0384   
 0385      while ($row = $db->sql_fetchrow($result))
 0386      {
 0387          $row = array(
 0388              'user_id'            => (int) $row['user_id'],
 0389              'auth_option_id'    => (int) $row['auth_option_id'],
 0390              'auth_role_id'        => (int) $row['auth_role_id'],
 0391              'auth_setting'        => (int) $row['auth_setting'],
 0392          );
 0393   
 0394          foreach ($dest_forum_ids as $dest_forum_id)
 0395          {
 0396              $users_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
 0397          }
 0398      }
 0399      $db->sql_freeresult($result);
 0400   
 0401      // Query acl groups table for source forum data
 0402      $sql = 'SELECT group_id, auth_option_id, auth_role_id, auth_setting
 0403          FROM ' . ACL_GROUPS_TABLE . '
 0404          WHERE forum_id = ' . $src_forum_id;
 0405      $result = $db->sql_query($sql);
 0406   
 0407      while ($row = $db->sql_fetchrow($result))
 0408      {
 0409          $row = array(
 0410              'group_id'            => (int) $row['group_id'],
 0411              'auth_option_id'    => (int) $row['auth_option_id'],
 0412              'auth_role_id'        => (int) $row['auth_role_id'],
 0413              'auth_setting'        => (int) $row['auth_setting'],
 0414          );
 0415   
 0416          foreach ($dest_forum_ids as $dest_forum_id)
 0417          {
 0418              $groups_sql_ary[] = $row + array('forum_id' => $dest_forum_id);
 0419          }
 0420      }
 0421      $db->sql_freeresult($result);
 0422   
 0423      $db->sql_transaction('begin');
 0424   
 0425      // Clear current permissions of destination forums
 0426      if ($clear_dest_perms)
 0427      {
 0428          $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
 0429              WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 0430          $db->sql_query($sql);
 0431   
 0432          $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
 0433              WHERE ' . $db->sql_in_set('forum_id', $dest_forum_ids);
 0434          $db->sql_query($sql);
 0435      }
 0436   
 0437      $db->sql_multi_insert(ACL_USERS_TABLE, $users_sql_ary);
 0438      $db->sql_multi_insert(ACL_GROUPS_TABLE, $groups_sql_ary);
 0439   
 0440      if ($add_log)
 0441      {
 0442          add_log('admin', 'LOG_FORUM_COPIED_PERMISSIONS', $src_forum_name, implode(', ', $dest_forum_names));
 0443      }
 0444   
 0445      $db->sql_transaction('commit');
 0446   
 0447      return true;
 0448  }
 0449   
 0450  /**
 0451  * Get physical file listing
 0452  */
 0453  function filelist($rootdir, $dir = '', $type = 'gif|jpg|jpeg|png')
 0454  {
 0455      $matches = array($dir => array());
 0456   
 0457      // Remove initial / if present
 0458      $rootdir = (substr($rootdir, 0, 1) == '/') ? substr($rootdir, 1) : $rootdir;
 0459      // Add closing / if not present
 0460      $rootdir = ($rootdir && substr($rootdir, -1) != '/') ? $rootdir . '/' : $rootdir;
 0461   
 0462      // Remove initial / if present
 0463      $dir = (substr($dir, 0, 1) == '/') ? substr($dir, 1) : $dir;
 0464      // Add closing / if not present
 0465      $dir = ($dir && substr($dir, -1) != '/') ? $dir . '/' : $dir;
 0466   
 0467      if (!is_dir($rootdir . $dir))
 0468      {
 0469          return $matches;
 0470      }
 0471   
 0472      $dh = @opendir($rootdir . $dir);
 0473   
 0474      if (!$dh)
 0475      {
 0476          return $matches;
 0477      }
 0478   
 0479      while (($fname = readdir($dh)) !== false)
 0480      {
 0481          if (is_file("$rootdir$dir$fname"))
 0482          {
 0483              if (filesize("$rootdir$dir$fname") && preg_match('#\.' . $type . '$#i', $fname))
 0484              {
 0485                  $matches[$dir][] = $fname;
 0486              }
 0487          }
 0488          else if ($fname[0] != '.' && is_dir("$rootdir$dir$fname"))
 0489          {
 0490              $matches += filelist($rootdir, "$dir$fname", $type);
 0491          }
 0492      }
 0493      closedir($dh);
 0494   
 0495      return $matches;
 0496  }
 0497   
 0498  /**
 0499  * Move topic(s)
 0500  */
 0501  function move_topics($topic_ids, $forum_id, $auto_sync = true)
 0502  {
 0503      global $db;
 0504   
 0505      if (empty($topic_ids))
 0506      {
 0507          return;
 0508      }
 0509   
 0510      $forum_ids = array($forum_id);
 0511   
 0512      if (!is_array($topic_ids))
 0513      {
 0514          $topic_ids = array($topic_ids);
 0515      }
 0516   
 0517      $sql = 'DELETE FROM ' . TOPICS_TABLE . '
 0518          WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids) . '
 0519              AND forum_id = ' . $forum_id;
 0520      $db->sql_query($sql);
 0521   
 0522      if ($auto_sync)
 0523      {
 0524          $sql = 'SELECT DISTINCT forum_id
 0525              FROM ' . TOPICS_TABLE . '
 0526              WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 0527          $result = $db->sql_query($sql);
 0528   
 0529          while ($row = $db->sql_fetchrow($result))
 0530          {
 0531              $forum_ids[] = $row['forum_id'];
 0532          }
 0533          $db->sql_freeresult($result);
 0534      }
 0535   
 0536      $table_ary = array(TOPICS_TABLE, POSTS_TABLE, LOG_TABLE, DRAFTS_TABLE, TOPICS_TRACK_TABLE);
 0537      foreach ($table_ary as $table)
 0538      {
 0539          $sql = "UPDATE $table
 0540              SET forum_id = $forum_id
 0541              WHERE " . $db->sql_in_set('topic_id', $topic_ids);
 0542          $db->sql_query($sql);
 0543      }
 0544      unset($table_ary);
 0545   
 0546      if ($auto_sync)
 0547      {
 0548          sync('forum', 'forum_id', $forum_ids, true, true);
 0549          unset($forum_ids);
 0550      }
 0551  }
 0552   
 0553  /**
 0554  * Move post(s)
 0555  */
 0556  function move_posts($post_ids, $topic_id, $auto_sync = true)
 0557  {
 0558      global $db;
 0559   
 0560      if (!is_array($post_ids))
 0561      {
 0562          $post_ids = array($post_ids);
 0563      }
 0564   
 0565      $forum_ids = array();
 0566      $topic_ids = array($topic_id);
 0567   
 0568      $sql = 'SELECT DISTINCT topic_id, forum_id
 0569          FROM ' . POSTS_TABLE . '
 0570          WHERE ' . $db->sql_in_set('post_id', $post_ids);
 0571      $result = $db->sql_query($sql);
 0572   
 0573      while ($row = $db->sql_fetchrow($result))
 0574      {
 0575          $forum_ids[] = (int) $row['forum_id'];
 0576          $topic_ids[] = (int) $row['topic_id'];
 0577      }
 0578      $db->sql_freeresult($result);
 0579   
 0580      $sql = 'SELECT forum_id
 0581          FROM ' . TOPICS_TABLE . '
 0582          WHERE topic_id = ' . $topic_id;
 0583      $result = $db->sql_query($sql);
 0584      $forum_row = $db->sql_fetchrow($result);
 0585      $db->sql_freeresult($result);
 0586   
 0587      if (!$forum_row)
 0588      {
 0589          trigger_error('NO_TOPIC');
 0590      }
 0591   
 0592      $sql = 'UPDATE ' . POSTS_TABLE . '
 0593          SET forum_id = ' . (int) $forum_row['forum_id'] . ", topic_id = $topic_id
 0594          WHERE " . $db->sql_in_set('post_id', $post_ids);
 0595      $db->sql_query($sql);
 0596   
 0597      $sql = 'UPDATE ' . ATTACHMENTS_TABLE . "
 0598          SET topic_id = $topic_id, in_message = 0
 0599          WHERE " . $db->sql_in_set('post_msg_id', $post_ids);
 0600      $db->sql_query($sql);
 0601   
 0602      if ($auto_sync)
 0603      {
 0604          $forum_ids[] = (int) $forum_row['forum_id'];
 0605   
 0606          sync('topic_reported', 'topic_id', $topic_ids);
 0607          sync('topic_attachment', 'topic_id', $topic_ids);
 0608          sync('topic', 'topic_id', $topic_ids, true);
 0609          sync('forum', 'forum_id', $forum_ids, true, true);
 0610      }
 0611   
 0612      // Update posted information
 0613      update_posted_info($topic_ids);
 0614  }
 0615   
 0616  /**
 0617  * Remove topic(s)
 0618  */
 0619  function delete_topics($where_type, $where_ids, $auto_sync = true, $post_count_sync = true, $call_delete_posts = true)
 0620  {
 0621      global $db, $config, $phpbb_container;
 0622   
 0623      $approved_topics = 0;
 0624      $forum_ids = $topic_ids = array();
 0625   
 0626      if ($where_type === 'range')
 0627      {
 0628          $where_clause = $where_ids;
 0629      }
 0630      else
 0631      {
 0632          $where_ids = (is_array($where_ids)) ? array_unique($where_ids) : array($where_ids);
 0633   
 0634          if (!sizeof($where_ids))
 0635          {
 0636              return array('topics' => 0, 'posts' => 0);
 0637          }
 0638   
 0639          $where_clause = $db->sql_in_set($where_type, $where_ids);
 0640      }
 0641   
 0642      // Making sure that delete_posts does not call delete_topics again...
 0643      $return = array(
 0644          'posts' => ($call_delete_posts) ? delete_posts($where_type, $where_ids, false, true, $post_count_sync, false) : 0,
 0645      );
 0646   
 0647      $sql = 'SELECT topic_id, forum_id, topic_visibility, topic_moved_id
 0648          FROM ' . TOPICS_TABLE . '
 0649          WHERE ' . $where_clause;
 0650      $result = $db->sql_query($sql);
 0651   
 0652      while ($row = $db->sql_fetchrow($result))
 0653      {
 0654          $forum_ids[] = $row['forum_id'];
 0655          $topic_ids[] = $row['topic_id'];
 0656   
 0657          if ($row['topic_visibility'] == ITEM_APPROVED && !$row['topic_moved_id'])
 0658          {
 0659              $approved_topics++;
 0660          }
 0661      }
 0662      $db->sql_freeresult($result);
 0663   
 0664      $return['topics'] = sizeof($topic_ids);
 0665   
 0666      if (!sizeof($topic_ids))
 0667      {
 0668          return $return;
 0669      }
 0670   
 0671      $db->sql_transaction('begin');
 0672   
 0673      $table_ary = array(BOOKMARKS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, POLL_VOTES_TABLE, POLL_OPTIONS_TABLE, TOPICS_WATCH_TABLE, TOPICS_TABLE);
 0674   
 0675      foreach ($table_ary as $table)
 0676      {
 0677          $sql = "DELETE FROM $table
 0678              WHERE " . $db->sql_in_set('topic_id', $topic_ids);
 0679          $db->sql_query($sql);
 0680      }
 0681      unset($table_ary);
 0682   
 0683      $moved_topic_ids = array();
 0684   
 0685      // update the other forums
 0686      $sql = 'SELECT topic_id, forum_id
 0687          FROM ' . TOPICS_TABLE . '
 0688          WHERE ' . $db->sql_in_set('topic_moved_id', $topic_ids);
 0689      $result = $db->sql_query($sql);
 0690   
 0691      while ($row = $db->sql_fetchrow($result))
 0692      {
 0693          $forum_ids[] = $row['forum_id'];
 0694          $moved_topic_ids[] = $row['topic_id'];
 0695      }
 0696      $db->sql_freeresult($result);
 0697   
 0698      if (sizeof($moved_topic_ids))
 0699      {
 0700          $sql = 'DELETE FROM ' . TOPICS_TABLE . '
 0701              WHERE ' . $db->sql_in_set('topic_id', $moved_topic_ids);
 0702          $db->sql_query($sql);
 0703      }
 0704   
 0705      $db->sql_transaction('commit');
 0706   
 0707      if ($auto_sync)
 0708      {
 0709          sync('forum', 'forum_id', array_unique($forum_ids), true, true);
 0710          sync('topic_reported', $where_type, $where_ids);
 0711      }
 0712   
 0713      if ($approved_topics)
 0714      {
 0715          set_config_count('num_topics', $approved_topics * (-1), true);
 0716      }
 0717   
 0718      $phpbb_notifications = $phpbb_container->get('notification_manager');
 0719   
 0720      $phpbb_notifications->delete_notifications(array(
 0721          'notification.type.topic',
 0722          'notification.type.approve_topic',
 0723          'notification.type.topic_in_queue',
 0724      ), $topic_ids);
 0725   
 0726      return $return;
 0727  }
 0728   
 0729  /**
 0730  * Remove post(s)
 0731  */
 0732  function delete_posts($where_type, $where_ids, $auto_sync = true, $posted_sync = true, $post_count_sync = true, $call_delete_topics = true)
 0733  {
 0734      global $db, $config, $phpbb_root_path, $phpEx, $auth, $user, $phpbb_container, $phpbb_dispatcher;
 0735   
 0736      // Notifications types to delete
 0737      $delete_notifications_types = array(
 0738          'notification.type.quote',
 0739          'notification.type.approve_post',
 0740          'notification.type.post_in_queue',
 0741      );
 0742   
 0743      /**
 0744      * Perform additional actions before post(s) deletion
 0745      *
 0746      * @event core.delete_posts_before
 0747      * @var    string    where_type                    Variable containing posts deletion mode
 0748      * @var    mixed    where_ids                    Array or comma separated list of posts ids to delete
 0749      * @var    bool    auto_sync                    Flag indicating if topics/forums should be synchronized
 0750      * @var    bool    posted_sync                    Flag indicating if topics_posted table should be resynchronized
 0751      * @var    bool    post_count_sync                Flag indicating if posts count should be resynchronized
 0752      * @var    bool    call_delete_topics            Flag indicating if topics having no posts should be deleted
 0753      * @var    array    delete_notifications_types    Array with notifications types to delete
 0754      * @since 3.1.0-a4
 0755      */
 0756      $vars = array(
 0757          'where_type',
 0758          'where_ids',
 0759          'auto_sync',
 0760          'posted_sync',
 0761          'post_count_sync',
 0762          'call_delete_topics',
 0763          'delete_notifications_types',
 0764      );
 0765      extract($phpbb_dispatcher->trigger_event('core.delete_posts_before', compact($vars)));
 0766   
 0767      if ($where_type === 'range')
 0768      {
 0769          $where_clause = $where_ids;
 0770      }
 0771      else
 0772      {
 0773          if (is_array($where_ids))
 0774          {
 0775              $where_ids = array_unique($where_ids);
 0776          }
 0777          else
 0778          {
 0779              $where_ids = array($where_ids);
 0780          }
 0781   
 0782          if (!sizeof($where_ids))
 0783          {
 0784              return false;
 0785          }
 0786   
 0787          $where_ids = array_map('intval', $where_ids);
 0788   
 0789  /*        Possible code for splitting post deletion
 0790          if (sizeof($where_ids) >= 1001)
 0791          {
 0792              // Split into chunks of 1000
 0793              $chunks = array_chunk($where_ids, 1000);
 0794   
 0795              foreach ($chunks as $_where_ids)
 0796              {
 0797                  delete_posts($where_type, $_where_ids, $auto_sync, $posted_sync, $post_count_sync, $call_delete_topics);
 0798              }
 0799   
 0800              return;
 0801          }*/
 0802   
 0803          $where_clause = $db->sql_in_set($where_type, $where_ids);
 0804      }
 0805   
 0806      $approved_posts = 0;
 0807      $post_ids = $topic_ids = $forum_ids = $post_counts = $remove_topics = array();
 0808   
 0809      $sql = 'SELECT post_id, poster_id, post_visibility, post_postcount, topic_id, forum_id
 0810          FROM ' . POSTS_TABLE . '
 0811          WHERE ' . $where_clause;
 0812      $result = $db->sql_query($sql);
 0813   
 0814      while ($row = $db->sql_fetchrow($result))
 0815      {
 0816          $post_ids[] = (int) $row['post_id'];
 0817          $poster_ids[] = (int) $row['poster_id'];
 0818          $topic_ids[] = (int) $row['topic_id'];
 0819          $forum_ids[] = (int) $row['forum_id'];
 0820   
 0821          if ($row['post_postcount'] && $post_count_sync && $row['post_visibility'] == ITEM_APPROVED)
 0822          {
 0823              $post_counts[$row['poster_id']] = (!empty($post_counts[$row['poster_id']])) ? $post_counts[$row['poster_id']] + 1 : 1;
 0824          }
 0825   
 0826          if ($row['post_visibility'] == ITEM_APPROVED)
 0827          {
 0828              $approved_posts++;
 0829          }
 0830      }
 0831      $db->sql_freeresult($result);
 0832   
 0833      if (!sizeof($post_ids))
 0834      {
 0835          return false;
 0836      }
 0837   
 0838      $db->sql_transaction('begin');
 0839   
 0840      $table_ary = array(POSTS_TABLE, REPORTS_TABLE);
 0841   
 0842      foreach ($table_ary as $table)
 0843      {
 0844          $sql = "DELETE FROM $table
 0845              WHERE " . $db->sql_in_set('post_id', $post_ids);
 0846          $db->sql_query($sql);
 0847      }
 0848      unset($table_ary);
 0849   
 0850      // Adjust users post counts
 0851      if (sizeof($post_counts) && $post_count_sync)
 0852      {
 0853          foreach ($post_counts as $poster_id => $substract)
 0854          {
 0855              $sql = 'UPDATE ' . USERS_TABLE . '
 0856                  SET user_posts = 0
 0857                  WHERE user_id = ' . $poster_id . '
 0858                  AND user_posts < ' . $substract;
 0859              $db->sql_query($sql);
 0860   
 0861              $sql = 'UPDATE ' . USERS_TABLE . '
 0862                  SET user_posts = user_posts - ' . $substract . '
 0863                  WHERE user_id = ' . $poster_id . '
 0864                  AND user_posts >= ' . $substract;
 0865              $db->sql_query($sql);
 0866          }
 0867      }
 0868   
 0869      // Remove topics now having no posts?
 0870      if (sizeof($topic_ids))
 0871      {
 0872          $sql = 'SELECT topic_id
 0873              FROM ' . POSTS_TABLE . '
 0874              WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
 0875              GROUP BY topic_id';
 0876          $result = $db->sql_query($sql);
 0877   
 0878          while ($row = $db->sql_fetchrow($result))
 0879          {
 0880              $remove_topics[] = $row['topic_id'];
 0881          }
 0882          $db->sql_freeresult($result);
 0883   
 0884          // Actually, those not within remove_topics should be removed. ;)
 0885          $remove_topics = array_diff($topic_ids, $remove_topics);
 0886      }
 0887   
 0888      // Remove the message from the search index
 0889      $search_type = $config['search_type'];
 0890   
 0891      if (!class_exists($search_type))
 0892      {
 0893          trigger_error('NO_SUCH_SEARCH_MODULE');
 0894      }
 0895   
 0896      $error = false;
 0897      $search = new $search_type($error, $phpbb_root_path, $phpEx, $auth, $config, $db, $user);
 0898   
 0899      if ($error)
 0900      {
 0901          trigger_error($error);
 0902      }
 0903   
 0904      $search->index_remove($post_ids, $poster_ids, $forum_ids);
 0905   
 0906      delete_attachments('post', $post_ids, false);
 0907   
 0908      /**
 0909      * Perform additional actions during post(s) deletion
 0910      *
 0911      * @event core.delete_posts_in_transaction
 0912      * @var    array    post_ids                    Array with deleted posts' ids
 0913      * @var    array    poster_ids                    Array with deleted posts' author ids
 0914      * @var    array    topic_ids                    Array with deleted posts' topic ids
 0915      * @var    array    forum_ids                    Array with deleted posts' forum ids
 0916      * @var    string    where_type                    Variable containing posts deletion mode
 0917      * @var    mixed    where_ids                    Array or comma separated list of posts ids to delete
 0918      * @var    array    delete_notifications_types    Array with notifications types to delete
 0919      * @since 3.1.0-a4
 0920      */
 0921      $vars = array(
 0922          'post_ids',
 0923          'poster_ids',
 0924          'topic_ids',
 0925          'forum_ids',
 0926          'where_type',
 0927          'where_ids',
 0928          'delete_notifications_types',
 0929      );
 0930      extract($phpbb_dispatcher->trigger_event('core.delete_posts_in_transaction', compact($vars)));
 0931   
 0932      $db->sql_transaction('commit');
 0933   
 0934      /**
 0935      * Perform additional actions after post(s) deletion
 0936      *
 0937      * @event core.delete_posts_after
 0938      * @var    array    post_ids                    Array with deleted posts' ids
 0939      * @var    array    poster_ids                    Array with deleted posts' author ids
 0940      * @var    array    topic_ids                    Array with deleted posts' topic ids
 0941      * @var    array    forum_ids                    Array with deleted posts' forum ids
 0942      * @var    string    where_type                    Variable containing posts deletion mode
 0943      * @var    mixed    where_ids                    Array or comma separated list of posts ids to delete
 0944      * @var    array    delete_notifications_types    Array with notifications types to delete
 0945      * @since 3.1.0-a4
 0946      */
 0947      $vars = array(
 0948          'post_ids',
 0949          'poster_ids',
 0950          'topic_ids',
 0951          'forum_ids',
 0952          'where_type',
 0953          'where_ids',
 0954          'delete_notifications_types',
 0955      );
 0956      extract($phpbb_dispatcher->trigger_event('core.delete_posts_after', compact($vars)));
 0957   
 0958      // Resync topics_posted table
 0959      if ($posted_sync)
 0960      {
 0961          update_posted_info($topic_ids);
 0962      }
 0963   
 0964      if ($auto_sync)
 0965      {
 0966          sync('topic_reported', 'topic_id', $topic_ids);
 0967          sync('topic', 'topic_id', $topic_ids, true);
 0968          sync('forum', 'forum_id', $forum_ids, true, true);
 0969      }
 0970   
 0971      if ($approved_posts && $post_count_sync)
 0972      {
 0973          set_config_count('num_posts', $approved_posts * (-1), true);
 0974      }
 0975   
 0976      // We actually remove topics now to not be inconsistent (the delete_topics function calls this function too)
 0977      if (sizeof($remove_topics) && $call_delete_topics)
 0978      {
 0979          delete_topics('topic_id', $remove_topics, $auto_sync, $post_count_sync, false);
 0980      }
 0981   
 0982      $phpbb_notifications = $phpbb_container->get('notification_manager');
 0983   
 0984      $phpbb_notifications->delete_notifications($delete_notifications_types, $post_ids);
 0985   
 0986      return sizeof($post_ids);
 0987  }
 0988   
 0989  /**
 0990  * Delete Attachments
 0991  *
 0992  * @param string $mode can be: post|message|topic|attach|user
 0993  * @param mixed $ids can be: post_ids, message_ids, topic_ids, attach_ids, user_ids
 0994  * @param bool $resync set this to false if you are deleting posts or topics
 0995  */
 0996  function delete_attachments($mode, $ids, $resync = true)
 0997  {
 0998      global $db, $config;
 0999   
 1000      // 0 is as bad as an empty array
 1001      if (empty($ids))
 1002      {
 1003          return false;
 1004      }
 1005   
 1006      if (is_array($ids))
 1007      {
 1008          $ids = array_unique($ids);
 1009          $ids = array_map('intval', $ids);
 1010      }
 1011      else
 1012      {
 1013          $ids = array((int) $ids);
 1014      }
 1015   
 1016      $sql_where = '';
 1017   
 1018      switch ($mode)
 1019      {
 1020          case 'post':
 1021          case 'message':
 1022              $sql_id = 'post_msg_id';
 1023              $sql_where = ' AND in_message = ' . ($mode == 'message' ? 1 : 0);
 1024          break;
 1025   
 1026          case 'topic':
 1027              $sql_id = 'topic_id';
 1028          break;
 1029   
 1030          case 'user':
 1031              $sql_id = 'poster_id';
 1032          break;
 1033   
 1034          case 'attach':
 1035          default:
 1036              $sql_id = 'attach_id';
 1037              $mode = 'attach';
 1038          break;
 1039      }
 1040   
 1041      $post_ids = $message_ids = $topic_ids = $physical = array();
 1042   
 1043      // Collect post and topic ids for later use if we need to touch remaining entries (if resync is enabled)
 1044      $sql = 'SELECT post_msg_id, topic_id, in_message, physical_filename, thumbnail, filesize, is_orphan
 1045              FROM ' . ATTACHMENTS_TABLE . '
 1046              WHERE ' . $db->sql_in_set($sql_id, $ids);
 1047   
 1048      $sql .= $sql_where;
 1049   
 1050      $result = $db->sql_query($sql);
 1051   
 1052      while ($row = $db->sql_fetchrow($result))
 1053      {
 1054          // We only need to store post/message/topic ids if resync is enabled and the file is not orphaned
 1055          if ($resync && !$row['is_orphan'])
 1056          {
 1057              if (!$row['in_message'])
 1058              {
 1059                  $post_ids[] = $row['post_msg_id'];
 1060                  $topic_ids[] = $row['topic_id'];
 1061              }
 1062              else
 1063              {
 1064                  $message_ids[] = $row['post_msg_id'];
 1065              }
 1066          }
 1067   
 1068          $physical[] = array('filename' => $row['physical_filename'], 'thumbnail' => $row['thumbnail'], 'filesize' => $row['filesize'], 'is_orphan' => $row['is_orphan']);
 1069      }
 1070      $db->sql_freeresult($result);
 1071   
 1072      // Delete attachments
 1073      $sql = 'DELETE FROM ' . ATTACHMENTS_TABLE . '
 1074          WHERE ' . $db->sql_in_set($sql_id, $ids);
 1075   
 1076      $sql .= $sql_where;
 1077   
 1078      $db->sql_query($sql);
 1079      $num_deleted = $db->sql_affectedrows();
 1080   
 1081      if (!$num_deleted)
 1082      {
 1083          return 0;
 1084      }
 1085   
 1086      // Delete attachments from filesystem
 1087      $space_removed = $files_removed = 0;
 1088      foreach ($physical as $file_ary)
 1089      {
 1090          if (phpbb_unlink($file_ary['filename'], 'file', true) && !$file_ary['is_orphan'])
 1091          {
 1092              // Only non-orphaned files count to the file size
 1093              $space_removed += $file_ary['filesize'];
 1094              $files_removed++;
 1095          }
 1096   
 1097          if ($file_ary['thumbnail'])
 1098          {
 1099              phpbb_unlink($file_ary['filename'], 'thumbnail', true);
 1100          }
 1101      }
 1102   
 1103      if ($space_removed || $files_removed)
 1104      {
 1105          set_config_count('upload_dir_size', $space_removed * (-1), true);
 1106          set_config_count('num_files', $files_removed * (-1), true);
 1107      }
 1108   
 1109      // If we do not resync, we do not need to adjust any message, post, topic or user entries
 1110      if (!$resync)
 1111      {
 1112          return $num_deleted;
 1113      }
 1114   
 1115      // No more use for the original ids
 1116      unset($ids);
 1117   
 1118      // Now, we need to resync posts, messages, topics. We go through every one of them
 1119      $post_ids = array_unique($post_ids);
 1120      $message_ids = array_unique($message_ids);
 1121      $topic_ids = array_unique($topic_ids);
 1122   
 1123      // Update post indicators for posts now no longer having attachments
 1124      if (sizeof($post_ids))
 1125      {
 1126          // Just check which posts are still having an assigned attachment not orphaned by querying the attachments table
 1127          $sql = 'SELECT post_msg_id
 1128              FROM ' . ATTACHMENTS_TABLE . '
 1129              WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
 1130                  AND in_message = 0
 1131                  AND is_orphan = 0';
 1132          $result = $db->sql_query($sql);
 1133   
 1134          $remaining_ids = array();
 1135          while ($row = $db->sql_fetchrow($result))
 1136          {
 1137              $remaining_ids[] = $row['post_msg_id'];
 1138          }
 1139          $db->sql_freeresult($result);
 1140   
 1141          // Now only unset those ids remaining
 1142          $post_ids = array_diff($post_ids, $remaining_ids);
 1143   
 1144          if (sizeof($post_ids))
 1145          {
 1146              $sql = 'UPDATE ' . POSTS_TABLE . '
 1147                  SET post_attachment = 0
 1148                  WHERE ' . $db->sql_in_set('post_id', $post_ids);
 1149              $db->sql_query($sql);
 1150          }
 1151      }
 1152   
 1153      // Update message table if messages are affected
 1154      if (sizeof($message_ids))
 1155      {
 1156          // Just check which messages are still having an assigned attachment not orphaned by querying the attachments table
 1157          $sql = 'SELECT post_msg_id
 1158              FROM ' . ATTACHMENTS_TABLE . '
 1159              WHERE ' . $db->sql_in_set('post_msg_id', $message_ids) . '
 1160                  AND in_message = 1
 1161                  AND is_orphan = 0';
 1162          $result = $db->sql_query($sql);
 1163   
 1164          $remaining_ids = array();
 1165          while ($row = $db->sql_fetchrow($result))
 1166          {
 1167              $remaining_ids[] = $row['post_msg_id'];
 1168          }
 1169          $db->sql_freeresult($result);
 1170   
 1171          // Now only unset those ids remaining
 1172          $message_ids = array_diff($message_ids, $remaining_ids);
 1173   
 1174          if (sizeof($message_ids))
 1175          {
 1176              $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
 1177                  SET message_attachment = 0
 1178                  WHERE ' . $db->sql_in_set('msg_id', $message_ids);
 1179              $db->sql_query($sql);
 1180          }
 1181      }
 1182   
 1183      // Now update the topics. This is a bit trickier, because there could be posts still having attachments within the topic
 1184      if (sizeof($topic_ids))
 1185      {
 1186          // Just check which topics are still having an assigned attachment not orphaned by querying the attachments table (much less entries expected)
 1187          $sql = 'SELECT topic_id
 1188              FROM ' . ATTACHMENTS_TABLE . '
 1189              WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
 1190                  AND is_orphan = 0';
 1191          $result = $db->sql_query($sql);
 1192   
 1193          $remaining_ids = array();
 1194          while ($row = $db->sql_fetchrow($result))
 1195          {
 1196              $remaining_ids[] = $row['topic_id'];
 1197          }
 1198          $db->sql_freeresult($result);
 1199   
 1200          // Now only unset those ids remaining
 1201          $topic_ids = array_diff($topic_ids, $remaining_ids);
 1202   
 1203          if (sizeof($topic_ids))
 1204          {
 1205              $sql = 'UPDATE ' . TOPICS_TABLE . '
 1206                  SET topic_attachment = 0
 1207                  WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 1208              $db->sql_query($sql);
 1209          }
 1210      }
 1211   
 1212      return $num_deleted;
 1213  }
 1214   
 1215  /**
 1216  * Deletes shadow topics pointing to a specified forum.
 1217  *
 1218  * @param int        $forum_id        The forum id
 1219  * @param string        $sql_more        Additional WHERE statement, e.g. t.topic_time < (time() - 1234)
 1220  * @param bool        $auto_sync        Will call sync() if this is true
 1221  *
 1222  * @return array        Array with affected forums
 1223  */
 1224  function delete_topic_shadows($forum_id, $sql_more = '', $auto_sync = true)
 1225  {
 1226      global $db;
 1227   
 1228      if (!$forum_id)
 1229      {
 1230          // Nothing to do.
 1231          return;
 1232      }
 1233   
 1234      // Set of affected forums we have to resync
 1235      $sync_forum_ids = array();
 1236   
 1237      // Amount of topics we select and delete at once.
 1238      $batch_size = 500;
 1239   
 1240      do
 1241      {
 1242          $sql = 'SELECT t2.forum_id, t2.topic_id
 1243              FROM ' . TOPICS_TABLE . ' t2, ' . TOPICS_TABLE . ' t
 1244              WHERE t2.topic_moved_id = t.topic_id
 1245                  AND t.forum_id = ' . (int) $forum_id . '
 1246                  ' . (($sql_more) ? 'AND ' . $sql_more : '');
 1247          $result = $db->sql_query_limit($sql, $batch_size);
 1248   
 1249          $topic_ids = array();
 1250          while ($row = $db->sql_fetchrow($result))
 1251          {
 1252              $topic_ids[] = (int) $row['topic_id'];
 1253   
 1254              $sync_forum_ids[(int) $row['forum_id']] = (int) $row['forum_id'];
 1255          }
 1256          $db->sql_freeresult($result);
 1257   
 1258          if (!empty($topic_ids))
 1259          {
 1260              $sql = 'DELETE FROM ' . TOPICS_TABLE . '
 1261                  WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 1262              $db->sql_query($sql);
 1263          }
 1264      }
 1265      while (sizeof($topic_ids) == $batch_size);
 1266   
 1267      if ($auto_sync)
 1268      {
 1269          sync('forum', 'forum_id', $sync_forum_ids, true, true);
 1270      }
 1271   
 1272      return $sync_forum_ids;
 1273  }
 1274   
 1275  /**
 1276  * Update/Sync posted information for topics
 1277  */
 1278  function update_posted_info(&$topic_ids)
 1279  {
 1280      global $db, $config;
 1281   
 1282      if (empty($topic_ids) || !$config['load_db_track'])
 1283      {
 1284          return;
 1285      }
 1286   
 1287      // First of all, let us remove any posted information for these topics
 1288      $sql = 'DELETE FROM ' . TOPICS_POSTED_TABLE . '
 1289          WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 1290      $db->sql_query($sql);
 1291   
 1292      // Now, let us collect the user/topic combos for rebuilding the information
 1293      $sql = 'SELECT poster_id, topic_id
 1294          FROM ' . POSTS_TABLE . '
 1295          WHERE ' . $db->sql_in_set('topic_id', $topic_ids) . '
 1296              AND poster_id <> ' . ANONYMOUS . '
 1297          GROUP BY poster_id, topic_id';
 1298      $result = $db->sql_query($sql);
 1299   
 1300      $posted = array();
 1301      while ($row = $db->sql_fetchrow($result))
 1302      {
 1303          // Add as key to make them unique (grouping by) and circumvent empty keys on array_unique
 1304          $posted[$row['poster_id']][] = $row['topic_id'];
 1305      }
 1306      $db->sql_freeresult($result);
 1307   
 1308      // Now add the information...
 1309      $sql_ary = array();
 1310      foreach ($posted as $user_id => $topic_row)
 1311      {
 1312          foreach ($topic_row as $topic_id)
 1313          {
 1314              $sql_ary[] = array(
 1315                  'user_id'        => (int) $user_id,
 1316                  'topic_id'        => (int) $topic_id,
 1317                  'topic_posted'    => 1,
 1318              );
 1319          }
 1320      }
 1321      unset($posted);
 1322   
 1323      $db->sql_multi_insert(TOPICS_POSTED_TABLE, $sql_ary);
 1324  }
 1325   
 1326  /**
 1327  * Delete attached file
 1328  */
 1329  function phpbb_unlink($filename, $mode = 'file', $entry_removed = false)
 1330  {
 1331      global $db, $phpbb_root_path, $config;
 1332   
 1333      // Because of copying topics or modifications a physical filename could be assigned more than once. If so, do not remove the file itself.
 1334      $sql = 'SELECT COUNT(attach_id) AS num_entries
 1335          FROM ' . ATTACHMENTS_TABLE . "
 1336          WHERE physical_filename = '" . $db->sql_escape(utf8_basename($filename)) . "'";
 1337      $result = $db->sql_query($sql);
 1338      $num_entries = (int) $db->sql_fetchfield('num_entries');
 1339      $db->sql_freeresult($result);
 1340   
 1341      // Do not remove file if at least one additional entry with the same name exist.
 1342      if (($entry_removed && $num_entries > 0) || (!$entry_removed && $num_entries > 1))
 1343      {
 1344          return false;
 1345      }
 1346   
 1347      $filename = ($mode == 'thumbnail') ? 'thumb_' . utf8_basename($filename) : utf8_basename($filename);
 1348      return @unlink($phpbb_root_path . $config['upload_path'] . '/' . $filename);
 1349  }
 1350   
 1351  /**
 1352  * All-encompasing sync function
 1353  *
 1354  * Exaples:
 1355  * <code>
 1356  * sync('topic', 'topic_id', 123);            // resync topic #123
 1357  * sync('topic', 'forum_id', array(2, 3));    // resync topics from forum #2 and #3
 1358  * sync('topic');                            // resync all topics
 1359  * sync('topic', 'range', 'topic_id BETWEEN 1 AND 60');    // resync a range of topics/forums (only available for 'topic' and 'forum' modes)
 1360  * </code>
 1361  *
 1362  * Modes:
 1363  * - forum                Resync complete forum
 1364  * - topic                Resync topics
 1365  * - topic_moved            Removes topic shadows that would be in the same forum as the topic they link to
 1366  * - topic_visibility    Resyncs the topic_visibility flag according to the status of the first post
 1367  * - post_reported        Resyncs the post_reported flag, relying on actual reports
 1368  * - topic_reported        Resyncs the topic_reported flag, relying on post_reported flags
 1369  * - post_attachement    Same as post_reported, but with attachment flags
 1370  * - topic_attachement    Same as topic_reported, but with attachment flags
 1371  */
 1372  function sync($mode, $where_type = '', $where_ids = '', $resync_parents = false, $sync_extra = false)
 1373  {
 1374      global $db;
 1375   
 1376      if (is_array($where_ids))
 1377      {
 1378          $where_ids = array_unique($where_ids);
 1379          $where_ids = array_map('intval', $where_ids);
 1380      }
 1381      else if ($where_type != 'range')
 1382      {
 1383          $where_ids = ($where_ids) ? array((int) $where_ids) : array();
 1384      }
 1385   
 1386      if ($mode == 'forum' || $mode == 'topic' || $mode == 'topic_visibility' || $mode == 'topic_reported' || $mode == 'post_reported')
 1387      {
 1388          if (!$where_type)
 1389          {
 1390              $where_sql = '';
 1391              $where_sql_and = 'WHERE';
 1392          }
 1393          else if ($where_type == 'range')
 1394          {
 1395              // Only check a range of topics/forums. For instance: 'topic_id BETWEEN 1 AND 60'
 1396              $where_sql = 'WHERE (' . $mode[0] . ".$where_ids)";
 1397              $where_sql_and = $where_sql . "\n\tAND";
 1398          }
 1399          else
 1400          {
 1401              // Do not sync the "global forum"
 1402              $where_ids = array_diff($where_ids, array(0));
 1403   
 1404              if (!sizeof($where_ids))
 1405              {
 1406                  // Empty array with IDs. This means that we don't have any work to do. Just return.
 1407                  return;
 1408              }
 1409   
 1410              // Limit the topics/forums we are syncing, use specific topic/forum IDs.
 1411              // $where_type contains the field for the where clause (forum_id, topic_id)
 1412              $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
 1413              $where_sql_and = $where_sql . "\n\tAND";
 1414          }
 1415      }
 1416      else
 1417      {
 1418          if (!sizeof($where_ids))
 1419          {
 1420              return;
 1421          }
 1422   
 1423          // $where_type contains the field for the where clause (forum_id, topic_id)
 1424          $where_sql = 'WHERE ' . $db->sql_in_set($mode[0] . '.' . $where_type, $where_ids);
 1425          $where_sql_and = $where_sql . "\n\tAND";
 1426      }
 1427   
 1428      switch ($mode)
 1429      {
 1430          case 'topic_moved':
 1431              $db->sql_transaction('begin');
 1432              switch ($db->get_sql_layer())
 1433              {
 1434                  case 'mysql4':
 1435                  case 'mysqli':
 1436                      $sql = 'DELETE FROM ' . TOPICS_TABLE . '
 1437                          USING ' . TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
 1438                          WHERE t1.topic_moved_id = t2.topic_id
 1439                              AND t1.forum_id = t2.forum_id";
 1440                      $db->sql_query($sql);
 1441                  break;
 1442   
 1443                  default:
 1444                      $sql = 'SELECT t1.topic_id
 1445                          FROM ' .TOPICS_TABLE . ' t1, ' . TOPICS_TABLE . " t2
 1446                          WHERE t1.topic_moved_id = t2.topic_id
 1447                              AND t1.forum_id = t2.forum_id";
 1448                      $result = $db->sql_query($sql);
 1449   
 1450                      $topic_id_ary = array();
 1451                      while ($row = $db->sql_fetchrow($result))
 1452                      {
 1453                          $topic_id_ary[] = $row['topic_id'];
 1454                      }
 1455                      $db->sql_freeresult($result);
 1456   
 1457                      if (!sizeof($topic_id_ary))
 1458                      {
 1459                          return;
 1460                      }
 1461   
 1462                      $sql = 'DELETE FROM ' . TOPICS_TABLE . '
 1463                          WHERE ' . $db->sql_in_set('topic_id', $topic_id_ary);
 1464                      $db->sql_query($sql);
 1465   
 1466                  break;
 1467              }
 1468   
 1469              $db->sql_transaction('commit');
 1470              break;
 1471   
 1472          case 'topic_visibility':
 1473   
 1474              $db->sql_transaction('begin');
 1475   
 1476              $sql = 'SELECT t.topic_id, p.post_visibility
 1477                  FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
 1478                  $where_sql_and p.topic_id = t.topic_id
 1479                      AND p.post_visibility = " . ITEM_APPROVED;
 1480              $result = $db->sql_query($sql);
 1481   
 1482              $topics_approved = array();
 1483              while ($row = $db->sql_fetchrow($result))
 1484              {
 1485                  $topics_approved[] = (int) $row['topic_id'];
 1486              }
 1487              $db->sql_freeresult($result);
 1488   
 1489              $sql = 'SELECT t.topic_id, p.post_visibility
 1490                  FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
 1491                  $where_sql_and " . $db->sql_in_set('t.topic_id', $topics_approved, true, true) . '
 1492                      AND p.topic_id = t.topic_id
 1493                      AND p.post_visibility = ' . ITEM_DELETED;
 1494              $result = $db->sql_query($sql);
 1495   
 1496              $topics_softdeleted = array();
 1497              while ($row = $db->sql_fetchrow($result))
 1498              {
 1499                  $topics_softdeleted[] = (int) $row['topic_id'];
 1500              }
 1501              $db->sql_freeresult($result);
 1502   
 1503              $topics_softdeleted = array_diff($topics_softdeleted, $topics_approved);
 1504              $topics_not_unapproved = array_merge($topics_softdeleted, $topics_approved);
 1505   
 1506              $update_ary = array(
 1507                  ITEM_UNAPPROVED    => (!empty($topics_not_unapproved)) ? $where_sql_and . ' ' . $db->sql_in_set('topic_id', $topics_not_unapproved, true) : '',
 1508                  ITEM_APPROVED    => (!empty($topics_approved)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_approved) : '',
 1509                  ITEM_DELETED    => (!empty($topics_softdeleted)) ? ' WHERE ' . $db->sql_in_set('topic_id', $topics_softdeleted) : '',
 1510              );
 1511   
 1512              foreach ($update_ary as $visibility => $sql_where)
 1513              {
 1514                  if ($sql_where)
 1515                  {
 1516                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 1517                          SET topic_visibility = ' . $visibility . '
 1518                          ' . $sql_where;
 1519                      $db->sql_query($sql);
 1520                  }
 1521              }
 1522   
 1523              $db->sql_transaction('commit');
 1524              break;
 1525   
 1526          case 'post_reported':
 1527              $post_ids = $post_reported = array();
 1528   
 1529              $db->sql_transaction('begin');
 1530   
 1531              $sql = 'SELECT p.post_id, p.post_reported
 1532                  FROM ' . POSTS_TABLE . " p
 1533                  $where_sql
 1534                  GROUP BY p.post_id, p.post_reported";
 1535              $result = $db->sql_query($sql);
 1536   
 1537              while ($row = $db->sql_fetchrow($result))
 1538              {
 1539                  $post_ids[$row['post_id']] = $row['post_id'];
 1540                  if ($row['post_reported'])
 1541                  {
 1542                      $post_reported[$row['post_id']] = 1;
 1543                  }
 1544              }
 1545              $db->sql_freeresult($result);
 1546   
 1547              $sql = 'SELECT DISTINCT(post_id)
 1548                  FROM ' . REPORTS_TABLE . '
 1549                  WHERE ' . $db->sql_in_set('post_id', $post_ids) . '
 1550                      AND report_closed = 0';
 1551              $result = $db->sql_query($sql);
 1552   
 1553              $post_ids = array();
 1554              while ($row = $db->sql_fetchrow($result))
 1555              {
 1556                  if (!isset($post_reported[$row['post_id']]))
 1557                  {
 1558                      $post_ids[] = $row['post_id'];
 1559                  }
 1560                  else
 1561                  {
 1562                      unset($post_reported[$row['post_id']]);
 1563                  }
 1564              }
 1565              $db->sql_freeresult($result);
 1566   
 1567              // $post_reported should be empty by now, if it's not it contains
 1568              // posts that are falsely flagged as reported
 1569              foreach ($post_reported as $post_id => $void)
 1570              {
 1571                  $post_ids[] = $post_id;
 1572              }
 1573   
 1574              if (sizeof($post_ids))
 1575              {
 1576                  $sql = 'UPDATE ' . POSTS_TABLE . '
 1577                      SET post_reported = 1 - post_reported
 1578                      WHERE ' . $db->sql_in_set('post_id', $post_ids);
 1579                  $db->sql_query($sql);
 1580              }
 1581   
 1582              $db->sql_transaction('commit');
 1583              break;
 1584   
 1585          case 'topic_reported':
 1586              if ($sync_extra)
 1587              {
 1588                  sync('post_reported', $where_type, $where_ids);
 1589              }
 1590   
 1591              $topic_ids = $topic_reported = array();
 1592   
 1593              $db->sql_transaction('begin');
 1594   
 1595              $sql = 'SELECT DISTINCT(t.topic_id)
 1596                  FROM ' . POSTS_TABLE . " t
 1597                  $where_sql_and t.post_reported = 1";
 1598              $result = $db->sql_query($sql);
 1599   
 1600              while ($row = $db->sql_fetchrow($result))
 1601              {
 1602                  $topic_reported[$row['topic_id']] = 1;
 1603              }
 1604              $db->sql_freeresult($result);
 1605   
 1606              $sql = 'SELECT t.topic_id, t.topic_reported
 1607                  FROM ' . TOPICS_TABLE . " t
 1608                  $where_sql";
 1609              $result = $db->sql_query($sql);
 1610   
 1611              while ($row = $db->sql_fetchrow($result))
 1612              {
 1613                  if ($row['topic_reported'] ^ isset($topic_reported[$row['topic_id']]))
 1614                  {
 1615                      $topic_ids[] = $row['topic_id'];
 1616                  }
 1617              }
 1618              $db->sql_freeresult($result);
 1619   
 1620              if (sizeof($topic_ids))
 1621              {
 1622                  $sql = 'UPDATE ' . TOPICS_TABLE . '
 1623                      SET topic_reported = 1 - topic_reported
 1624                      WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 1625                  $db->sql_query($sql);
 1626              }
 1627   
 1628              $db->sql_transaction('commit');
 1629              break;
 1630   
 1631          case 'post_attachment':
 1632              $post_ids = $post_attachment = array();
 1633   
 1634              $db->sql_transaction('begin');
 1635   
 1636              $sql = 'SELECT p.post_id, p.post_attachment
 1637                  FROM ' . POSTS_TABLE . " p
 1638                  $where_sql
 1639                  GROUP BY p.post_id, p.post_attachment";
 1640              $result = $db->sql_query($sql);
 1641   
 1642              while ($row = $db->sql_fetchrow($result))
 1643              {
 1644                  $post_ids[$row['post_id']] = $row['post_id'];
 1645                  if ($row['post_attachment'])
 1646                  {
 1647                      $post_attachment[$row['post_id']] = 1;
 1648                  }
 1649              }
 1650              $db->sql_freeresult($result);
 1651   
 1652              $sql = 'SELECT DISTINCT(post_msg_id)
 1653                  FROM ' . ATTACHMENTS_TABLE . '
 1654                  WHERE ' . $db->sql_in_set('post_msg_id', $post_ids) . '
 1655                      AND in_message = 0';
 1656              $result = $db->sql_query($sql);
 1657   
 1658              $post_ids = array();
 1659              while ($row = $db->sql_fetchrow($result))
 1660              {
 1661                  if (!isset($post_attachment[$row['post_msg_id']]))
 1662                  {
 1663                      $post_ids[] = $row['post_msg_id'];
 1664                  }
 1665                  else
 1666                  {
 1667                      unset($post_attachment[$row['post_msg_id']]);
 1668                  }
 1669              }
 1670              $db->sql_freeresult($result);
 1671   
 1672              // $post_attachment should be empty by now, if it's not it contains
 1673              // posts that are falsely flagged as having attachments
 1674              foreach ($post_attachment as $post_id => $void)
 1675              {
 1676                  $post_ids[] = $post_id;
 1677              }
 1678   
 1679              if (sizeof($post_ids))
 1680              {
 1681                  $sql = 'UPDATE ' . POSTS_TABLE . '
 1682                      SET post_attachment = 1 - post_attachment
 1683                      WHERE ' . $db->sql_in_set('post_id', $post_ids);
 1684                  $db->sql_query($sql);
 1685              }
 1686   
 1687              $db->sql_transaction('commit');
 1688              break;
 1689   
 1690          case 'topic_attachment':
 1691              if ($sync_extra)
 1692              {
 1693                  sync('post_attachment', $where_type, $where_ids);
 1694              }
 1695   
 1696              $topic_ids = $topic_attachment = array();
 1697   
 1698              $db->sql_transaction('begin');
 1699   
 1700              $sql = 'SELECT DISTINCT(t.topic_id)
 1701                  FROM ' . POSTS_TABLE . " t
 1702                  $where_sql_and t.post_attachment = 1";
 1703              $result = $db->sql_query($sql);
 1704   
 1705              while ($row = $db->sql_fetchrow($result))
 1706              {
 1707                  $topic_attachment[$row['topic_id']] = 1;
 1708              }
 1709              $db->sql_freeresult($result);
 1710   
 1711              $sql = 'SELECT t.topic_id, t.topic_attachment
 1712                  FROM ' . TOPICS_TABLE . " t
 1713                  $where_sql";
 1714              $result = $db->sql_query($sql);
 1715   
 1716              while ($row = $db->sql_fetchrow($result))
 1717              {
 1718                  if ($row['topic_attachment'] ^ isset($topic_attachment[$row['topic_id']]))
 1719                  {
 1720                      $topic_ids[] = $row['topic_id'];
 1721                  }
 1722              }
 1723              $db->sql_freeresult($result);
 1724   
 1725              if (sizeof($topic_ids))
 1726              {
 1727                  $sql = 'UPDATE ' . TOPICS_TABLE . '
 1728                      SET topic_attachment = 1 - topic_attachment
 1729                      WHERE ' . $db->sql_in_set('topic_id', $topic_ids);
 1730                  $db->sql_query($sql);
 1731              }
 1732   
 1733              $db->sql_transaction('commit');
 1734   
 1735              break;
 1736   
 1737          case 'forum':
 1738   
 1739              $db->sql_transaction('begin');
 1740   
 1741              // 1: Get the list of all forums
 1742              $sql = 'SELECT f.*
 1743                  FROM ' . FORUMS_TABLE . " f
 1744                  $where_sql";
 1745              $result = $db->sql_query($sql);
 1746   
 1747              $forum_data = $forum_ids = $post_ids = $last_post_id = $post_info = array();
 1748              while ($row = $db->sql_fetchrow($result))
 1749              {
 1750                  if ($row['forum_type'] == FORUM_LINK)
 1751                  {
 1752                      continue;
 1753                  }
 1754   
 1755                  $forum_id = (int) $row['forum_id'];
 1756                  $forum_ids[$forum_id] = $forum_id;
 1757   
 1758                  $forum_data[$forum_id] = $row;
 1759                  if ($sync_extra)
 1760                  {
 1761                      $forum_data[$forum_id]['posts_approved'] = 0;
 1762                      $forum_data[$forum_id]['posts_unapproved'] = 0;
 1763                      $forum_data[$forum_id]['posts_softdeleted'] = 0;
 1764                      $forum_data[$forum_id]['topics_approved'] = 0;
 1765                      $forum_data[$forum_id]['topics_unapproved'] = 0;
 1766                      $forum_data[$forum_id]['topics_softdeleted'] = 0;
 1767                  }
 1768                  $forum_data[$forum_id]['last_post_id'] = 0;
 1769                  $forum_data[$forum_id]['last_post_subject'] = '';
 1770                  $forum_data[$forum_id]['last_post_time'] = 0;
 1771                  $forum_data[$forum_id]['last_poster_id'] = 0;
 1772                  $forum_data[$forum_id]['last_poster_name'] = '';
 1773                  $forum_data[$forum_id]['last_poster_colour'] = '';
 1774              }
 1775              $db->sql_freeresult($result);
 1776   
 1777              if (!sizeof($forum_ids))
 1778              {
 1779                  break;
 1780              }
 1781   
 1782              $forum_ids = array_values($forum_ids);
 1783   
 1784              // 2: Get topic counts for each forum (optional)
 1785              if ($sync_extra)
 1786              {
 1787                  $sql = 'SELECT forum_id, topic_visibility, COUNT(topic_id) AS total_topics
 1788                      FROM ' . TOPICS_TABLE . '
 1789                      WHERE ' . $db->sql_in_set('forum_id', $forum_ids) . '
 1790                      GROUP BY forum_id, topic_visibility';
 1791                  $result = $db->sql_query($sql);
 1792   
 1793                  while ($row = $db->sql_fetchrow($result))
 1794                  {
 1795                      $forum_id = (int) $row['forum_id'];
 1796   
 1797                      if ($row['topic_visibility'] == ITEM_APPROVED)
 1798                      {
 1799                          $forum_data[$forum_id]['topics_approved'] = $row['total_topics'];
 1800                      }
 1801                      else if ($row['topic_visibility'] == ITEM_UNAPPROVED || $row['topic_visibility'] == ITEM_REAPPROVE)
 1802                      {
 1803                          $forum_data[$forum_id]['topics_unapproved'] = $row['total_topics'];
 1804                      }
 1805                      else if ($row['topic_visibility'] == ITEM_DELETED)
 1806                      {
 1807                          $forum_data[$forum_id]['topics_softdeleted'] = $row['total_topics'];
 1808                      }
 1809                  }
 1810                  $db->sql_freeresult($result);
 1811              }
 1812   
 1813              // 3: Get post count for each forum (optional)
 1814              if ($sync_extra)
 1815              {
 1816                  if (sizeof($forum_ids) == 1)
 1817                  {
 1818                      $sql = 'SELECT SUM(t.topic_posts_approved) AS forum_posts_approved, SUM(t.topic_posts_unapproved) AS forum_posts_unapproved, SUM(t.topic_posts_softdeleted) AS forum_posts_softdeleted
 1819                          FROM ' . TOPICS_TABLE . ' t
 1820                          WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
 1821                              AND t.topic_status <> ' . ITEM_MOVED;
 1822                  }
 1823                  else
 1824                  {
 1825                      $sql = 'SELECT t.forum_id, SUM(t.topic_posts_approved) AS forum_posts_approved, SUM(t.topic_posts_unapproved) AS forum_posts_unapproved, SUM(t.topic_posts_softdeleted) AS forum_posts_softdeleted
 1826                          FROM ' . TOPICS_TABLE . ' t
 1827                          WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
 1828                              AND t.topic_status <> ' . ITEM_MOVED . '
 1829                          GROUP BY t.forum_id';
 1830                  }
 1831   
 1832                  $result = $db->sql_query($sql);
 1833   
 1834                  while ($row = $db->sql_fetchrow($result))
 1835                  {
 1836                      $forum_id = (sizeof($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
 1837   
 1838                      $forum_data[$forum_id]['posts_approved'] = (int) $row['forum_posts_approved'];
 1839                      $forum_data[$forum_id]['posts_unapproved'] = (int) $row['forum_posts_unapproved'];
 1840                      $forum_data[$forum_id]['posts_softdeleted'] = (int) $row['forum_posts_softdeleted'];
 1841                  }
 1842                  $db->sql_freeresult($result);
 1843              }
 1844   
 1845              // 4: Get last_post_id for each forum
 1846              if (sizeof($forum_ids) == 1)
 1847              {
 1848                  $sql = 'SELECT MAX(t.topic_last_post_id) as last_post_id
 1849                      FROM ' . TOPICS_TABLE . ' t
 1850                      WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
 1851                          AND t.topic_visibility = ' . ITEM_APPROVED;
 1852              }
 1853              else
 1854              {
 1855                  $sql = 'SELECT t.forum_id, MAX(t.topic_last_post_id) as last_post_id
 1856                      FROM ' . TOPICS_TABLE . ' t
 1857                      WHERE ' . $db->sql_in_set('t.forum_id', $forum_ids) . '
 1858                          AND t.topic_visibility = ' . ITEM_APPROVED . '
 1859                      GROUP BY t.forum_id';
 1860              }
 1861   
 1862              $result = $db->sql_query($sql);
 1863   
 1864              while ($row = $db->sql_fetchrow($result))
 1865              {
 1866                  $forum_id = (sizeof($forum_ids) == 1) ? (int) $forum_ids[0] : (int) $row['forum_id'];
 1867   
 1868                  $forum_data[$forum_id]['last_post_id'] = (int) $row['last_post_id'];
 1869   
 1870                  $post_ids[] = $row['last_post_id'];
 1871              }
 1872              $db->sql_freeresult($result);
 1873   
 1874              // 5: Retrieve last_post infos
 1875              if (sizeof($post_ids))
 1876              {
 1877                  $sql = 'SELECT p.post_id, p.poster_id, p.post_subject, p.post_time, p.post_username, u.username, u.user_colour
 1878                      FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
 1879                      WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
 1880                          AND p.poster_id = u.user_id';
 1881                  $result = $db->sql_query($sql);
 1882   
 1883                  while ($row = $db->sql_fetchrow($result))
 1884                  {
 1885                      $post_info[$row['post_id']] = $row;
 1886                  }
 1887                  $db->sql_freeresult($result);
 1888   
 1889                  foreach ($forum_data as $forum_id => $data)
 1890                  {
 1891                      if ($data['last_post_id'])
 1892                      {
 1893                          if (isset($post_info[$data['last_post_id']]))
 1894                          {
 1895                              $forum_data[$forum_id]['last_post_subject'] = $post_info[$data['last_post_id']]['post_subject'];
 1896                              $forum_data[$forum_id]['last_post_time'] = $post_info[$data['last_post_id']]['post_time'];
 1897                              $forum_data[$forum_id]['last_poster_id'] = $post_info[$data['last_post_id']]['poster_id'];
 1898                              $forum_data[$forum_id]['last_poster_name'] = ($post_info[$data['last_post_id']]['poster_id'] != ANONYMOUS) ? $post_info[$data['last_post_id']]['username'] : $post_info[$data['last_post_id']]['post_username'];
 1899                              $forum_data[$forum_id]['last_poster_colour'] = $post_info[$data['last_post_id']]['user_colour'];
 1900                          }
 1901                          else
 1902                          {
 1903                              // For some reason we did not find the post in the db
 1904                              $forum_data[$forum_id]['last_post_id'] = 0;
 1905                              $forum_data[$forum_id]['last_post_subject'] = '';
 1906                              $forum_data[$forum_id]['last_post_time'] = 0;
 1907                              $forum_data[$forum_id]['last_poster_id'] = 0;
 1908                              $forum_data[$forum_id]['last_poster_name'] = '';
 1909                              $forum_data[$forum_id]['last_poster_colour'] = '';
 1910                          }
 1911                      }
 1912                  }
 1913                  unset($post_info);
 1914              }
 1915   
 1916              // 6: Now do that thing
 1917              $fieldnames = array('last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour');
 1918   
 1919              if ($sync_extra)
 1920              {
 1921                  array_push($fieldnames, 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'topics_approved', 'topics_unapproved', 'topics_softdeleted');
 1922              }
 1923   
 1924              foreach ($forum_data as $forum_id => $row)
 1925              {
 1926                  $sql_ary = array();
 1927   
 1928                  foreach ($fieldnames as $fieldname)
 1929                  {
 1930                      if ($row['forum_' . $fieldname] != $row[$fieldname])
 1931                      {
 1932                          if (preg_match('#(name|colour|subject)$#', $fieldname))
 1933                          {
 1934                              $sql_ary['forum_' . $fieldname] = (string) $row[$fieldname];
 1935                          }
 1936                          else
 1937                          {
 1938                              $sql_ary['forum_' . $fieldname] = (int) $row[$fieldname];
 1939                          }
 1940                      }
 1941                  }
 1942   
 1943                  if (sizeof($sql_ary))
 1944                  {
 1945                      $sql = 'UPDATE ' . FORUMS_TABLE . '
 1946                          SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 1947                          WHERE forum_id = ' . $forum_id;
 1948                      $db->sql_query($sql);
 1949                  }
 1950              }
 1951   
 1952              $db->sql_transaction('commit');
 1953              break;
 1954   
 1955          case 'topic':
 1956              $topic_data = $post_ids = $resync_forums = $delete_topics = $delete_posts = $moved_topics = array();
 1957   
 1958              $db->sql_transaction('begin');
 1959   
 1960              $sql = 'SELECT t.topic_id, t.forum_id, t.topic_moved_id, t.topic_visibility, ' . (($sync_extra) ? 't.topic_attachment, t.topic_reported, ' : '') . 't.topic_poster, t.topic_time, t.topic_posts_approved, t.topic_posts_unapproved, t.topic_posts_softdeleted, t.topic_first_post_id, t.topic_first_poster_name, t.topic_first_poster_colour, t.topic_last_post_id, t.topic_last_post_subject, t.topic_last_poster_id, t.topic_last_poster_name, t.topic_last_poster_colour, t.topic_last_post_time
 1961                  FROM ' . TOPICS_TABLE . " t
 1962                  $where_sql";
 1963              $result = $db->sql_query($sql);
 1964   
 1965              while ($row = $db->sql_fetchrow($result))
 1966              {
 1967                  if ($row['topic_moved_id'])
 1968                  {
 1969                      $moved_topics[] = $row['topic_id'];
 1970                      continue;
 1971                  }
 1972   
 1973                  $topic_id = (int) $row['topic_id'];
 1974                  $topic_data[$topic_id] = $row;
 1975                  $topic_data[$topic_id]['visibility'] = ITEM_UNAPPROVED;
 1976                  $topic_data[$topic_id]['posts_approved'] = 0;
 1977                  $topic_data[$topic_id]['posts_unapproved'] = 0;
 1978                  $topic_data[$topic_id]['posts_softdeleted'] = 0;
 1979                  $topic_data[$topic_id]['first_post_id'] = 0;
 1980                  $topic_data[$topic_id]['last_post_id'] = 0;
 1981                  unset($topic_data[$topic_id]['topic_id']);
 1982   
 1983                  // This array holds all topic_ids
 1984                  $delete_topics[$topic_id] = '';
 1985   
 1986                  if ($sync_extra)
 1987                  {
 1988                      $topic_data[$topic_id]['reported'] = 0;
 1989                      $topic_data[$topic_id]['attachment'] = 0;
 1990                  }
 1991              }
 1992              $db->sql_freeresult($result);
 1993   
 1994              // Use "t" as table alias because of the $where_sql clause
 1995              // NOTE: 't.post_visibility' in the GROUP BY is causing a major slowdown.
 1996              $sql = 'SELECT t.topic_id, t.post_visibility, COUNT(t.post_id) AS total_posts, MIN(t.post_id) AS first_post_id, MAX(t.post_id) AS last_post_id
 1997                  FROM ' . POSTS_TABLE . " t
 1998                  $where_sql
 1999                  GROUP BY t.topic_id, t.post_visibility";
 2000              $result = $db->sql_query($sql);
 2001   
 2002              while ($row = $db->sql_fetchrow($result))
 2003              {
 2004                  $topic_id = (int) $row['topic_id'];
 2005   
 2006                  $row['first_post_id'] = (int) $row['first_post_id'];
 2007                  $row['last_post_id'] = (int) $row['last_post_id'];
 2008   
 2009                  if (!isset($topic_data[$topic_id]))
 2010                  {
 2011                      // Hey, these posts come from a topic that does not exist
 2012                      $delete_posts[$topic_id] = '';
 2013                  }
 2014                  else
 2015                  {
 2016                      // Unset the corresponding entry in $delete_topics
 2017                      // When we'll be done, only topics with no posts will remain
 2018                      unset($delete_topics[$topic_id]);
 2019   
 2020                      if ($row['post_visibility'] == ITEM_APPROVED)
 2021                      {
 2022                          $topic_data[$topic_id]['posts_approved'] = $row['total_posts'];
 2023                      }
 2024                      else if ($row['post_visibility'] == ITEM_UNAPPROVED || $row['post_visibility'] == ITEM_REAPPROVE)
 2025                      {
 2026                          $topic_data[$topic_id]['posts_unapproved'] = $row['total_posts'];
 2027                      }
 2028                      else if ($row['post_visibility'] == ITEM_DELETED)
 2029                      {
 2030                          $topic_data[$topic_id]['posts_softdeleted'] = $row['total_posts'];
 2031                      }
 2032   
 2033                      if ($row['post_visibility'] == ITEM_APPROVED)
 2034                      {
 2035                          $topic_data[$topic_id]['visibility'] = ITEM_APPROVED;
 2036                          $topic_data[$topic_id]['first_post_id'] = $row['first_post_id'];
 2037                          $topic_data[$topic_id]['last_post_id'] = $row['last_post_id'];
 2038                      }
 2039                      else if ($topic_data[$topic_id]['visibility'] != ITEM_APPROVED)
 2040                      {
 2041                          // If there is no approved post, we take the min/max of the other visibilities
 2042                          // for the last and first post info, because it is only visible to moderators anyway
 2043                          $topic_data[$topic_id]['first_post_id'] = (!empty($topic_data[$topic_id]['first_post_id'])) ? min($topic_data[$topic_id]['first_post_id'], $row['first_post_id']) : $row['first_post_id'];
 2044                          $topic_data[$topic_id]['last_post_id'] = max($topic_data[$topic_id]['last_post_id'], $row['last_post_id']);
 2045   
 2046                          if ($topic_data[$topic_id]['visibility'] == ITEM_UNAPPROVED || $topic_data[$topic_id]['visibility'] == ITEM_REAPPROVE)
 2047                          {
 2048                              // Soft delete status is stronger than unapproved.
 2049                              $topic_data[$topic_id]['visibility'] = $row['post_visibility'];
 2050                          }
 2051                      }
 2052                  }
 2053              }
 2054              $db->sql_freeresult($result);
 2055   
 2056              foreach ($topic_data as $topic_id => $row)
 2057              {
 2058                  $post_ids[] = $row['first_post_id'];
 2059                  if ($row['first_post_id'] != $row['last_post_id'])
 2060                  {
 2061                      $post_ids[] = $row['last_post_id'];
 2062                  }
 2063              }
 2064   
 2065              // Now we delete empty topics and orphan posts
 2066              if (sizeof($delete_posts))
 2067              {
 2068                  delete_posts('topic_id', array_keys($delete_posts), false);
 2069                  unset($delete_posts);
 2070              }
 2071   
 2072              if (!sizeof($topic_data))
 2073              {
 2074                  // If we get there, topic ids were invalid or topics did not contain any posts
 2075                  delete_topics($where_type, $where_ids, true);
 2076                  return;
 2077              }
 2078   
 2079              if (sizeof($delete_topics))
 2080              {
 2081                  $delete_topic_ids = array();
 2082                  foreach ($delete_topics as $topic_id => $void)
 2083                  {
 2084                      unset($topic_data[$topic_id]);
 2085                      $delete_topic_ids[] = $topic_id;
 2086                  }
 2087   
 2088                  delete_topics('topic_id', $delete_topic_ids, false);
 2089                  unset($delete_topics, $delete_topic_ids);
 2090              }
 2091   
 2092              $sql = 'SELECT p.post_id, p.topic_id, p.post_visibility, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour
 2093                  FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
 2094                  WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
 2095                      AND u.user_id = p.poster_id';
 2096              $result = $db->sql_query($sql);
 2097   
 2098              $post_ids = array();
 2099              while ($row = $db->sql_fetchrow($result))
 2100              {
 2101                  $topic_id = intval($row['topic_id']);
 2102   
 2103                  if ($row['post_id'] == $topic_data[$topic_id]['first_post_id'])
 2104                  {
 2105                      $topic_data[$topic_id]['time'] = $row['post_time'];
 2106                      $topic_data[$topic_id]['poster'] = $row['poster_id'];
 2107                      $topic_data[$topic_id]['first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
 2108                      $topic_data[$topic_id]['first_poster_colour'] = $row['user_colour'];
 2109                  }
 2110   
 2111                  if ($row['post_id'] == $topic_data[$topic_id]['last_post_id'])
 2112                  {
 2113                      $topic_data[$topic_id]['last_poster_id'] = $row['poster_id'];
 2114                      $topic_data[$topic_id]['last_post_subject'] = $row['post_subject'];
 2115                      $topic_data[$topic_id]['last_post_time'] = $row['post_time'];
 2116                      $topic_data[$topic_id]['last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
 2117                      $topic_data[$topic_id]['last_poster_colour'] = $row['user_colour'];
 2118                  }
 2119              }
 2120              $db->sql_freeresult($result);
 2121   
 2122              // Make sure shadow topics do link to existing topics
 2123              if (sizeof($moved_topics))
 2124              {
 2125                  $delete_topics = array();
 2126   
 2127                  $sql = 'SELECT t1.topic_id, t1.topic_moved_id
 2128                      FROM ' . TOPICS_TABLE . ' t1
 2129                      LEFT JOIN ' . TOPICS_TABLE . ' t2 ON (t2.topic_id = t1.topic_moved_id)
 2130                      WHERE ' . $db->sql_in_set('t1.topic_id', $moved_topics) . '
 2131                          AND t2.topic_id IS NULL';
 2132                  $result = $db->sql_query($sql);
 2133   
 2134                  while ($row = $db->sql_fetchrow($result))
 2135                  {
 2136                      $delete_topics[] = $row['topic_id'];
 2137                  }
 2138                  $db->sql_freeresult($result);
 2139   
 2140                  if (sizeof($delete_topics))
 2141                  {
 2142                      delete_topics('topic_id', $delete_topics, false);
 2143                  }
 2144                  unset($delete_topics);
 2145   
 2146                  // Make sure shadow topics having no last post data being updated (this only rarely happens...)
 2147                  $sql = 'SELECT topic_id, topic_moved_id, topic_last_post_id, topic_first_post_id
 2148                      FROM ' . TOPICS_TABLE . '
 2149                      WHERE ' . $db->sql_in_set('topic_id', $moved_topics) . '
 2150                          AND topic_last_post_time = 0';
 2151                  $result = $db->sql_query($sql);
 2152   
 2153                  $shadow_topic_data = $post_ids = array();
 2154                  while ($row = $db->sql_fetchrow($result))
 2155                  {
 2156                      $shadow_topic_data[$row['topic_moved_id']] = $row;
 2157                      $post_ids[] = $row['topic_last_post_id'];
 2158                      $post_ids[] = $row['topic_first_post_id'];
 2159                  }
 2160                  $db->sql_freeresult($result);
 2161   
 2162                  $sync_shadow_topics = array();
 2163                  if (sizeof($post_ids))
 2164                  {
 2165                      $sql = 'SELECT p.post_id, p.topic_id, p.post_visibility, p.poster_id, p.post_subject, p.post_username, p.post_time, u.username, u.user_colour
 2166                          FROM ' . POSTS_TABLE . ' p, ' . USERS_TABLE . ' u
 2167                          WHERE ' . $db->sql_in_set('p.post_id', $post_ids) . '
 2168                              AND u.user_id = p.poster_id';
 2169                      $result = $db->sql_query($sql);
 2170   
 2171                      $post_ids = array();
 2172                      while ($row = $db->sql_fetchrow($result))
 2173                      {
 2174                          $topic_id = (int) $row['topic_id'];
 2175   
 2176                          // Ok, there should be a shadow topic. If there isn't, then there's something wrong with the db.
 2177                          // However, there's not much we can do about it.
 2178                          if (!empty($shadow_topic_data[$topic_id]))
 2179                          {
 2180                              if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_first_post_id'])
 2181                              {
 2182                                  $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id'];
 2183   
 2184                                  if (!isset($sync_shadow_topics[$orig_topic_id]))
 2185                                  {
 2186                                      $sync_shadow_topics[$orig_topic_id] = array();
 2187                                  }
 2188   
 2189                                  $sync_shadow_topics[$orig_topic_id]['topic_time'] = $row['post_time'];
 2190                                  $sync_shadow_topics[$orig_topic_id]['topic_poster'] = $row['poster_id'];
 2191                                  $sync_shadow_topics[$orig_topic_id]['topic_first_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
 2192                                  $sync_shadow_topics[$orig_topic_id]['topic_first_poster_colour'] = $row['user_colour'];
 2193                              }
 2194   
 2195                              if ($row['post_id'] == $shadow_topic_data[$topic_id]['topic_last_post_id'])
 2196                              {
 2197                                  $orig_topic_id = $shadow_topic_data[$topic_id]['topic_id'];
 2198   
 2199                                  if (!isset($sync_shadow_topics[$orig_topic_id]))
 2200                                  {
 2201                                      $sync_shadow_topics[$orig_topic_id] = array();
 2202                                  }
 2203   
 2204                                  $sync_shadow_topics[$orig_topic_id]['topic_last_poster_id'] = $row['poster_id'];
 2205                                  $sync_shadow_topics[$orig_topic_id]['topic_last_post_subject'] = $row['post_subject'];
 2206                                  $sync_shadow_topics[$orig_topic_id]['topic_last_post_time'] = $row['post_time'];
 2207                                  $sync_shadow_topics[$orig_topic_id]['topic_last_poster_name'] = ($row['poster_id'] == ANONYMOUS) ? $row['post_username'] : $row['username'];
 2208                                  $sync_shadow_topics[$orig_topic_id]['topic_last_poster_colour'] = $row['user_colour'];
 2209                              }
 2210                          }
 2211                      }
 2212                      $db->sql_freeresult($result);
 2213   
 2214                      $shadow_topic_data = array();
 2215   
 2216                      // Update the information we collected
 2217                      if (sizeof($sync_shadow_topics))
 2218                      {
 2219                          foreach ($sync_shadow_topics as $sync_topic_id => $sql_ary)
 2220                          {
 2221                              $sql = 'UPDATE ' . TOPICS_TABLE . '
 2222                                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 2223                                  WHERE topic_id = ' . $sync_topic_id;
 2224                              $db->sql_query($sql);
 2225                          }
 2226                      }
 2227                  }
 2228   
 2229                  unset($sync_shadow_topics, $shadow_topic_data);
 2230              }
 2231   
 2232              // These are fields that will be synchronised
 2233              $fieldnames = array('time', 'visibility', 'posts_approved', 'posts_unapproved', 'posts_softdeleted', 'poster', 'first_post_id', 'first_poster_name', 'first_poster_colour', 'last_post_id', 'last_post_subject', 'last_post_time', 'last_poster_id', 'last_poster_name', 'last_poster_colour');
 2234   
 2235              if ($sync_extra)
 2236              {
 2237                  // This routine assumes that post_reported values are correct
 2238                  // if they are not, use sync('post_reported') first
 2239                  $sql = 'SELECT t.topic_id, p.post_id
 2240                      FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
 2241                      $where_sql_and p.topic_id = t.topic_id
 2242                          AND p.post_reported = 1
 2243                      GROUP BY t.topic_id, p.post_id";
 2244                  $result = $db->sql_query($sql);
 2245   
 2246                  $fieldnames[] = 'reported';
 2247                  while ($row = $db->sql_fetchrow($result))
 2248                  {
 2249                      $topic_data[intval($row['topic_id'])]['reported'] = 1;
 2250                  }
 2251                  $db->sql_freeresult($result);
 2252   
 2253                  // This routine assumes that post_attachment values are correct
 2254                  // if they are not, use sync('post_attachment') first
 2255                  $sql = 'SELECT t.topic_id, p.post_id
 2256                      FROM ' . TOPICS_TABLE . ' t, ' . POSTS_TABLE . " p
 2257                      $where_sql_and p.topic_id = t.topic_id
 2258                          AND p.post_attachment = 1
 2259                      GROUP BY t.topic_id, p.post_id";
 2260                  $result = $db->sql_query($sql);
 2261   
 2262                  $fieldnames[] = 'attachment';
 2263                  while ($row = $db->sql_fetchrow($result))
 2264                  {
 2265                      $topic_data[intval($row['topic_id'])]['attachment'] = 1;
 2266                  }
 2267                  $db->sql_freeresult($result);
 2268              }
 2269   
 2270              foreach ($topic_data as $topic_id => $row)
 2271              {
 2272                  $sql_ary = array();
 2273   
 2274                  foreach ($fieldnames as $fieldname)
 2275                  {
 2276                      if (isset($row[$fieldname]) && isset($row['topic_' . $fieldname]) && $row['topic_' . $fieldname] != $row[$fieldname])
 2277                      {
 2278                          $sql_ary['topic_' . $fieldname] = $row[$fieldname];
 2279                      }
 2280                  }
 2281   
 2282                  if (sizeof($sql_ary))
 2283                  {
 2284                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 2285                          SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 2286                          WHERE topic_id = ' . $topic_id;
 2287                      $db->sql_query($sql);
 2288   
 2289                      $resync_forums[$row['forum_id']] = $row['forum_id'];
 2290                  }
 2291              }
 2292              unset($topic_data);
 2293   
 2294              $db->sql_transaction('commit');
 2295   
 2296              // if some topics have been resync'ed then resync parent forums
 2297              // except when we're only syncing a range, we don't want to sync forums during
 2298              // batch processing.
 2299              if ($resync_parents && sizeof($resync_forums) && $where_type != 'range')
 2300              {
 2301                  sync('forum', 'forum_id', array_values($resync_forums), true, true);
 2302              }
 2303              break;
 2304      }
 2305   
 2306      return;
 2307  }
 2308   
 2309  /**
 2310  * Prune function
 2311  */
 2312  function prune($forum_id, $prune_mode, $prune_date, $prune_flags = 0, $auto_sync = true)
 2313  {
 2314      global $db;
 2315   
 2316      if (!is_array($forum_id))
 2317      {
 2318          $forum_id = array($forum_id);
 2319      }
 2320   
 2321      if (!sizeof($forum_id))
 2322      {
 2323          return;
 2324      }
 2325   
 2326      $sql_and = '';
 2327   
 2328      if (!($prune_flags & FORUM_FLAG_PRUNE_ANNOUNCE))
 2329      {
 2330          $sql_and .= ' AND topic_type <> ' . POST_ANNOUNCE;
 2331          $sql_and .= ' AND topic_type <> ' . POST_GLOBAL;
 2332      }
 2333   
 2334      if (!($prune_flags & FORUM_FLAG_PRUNE_STICKY))
 2335      {
 2336          $sql_and .= ' AND topic_type <> ' . POST_STICKY;
 2337      }
 2338   
 2339      if ($prune_mode == 'posted')
 2340      {
 2341          $sql_and .= " AND topic_last_post_time < $prune_date";
 2342      }
 2343   
 2344      if ($prune_mode == 'viewed')
 2345      {
 2346          $sql_and .= " AND topic_last_view_time < $prune_date";
 2347      }
 2348   
 2349      if ($prune_mode == 'shadow')
 2350      {
 2351          $sql_and .= ' AND topic_status = ' . ITEM_MOVED . " AND topic_last_post_time < $prune_date";
 2352      }
 2353   
 2354      $sql = 'SELECT topic_id
 2355          FROM ' . TOPICS_TABLE . '
 2356          WHERE ' . $db->sql_in_set('forum_id', $forum_id) . "
 2357              AND poll_start = 0
 2358              $sql_and";
 2359      $result = $db->sql_query($sql);
 2360   
 2361      $topic_list = array();
 2362      while ($row = $db->sql_fetchrow($result))
 2363      {
 2364          $topic_list[] = $row['topic_id'];
 2365      }
 2366      $db->sql_freeresult($result);
 2367   
 2368      if ($prune_flags & FORUM_FLAG_PRUNE_POLL)
 2369      {
 2370          $sql = 'SELECT topic_id
 2371              FROM ' . TOPICS_TABLE . '
 2372              WHERE ' . $db->sql_in_set('forum_id', $forum_id) . "
 2373                  AND poll_start > 0
 2374                  AND poll_last_vote < $prune_date
 2375                  $sql_and";
 2376          $result = $db->sql_query($sql);
 2377   
 2378          while ($row = $db->sql_fetchrow($result))
 2379          {
 2380              $topic_list[] = $row['topic_id'];
 2381          }
 2382          $db->sql_freeresult($result);
 2383   
 2384          $topic_list = array_unique($topic_list);
 2385      }
 2386   
 2387      return delete_topics('topic_id', $topic_list, $auto_sync, false);
 2388  }
 2389   
 2390  /**
 2391  * Function auto_prune(), this function now relies on passed vars
 2392  */
 2393  function auto_prune($forum_id, $prune_mode, $prune_flags, $prune_days, $prune_freq)
 2394  {
 2395      global $db;
 2396   
 2397      $sql = 'SELECT forum_name
 2398          FROM ' . FORUMS_TABLE . "
 2399          WHERE forum_id = $forum_id";
 2400      $result = $db->sql_query($sql, 3600);
 2401      $row = $db->sql_fetchrow($result);
 2402      $db->sql_freeresult($result);
 2403   
 2404      if ($row)
 2405      {
 2406          $prune_date = time() - ($prune_days * 86400);
 2407          $next_prune = time() + ($prune_freq * 86400);
 2408   
 2409          prune($forum_id, $prune_mode, $prune_date, $prune_flags, true);
 2410   
 2411          $sql = 'UPDATE ' . FORUMS_TABLE . "
 2412              SET prune_next = $next_prune
 2413              WHERE forum_id = $forum_id";
 2414          $db->sql_query($sql);
 2415   
 2416          add_log('admin', 'LOG_AUTO_PRUNE', $row['forum_name']);
 2417      }
 2418   
 2419      return;
 2420  }
 2421   
 2422  /**
 2423  * Cache moderators. Called whenever permissions are changed
 2424  * via admin_permissions. Changes of usernames and group names
 2425  * must be carried through for the moderators table.
 2426  *
 2427  * @param \phpbb\db\driver\driver_interface $db Database connection
 2428  * @param \phpbb\cache\driver\driver_interface Cache driver
 2429  * @param \phpbb\auth\auth $auth Authentication object
 2430  * @return null
 2431  */
 2432  function phpbb_cache_moderators($db, $cache, $auth)
 2433  {
 2434      // Remove cached sql results
 2435      $cache->destroy('sql', MODERATOR_CACHE_TABLE);
 2436   
 2437      // Clear table
 2438      switch ($db->get_sql_layer())
 2439      {
 2440          case 'sqlite':
 2441          case 'sqlite3':
 2442              $db->sql_query('DELETE FROM ' . MODERATOR_CACHE_TABLE);
 2443          break;
 2444   
 2445          default:
 2446              $db->sql_query('TRUNCATE TABLE ' . MODERATOR_CACHE_TABLE);
 2447          break;
 2448      }
 2449   
 2450      // We add moderators who have forum moderator permissions without an explicit ACL_NEVER setting
 2451      $hold_ary = $ug_id_ary = $sql_ary = array();
 2452   
 2453      // Grab all users having moderative options...
 2454      $hold_ary = $auth->acl_user_raw_data(false, 'm_%', false);
 2455   
 2456      // Add users?
 2457      if (sizeof($hold_ary))
 2458      {
 2459          // At least one moderative option warrants a display
 2460          $ug_id_ary = array_keys($hold_ary);
 2461   
 2462          // Remove users who have group memberships with DENY moderator permissions
 2463          $sql_ary_deny = array(
 2464              'SELECT'    => 'a.forum_id, ug.user_id, g.group_id',
 2465   
 2466              'FROM'        => array(
 2467                  ACL_OPTIONS_TABLE    => 'o',
 2468                  USER_GROUP_TABLE    => 'ug',
 2469                  GROUPS_TABLE        => 'g',
 2470                  ACL_GROUPS_TABLE    => 'a',
 2471              ),
 2472   
 2473              'LEFT_JOIN'    => array(
 2474                  array(
 2475                      'FROM'    => array(ACL_ROLES_DATA_TABLE => 'r'),
 2476                      'ON'    => 'a.auth_role_id = r.role_id',
 2477                  ),
 2478              ),
 2479   
 2480              'WHERE'        => '(o.auth_option_id = a.auth_option_id OR o.auth_option_id = r.auth_option_id)
 2481                  AND ((a.auth_setting = ' . ACL_NEVER . ' AND r.auth_setting IS NULL)
 2482                      OR r.auth_setting = ' . ACL_NEVER . ')
 2483                  AND a.group_id = ug.group_id
 2484                  AND g.group_id = ug.group_id
 2485                  AND NOT (ug.group_leader = 1 AND g.group_skip_auth = 1)
 2486                  AND ' . $db->sql_in_set('ug.user_id', $ug_id_ary) . "
 2487                  AND ug.user_pending = 0
 2488                  AND o.auth_option " . $db->sql_like_expression('m_' . $db->get_any_char()),
 2489          );
 2490          $sql = $db->sql_build_query('SELECT', $sql_ary_deny);
 2491          $result = $db->sql_query($sql);
 2492   
 2493          while ($row = $db->sql_fetchrow($result))
 2494          {
 2495              if (isset($hold_ary[$row['user_id']][$row['forum_id']]))
 2496              {
 2497                  unset($hold_ary[$row['user_id']][$row['forum_id']]);
 2498              }
 2499          }
 2500          $db->sql_freeresult($result);
 2501   
 2502          if (sizeof($hold_ary))
 2503          {
 2504              // Get usernames...
 2505              $sql = 'SELECT user_id, username
 2506                  FROM ' . USERS_TABLE . '
 2507                  WHERE ' . $db->sql_in_set('user_id', array_keys($hold_ary));
 2508              $result = $db->sql_query($sql);
 2509   
 2510              $usernames_ary = array();
 2511              while ($row = $db->sql_fetchrow($result))
 2512              {
 2513                  $usernames_ary[$row['user_id']] = $row['username'];
 2514              }
 2515   
 2516              foreach ($hold_ary as $user_id => $forum_id_ary)
 2517              {
 2518                  // Do not continue if user does not exist
 2519                  if (!isset($usernames_ary[$user_id]))
 2520                  {
 2521                      continue;
 2522                  }
 2523   
 2524                  foreach ($forum_id_ary as $forum_id => $auth_ary)
 2525                  {
 2526                      $sql_ary[] = array(
 2527                          'forum_id'        => (int) $forum_id,
 2528                          'user_id'        => (int) $user_id,
 2529                          'username'        => (string) $usernames_ary[$user_id],
 2530                          'group_id'        => 0,
 2531                          'group_name'    => ''
 2532                      );
 2533                  }
 2534              }
 2535          }
 2536      }
 2537   
 2538      // Now to the groups...
 2539      $hold_ary = $auth->acl_group_raw_data(false, 'm_%', false);
 2540   
 2541      if (sizeof($hold_ary))
 2542      {
 2543          $ug_id_ary = array_keys($hold_ary);
 2544   
 2545          // Make sure not hidden or special groups are involved...
 2546          $sql = 'SELECT group_name, group_id, group_type
 2547              FROM ' . GROUPS_TABLE . '
 2548              WHERE ' . $db->sql_in_set('group_id', $ug_id_ary);
 2549          $result = $db->sql_query($sql);
 2550   
 2551          $groupnames_ary = array();
 2552          while ($row = $db->sql_fetchrow($result))
 2553          {
 2554              if ($row['group_type'] == GROUP_HIDDEN || $row['group_type'] == GROUP_SPECIAL)
 2555              {
 2556                  unset($hold_ary[$row['group_id']]);
 2557              }
 2558   
 2559              $groupnames_ary[$row['group_id']] = $row['group_name'];
 2560          }
 2561          $db->sql_freeresult($result);
 2562   
 2563          foreach ($hold_ary as $group_id => $forum_id_ary)
 2564          {
 2565              // If there is no group, we do not assign it...
 2566              if (!isset($groupnames_ary[$group_id]))
 2567              {
 2568                  continue;
 2569              }
 2570   
 2571              foreach ($forum_id_ary as $forum_id => $auth_ary)
 2572              {
 2573                  $flag = false;
 2574                  foreach ($auth_ary as $auth_option => $setting)
 2575                  {
 2576                      // Make sure at least one ACL_YES option is set...
 2577                      if ($setting == ACL_YES)
 2578                      {
 2579                          $flag = true;
 2580                          break;
 2581                      }
 2582                  }
 2583   
 2584                  if (!$flag)
 2585                  {
 2586                      continue;
 2587                  }
 2588   
 2589                  $sql_ary[] = array(
 2590                      'forum_id'        => (int) $forum_id,
 2591                      'user_id'        => 0,
 2592                      'username'        => '',
 2593                      'group_id'        => (int) $group_id,
 2594                      'group_name'    => (string) $groupnames_ary[$group_id]
 2595                  );
 2596              }
 2597          }
 2598      }
 2599   
 2600      $db->sql_multi_insert(MODERATOR_CACHE_TABLE, $sql_ary);
 2601  }
 2602   
 2603  /**
 2604  * View log
 2605  *
 2606  * @param    string    $mode            The mode defines which log_type is used and from which log the entry is retrieved
 2607  * @param    array    &$log            The result array with the logs
 2608  * @param    mixed    &$log_count        If $log_count is set to false, we will skip counting all entries in the database.
 2609  *                                    Otherwise an integer with the number of total matching entries is returned.
 2610  * @param    int        $limit            Limit the number of entries that are returned
 2611  * @param    int        $offset            Offset when fetching the log entries, f.e. when paginating
 2612  * @param    mixed    $forum_id        Restrict the log entries to the given forum_id (can also be an array of forum_ids)
 2613  * @param    int        $topic_id        Restrict the log entries to the given topic_id
 2614  * @param    int        $user_id        Restrict the log entries to the given user_id
 2615  * @param    int        $log_time        Only get log entries newer than the given timestamp
 2616  * @param    string    $sort_by        SQL order option, e.g. 'l.log_time DESC'
 2617  * @param    string    $keywords        Will only return log entries that have the keywords in log_operation or log_data
 2618  *
 2619  * @return    int                Returns the offset of the last valid page, if the specified offset was invalid (too high)
 2620  */
 2621  function view_log($mode, &$log, &$log_count, $limit = 0, $offset = 0, $forum_id = 0, $topic_id = 0, $user_id = 0, $limit_days = 0, $sort_by = 'l.log_time DESC', $keywords = '')
 2622  {
 2623      global $phpbb_log;
 2624   
 2625      $count_logs = ($log_count !== false);
 2626   
 2627      $log = $phpbb_log->get_logs($mode, $count_logs, $limit, $offset, $forum_id, $topic_id, $user_id, $limit_days, $sort_by, $keywords);
 2628      $log_count = $phpbb_log->get_log_count();
 2629   
 2630      return $phpbb_log->get_valid_offset();
 2631  }
 2632   
 2633  /**
 2634  * Removes moderators and administrators from foe lists.
 2635  *
 2636  * @param \phpbb\db\driver\driver_interface $db Database connection
 2637  * @param \phpbb\auth\auth $auth Authentication object
 2638  * @param array|bool $group_id If an array, remove all members of this group from foe lists, or false to ignore
 2639  * @param array|bool $user_id If an array, remove this user from foe lists, or false to ignore
 2640  * @return null
 2641  */
 2642  function phpbb_update_foes($db, $auth, $group_id = false, $user_id = false)
 2643  {
 2644      // update foes for some user
 2645      if (is_array($user_id) && sizeof($user_id))
 2646      {
 2647          $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
 2648              WHERE ' . $db->sql_in_set('zebra_id', $user_id) . '
 2649                  AND foe = 1';
 2650          $db->sql_query($sql);
 2651          return;
 2652      }
 2653   
 2654      // update foes for some group
 2655      if (is_array($group_id) && sizeof($group_id))
 2656      {
 2657          // Grab group settings...
 2658          $sql_ary = array(
 2659              'SELECT'    => 'a.group_id',
 2660   
 2661              'FROM'        => array(
 2662                  ACL_OPTIONS_TABLE    => 'ao',
 2663                  ACL_GROUPS_TABLE    => 'a',
 2664              ),
 2665   
 2666              'LEFT_JOIN'    => array(
 2667                  array(
 2668                      'FROM'    => array(ACL_ROLES_DATA_TABLE => 'r'),
 2669                      'ON'    => 'a.auth_role_id = r.role_id',
 2670                  ),
 2671              ),
 2672   
 2673              'WHERE'        => '(ao.auth_option_id = a.auth_option_id OR ao.auth_option_id = r.auth_option_id)
 2674                  AND ' . $db->sql_in_set('a.group_id', $group_id) . "
 2675                  AND ao.auth_option IN ('a_', 'm_')",
 2676   
 2677              'GROUP_BY'    => 'a.group_id',
 2678          );
 2679          $sql = $db->sql_build_query('SELECT', $sql_ary);
 2680          $result = $db->sql_query($sql);
 2681   
 2682          $groups = array();
 2683          while ($row = $db->sql_fetchrow($result))
 2684          {
 2685              $groups[] = (int) $row['group_id'];
 2686          }
 2687          $db->sql_freeresult($result);
 2688   
 2689          if (!sizeof($groups))
 2690          {
 2691              return;
 2692          }
 2693   
 2694          switch ($db->get_sql_layer())
 2695          {
 2696              case 'mysqli':
 2697              case 'mysql4':
 2698                  $sql = 'DELETE ' . (($db->get_sql_layer() === 'mysqli' || version_compare($db->sql_server_info(true), '4.1', '>=')) ? 'z.*' : ZEBRA_TABLE) . '
 2699                      FROM ' . ZEBRA_TABLE . ' z, ' . USER_GROUP_TABLE . ' ug
 2700                      WHERE z.zebra_id = ug.user_id
 2701                          AND z.foe = 1
 2702                          AND ' . $db->sql_in_set('ug.group_id', $groups);
 2703                  $db->sql_query($sql);
 2704              break;
 2705   
 2706              default:
 2707                  $sql = 'SELECT user_id
 2708                      FROM ' . USER_GROUP_TABLE . '
 2709                      WHERE ' . $db->sql_in_set('group_id', $groups);
 2710                  $result = $db->sql_query($sql);
 2711   
 2712                  $users = array();
 2713                  while ($row = $db->sql_fetchrow($result))
 2714                  {
 2715                      $users[] = (int) $row['user_id'];
 2716                  }
 2717                  $db->sql_freeresult($result);
 2718   
 2719                  if (sizeof($users))
 2720                  {
 2721                      $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
 2722                          WHERE ' . $db->sql_in_set('zebra_id', $users) . '
 2723                              AND foe = 1';
 2724                      $db->sql_query($sql);
 2725                  }
 2726              break;
 2727          }
 2728   
 2729          return;
 2730      }
 2731   
 2732      // update foes for everyone
 2733      $perms = array();
 2734      foreach ($auth->acl_get_list(false, array('a_', 'm_'), false) as $forum_id => $forum_ary)
 2735      {
 2736          foreach ($forum_ary as $auth_option => $user_ary)
 2737          {
 2738              $perms = array_merge($perms, $user_ary);
 2739          }
 2740      }
 2741   
 2742      if (sizeof($perms))
 2743      {
 2744          $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
 2745              WHERE ' . $db->sql_in_set('zebra_id', array_unique($perms)) . '
 2746                  AND foe = 1';
 2747          $db->sql_query($sql);
 2748      }
 2749      unset($perms);
 2750  }
 2751   
 2752  /**
 2753  * Lists inactive users
 2754  */
 2755  function view_inactive_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_inactive_time DESC')
 2756  {
 2757      global $db, $user;
 2758   
 2759      $sql = 'SELECT COUNT(user_id) AS user_count
 2760          FROM ' . USERS_TABLE . '
 2761          WHERE user_type = ' . USER_INACTIVE .
 2762          (($limit_days) ? " AND user_inactive_time >= $limit_days" : '');
 2763      $result = $db->sql_query($sql);
 2764      $user_count = (int) $db->sql_fetchfield('user_count');
 2765      $db->sql_freeresult($result);
 2766   
 2767      if ($user_count == 0)
 2768      {
 2769          // Save the queries, because there are no users to display
 2770          return 0;
 2771      }
 2772   
 2773      if ($offset >= $user_count)
 2774      {
 2775          $offset = ($offset - $limit < 0) ? 0 : $offset - $limit;
 2776      }
 2777   
 2778      $sql = 'SELECT *
 2779          FROM ' . USERS_TABLE . '
 2780          WHERE user_type = ' . USER_INACTIVE .
 2781          (($limit_days) ? " AND user_inactive_time >= $limit_days" : '') . "
 2782          ORDER BY $sort_by";
 2783      $result = $db->sql_query_limit($sql, $limit, $offset);
 2784   
 2785      while ($row = $db->sql_fetchrow($result))
 2786      {
 2787          $row['inactive_reason'] = $user->lang['INACTIVE_REASON_UNKNOWN'];
 2788          switch ($row['user_inactive_reason'])
 2789          {
 2790              case INACTIVE_REGISTER:
 2791                  $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REGISTER'];
 2792              break;
 2793   
 2794              case INACTIVE_PROFILE:
 2795                  $row['inactive_reason'] = $user->lang['INACTIVE_REASON_PROFILE'];
 2796              break;
 2797   
 2798              case INACTIVE_MANUAL:
 2799                  $row['inactive_reason'] = $user->lang['INACTIVE_REASON_MANUAL'];
 2800              break;
 2801   
 2802              case INACTIVE_REMIND:
 2803                  $row['inactive_reason'] = $user->lang['INACTIVE_REASON_REMIND'];
 2804              break;
 2805          }
 2806   
 2807          $users[] = $row;
 2808      }
 2809   
 2810      return $offset;
 2811  }
 2812   
 2813  /**
 2814  * Lists warned users
 2815  */
 2816  function view_warned_users(&$users, &$user_count, $limit = 0, $offset = 0, $limit_days = 0, $sort_by = 'user_warnings DESC')
 2817  {
 2818      global $db;
 2819   
 2820      $sql = 'SELECT user_id, username, user_colour, user_warnings, user_last_warning
 2821          FROM ' . USERS_TABLE . '
 2822          WHERE user_warnings > 0
 2823          ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '') . "
 2824          ORDER BY $sort_by";
 2825      $result = $db->sql_query_limit($sql, $limit, $offset);
 2826      $users = $db->sql_fetchrowset($result);
 2827      $db->sql_freeresult($result);
 2828   
 2829      $sql = 'SELECT count(user_id) AS user_count
 2830          FROM ' . USERS_TABLE . '
 2831          WHERE user_warnings > 0
 2832          ' . (($limit_days) ? "AND user_last_warning >= $limit_days" : '');
 2833      $result = $db->sql_query($sql);
 2834      $user_count = (int) $db->sql_fetchfield('user_count');
 2835      $db->sql_freeresult($result);
 2836   
 2837      return;
 2838  }
 2839   
 2840  /**
 2841  * Get database size
 2842  * Currently only mysql and mssql are supported
 2843  */
 2844  function get_database_size()
 2845  {
 2846      global $db, $user, $table_prefix;
 2847   
 2848      $database_size = false;
 2849   
 2850      // This code is heavily influenced by a similar routine in phpMyAdmin 2.2.0
 2851      switch ($db->get_sql_layer())
 2852      {
 2853          case 'mysql':
 2854          case 'mysql4':
 2855          case 'mysqli':
 2856              $sql = 'SELECT VERSION() AS mysql_version';
 2857              $result = $db->sql_query($sql);
 2858              $row = $db->sql_fetchrow($result);
 2859              $db->sql_freeresult($result);
 2860   
 2861              if ($row)
 2862              {
 2863                  $version = $row['mysql_version'];
 2864   
 2865                  if (preg_match('#(3\.23|[45]\.)#', $version))
 2866                  {
 2867                      $db_name = (preg_match('#^(?:3\.23\.(?:[6-9]|[1-9]{2}))|[45]\.#', $version)) ? "`{$db->get_db_name()}`" : $db->get_db_name();
 2868   
 2869                      $sql = 'SHOW TABLE STATUS
 2870                          FROM ' . $db_name;
 2871                      $result = $db->sql_query($sql, 7200);
 2872   
 2873                      $database_size = 0;
 2874                      while ($row = $db->sql_fetchrow($result))
 2875                      {
 2876                          if ((isset($row['Type']) && $row['Type'] != 'MRG_MyISAM') || (isset($row['Engine']) && ($row['Engine'] == 'MyISAM' || $row['Engine'] == 'InnoDB')))
 2877                          {
 2878                              if ($table_prefix != '')
 2879                              {
 2880                                  if (strpos($row['Name'], $table_prefix) !== false)
 2881                                  {
 2882                                      $database_size += $row['Data_length'] + $row['Index_length'];
 2883                                  }
 2884                              }
 2885                              else
 2886                              {
 2887                                  $database_size += $row['Data_length'] + $row['Index_length'];
 2888                              }
 2889                          }
 2890                      }
 2891                      $db->sql_freeresult($result);
 2892                  }
 2893              }
 2894          break;
 2895   
 2896          case 'sqlite':
 2897          case 'sqlite3':
 2898              global $dbhost;
 2899   
 2900              if (file_exists($dbhost))
 2901              {
 2902                  $database_size = filesize($dbhost);
 2903              }
 2904   
 2905          break;
 2906   
 2907          case 'mssql':
 2908          case 'mssql_odbc':
 2909          case 'mssqlnative':
 2910              $sql = 'SELECT @@VERSION AS mssql_version';
 2911              $result = $db->sql_query($sql);
 2912              $row = $db->sql_fetchrow($result);
 2913              $db->sql_freeresult($result);
 2914   
 2915              $sql = 'SELECT ((SUM(size) * 8.0) * 1024.0) as dbsize
 2916                  FROM sysfiles';
 2917   
 2918              if ($row)
 2919              {
 2920                  // Azure stats are stored elsewhere
 2921                  if (strpos($row['mssql_version'], 'SQL Azure') !== false)
 2922                  {
 2923                      $sql = 'SELECT ((SUM(reserved_page_count) * 8.0) * 1024.0) as dbsize
 2924                      FROM sys.dm_db_partition_stats';
 2925                  }
 2926              }
 2927   
 2928              $result = $db->sql_query($sql, 7200);
 2929              $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false;
 2930              $db->sql_freeresult($result);
 2931          break;
 2932   
 2933          case 'postgres':
 2934              $sql = "SELECT proname
 2935                  FROM pg_proc
 2936                  WHERE proname = 'pg_database_size'";
 2937              $result = $db->sql_query($sql);
 2938              $row = $db->sql_fetchrow($result);
 2939              $db->sql_freeresult($result);
 2940   
 2941              if ($row['proname'] == 'pg_database_size')
 2942              {
 2943                  $database = $db->get_db_name();
 2944                  if (strpos($database, '.') !== false)
 2945                  {
 2946                      list($database, ) = explode('.', $database);
 2947                  }
 2948   
 2949                  $sql = "SELECT oid
 2950                      FROM pg_database
 2951                      WHERE datname = '$database'";
 2952                  $result = $db->sql_query($sql);
 2953                  $row = $db->sql_fetchrow($result);
 2954                  $db->sql_freeresult($result);
 2955   
 2956                  $oid = $row['oid'];
 2957   
 2958                  $sql = 'SELECT pg_database_size(' . $oid . ') as size';
 2959                  $result = $db->sql_query($sql);
 2960                  $row = $db->sql_fetchrow($result);
 2961                  $db->sql_freeresult($result);
 2962   
 2963                  $database_size = $row['size'];
 2964              }
 2965          break;
 2966   
 2967          case 'oracle':
 2968              $sql = 'SELECT SUM(bytes) as dbsize
 2969                  FROM user_segments';
 2970              $result = $db->sql_query($sql, 7200);
 2971              $database_size = ($row = $db->sql_fetchrow($result)) ? $row['dbsize'] : false;
 2972              $db->sql_freeresult($result);
 2973          break;
 2974      }
 2975   
 2976      $database_size = ($database_size !== false) ? get_formatted_filesize($database_size) : $user->lang['NOT_AVAILABLE'];
 2977   
 2978      return $database_size;
 2979  }
 2980   
 2981  /**
 2982  * Retrieve contents from remotely stored file
 2983  */
 2984  function get_remote_file($host, $directory, $filename, &$errstr, &$errno, $port = 80, $timeout = 6)
 2985  {
 2986      global $user;
 2987   
 2988      if ($fsock = @fsockopen($host, $port, $errno, $errstr, $timeout))
 2989      {
 2990          @fputs($fsock, "GET $directory/$filename HTTP/1.0\r\n");
 2991          @fputs($fsock, "HOST: $host\r\n");
 2992          @fputs($fsock, "Connection: close\r\n\r\n");
 2993   
 2994          $timer_stop = time() + $timeout;
 2995          stream_set_timeout($fsock, $timeout);
 2996   
 2997          $file_info = '';
 2998          $get_info = false;
 2999   
 3000          while (!@feof($fsock))
 3001          {
 3002              if ($get_info)
 3003              {
 3004                  $file_info .= @fread($fsock, 1024);
 3005              }
 3006              else
 3007              {
 3008                  $line = @fgets($fsock, 1024);
 3009                  if ($line == "\r\n")
 3010                  {
 3011                      $get_info = true;
 3012                  }
 3013                  else if (stripos($line, '404 not found') !== false)
 3014                  {
 3015                      $errstr = $user->lang('FILE_NOT_FOUND', $filename);
 3016                      return false;
 3017                  }
 3018              }
 3019   
 3020              $stream_meta_data = stream_get_meta_data($fsock);
 3021   
 3022              if (!empty($stream_meta_data['timed_out']) || time() >= $timer_stop)
 3023              {
 3024                  $errstr = $user->lang['FSOCK_TIMEOUT'];
 3025                  return false;
 3026              }
 3027          }
 3028          @fclose($fsock);
 3029      }
 3030      else
 3031      {
 3032          if ($errstr)
 3033          {
 3034              $errstr = utf8_convert_message($errstr);
 3035              return false;
 3036          }
 3037          else
 3038          {
 3039              $errstr = $user->lang['FSOCK_DISABLED'];
 3040              return false;
 3041          }
 3042      }
 3043   
 3044      return $file_info;
 3045  }
 3046   
 3047  /*
 3048  * Tidy Warnings
 3049  * Remove all warnings which have now expired from the database
 3050  * The duration of a warning can be defined by the administrator
 3051  * This only removes the warning and reduces the associated count,
 3052  * it does not remove the user note recording the contents of the warning
 3053  */
 3054  function tidy_warnings()
 3055  {
 3056      global $db, $config;
 3057   
 3058      $expire_date = time() - ($config['warnings_expire_days'] * 86400);
 3059      $warning_list = $user_list = array();
 3060   
 3061      $sql = 'SELECT * FROM ' . WARNINGS_TABLE . "
 3062          WHERE warning_time < $expire_date";
 3063      $result = $db->sql_query($sql);
 3064   
 3065      while ($row = $db->sql_fetchrow($result))
 3066      {
 3067          $warning_list[] = $row['warning_id'];
 3068          $user_list[$row['user_id']] = isset($user_list[$row['user_id']]) ? ++$user_list[$row['user_id']] : 1;
 3069      }
 3070      $db->sql_freeresult($result);
 3071   
 3072      if (sizeof($warning_list))
 3073      {
 3074          $db->sql_transaction('begin');
 3075   
 3076          $sql = 'DELETE FROM ' . WARNINGS_TABLE . '
 3077              WHERE ' . $db->sql_in_set('warning_id', $warning_list);
 3078          $db->sql_query($sql);
 3079   
 3080          foreach ($user_list as $user_id => $value)
 3081          {
 3082              $sql = 'UPDATE ' . USERS_TABLE . " SET user_warnings = user_warnings - $value
 3083                  WHERE user_id = $user_id";
 3084              $db->sql_query($sql);
 3085          }
 3086   
 3087          $db->sql_transaction('commit');
 3088      }
 3089   
 3090      set_config('warnings_last_gc', time(), true);
 3091  }
 3092   
 3093  /**
 3094  * Tidy database, doing some maintanance tasks
 3095  */
 3096  function tidy_database()
 3097  {
 3098      global $db;
 3099   
 3100      // Here we check permission consistency
 3101   
 3102      // Sometimes, it can happen permission tables having forums listed which do not exist
 3103      $sql = 'SELECT forum_id
 3104          FROM ' . FORUMS_TABLE;
 3105      $result = $db->sql_query($sql);
 3106   
 3107      $forum_ids = array(0);
 3108      while ($row = $db->sql_fetchrow($result))
 3109      {
 3110          $forum_ids[] = $row['forum_id'];
 3111      }
 3112      $db->sql_freeresult($result);
 3113   
 3114      // Delete those rows from the acl tables not having listed the forums above
 3115      $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . '
 3116          WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
 3117      $db->sql_query($sql);
 3118   
 3119      $sql = 'DELETE FROM ' . ACL_USERS_TABLE . '
 3120          WHERE ' . $db->sql_in_set('forum_id', $forum_ids, true);
 3121      $db->sql_query($sql);
 3122   
 3123      set_config('database_last_gc', time(), true);
 3124  }
 3125   
 3126  /**
 3127  * Add permission language - this will make sure custom files will be included
 3128  */
 3129  function add_permission_language()
 3130  {
 3131      global $user, $phpEx, $phpbb_extension_manager;
 3132   
 3133      // add permission language files from extensions
 3134      $finder = $phpbb_extension_manager->get_finder();
 3135   
 3136      $lang_files = $finder
 3137          ->prefix('permissions_')
 3138          ->suffix(".$phpEx")
 3139          ->core_path('language/' . $user->lang_name . '/')
 3140          ->extension_directory('/language/' . $user->lang_name)
 3141          ->find();
 3142   
 3143      foreach ($lang_files as $lang_file => $ext_name)
 3144      {
 3145          if ($ext_name === '/')
 3146          {
 3147              $user->add_lang($lang_file);
 3148          }
 3149          else
 3150          {
 3151              $user->add_lang_ext($ext_name, $lang_file);
 3152          }
 3153      }
 3154  }
 3155   
 3156  /**
 3157   * Enables a particular flag in a bitfield column of a given table.
 3158   *
 3159   * @param string    $table_name        The table to update
 3160   * @param string    $column_name    The column containing a bitfield to update
 3161   * @param int        $flag            The binary flag which is OR-ed with the current column value
 3162   * @param string    $sql_more        This string is attached to the sql query generated to update the table.
 3163   *
 3164   * @return null
 3165   */
 3166  function enable_bitfield_column_flag($table_name, $column_name, $flag, $sql_more = '')
 3167  {
 3168      global $db;
 3169   
 3170      $sql = 'UPDATE ' . $table_name . '
 3171          SET ' . $column_name . ' = ' . $db->sql_bit_or($column_name, $flag) . '
 3172          ' . $sql_more;
 3173      $db->sql_query($sql);
 3174  }
 3175