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_user.php

Zuletzt modifiziert: 09.10.2024, 12:51 - Dateigröße: 91.88 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  * Obtain user_ids from usernames or vice versa. Returns false on
 0024  * success else the error string
 0025  *
 0026  * @param array &$user_id_ary The user ids to check or empty if usernames used
 0027  * @param array &$username_ary The usernames to check or empty if user ids used
 0028  * @param mixed $user_type Array of user types to check, false if not restricting by user type
 0029  */
 0030  function user_get_id_name(&$user_id_ary, &$username_ary, $user_type = false)
 0031  {
 0032      global $db;
 0033   
 0034      // Are both arrays already filled? Yep, return else
 0035      // are neither array filled?
 0036      if ($user_id_ary && $username_ary)
 0037      {
 0038          return false;
 0039      }
 0040      else if (!$user_id_ary && !$username_ary)
 0041      {
 0042          return 'NO_USERS';
 0043      }
 0044   
 0045      $which_ary = ($user_id_ary) ? 'user_id_ary' : 'username_ary';
 0046   
 0047      if ($$which_ary && !is_array($$which_ary))
 0048      {
 0049          $$which_ary = array($$which_ary);
 0050      }
 0051   
 0052      $sql_in = ($which_ary == 'user_id_ary') ? array_map('intval', $$which_ary) : array_map('utf8_clean_string', $$which_ary);
 0053      unset($$which_ary);
 0054   
 0055      $user_id_ary = $username_ary = array();
 0056   
 0057      // Grab the user id/username records
 0058      $sql_where = ($which_ary == 'user_id_ary') ? 'user_id' : 'username_clean';
 0059      $sql = 'SELECT user_id, username
 0060          FROM ' . USERS_TABLE . '
 0061          WHERE ' . $db->sql_in_set($sql_where, $sql_in);
 0062   
 0063      if ($user_type !== false && !empty($user_type))
 0064      {
 0065          $sql .= ' AND ' . $db->sql_in_set('user_type', $user_type);
 0066      }
 0067   
 0068      $result = $db->sql_query($sql);
 0069   
 0070      if (!($row = $db->sql_fetchrow($result)))
 0071      {
 0072          $db->sql_freeresult($result);
 0073          return 'NO_USERS';
 0074      }
 0075   
 0076      do
 0077      {
 0078          $username_ary[$row['user_id']] = $row['username'];
 0079          $user_id_ary[] = $row['user_id'];
 0080      }
 0081      while ($row = $db->sql_fetchrow($result));
 0082      $db->sql_freeresult($result);
 0083   
 0084      return false;
 0085  }
 0086   
 0087  /**
 0088  * Get latest registered username and update database to reflect it
 0089  */
 0090  function update_last_username()
 0091  {
 0092      global $db;
 0093   
 0094      // Get latest username
 0095      $sql = 'SELECT user_id, username, user_colour
 0096          FROM ' . USERS_TABLE . '
 0097          WHERE user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')
 0098          ORDER BY user_id DESC';
 0099      $result = $db->sql_query_limit($sql, 1);
 0100      $row = $db->sql_fetchrow($result);
 0101      $db->sql_freeresult($result);
 0102   
 0103      if ($row)
 0104      {
 0105          set_config('newest_user_id', $row['user_id'], true);
 0106          set_config('newest_username', $row['username'], true);
 0107          set_config('newest_user_colour', $row['user_colour'], true);
 0108      }
 0109  }
 0110   
 0111  /**
 0112  * Updates a username across all relevant tables/fields
 0113  *
 0114  * @param string $old_name the old/current username
 0115  * @param string $new_name the new username
 0116  */
 0117  function user_update_name($old_name, $new_name)
 0118  {
 0119      global $config, $db, $cache, $phpbb_dispatcher;
 0120   
 0121      $update_ary = array(
 0122          FORUMS_TABLE            => array('forum_last_poster_name'),
 0123          MODERATOR_CACHE_TABLE    => array('username'),
 0124          POSTS_TABLE                => array('post_username'),
 0125          TOPICS_TABLE            => array('topic_first_poster_name', 'topic_last_poster_name'),
 0126      );
 0127   
 0128      foreach ($update_ary as $table => $field_ary)
 0129      {
 0130          foreach ($field_ary as $field)
 0131          {
 0132              $sql = "UPDATE $table
 0133                  SET $field = '" . $db->sql_escape($new_name) . "'
 0134                  WHERE $field = '" . $db->sql_escape($old_name) . "'";
 0135              $db->sql_query($sql);
 0136          }
 0137      }
 0138   
 0139      if ($config['newest_username'] == $old_name)
 0140      {
 0141          set_config('newest_username', $new_name, true);
 0142      }
 0143   
 0144      /**
 0145      * Update a username when it is changed
 0146      *
 0147      * @event core.update_username
 0148      * @var    string    old_name    The old username that is replaced
 0149      * @var    string    new_name    The new username
 0150      * @since 3.1.0-a1
 0151      */
 0152      $vars = array('old_name', 'new_name');
 0153      extract($phpbb_dispatcher->trigger_event('core.update_username', compact($vars)));
 0154   
 0155      // Because some tables/caches use username-specific data we need to purge this here.
 0156      $cache->destroy('sql', MODERATOR_CACHE_TABLE);
 0157  }
 0158   
 0159  /**
 0160  * Adds an user
 0161  *
 0162  * @param mixed $user_row An array containing the following keys (and the appropriate values): username, group_id (the group to place the user in), user_email and the user_type(usually 0). Additional entries not overridden by defaults will be forwarded.
 0163  * @param string $cp_data custom profile fields, see custom_profile::build_insert_sql_array
 0164  * @param array $notifications_data The notifications settings for the new user
 0165  * @return the new user's ID.
 0166  */
 0167  function user_add($user_row, $cp_data = false, $notifications_data = null)
 0168  {
 0169      global $db, $user, $auth, $config, $phpbb_root_path, $phpEx;
 0170      global $phpbb_dispatcher, $phpbb_container;
 0171   
 0172      if (empty($user_row['username']) || !isset($user_row['group_id']) || !isset($user_row['user_email']) || !isset($user_row['user_type']))
 0173      {
 0174          return false;
 0175      }
 0176   
 0177      $username_clean = utf8_clean_string($user_row['username']);
 0178   
 0179      if (empty($username_clean))
 0180      {
 0181          return false;
 0182      }
 0183   
 0184      $sql_ary = array(
 0185          'username'            => $user_row['username'],
 0186          'username_clean'    => $username_clean,
 0187          'user_password'        => (isset($user_row['user_password'])) ? $user_row['user_password'] : '',
 0188          'user_email'        => strtolower($user_row['user_email']),
 0189          'user_email_hash'    => phpbb_email_hash($user_row['user_email']),
 0190          'group_id'            => $user_row['group_id'],
 0191          'user_type'            => $user_row['user_type'],
 0192      );
 0193   
 0194      // These are the additional vars able to be specified
 0195      $additional_vars = array(
 0196          'user_permissions'    => '',
 0197          'user_timezone'        => $config['board_timezone'],
 0198          'user_dateformat'    => $config['default_dateformat'],
 0199          'user_lang'            => $config['default_lang'],
 0200          'user_style'        => (int) $config['default_style'],
 0201          'user_actkey'        => '',
 0202          'user_ip'            => '',
 0203          'user_regdate'        => time(),
 0204          'user_passchg'        => time(),
 0205          'user_options'        => 230271,
 0206          // We do not set the new flag here - registration scripts need to specify it
 0207          'user_new'            => 0,
 0208   
 0209          'user_inactive_reason'    => 0,
 0210          'user_inactive_time'    => 0,
 0211          'user_lastmark'            => time(),
 0212          'user_lastvisit'        => 0,
 0213          'user_lastpost_time'    => 0,
 0214          'user_lastpage'            => '',
 0215          'user_posts'            => 0,
 0216          'user_colour'            => '',
 0217          'user_avatar'            => '',
 0218          'user_avatar_type'        => '',
 0219          'user_avatar_width'        => 0,
 0220          'user_avatar_height'    => 0,
 0221          'user_new_privmsg'        => 0,
 0222          'user_unread_privmsg'    => 0,
 0223          'user_last_privmsg'        => 0,
 0224          'user_message_rules'    => 0,
 0225          'user_full_folder'        => PRIVMSGS_NO_BOX,
 0226          'user_emailtime'        => 0,
 0227   
 0228          'user_notify'            => 0,
 0229          'user_notify_pm'        => 1,
 0230          'user_notify_type'        => NOTIFY_EMAIL,
 0231          'user_allow_pm'            => 1,
 0232          'user_allow_viewonline'    => 1,
 0233          'user_allow_viewemail'    => 1,
 0234          'user_allow_massemail'    => 1,
 0235   
 0236          'user_sig'                    => '',
 0237          'user_sig_bbcode_uid'        => '',
 0238          'user_sig_bbcode_bitfield'    => '',
 0239   
 0240          'user_form_salt'            => unique_id(),
 0241      );
 0242   
 0243      // Now fill the sql array with not required variables
 0244      foreach ($additional_vars as $key => $default_value)
 0245      {
 0246          $sql_ary[$key] = (isset($user_row[$key])) ? $user_row[$key] : $default_value;
 0247      }
 0248   
 0249      // Any additional variables in $user_row not covered above?
 0250      $remaining_vars = array_diff(array_keys($user_row), array_keys($sql_ary));
 0251   
 0252      // Now fill our sql array with the remaining vars
 0253      if (sizeof($remaining_vars))
 0254      {
 0255          foreach ($remaining_vars as $key)
 0256          {
 0257              $sql_ary[$key] = $user_row[$key];
 0258          }
 0259      }
 0260   
 0261      /**
 0262      * Use this event to modify the values to be inserted when a user is added
 0263      *
 0264      * @event core.user_add_modify_data
 0265      * @var array    user_row        Array of user details submited to user_add
 0266      * @var array    cp_data            Array of Custom profile fields submited to user_add
 0267      * @var array    sql_ary        Array of data to be inserted when a user is added
 0268      * @since 3.1.0-a1
 0269      * @change 3.1.0-b5
 0270      */
 0271      $vars = array('user_row', 'cp_data', 'sql_ary');
 0272      extract($phpbb_dispatcher->trigger_event('core.user_add_modify_data', compact($vars)));
 0273   
 0274      $sql = 'INSERT INTO ' . USERS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
 0275      $db->sql_query($sql);
 0276   
 0277      $user_id = $db->sql_nextid();
 0278   
 0279      // Insert Custom Profile Fields
 0280      if ($cp_data !== false && sizeof($cp_data))
 0281      {
 0282          $cp_data['user_id'] = (int) $user_id;
 0283   
 0284          $cp = $phpbb_container->get('profilefields.manager');
 0285          $sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' .
 0286              $db->sql_build_array('INSERT', $cp->build_insert_sql_array($cp_data));
 0287          $db->sql_query($sql);
 0288      }
 0289   
 0290      // Place into appropriate group...
 0291      $sql = 'INSERT INTO ' . USER_GROUP_TABLE . ' ' . $db->sql_build_array('INSERT', array(
 0292          'user_id'        => (int) $user_id,
 0293          'group_id'        => (int) $user_row['group_id'],
 0294          'user_pending'    => 0)
 0295      );
 0296      $db->sql_query($sql);
 0297   
 0298      // Now make it the users default group...
 0299      group_set_user_default($user_row['group_id'], array($user_id), false);
 0300   
 0301      // Add to newly registered users group if user_new is 1
 0302      if ($config['new_member_post_limit'] && $sql_ary['user_new'])
 0303      {
 0304          $sql = 'SELECT group_id
 0305              FROM ' . GROUPS_TABLE . "
 0306              WHERE group_name = 'NEWLY_REGISTERED'
 0307                  AND group_type = " . GROUP_SPECIAL;
 0308          $result = $db->sql_query($sql);
 0309          $add_group_id = (int) $db->sql_fetchfield('group_id');
 0310          $db->sql_freeresult($result);
 0311   
 0312          if ($add_group_id)
 0313          {
 0314              global $phpbb_log;
 0315   
 0316              // Because these actions only fill the log unneccessarily we skip the add_log() entry.
 0317              $phpbb_log->disable('admin');
 0318   
 0319              // Add user to "newly registered users" group and set to default group if admin specified so.
 0320              if ($config['new_member_group_default'])
 0321              {
 0322                  group_user_add($add_group_id, $user_id, false, false, true);
 0323                  $user_row['group_id'] = $add_group_id;
 0324              }
 0325              else
 0326              {
 0327                  group_user_add($add_group_id, $user_id);
 0328              }
 0329   
 0330              $phpbb_log->enable('admin');
 0331          }
 0332      }
 0333   
 0334      // set the newest user and adjust the user count if the user is a normal user and no activation mail is sent
 0335      if ($user_row['user_type'] == USER_NORMAL || $user_row['user_type'] == USER_FOUNDER)
 0336      {
 0337          set_config('newest_user_id', $user_id, true);
 0338          set_config('newest_username', $user_row['username'], true);
 0339          set_config_count('num_users', 1, true);
 0340   
 0341          $sql = 'SELECT group_colour
 0342              FROM ' . GROUPS_TABLE . '
 0343              WHERE group_id = ' . (int) $user_row['group_id'];
 0344          $result = $db->sql_query_limit($sql, 1);
 0345          $row = $db->sql_fetchrow($result);
 0346          $db->sql_freeresult($result);
 0347   
 0348          set_config('newest_user_colour', $row['group_colour'], true);
 0349      }
 0350   
 0351      // Use default notifications settings if notifications_data is not set
 0352      if ($notifications_data === null)
 0353      {
 0354          $notifications_data = array(
 0355              array(
 0356                  'item_type'    => 'notification.type.post',
 0357                  'method'    => 'notification.method.email',
 0358              ),
 0359              array(
 0360                  'item_type'    => 'notification.type.topic',
 0361                  'method'    => 'notification.method.email',
 0362              ),
 0363          );
 0364      }
 0365   
 0366      // Subscribe user to notifications if necessary
 0367      if (!empty($notifications_data))
 0368      {
 0369          $phpbb_notifications = $phpbb_container->get('notification_manager');
 0370          foreach ($notifications_data as $subscription)
 0371          {
 0372              $phpbb_notifications->add_subscription($subscription['item_type'], 0, $subscription['method'], $user_id);
 0373          }
 0374      }
 0375   
 0376      /**
 0377      * Event that returns user id, user detals and user CPF of newly registared user
 0378      *
 0379      * @event core.user_add_after
 0380      * @var int        user_id            User id of newly registared user
 0381      * @var array    user_row        Array of user details submited to user_add
 0382      * @var array    cp_data            Array of Custom profile fields submited to user_add
 0383      * @since 3.1.0-b5
 0384      */
 0385      $vars = array('user_id', 'user_row', 'cp_data');
 0386      extract($phpbb_dispatcher->trigger_event('core.user_add_after', compact($vars)));
 0387   
 0388      return $user_id;
 0389  }
 0390   
 0391  /**
 0392   * Remove User
 0393   *
 0394   * @param string    $mode        Either 'retain' or 'remove'
 0395   * @param mixed        $user_ids    Either an array of integers or an integer
 0396   * @param bool        $retain_username
 0397   * @return bool
 0398   */
 0399  function user_delete($mode, $user_ids, $retain_username = true)
 0400  {
 0401      global $cache, $config, $db, $user, $phpbb_dispatcher;
 0402      global $phpbb_root_path, $phpEx;
 0403   
 0404      $db->sql_transaction('begin');
 0405   
 0406      $user_rows = array();
 0407      if (!is_array($user_ids))
 0408      {
 0409          $user_ids = array($user_ids);
 0410      }
 0411   
 0412      $user_id_sql = $db->sql_in_set('user_id', $user_ids);
 0413   
 0414      $sql = 'SELECT *
 0415          FROM ' . USERS_TABLE . '
 0416          WHERE ' . $user_id_sql;
 0417      $result = $db->sql_query($sql);
 0418      while ($row = $db->sql_fetchrow($result))
 0419      {
 0420          $user_rows[(int) $row['user_id']] = $row;
 0421      }
 0422      $db->sql_freeresult($result);
 0423   
 0424      if (empty($user_rows))
 0425      {
 0426          return false;
 0427      }
 0428   
 0429      /**
 0430      * Event before a user is deleted
 0431      *
 0432      * @event core.delete_user_before
 0433      * @var    string    mode        Mode of deletion (retain/delete posts)
 0434      * @var    array    user_ids    IDs of the deleted user
 0435      * @var    mixed    retain_username    True if username should be retained
 0436      *                or false if not
 0437      * @since 3.1.0-a1
 0438      */
 0439      $vars = array('mode', 'user_ids', 'retain_username');
 0440      extract($phpbb_dispatcher->trigger_event('core.delete_user_before', compact($vars)));
 0441   
 0442      // Before we begin, we will remove the reports the user issued.
 0443      $sql = 'SELECT r.post_id, p.topic_id
 0444          FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p
 0445          WHERE ' . $db->sql_in_set('r.user_id', $user_ids) . '
 0446              AND p.post_id = r.post_id';
 0447      $result = $db->sql_query($sql);
 0448   
 0449      $report_posts = $report_topics = array();
 0450      while ($row = $db->sql_fetchrow($result))
 0451      {
 0452          $report_posts[] = $row['post_id'];
 0453          $report_topics[] = $row['topic_id'];
 0454      }
 0455      $db->sql_freeresult($result);
 0456   
 0457      if (sizeof($report_posts))
 0458      {
 0459          $report_posts = array_unique($report_posts);
 0460          $report_topics = array_unique($report_topics);
 0461   
 0462          // Get a list of topics that still contain reported posts
 0463          $sql = 'SELECT DISTINCT topic_id
 0464              FROM ' . POSTS_TABLE . '
 0465              WHERE ' . $db->sql_in_set('topic_id', $report_topics) . '
 0466                  AND post_reported = 1
 0467                  AND ' . $db->sql_in_set('post_id', $report_posts, true);
 0468          $result = $db->sql_query($sql);
 0469   
 0470          $keep_report_topics = array();
 0471          while ($row = $db->sql_fetchrow($result))
 0472          {
 0473              $keep_report_topics[] = $row['topic_id'];
 0474          }
 0475          $db->sql_freeresult($result);
 0476   
 0477          if (sizeof($keep_report_topics))
 0478          {
 0479              $report_topics = array_diff($report_topics, $keep_report_topics);
 0480          }
 0481          unset($keep_report_topics);
 0482   
 0483          // Now set the flags back
 0484          $sql = 'UPDATE ' . POSTS_TABLE . '
 0485              SET post_reported = 0
 0486              WHERE ' . $db->sql_in_set('post_id', $report_posts);
 0487          $db->sql_query($sql);
 0488   
 0489          if (sizeof($report_topics))
 0490          {
 0491              $sql = 'UPDATE ' . TOPICS_TABLE . '
 0492                  SET topic_reported = 0
 0493                  WHERE ' . $db->sql_in_set('topic_id', $report_topics);
 0494              $db->sql_query($sql);
 0495          }
 0496      }
 0497   
 0498      // Remove reports
 0499      $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE ' . $user_id_sql);
 0500   
 0501      $num_users_delta = 0;
 0502   
 0503      // Some things need to be done in the loop (if the query changes based
 0504      // on which user is currently being deleted)
 0505      $added_guest_posts = 0;
 0506      foreach ($user_rows as $user_id => $user_row)
 0507      {
 0508          if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == 'avatar.driver.upload')
 0509          {
 0510              avatar_delete('user', $user_row);
 0511          }
 0512   
 0513          // Decrement number of users if this user is active
 0514          if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
 0515          {
 0516              --$num_users_delta;
 0517          }
 0518   
 0519          switch ($mode)
 0520          {
 0521              case 'retain':
 0522                  if ($retain_username === false)
 0523                  {
 0524                      $post_username = $user->lang['GUEST'];
 0525                  }
 0526                  else
 0527                  {
 0528                      $post_username = $user_row['username'];
 0529                  }
 0530   
 0531                  // If the user is inactive and newly registered
 0532                  // we assume no posts from the user, and save
 0533                  // the queries
 0534                  if ($user_row['user_type'] != USER_INACTIVE || $user_row['user_inactive_reason'] != INACTIVE_REGISTER || $user_row['user_posts'])
 0535                  {
 0536                      // When we delete these users and retain the posts, we must assign all the data to the guest user
 0537                      $sql = 'UPDATE ' . FORUMS_TABLE . '
 0538                          SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = ''
 0539                          WHERE forum_last_poster_id = $user_id";
 0540                      $db->sql_query($sql);
 0541   
 0542                      $sql = 'UPDATE ' . POSTS_TABLE . '
 0543                          SET poster_id = ' . ANONYMOUS . ", post_username = '" . $db->sql_escape($post_username) . "'
 0544                          WHERE poster_id = $user_id";
 0545                      $db->sql_query($sql);
 0546   
 0547                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 0548                          SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = ''
 0549                          WHERE topic_poster = $user_id";
 0550                      $db->sql_query($sql);
 0551   
 0552                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 0553                          SET topic_last_poster_id = ' . ANONYMOUS . ", topic_last_poster_name = '" . $db->sql_escape($post_username) . "', topic_last_poster_colour = ''
 0554                          WHERE topic_last_poster_id = $user_id";
 0555                      $db->sql_query($sql);
 0556   
 0557                      // Since we change every post by this author, we need to count this amount towards the anonymous user
 0558   
 0559                      if ($user_row['user_posts'])
 0560                      {
 0561                          $added_guest_posts += $user_row['user_posts'];
 0562                      }
 0563                  }
 0564              break;
 0565   
 0566              case 'remove':
 0567                  // there is nothing variant specific to deleting posts
 0568              break;
 0569          }
 0570      }
 0571   
 0572      if ($num_users_delta != 0)
 0573      {
 0574          set_config_count('num_users', $num_users_delta, true);
 0575      }
 0576   
 0577      // Now do the invariant tasks
 0578      // all queries performed in one call of this function are in a single transaction
 0579      // so this is kosher
 0580      if ($mode == 'retain')
 0581      {
 0582          // Assign more data to the Anonymous user
 0583          $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
 0584              SET poster_id = ' . ANONYMOUS . '
 0585              WHERE ' . $db->sql_in_set('poster_id', $user_ids);
 0586          $db->sql_query($sql);
 0587   
 0588          $sql = 'UPDATE ' . USERS_TABLE . '
 0589              SET user_posts = user_posts + ' . $added_guest_posts . '
 0590              WHERE user_id = ' . ANONYMOUS;
 0591          $db->sql_query($sql);
 0592      }
 0593      else if ($mode == 'remove')
 0594      {
 0595          if (!function_exists('delete_posts'))
 0596          {
 0597              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
 0598          }
 0599   
 0600          // Delete posts, attachments, etc.
 0601          // delete_posts can handle any number of IDs in its second argument
 0602          delete_posts('poster_id', $user_ids);
 0603      }
 0604   
 0605      $table_ary = array(USERS_TABLE, USER_GROUP_TABLE, TOPICS_WATCH_TABLE, FORUMS_WATCH_TABLE, ACL_USERS_TABLE, TOPICS_TRACK_TABLE, TOPICS_POSTED_TABLE, FORUMS_TRACK_TABLE, PROFILE_FIELDS_DATA_TABLE, MODERATOR_CACHE_TABLE, DRAFTS_TABLE, BOOKMARKS_TABLE, SESSIONS_KEYS_TABLE, PRIVMSGS_FOLDER_TABLE, PRIVMSGS_RULES_TABLE);
 0606   
 0607      // Delete the miscellaneous (non-post) data for the user
 0608      foreach ($table_ary as $table)
 0609      {
 0610          $sql = "DELETE FROM $table
 0611              WHERE " . $user_id_sql;
 0612          $db->sql_query($sql);
 0613      }
 0614   
 0615      $cache->destroy('sql', MODERATOR_CACHE_TABLE);
 0616   
 0617      // Change user_id to anonymous for posts edited by this user
 0618      $sql = 'UPDATE ' . POSTS_TABLE . '
 0619          SET post_edit_user = ' . ANONYMOUS . '
 0620          WHERE ' . $db->sql_in_set('post_edit_user', $user_ids);
 0621      $db->sql_query($sql);
 0622   
 0623      // Change user_id to anonymous for pms edited by this user
 0624      $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
 0625          SET message_edit_user = ' . ANONYMOUS . '
 0626          WHERE ' . $db->sql_in_set('message_edit_user', $user_ids);
 0627      $db->sql_query($sql);
 0628   
 0629      // Change user_id to anonymous for posts deleted by this user
 0630      $sql = 'UPDATE ' . POSTS_TABLE . '
 0631          SET post_delete_user = ' . ANONYMOUS . '
 0632          WHERE ' . $db->sql_in_set('post_delete_user', $user_ids);
 0633      $db->sql_query($sql);
 0634   
 0635      // Change user_id to anonymous for topics deleted by this user
 0636      $sql = 'UPDATE ' . TOPICS_TABLE . '
 0637          SET topic_delete_user = ' . ANONYMOUS . '
 0638          WHERE ' . $db->sql_in_set('topic_delete_user', $user_ids);
 0639      $db->sql_query($sql);
 0640   
 0641      // Delete user log entries about this user
 0642      $sql = 'DELETE FROM ' . LOG_TABLE . '
 0643          WHERE ' . $db->sql_in_set('reportee_id', $user_ids);
 0644      $db->sql_query($sql);
 0645   
 0646      // Change user_id to anonymous for this users triggered events
 0647      $sql = 'UPDATE ' . LOG_TABLE . '
 0648          SET user_id = ' . ANONYMOUS . '
 0649          WHERE ' . $user_id_sql;
 0650      $db->sql_query($sql);
 0651   
 0652      // Delete the user_id from the zebra table
 0653      $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
 0654          WHERE ' . $user_id_sql . '
 0655              OR ' . $db->sql_in_set('zebra_id', $user_ids);
 0656      $db->sql_query($sql);
 0657   
 0658      // Delete the user_id from the banlist
 0659      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 0660          WHERE ' . $db->sql_in_set('ban_userid', $user_ids);
 0661      $db->sql_query($sql);
 0662   
 0663      // Delete the user_id from the session table
 0664      $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
 0665          WHERE ' . $db->sql_in_set('session_user_id', $user_ids);
 0666      $db->sql_query($sql);
 0667   
 0668      // Clean the private messages tables from the user
 0669      if (!function_exists('phpbb_delete_user_pms'))
 0670      {
 0671          include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx);
 0672      }
 0673      phpbb_delete_users_pms($user_ids);
 0674   
 0675      $db->sql_transaction('commit');
 0676   
 0677      /**
 0678      * Event after a user is deleted
 0679      *
 0680      * @event core.delete_user_after
 0681      * @var    string    mode        Mode of deletion (retain/delete posts)
 0682      * @var    array    user_ids    IDs of the deleted user
 0683      * @var    mixed    retain_username    True if username should be retained
 0684      *                or false if not
 0685      * @since 3.1.0-a1
 0686      */
 0687      $vars = array('mode', 'user_ids', 'retain_username');
 0688      extract($phpbb_dispatcher->trigger_event('core.delete_user_after', compact($vars)));
 0689   
 0690      // Reset newest user info if appropriate
 0691      if (in_array($config['newest_user_id'], $user_ids))
 0692      {
 0693          update_last_username();
 0694      }
 0695   
 0696      return false;
 0697  }
 0698   
 0699  /**
 0700  * Flips user_type from active to inactive and vice versa, handles group membership updates
 0701  *
 0702  * @param string $mode can be flip for flipping from active/inactive, activate or deactivate
 0703  */
 0704  function user_active_flip($mode, $user_id_ary, $reason = INACTIVE_MANUAL)
 0705  {
 0706      global $config, $db, $user, $auth;
 0707   
 0708      $deactivated = $activated = 0;
 0709      $sql_statements = array();
 0710   
 0711      if (!is_array($user_id_ary))
 0712      {
 0713          $user_id_ary = array($user_id_ary);
 0714      }
 0715   
 0716      if (!sizeof($user_id_ary))
 0717      {
 0718          return;
 0719      }
 0720   
 0721      $sql = 'SELECT user_id, group_id, user_type, user_inactive_reason
 0722          FROM ' . USERS_TABLE . '
 0723          WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
 0724      $result = $db->sql_query($sql);
 0725   
 0726      while ($row = $db->sql_fetchrow($result))
 0727      {
 0728          $sql_ary = array();
 0729   
 0730          if ($row['user_type'] == USER_IGNORE || $row['user_type'] == USER_FOUNDER ||
 0731              ($mode == 'activate' && $row['user_type'] != USER_INACTIVE) ||
 0732              ($mode == 'deactivate' && $row['user_type'] == USER_INACTIVE))
 0733          {
 0734              continue;
 0735          }
 0736   
 0737          if ($row['user_type'] == USER_INACTIVE)
 0738          {
 0739              $activated++;
 0740          }
 0741          else
 0742          {
 0743              $deactivated++;
 0744   
 0745              // Remove the users session key...
 0746              $user->reset_login_keys($row['user_id']);
 0747          }
 0748   
 0749          $sql_ary += array(
 0750              'user_type'                => ($row['user_type'] == USER_NORMAL) ? USER_INACTIVE : USER_NORMAL,
 0751              'user_inactive_time'    => ($row['user_type'] == USER_NORMAL) ? time() : 0,
 0752              'user_inactive_reason'    => ($row['user_type'] == USER_NORMAL) ? $reason : 0,
 0753          );
 0754   
 0755          $sql_statements[$row['user_id']] = $sql_ary;
 0756      }
 0757      $db->sql_freeresult($result);
 0758   
 0759      if (sizeof($sql_statements))
 0760      {
 0761          foreach ($sql_statements as $user_id => $sql_ary)
 0762          {
 0763              $sql = 'UPDATE ' . USERS_TABLE . '
 0764                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 0765                  WHERE user_id = ' . $user_id;
 0766              $db->sql_query($sql);
 0767          }
 0768   
 0769          $auth->acl_clear_prefetch(array_keys($sql_statements));
 0770      }
 0771   
 0772      if ($deactivated)
 0773      {
 0774          set_config_count('num_users', $deactivated * (-1), true);
 0775      }
 0776   
 0777      if ($activated)
 0778      {
 0779          set_config_count('num_users', $activated, true);
 0780      }
 0781   
 0782      // Update latest username
 0783      update_last_username();
 0784  }
 0785   
 0786  /**
 0787  * Add a ban or ban exclusion to the banlist. Bans either a user, an IP or an email address
 0788  *
 0789  * @param string $mode Type of ban. One of the following: user, ip, email
 0790  * @param mixed $ban Banned entity. Either string or array with usernames, ips or email addresses
 0791  * @param int $ban_len Ban length in minutes
 0792  * @param string $ban_len_other Ban length as a date (YYYY-MM-DD)
 0793  * @param boolean $ban_exclude Exclude these entities from banning?
 0794  * @param string $ban_reason String describing the reason for this ban
 0795  * @return boolean
 0796  */
 0797  function user_ban($mode, $ban, $ban_len, $ban_len_other, $ban_exclude, $ban_reason, $ban_give_reason = '')
 0798  {
 0799      global $db, $user, $auth, $cache;
 0800   
 0801      // Delete stale bans
 0802      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 0803          WHERE ban_end < ' . time() . '
 0804              AND ban_end <> 0';
 0805      $db->sql_query($sql);
 0806   
 0807      $ban_list = (!is_array($ban)) ? array_unique(explode("\n", $ban)) : $ban;
 0808      $ban_list_log = implode(', ', $ban_list);
 0809   
 0810      $current_time = time();
 0811   
 0812      // Set $ban_end to the unix time when the ban should end. 0 is a permanent ban.
 0813      if ($ban_len)
 0814      {
 0815          if ($ban_len != -1 || !$ban_len_other)
 0816          {
 0817              $ban_end = max($current_time, $current_time + ($ban_len) * 60);
 0818          }
 0819          else
 0820          {
 0821              $ban_other = explode('-', $ban_len_other);
 0822              if (sizeof($ban_other) == 3 && ((int) $ban_other[0] < 9999) &&
 0823                  (strlen($ban_other[0]) == 4) && (strlen($ban_other[1]) == 2) && (strlen($ban_other[2]) == 2))
 0824              {
 0825                  $ban_end = max($current_time, $user->create_datetime()
 0826                      ->setDate((int) $ban_other[0], (int) $ban_other[1], (int) $ban_other[2])
 0827                      ->setTime(0, 0, 0)
 0828                      ->getTimestamp() + $user->timezone->getOffset(new DateTime('UTC')));
 0829              }
 0830              else
 0831              {
 0832                  trigger_error('LENGTH_BAN_INVALID', E_USER_WARNING);
 0833              }
 0834          }
 0835      }
 0836      else
 0837      {
 0838          $ban_end = 0;
 0839      }
 0840   
 0841      $founder = $founder_names = array();
 0842   
 0843      if (!$ban_exclude)
 0844      {
 0845          // Create a list of founder...
 0846          $sql = 'SELECT user_id, user_email, username_clean
 0847              FROM ' . USERS_TABLE . '
 0848              WHERE user_type = ' . USER_FOUNDER;
 0849          $result = $db->sql_query($sql);
 0850   
 0851          while ($row = $db->sql_fetchrow($result))
 0852          {
 0853              $founder[$row['user_id']] = $row['user_email'];
 0854              $founder_names[$row['user_id']] = $row['username_clean'];
 0855          }
 0856          $db->sql_freeresult($result);
 0857      }
 0858   
 0859      $banlist_ary = array();
 0860   
 0861      switch ($mode)
 0862      {
 0863          case 'user':
 0864              $type = 'ban_userid';
 0865   
 0866              // At the moment we do not support wildcard username banning
 0867   
 0868              // Select the relevant user_ids.
 0869              $sql_usernames = array();
 0870   
 0871              foreach ($ban_list as $username)
 0872              {
 0873                  $username = trim($username);
 0874                  if ($username != '')
 0875                  {
 0876                      $clean_name = utf8_clean_string($username);
 0877                      if ($clean_name == $user->data['username_clean'])
 0878                      {
 0879                          trigger_error('CANNOT_BAN_YOURSELF', E_USER_WARNING);
 0880                      }
 0881                      if (in_array($clean_name, $founder_names))
 0882                      {
 0883                          trigger_error('CANNOT_BAN_FOUNDER', E_USER_WARNING);
 0884                      }
 0885                      $sql_usernames[] = $clean_name;
 0886                  }
 0887              }
 0888   
 0889              // Make sure we have been given someone to ban
 0890              if (!sizeof($sql_usernames))
 0891              {
 0892                  trigger_error('NO_USER_SPECIFIED', E_USER_WARNING);
 0893              }
 0894   
 0895              $sql = 'SELECT user_id
 0896                  FROM ' . USERS_TABLE . '
 0897                  WHERE ' . $db->sql_in_set('username_clean', $sql_usernames);
 0898   
 0899              // Do not allow banning yourself, the guest account, or founders.
 0900              $non_bannable = array($user->data['user_id'], ANONYMOUS);
 0901              if (sizeof($founder))
 0902              {
 0903                  $sql .= ' AND ' . $db->sql_in_set('user_id', array_merge(array_keys($founder), $non_bannable), true);
 0904              }
 0905              else
 0906              {
 0907                  $sql .= ' AND ' . $db->sql_in_set('user_id', $non_bannable, true);
 0908              }
 0909   
 0910              $result = $db->sql_query($sql);
 0911   
 0912              if ($row = $db->sql_fetchrow($result))
 0913              {
 0914                  do
 0915                  {
 0916                      $banlist_ary[] = (int) $row['user_id'];
 0917                  }
 0918                  while ($row = $db->sql_fetchrow($result));
 0919              }
 0920              else
 0921              {
 0922                  $db->sql_freeresult($result);
 0923                  trigger_error('NO_USERS', E_USER_WARNING);
 0924              }
 0925              $db->sql_freeresult($result);
 0926          break;
 0927   
 0928          case 'ip':
 0929              $type = 'ban_ip';
 0930   
 0931              foreach ($ban_list as $ban_item)
 0932              {
 0933                  if (preg_match('#^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})[ ]*\-[ ]*([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$#', trim($ban_item), $ip_range_explode))
 0934                  {
 0935                      // This is an IP range
 0936                      // Don't ask about all this, just don't ask ... !
 0937                      $ip_1_counter = $ip_range_explode[1];
 0938                      $ip_1_end = $ip_range_explode[5];
 0939   
 0940                      while ($ip_1_counter <= $ip_1_end)
 0941                      {
 0942                          $ip_2_counter = ($ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[2] : 0;
 0943                          $ip_2_end = ($ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[6];
 0944   
 0945                          if ($ip_2_counter == 0 && $ip_2_end == 254)
 0946                          {
 0947                              $ip_2_counter = 256;
 0948                              $ip_2_fragment = 256;
 0949   
 0950                              $banlist_ary[] = "$ip_1_counter.*";
 0951                          }
 0952   
 0953                          while ($ip_2_counter <= $ip_2_end)
 0954                          {
 0955                              $ip_3_counter = ($ip_2_counter == $ip_range_explode[2] && $ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[3] : 0;
 0956                              $ip_3_end = ($ip_2_counter < $ip_2_end || $ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[7];
 0957   
 0958                              if ($ip_3_counter == 0 && $ip_3_end == 254)
 0959                              {
 0960                                  $ip_3_counter = 256;
 0961                                  $ip_3_fragment = 256;
 0962   
 0963                                  $banlist_ary[] = "$ip_1_counter.$ip_2_counter.*";
 0964                              }
 0965   
 0966                              while ($ip_3_counter <= $ip_3_end)
 0967                              {
 0968                                  $ip_4_counter = ($ip_3_counter == $ip_range_explode[3] && $ip_2_counter == $ip_range_explode[2] && $ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[4] : 0;
 0969                                  $ip_4_end = ($ip_3_counter < $ip_3_end || $ip_2_counter < $ip_2_end) ? 254 : $ip_range_explode[8];
 0970   
 0971                                  if ($ip_4_counter == 0 && $ip_4_end == 254)
 0972                                  {
 0973                                      $ip_4_counter = 256;
 0974                                      $ip_4_fragment = 256;
 0975   
 0976                                      $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.*";
 0977                                  }
 0978   
 0979                                  while ($ip_4_counter <= $ip_4_end)
 0980                                  {
 0981                                      $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.$ip_4_counter";
 0982                                      $ip_4_counter++;
 0983                                  }
 0984                                  $ip_3_counter++;
 0985                              }
 0986                              $ip_2_counter++;
 0987                          }
 0988                          $ip_1_counter++;
 0989                      }
 0990                  }
 0991                  else if (preg_match('#^([0-9]{1,3})\.([0-9\*]{1,3})\.([0-9\*]{1,3})\.([0-9\*]{1,3})$#', trim($ban_item)) || preg_match('#^[a-f0-9:]+\*?$#i', trim($ban_item)))
 0992                  {
 0993                      // Normal IP address
 0994                      $banlist_ary[] = trim($ban_item);
 0995                  }
 0996                  else if (preg_match('#^\*$#', trim($ban_item)))
 0997                  {
 0998                      // Ban all IPs
 0999                      $banlist_ary[] = '*';
 1000                  }
 1001                  else if (preg_match('#^([\w\-_]\.?){2,}$#is', trim($ban_item)))
 1002                  {
 1003                      // hostname
 1004                      $ip_ary = gethostbynamel(trim($ban_item));
 1005   
 1006                      if (!empty($ip_ary))
 1007                      {
 1008                          foreach ($ip_ary as $ip)
 1009                          {
 1010                              if ($ip)
 1011                              {
 1012                                  if (strlen($ip) > 40)
 1013                                  {
 1014                                      continue;
 1015                                  }
 1016   
 1017                                  $banlist_ary[] = $ip;
 1018                              }
 1019                          }
 1020                      }
 1021                  }
 1022   
 1023                  if (empty($banlist_ary))
 1024                  {
 1025                      trigger_error('NO_IPS_DEFINED', E_USER_WARNING);
 1026                  }
 1027              }
 1028          break;
 1029   
 1030          case 'email':
 1031              $type = 'ban_email';
 1032   
 1033              foreach ($ban_list as $ban_item)
 1034              {
 1035                  $ban_item = trim($ban_item);
 1036   
 1037                  if (preg_match('#^.*?@*|(([a-z0-9\-]+\.)+([a-z]{2,3}))$#i', $ban_item))
 1038                  {
 1039                      if (strlen($ban_item) > 100)
 1040                      {
 1041                          continue;
 1042                      }
 1043   
 1044                      if (!sizeof($founder) || !in_array($ban_item, $founder))
 1045                      {
 1046                          $banlist_ary[] = $ban_item;
 1047                      }
 1048                  }
 1049              }
 1050   
 1051              if (sizeof($ban_list) == 0)
 1052              {
 1053                  trigger_error('NO_EMAILS_DEFINED', E_USER_WARNING);
 1054              }
 1055          break;
 1056   
 1057          default:
 1058              trigger_error('NO_MODE', E_USER_WARNING);
 1059          break;
 1060      }
 1061   
 1062      // Fetch currently set bans of the specified type and exclude state. Prevent duplicate bans.
 1063      $sql_where = ($type == 'ban_userid') ? 'ban_userid <> 0' : "$type <> ''";
 1064   
 1065      $sql = "SELECT $type
 1066          FROM " . BANLIST_TABLE . "
 1067          WHERE $sql_where
 1068              AND ban_exclude = " . (int) $ban_exclude;
 1069      $result = $db->sql_query($sql);
 1070   
 1071      // Reset $sql_where, because we use it later...
 1072      $sql_where = '';
 1073   
 1074      if ($row = $db->sql_fetchrow($result))
 1075      {
 1076          $banlist_ary_tmp = array();
 1077          do
 1078          {
 1079              switch ($mode)
 1080              {
 1081                  case 'user':
 1082                      $banlist_ary_tmp[] = $row['ban_userid'];
 1083                  break;
 1084   
 1085                  case 'ip':
 1086                      $banlist_ary_tmp[] = $row['ban_ip'];
 1087                  break;
 1088   
 1089                  case 'email':
 1090                      $banlist_ary_tmp[] = $row['ban_email'];
 1091                  break;
 1092              }
 1093          }
 1094          while ($row = $db->sql_fetchrow($result));
 1095   
 1096          $banlist_ary_tmp = array_intersect($banlist_ary, $banlist_ary_tmp);
 1097   
 1098          if (sizeof($banlist_ary_tmp))
 1099          {
 1100              // One or more entities are already banned/excluded, delete the existing bans, so they can be re-inserted with the given new length
 1101              $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 1102                  WHERE ' . $db->sql_in_set($type, $banlist_ary_tmp) . '
 1103                      AND ban_exclude = ' . (int) $ban_exclude;
 1104              $db->sql_query($sql);
 1105          }
 1106   
 1107          unset($banlist_ary_tmp);
 1108      }
 1109      $db->sql_freeresult($result);
 1110   
 1111      // We have some entities to ban
 1112      if (sizeof($banlist_ary))
 1113      {
 1114          $sql_ary = array();
 1115   
 1116          foreach ($banlist_ary as $ban_entry)
 1117          {
 1118              $sql_ary[] = array(
 1119                  $type                => $ban_entry,
 1120                  'ban_start'            => (int) $current_time,
 1121                  'ban_end'            => (int) $ban_end,
 1122                  'ban_exclude'        => (int) $ban_exclude,
 1123                  'ban_reason'        => (string) $ban_reason,
 1124                  'ban_give_reason'    => (string) $ban_give_reason,
 1125              );
 1126          }
 1127   
 1128          $db->sql_multi_insert(BANLIST_TABLE, $sql_ary);
 1129   
 1130          // If we are banning we want to logout anyone matching the ban
 1131          if (!$ban_exclude)
 1132          {
 1133              switch ($mode)
 1134              {
 1135                  case 'user':
 1136                      $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $banlist_ary);
 1137                  break;
 1138   
 1139                  case 'ip':
 1140                      $sql_where = 'WHERE ' . $db->sql_in_set('session_ip', $banlist_ary);
 1141                  break;
 1142   
 1143                  case 'email':
 1144                      $banlist_ary_sql = array();
 1145   
 1146                      foreach ($banlist_ary as $ban_entry)
 1147                      {
 1148                          $banlist_ary_sql[] = (string) str_replace('*', '%', $ban_entry);
 1149                      }
 1150   
 1151                      $sql = 'SELECT user_id
 1152                          FROM ' . USERS_TABLE . '
 1153                          WHERE ' . $db->sql_in_set('user_email', $banlist_ary_sql);
 1154                      $result = $db->sql_query($sql);
 1155   
 1156                      $sql_in = array();
 1157   
 1158                      if ($row = $db->sql_fetchrow($result))
 1159                      {
 1160                          do
 1161                          {
 1162                              $sql_in[] = $row['user_id'];
 1163                          }
 1164                          while ($row = $db->sql_fetchrow($result));
 1165   
 1166                          $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $sql_in);
 1167                      }
 1168                      $db->sql_freeresult($result);
 1169                  break;
 1170              }
 1171   
 1172              if (isset($sql_where) && $sql_where)
 1173              {
 1174                  $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
 1175                      $sql_where";
 1176                  $db->sql_query($sql);
 1177   
 1178                  if ($mode == 'user')
 1179                  {
 1180                      $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . ' ' . ((in_array('*', $banlist_ary)) ? '' : 'WHERE ' . $db->sql_in_set('user_id', $banlist_ary));
 1181                      $db->sql_query($sql);
 1182                  }
 1183              }
 1184          }
 1185   
 1186          // Update log
 1187          $log_entry = ($ban_exclude) ? 'LOG_BAN_EXCLUDE_' : 'LOG_BAN_';
 1188   
 1189          // Add to admin log, moderator log and user notes
 1190          add_log('admin', $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
 1191          add_log('mod', 0, 0, $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
 1192          if ($mode == 'user')
 1193          {
 1194              foreach ($banlist_ary as $user_id)
 1195              {
 1196                  add_log('user', $user_id, $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
 1197              }
 1198          }
 1199   
 1200          $cache->destroy('sql', BANLIST_TABLE);
 1201   
 1202          return true;
 1203      }
 1204   
 1205      // There was nothing to ban/exclude. But destroying the cache because of the removal of stale bans.
 1206      $cache->destroy('sql', BANLIST_TABLE);
 1207   
 1208      return false;
 1209  }
 1210   
 1211  /**
 1212  * Unban User
 1213  */
 1214  function user_unban($mode, $ban)
 1215  {
 1216      global $db, $user, $auth, $cache;
 1217   
 1218      // Delete stale bans
 1219      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 1220          WHERE ban_end < ' . time() . '
 1221              AND ban_end <> 0';
 1222      $db->sql_query($sql);
 1223   
 1224      if (!is_array($ban))
 1225      {
 1226          $ban = array($ban);
 1227      }
 1228   
 1229      $unban_sql = array_map('intval', $ban);
 1230   
 1231      if (sizeof($unban_sql))
 1232      {
 1233          // Grab details of bans for logging information later
 1234          switch ($mode)
 1235          {
 1236              case 'user':
 1237                  $sql = 'SELECT u.username AS unban_info, u.user_id
 1238                      FROM ' . USERS_TABLE . ' u, ' . BANLIST_TABLE . ' b
 1239                      WHERE ' . $db->sql_in_set('b.ban_id', $unban_sql) . '
 1240                          AND u.user_id = b.ban_userid';
 1241              break;
 1242   
 1243              case 'email':
 1244                  $sql = 'SELECT ban_email AS unban_info
 1245                      FROM ' . BANLIST_TABLE . '
 1246                      WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
 1247              break;
 1248   
 1249              case 'ip':
 1250                  $sql = 'SELECT ban_ip AS unban_info
 1251                      FROM ' . BANLIST_TABLE . '
 1252                      WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
 1253              break;
 1254          }
 1255          $result = $db->sql_query($sql);
 1256   
 1257          $l_unban_list = '';
 1258          $user_ids_ary = array();
 1259          while ($row = $db->sql_fetchrow($result))
 1260          {
 1261              $l_unban_list .= (($l_unban_list != '') ? ', ' : '') . $row['unban_info'];
 1262              if ($mode == 'user')
 1263              {
 1264                  $user_ids_ary[] = $row['user_id'];
 1265              }
 1266          }
 1267          $db->sql_freeresult($result);
 1268   
 1269          $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 1270              WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
 1271          $db->sql_query($sql);
 1272   
 1273          // Add to moderator log, admin log and user notes
 1274          add_log('admin', 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
 1275          add_log('mod', 0, 0, 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
 1276          if ($mode == 'user')
 1277          {
 1278              foreach ($user_ids_ary as $user_id)
 1279              {
 1280                  add_log('user', $user_id, 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
 1281              }
 1282          }
 1283      }
 1284   
 1285      $cache->destroy('sql', BANLIST_TABLE);
 1286   
 1287      return false;
 1288  }
 1289   
 1290  /**
 1291  * Internet Protocol Address Whois
 1292  * RFC3912: WHOIS Protocol Specification
 1293  *
 1294  * @param string $ip        Ip address, either IPv4 or IPv6.
 1295  *
 1296  * @return string        Empty string if not a valid ip address.
 1297  *                        Otherwise make_clickable()'ed whois result.
 1298  */
 1299  function user_ipwhois($ip)
 1300  {
 1301      if (empty($ip))
 1302      {
 1303          return '';
 1304      }
 1305   
 1306      if (preg_match(get_preg_expression('ipv4'), $ip))
 1307      {
 1308          // IPv4 address
 1309          $whois_host = 'whois.arin.net.';
 1310      }
 1311      else if (preg_match(get_preg_expression('ipv6'), $ip))
 1312      {
 1313          // IPv6 address
 1314          $whois_host = 'whois.sixxs.net.';
 1315      }
 1316      else
 1317      {
 1318          return '';
 1319      }
 1320   
 1321      $ipwhois = '';
 1322   
 1323      if (($fsk = @fsockopen($whois_host, 43)))
 1324      {
 1325          // CRLF as per RFC3912
 1326          fputs($fsk, "$ip\r\n");
 1327          while (!feof($fsk))
 1328          {
 1329              $ipwhois .= fgets($fsk, 1024);
 1330          }
 1331          @fclose($fsk);
 1332      }
 1333   
 1334      $match = array();
 1335   
 1336      // Test for referrals from $whois_host to other whois databases, roll on rwhois
 1337      if (preg_match('#ReferralServer: whois://(.+)#im', $ipwhois, $match))
 1338      {
 1339          if (strpos($match[1], ':') !== false)
 1340          {
 1341              $pos    = strrpos($match[1], ':');
 1342              $server    = substr($match[1], 0, $pos);
 1343              $port    = (int) substr($match[1], $pos + 1);
 1344              unset($pos);
 1345          }
 1346          else
 1347          {
 1348              $server    = $match[1];
 1349              $port    = 43;
 1350          }
 1351   
 1352          $buffer = '';
 1353   
 1354          if (($fsk = @fsockopen($server, $port)))
 1355          {
 1356              fputs($fsk, "$ip\r\n");
 1357              while (!feof($fsk))
 1358              {
 1359                  $buffer .= fgets($fsk, 1024);
 1360              }
 1361              @fclose($fsk);
 1362          }
 1363   
 1364          // Use the result from $whois_host if we don't get any result here
 1365          $ipwhois = (empty($buffer)) ? $ipwhois : $buffer;
 1366      }
 1367   
 1368      $ipwhois = htmlspecialchars($ipwhois);
 1369   
 1370      // Magic URL ;)
 1371      return trim(make_clickable($ipwhois, false, ''));
 1372  }
 1373   
 1374  /**
 1375  * Data validation ... used primarily but not exclusively by ucp modules
 1376  *
 1377  * "Master" function for validating a range of data types
 1378  */
 1379  function validate_data($data, $val_ary)
 1380  {
 1381      global $user;
 1382   
 1383      $error = array();
 1384   
 1385      foreach ($val_ary as $var => $val_seq)
 1386      {
 1387          if (!is_array($val_seq[0]))
 1388          {
 1389              $val_seq = array($val_seq);
 1390          }
 1391   
 1392          foreach ($val_seq as $validate)
 1393          {
 1394              $function = array_shift($validate);
 1395              array_unshift($validate, $data[$var]);
 1396   
 1397              if (is_array($function))
 1398              {
 1399                  $result = call_user_func_array(array($function[0], 'validate_' . $function[1]), $validate);
 1400              }
 1401              else
 1402              {
 1403                  $function_prefix = (function_exists('phpbb_validate_' . $function)) ? 'phpbb_validate_' : 'validate_';
 1404                  $result = call_user_func_array($function_prefix . $function, $validate);
 1405              }
 1406   
 1407              if ($result)
 1408              {
 1409                  // Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted.
 1410                  $error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var);
 1411              }
 1412          }
 1413      }
 1414   
 1415      return $error;
 1416  }
 1417   
 1418  /**
 1419  * Validate String
 1420  *
 1421  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
 1422  */
 1423  function validate_string($string, $optional = false, $min = 0, $max = 0)
 1424  {
 1425      if (empty($string) && $optional)
 1426      {
 1427          return false;
 1428      }
 1429   
 1430      if ($min && utf8_strlen(htmlspecialchars_decode($string)) < $min)
 1431      {
 1432          return 'TOO_SHORT';
 1433      }
 1434      else if ($max && utf8_strlen(htmlspecialchars_decode($string)) > $max)
 1435      {
 1436          return 'TOO_LONG';
 1437      }
 1438   
 1439      return false;
 1440  }
 1441   
 1442  /**
 1443  * Validate Number
 1444  *
 1445  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
 1446  */
 1447  function validate_num($num, $optional = false, $min = 0, $max = 1E99)
 1448  {
 1449      if (empty($num) && $optional)
 1450      {
 1451          return false;
 1452      }
 1453   
 1454      if ($num < $min)
 1455      {
 1456          return 'TOO_SMALL';
 1457      }
 1458      else if ($num > $max)
 1459      {
 1460          return 'TOO_LARGE';
 1461      }
 1462   
 1463      return false;
 1464  }
 1465   
 1466  /**
 1467  * Validate Date
 1468  * @param String $string a date in the dd-mm-yyyy format
 1469  * @return    boolean
 1470  */
 1471  function validate_date($date_string, $optional = false)
 1472  {
 1473      $date = explode('-', $date_string);
 1474      if ((empty($date) || sizeof($date) != 3) && $optional)
 1475      {
 1476          return false;
 1477      }
 1478      else if ($optional)
 1479      {
 1480          for ($field = 0; $field <= 1; $field++)
 1481          {
 1482              $date[$field] = (int) $date[$field];
 1483              if (empty($date[$field]))
 1484              {
 1485                  $date[$field] = 1;
 1486              }
 1487          }
 1488          $date[2] = (int) $date[2];
 1489          // assume an arbitrary leap year
 1490          if (empty($date[2]))
 1491          {
 1492              $date[2] = 1980;
 1493          }
 1494      }
 1495   
 1496      if (sizeof($date) != 3 || !checkdate($date[1], $date[0], $date[2]))
 1497      {
 1498          return 'INVALID';
 1499      }
 1500   
 1501      return false;
 1502  }
 1503   
 1504   
 1505  /**
 1506  * Validate Match
 1507  *
 1508  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
 1509  */
 1510  function validate_match($string, $optional = false, $match = '')
 1511  {
 1512      if (empty($string) && $optional)
 1513      {
 1514          return false;
 1515      }
 1516   
 1517      if (empty($match))
 1518      {
 1519          return false;
 1520      }
 1521   
 1522      if (!preg_match($match, $string))
 1523      {
 1524          return 'WRONG_DATA';
 1525      }
 1526   
 1527      return false;
 1528  }
 1529   
 1530  /**
 1531  * Validate Language Pack ISO Name
 1532  *
 1533  * Tests whether a language name is valid and installed
 1534  *
 1535  * @param string $lang_iso    The language string to test
 1536  *
 1537  * @return bool|string        Either false if validation succeeded or
 1538  *                            a string which will be used as the error message
 1539  *                            (with the variable name appended)
 1540  */
 1541  function validate_language_iso_name($lang_iso)
 1542  {
 1543      global $db;
 1544   
 1545      $sql = 'SELECT lang_id
 1546          FROM ' . LANG_TABLE . "
 1547          WHERE lang_iso = '" . $db->sql_escape($lang_iso) . "'";
 1548      $result = $db->sql_query($sql);
 1549      $lang_id = (int) $db->sql_fetchfield('lang_id');
 1550      $db->sql_freeresult($result);
 1551   
 1552      return ($lang_id) ? false : 'WRONG_DATA';
 1553  }
 1554   
 1555  /**
 1556  * Validate Timezone Name
 1557  *
 1558  * Tests whether a timezone name is valid
 1559  *
 1560  * @param string $timezone    The timezone string to test
 1561  *
 1562  * @return bool|string        Either false if validation succeeded or
 1563  *                            a string which will be used as the error message
 1564  *                            (with the variable name appended)
 1565  */
 1566  function phpbb_validate_timezone($timezone)
 1567  {
 1568      return (in_array($timezone, phpbb_get_timezone_identifiers($timezone))) ? false : 'TIMEZONE_INVALID';
 1569  }
 1570   
 1571  /**
 1572  * Check to see if the username has been taken, or if it is disallowed.
 1573  * Also checks if it includes the " character, which we don't allow in usernames.
 1574  * Used for registering, changing names, and posting anonymously with a username
 1575  *
 1576  * @param string $username The username to check
 1577  * @param string $allowed_username An allowed username, default being $user->data['username']
 1578  *
 1579  * @return    mixed    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
 1580  */
 1581  function validate_username($username, $allowed_username = false)
 1582  {
 1583      global $config, $db, $user, $cache;
 1584   
 1585      $clean_username = utf8_clean_string($username);
 1586      $allowed_username = ($allowed_username === false) ? $user->data['username_clean'] : utf8_clean_string($allowed_username);
 1587   
 1588      if ($allowed_username == $clean_username)
 1589      {
 1590          return false;
 1591      }
 1592   
 1593      // ... fast checks first.
 1594      if (strpos($username, '&quot;') !== false || strpos($username, '"') !== false || empty($clean_username))
 1595      {
 1596          return 'INVALID_CHARS';
 1597      }
 1598   
 1599      $mbstring = $pcre = false;
 1600   
 1601      // generic UTF-8 character types supported?
 1602      if (phpbb_pcre_utf8_support())
 1603      {
 1604          $pcre = true;
 1605      }
 1606      else if (function_exists('mb_ereg_match'))
 1607      {
 1608          mb_regex_encoding('UTF-8');
 1609          $mbstring = true;
 1610      }
 1611   
 1612      switch ($config['allow_name_chars'])
 1613      {
 1614          case 'USERNAME_CHARS_ANY':
 1615              $pcre = true;
 1616              $regex = '.+';
 1617          break;
 1618   
 1619          case 'USERNAME_ALPHA_ONLY':
 1620              $pcre = true;
 1621              $regex = '[A-Za-z0-9]+';
 1622          break;
 1623   
 1624          case 'USERNAME_ALPHA_SPACERS':
 1625              $pcre = true;
 1626              $regex = '[A-Za-z0-9-[\]_+ ]+';
 1627          break;
 1628   
 1629          case 'USERNAME_LETTER_NUM':
 1630              if ($pcre)
 1631              {
 1632                  $regex = '[\p{Lu}\p{Ll}\p{N}]+';
 1633              }
 1634              else if ($mbstring)
 1635              {
 1636                  $regex = '[[:upper:][:lower:][:digit:]]+';
 1637              }
 1638              else
 1639              {
 1640                  $pcre = true;
 1641                  $regex = '[a-zA-Z0-9]+';
 1642              }
 1643          break;
 1644   
 1645          case 'USERNAME_LETTER_NUM_SPACERS':
 1646              if ($pcre)
 1647              {
 1648                  $regex = '[-\]_+ [\p{Lu}\p{Ll}\p{N}]+';
 1649              }
 1650              else if ($mbstring)
 1651              {
 1652                  $regex = '[-\]_+ \[[:upper:][:lower:][:digit:]]+';
 1653              }
 1654              else
 1655              {
 1656                  $pcre = true;
 1657                  $regex = '[-\]_+ [a-zA-Z0-9]+';
 1658              }
 1659          break;
 1660   
 1661          case 'USERNAME_ASCII':
 1662          default:
 1663              $pcre = true;
 1664              $regex = '[\x01-\x7F]+';
 1665          break;
 1666      }
 1667   
 1668      if ($pcre)
 1669      {
 1670          if (!preg_match('#^' . $regex . '$#u', $username))
 1671          {
 1672              return 'INVALID_CHARS';
 1673          }
 1674      }
 1675      else if ($mbstring)
 1676      {
 1677          mb_ereg_search_init($username, '^' . $regex . '$');
 1678          if (!mb_ereg_search())
 1679          {
 1680              return 'INVALID_CHARS';
 1681          }
 1682      }
 1683   
 1684      $sql = 'SELECT username
 1685          FROM ' . USERS_TABLE . "
 1686          WHERE username_clean = '" . $db->sql_escape($clean_username) . "'";
 1687      $result = $db->sql_query($sql);
 1688      $row = $db->sql_fetchrow($result);
 1689      $db->sql_freeresult($result);
 1690   
 1691      if ($row)
 1692      {
 1693          return 'USERNAME_TAKEN';
 1694      }
 1695   
 1696      $sql = 'SELECT group_name
 1697          FROM ' . GROUPS_TABLE . "
 1698          WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($username)) . "'";
 1699      $result = $db->sql_query($sql);
 1700      $row = $db->sql_fetchrow($result);
 1701      $db->sql_freeresult($result);
 1702   
 1703      if ($row)
 1704      {
 1705          return 'USERNAME_TAKEN';
 1706      }
 1707   
 1708      $bad_usernames = $cache->obtain_disallowed_usernames();
 1709   
 1710      foreach ($bad_usernames as $bad_username)
 1711      {
 1712          if (preg_match('#^' . $bad_username . '$#', $clean_username))
 1713          {
 1714              return 'USERNAME_DISALLOWED';
 1715          }
 1716      }
 1717   
 1718      return false;
 1719  }
 1720   
 1721  /**
 1722  * Check to see if the password meets the complexity settings
 1723  *
 1724  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
 1725  */
 1726  function validate_password($password)
 1727  {
 1728      global $config;
 1729   
 1730      if ($password === '' || $config['pass_complex'] === 'PASS_TYPE_ANY')
 1731      {
 1732          // Password empty or no password complexity required.
 1733          return false;
 1734      }
 1735   
 1736      $pcre = $mbstring = false;
 1737   
 1738      // generic UTF-8 character types supported?
 1739      if (phpbb_pcre_utf8_support())
 1740      {
 1741          $upp = '\p{Lu}';
 1742          $low = '\p{Ll}';
 1743          $num = '\p{N}';
 1744          $sym = '[^\p{Lu}\p{Ll}\p{N}]';
 1745          $pcre = true;
 1746      }
 1747      else if (function_exists('mb_ereg_match'))
 1748      {
 1749          mb_regex_encoding('UTF-8');
 1750          $upp = '[[:upper:]]';
 1751          $low = '[[:lower:]]';
 1752          $num = '[[:digit:]]';
 1753          $sym = '[^[:upper:][:lower:][:digit:]]';
 1754          $mbstring = true;
 1755      }
 1756      else
 1757      {
 1758          $upp = '[A-Z]';
 1759          $low = '[a-z]';
 1760          $num = '[0-9]';
 1761          $sym = '[^A-Za-z0-9]';
 1762          $pcre = true;
 1763      }
 1764   
 1765      $chars = array();
 1766   
 1767      switch ($config['pass_complex'])
 1768      {
 1769          // No break statements below ...
 1770          // We require strong passwords in case pass_complex is not set or is invalid
 1771          default:
 1772   
 1773          // Require mixed case letters, numbers and symbols
 1774          case 'PASS_TYPE_SYMBOL':
 1775              $chars[] = $sym;
 1776   
 1777          // Require mixed case letters and numbers
 1778          case 'PASS_TYPE_ALPHA':
 1779              $chars[] = $num;
 1780   
 1781          // Require mixed case letters
 1782          case 'PASS_TYPE_CASE':
 1783              $chars[] = $low;
 1784              $chars[] = $upp;
 1785      }
 1786   
 1787      if ($pcre)
 1788      {
 1789          foreach ($chars as $char)
 1790          {
 1791              if (!preg_match('#' . $char . '#u', $password))
 1792              {
 1793                  return 'INVALID_CHARS';
 1794              }
 1795          }
 1796      }
 1797      else if ($mbstring)
 1798      {
 1799          foreach ($chars as $char)
 1800          {
 1801              if (mb_ereg($char, $password) === false)
 1802              {
 1803                  return 'INVALID_CHARS';
 1804              }
 1805          }
 1806      }
 1807   
 1808      return false;
 1809  }
 1810   
 1811  /**
 1812  * Check to see if email address is a valid address and contains a MX record
 1813  *
 1814  * @param string $email The email to check
 1815  *
 1816  * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
 1817  */
 1818  function phpbb_validate_email($email, $config = null)
 1819  {
 1820      if ($config === null)
 1821      {
 1822          global $config;
 1823      }
 1824   
 1825      $email = strtolower($email);
 1826   
 1827      if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
 1828      {
 1829          return 'EMAIL_INVALID';
 1830      }
 1831   
 1832      // Check MX record.
 1833      // The idea for this is from reading the UseBB blog/announcement. :)
 1834      if ($config['email_check_mx'])
 1835      {
 1836          list(, $domain) = explode('@', $email);
 1837   
 1838          if (phpbb_checkdnsrr($domain, 'A') === false && phpbb_checkdnsrr($domain, 'MX') === false)
 1839          {
 1840              return 'DOMAIN_NO_MX_RECORD';
 1841          }
 1842      }
 1843   
 1844      return false;
 1845  }
 1846   
 1847  /**
 1848  * Check to see if email address is banned or already present in the DB
 1849  *
 1850  * @param string $email The email to check
 1851  * @param string $allowed_email An allowed email, default being $user->data['user_email']
 1852  *
 1853  * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
 1854  */
 1855  function validate_user_email($email, $allowed_email = false)
 1856  {
 1857      global $config, $db, $user;
 1858   
 1859      $email = strtolower($email);
 1860      $allowed_email = ($allowed_email === false) ? strtolower($user->data['user_email']) : strtolower($allowed_email);
 1861   
 1862      if ($allowed_email == $email)
 1863      {
 1864          return false;
 1865      }
 1866   
 1867      $validate_email = phpbb_validate_email($email, $config);
 1868      if ($validate_email)
 1869      {
 1870          return $validate_email;
 1871      }
 1872   
 1873      if (($ban_reason = $user->check_ban(false, false, $email, true)) !== false)
 1874      {
 1875          return ($ban_reason === true) ? 'EMAIL_BANNED' : $ban_reason;
 1876      }
 1877   
 1878      if (!$config['allow_emailreuse'])
 1879      {
 1880          $sql = 'SELECT user_email_hash
 1881              FROM ' . USERS_TABLE . "
 1882              WHERE user_email_hash = " . $db->sql_escape(phpbb_email_hash($email));
 1883          $result = $db->sql_query($sql);
 1884          $row = $db->sql_fetchrow($result);
 1885          $db->sql_freeresult($result);
 1886   
 1887          if ($row)
 1888          {
 1889              return 'EMAIL_TAKEN';
 1890          }
 1891      }
 1892   
 1893      return false;
 1894  }
 1895   
 1896  /**
 1897  * Validate jabber address
 1898  * Taken from the jabber class within flyspray (see author notes)
 1899  *
 1900  * @author flyspray.org
 1901  */
 1902  function validate_jabber($jid)
 1903  {
 1904      if (!$jid)
 1905      {
 1906          return false;
 1907      }
 1908   
 1909      $separator_pos = strpos($jid, '@');
 1910   
 1911      if ($separator_pos === false)
 1912      {
 1913          return 'WRONG_DATA';
 1914      }
 1915   
 1916      $username = substr($jid, 0, $separator_pos);
 1917      $realm = substr($jid, $separator_pos + 1);
 1918   
 1919      if (strlen($username) == 0 || strlen($realm) < 3)
 1920      {
 1921          return 'WRONG_DATA';
 1922      }
 1923   
 1924      $arr = explode('.', $realm);
 1925   
 1926      if (sizeof($arr) == 0)
 1927      {
 1928          return 'WRONG_DATA';
 1929      }
 1930   
 1931      foreach ($arr as $part)
 1932      {
 1933          if (substr($part, 0, 1) == '-' || substr($part, -1, 1) == '-')
 1934          {
 1935              return 'WRONG_DATA';
 1936          }
 1937   
 1938          if (!preg_match("@^[a-zA-Z0-9-.]+$@", $part))
 1939          {
 1940              return 'WRONG_DATA';
 1941          }
 1942      }
 1943   
 1944      $boundary = array(array(0, 127), array(192, 223), array(224, 239), array(240, 247), array(248, 251), array(252, 253));
 1945   
 1946      // Prohibited Characters RFC3454 + RFC3920
 1947      $prohibited = array(
 1948          // Table C.1.1
 1949          array(0x0020, 0x0020),        // SPACE
 1950          // Table C.1.2
 1951          array(0x00A0, 0x00A0),        // NO-BREAK SPACE
 1952          array(0x1680, 0x1680),        // OGHAM SPACE MARK
 1953          array(0x2000, 0x2001),        // EN QUAD
 1954          array(0x2001, 0x2001),        // EM QUAD
 1955          array(0x2002, 0x2002),        // EN SPACE
 1956          array(0x2003, 0x2003),        // EM SPACE
 1957          array(0x2004, 0x2004),        // THREE-PER-EM SPACE
 1958          array(0x2005, 0x2005),        // FOUR-PER-EM SPACE
 1959          array(0x2006, 0x2006),        // SIX-PER-EM SPACE
 1960          array(0x2007, 0x2007),        // FIGURE SPACE
 1961          array(0x2008, 0x2008),        // PUNCTUATION SPACE
 1962          array(0x2009, 0x2009),        // THIN SPACE
 1963          array(0x200A, 0x200A),        // HAIR SPACE
 1964          array(0x200B, 0x200B),        // ZERO WIDTH SPACE
 1965          array(0x202F, 0x202F),        // NARROW NO-BREAK SPACE
 1966          array(0x205F, 0x205F),        // MEDIUM MATHEMATICAL SPACE
 1967          array(0x3000, 0x3000),        // IDEOGRAPHIC SPACE
 1968          // Table C.2.1
 1969          array(0x0000, 0x001F),        // [CONTROL CHARACTERS]
 1970          array(0x007F, 0x007F),        // DELETE
 1971          // Table C.2.2
 1972          array(0x0080, 0x009F),        // [CONTROL CHARACTERS]
 1973          array(0x06DD, 0x06DD),        // ARABIC END OF AYAH
 1974          array(0x070F, 0x070F),        // SYRIAC ABBREVIATION MARK
 1975          array(0x180E, 0x180E),        // MONGOLIAN VOWEL SEPARATOR
 1976          array(0x200C, 0x200C),         // ZERO WIDTH NON-JOINER
 1977          array(0x200D, 0x200D),        // ZERO WIDTH JOINER
 1978          array(0x2028, 0x2028),        // LINE SEPARATOR
 1979          array(0x2029, 0x2029),        // PARAGRAPH SEPARATOR
 1980          array(0x2060, 0x2060),        // WORD JOINER
 1981          array(0x2061, 0x2061),        // FUNCTION APPLICATION
 1982          array(0x2062, 0x2062),        // INVISIBLE TIMES
 1983          array(0x2063, 0x2063),        // INVISIBLE SEPARATOR
 1984          array(0x206A, 0x206F),        // [CONTROL CHARACTERS]
 1985          array(0xFEFF, 0xFEFF),        // ZERO WIDTH NO-BREAK SPACE
 1986          array(0xFFF9, 0xFFFC),        // [CONTROL CHARACTERS]
 1987          array(0x1D173, 0x1D17A),    // [MUSICAL CONTROL CHARACTERS]
 1988          // Table C.3
 1989          array(0xE000, 0xF8FF),        // [PRIVATE USE, PLANE 0]
 1990          array(0xF0000, 0xFFFFD),    // [PRIVATE USE, PLANE 15]
 1991          array(0x100000, 0x10FFFD),    // [PRIVATE USE, PLANE 16]
 1992          // Table C.4
 1993          array(0xFDD0, 0xFDEF),        // [NONCHARACTER CODE POINTS]
 1994          array(0xFFFE, 0xFFFF),        // [NONCHARACTER CODE POINTS]
 1995          array(0x1FFFE, 0x1FFFF),    // [NONCHARACTER CODE POINTS]
 1996          array(0x2FFFE, 0x2FFFF),    // [NONCHARACTER CODE POINTS]
 1997          array(0x3FFFE, 0x3FFFF),    // [NONCHARACTER CODE POINTS]
 1998          array(0x4FFFE, 0x4FFFF),    // [NONCHARACTER CODE POINTS]
 1999          array(0x5FFFE, 0x5FFFF),    // [NONCHARACTER CODE POINTS]
 2000          array(0x6FFFE, 0x6FFFF),    // [NONCHARACTER CODE POINTS]
 2001          array(0x7FFFE, 0x7FFFF),    // [NONCHARACTER CODE POINTS]
 2002          array(0x8FFFE, 0x8FFFF),    // [NONCHARACTER CODE POINTS]
 2003          array(0x9FFFE, 0x9FFFF),    // [NONCHARACTER CODE POINTS]
 2004          array(0xAFFFE, 0xAFFFF),    // [NONCHARACTER CODE POINTS]
 2005          array(0xBFFFE, 0xBFFFF),    // [NONCHARACTER CODE POINTS]
 2006          array(0xCFFFE, 0xCFFFF),    // [NONCHARACTER CODE POINTS]
 2007          array(0xDFFFE, 0xDFFFF),    // [NONCHARACTER CODE POINTS]
 2008          array(0xEFFFE, 0xEFFFF),    // [NONCHARACTER CODE POINTS]
 2009          array(0xFFFFE, 0xFFFFF),    // [NONCHARACTER CODE POINTS]
 2010          array(0x10FFFE, 0x10FFFF),    // [NONCHARACTER CODE POINTS]
 2011          // Table C.5
 2012          array(0xD800, 0xDFFF),        // [SURROGATE CODES]
 2013          // Table C.6
 2014          array(0xFFF9, 0xFFF9),        // INTERLINEAR ANNOTATION ANCHOR
 2015          array(0xFFFA, 0xFFFA),        // INTERLINEAR ANNOTATION SEPARATOR
 2016          array(0xFFFB, 0xFFFB),        // INTERLINEAR ANNOTATION TERMINATOR
 2017          array(0xFFFC, 0xFFFC),        // OBJECT REPLACEMENT CHARACTER
 2018          array(0xFFFD, 0xFFFD),        // REPLACEMENT CHARACTER
 2019          // Table C.7
 2020          array(0x2FF0, 0x2FFB),        // [IDEOGRAPHIC DESCRIPTION CHARACTERS]
 2021          // Table C.8
 2022          array(0x0340, 0x0340),        // COMBINING GRAVE TONE MARK
 2023          array(0x0341, 0x0341),        // COMBINING ACUTE TONE MARK
 2024          array(0x200E, 0x200E),        // LEFT-TO-RIGHT MARK
 2025          array(0x200F, 0x200F),        // RIGHT-TO-LEFT MARK
 2026          array(0x202A, 0x202A),        // LEFT-TO-RIGHT EMBEDDING
 2027          array(0x202B, 0x202B),        // RIGHT-TO-LEFT EMBEDDING
 2028          array(0x202C, 0x202C),        // POP DIRECTIONAL FORMATTING
 2029          array(0x202D, 0x202D),        // LEFT-TO-RIGHT OVERRIDE
 2030          array(0x202E, 0x202E),        // RIGHT-TO-LEFT OVERRIDE
 2031          array(0x206A, 0x206A),        // INHIBIT SYMMETRIC SWAPPING
 2032          array(0x206B, 0x206B),        // ACTIVATE SYMMETRIC SWAPPING
 2033          array(0x206C, 0x206C),        // INHIBIT ARABIC FORM SHAPING
 2034          array(0x206D, 0x206D),        // ACTIVATE ARABIC FORM SHAPING
 2035          array(0x206E, 0x206E),        // NATIONAL DIGIT SHAPES
 2036          array(0x206F, 0x206F),        // NOMINAL DIGIT SHAPES
 2037          // Table C.9
 2038          array(0xE0001, 0xE0001),    // LANGUAGE TAG
 2039          array(0xE0020, 0xE007F),    // [TAGGING CHARACTERS]
 2040          // RFC3920
 2041          array(0x22, 0x22),            // "
 2042          array(0x26, 0x26),            // &
 2043          array(0x27, 0x27),            // '
 2044          array(0x2F, 0x2F),            // /
 2045          array(0x3A, 0x3A),            // :
 2046          array(0x3C, 0x3C),            // <
 2047          array(0x3E, 0x3E),            // >
 2048          array(0x40, 0x40)            // @
 2049      );
 2050   
 2051      $pos = 0;
 2052      $result = true;
 2053   
 2054      while ($pos < strlen($username))
 2055      {
 2056          $len = $uni = 0;
 2057          for ($i = 0; $i <= 5; $i++)
 2058          {
 2059              if (ord($username[$pos]) >= $boundary[$i][0] && ord($username[$pos]) <= $boundary[$i][1])
 2060              {
 2061                  $len = $i + 1;
 2062                  $uni = (ord($username[$pos]) - $boundary[$i][0]) * pow(2, $i * 6);
 2063   
 2064                  for ($k = 1; $k < $len; $k++)
 2065                  {
 2066                      $uni += (ord($username[$pos + $k]) - 128) * pow(2, ($i - $k) * 6);
 2067                  }
 2068   
 2069                  break;
 2070              }
 2071          }
 2072   
 2073          if ($len == 0)
 2074          {
 2075              return 'WRONG_DATA';
 2076          }
 2077   
 2078          foreach ($prohibited as $pval)
 2079          {
 2080              if ($uni >= $pval[0] && $uni <= $pval[1])
 2081              {
 2082                  $result = false;
 2083                  break 2;
 2084              }
 2085          }
 2086   
 2087          $pos = $pos + $len;
 2088      }
 2089   
 2090      if (!$result)
 2091      {
 2092          return 'WRONG_DATA';
 2093      }
 2094   
 2095      return false;
 2096  }
 2097   
 2098  /**
 2099  * Validate hex colour value
 2100  *
 2101  * @param string $colour The hex colour value
 2102  * @param bool $optional Whether the colour value is optional. True if an empty
 2103  *            string will be accepted as correct input, false if not.
 2104  * @return bool|string Error message if colour value is incorrect, false if it
 2105  *            fits the hex colour code
 2106  */
 2107  function phpbb_validate_hex_colour($colour, $optional = false)
 2108  {
 2109      if ($colour === '')
 2110      {
 2111          return (($optional) ? false : 'WRONG_DATA');
 2112      }
 2113   
 2114      if (!preg_match('/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/', $colour))
 2115      {
 2116          return 'WRONG_DATA';
 2117      }
 2118   
 2119      return false;
 2120  }
 2121   
 2122  /**
 2123  * Verifies whether a style ID corresponds to an active style.
 2124  *
 2125  * @param int $style_id The style_id of a style which should be checked if activated or not.
 2126  * @return boolean
 2127  */
 2128  function phpbb_style_is_active($style_id)
 2129  {
 2130      global $db;
 2131   
 2132      $sql = 'SELECT style_active
 2133          FROM ' . STYLES_TABLE . '
 2134          WHERE style_id = '. (int) $style_id;
 2135      $result = $db->sql_query($sql);
 2136   
 2137      $style_is_active = (bool) $db->sql_fetchfield('style_active');
 2138      $db->sql_freeresult($result);
 2139   
 2140      return $style_is_active;
 2141  }
 2142   
 2143  /**
 2144  * Remove avatar
 2145  */
 2146  function avatar_delete($mode, $row, $clean_db = false)
 2147  {
 2148      global $phpbb_root_path, $config, $db, $user;
 2149   
 2150      // Check if the users avatar is actually *not* a group avatar
 2151      if ($mode == 'user')
 2152      {
 2153          if (strpos($row['user_avatar'], 'g') === 0 || (((int) $row['user_avatar'] !== 0) && ((int) $row['user_avatar'] !== (int) $row['user_id'])))
 2154          {
 2155              return false;
 2156          }
 2157      }
 2158   
 2159      if ($clean_db)
 2160      {
 2161          avatar_remove_db($row[$mode . '_avatar']);
 2162      }
 2163      $filename = get_avatar_filename($row[$mode . '_avatar']);
 2164   
 2165      if (file_exists($phpbb_root_path . $config['avatar_path'] . '/' . $filename))
 2166      {
 2167          @unlink($phpbb_root_path . $config['avatar_path'] . '/' . $filename);
 2168          return true;
 2169      }
 2170   
 2171      return false;
 2172  }
 2173   
 2174  /**
 2175  * Generates avatar filename from the database entry
 2176  */
 2177  function get_avatar_filename($avatar_entry)
 2178  {
 2179      global $config;
 2180   
 2181      if ($avatar_entry[0] === 'g')
 2182      {
 2183          $avatar_group = true;
 2184          $avatar_entry = substr($avatar_entry, 1);
 2185      }
 2186      else
 2187      {
 2188          $avatar_group = false;
 2189      }
 2190      $ext             = substr(strrchr($avatar_entry, '.'), 1);
 2191      $avatar_entry    = intval($avatar_entry);
 2192      return $config['avatar_salt'] . '_' . (($avatar_group) ? 'g' : '') . $avatar_entry . '.' . $ext;
 2193  }
 2194   
 2195  /**
 2196  * Returns an explanation string with maximum avatar settings
 2197  *
 2198  * @return string
 2199  */
 2200  function phpbb_avatar_explanation_string()
 2201  {
 2202      global $config, $user;
 2203   
 2204      return $user->lang('AVATAR_EXPLAIN',
 2205          $user->lang('PIXELS', (int) $config['avatar_max_width']),
 2206          $user->lang('PIXELS', (int) $config['avatar_max_height']),
 2207          round($config['avatar_filesize'] / 1024));
 2208  }
 2209   
 2210  //
 2211  // Usergroup functions
 2212  //
 2213   
 2214  /**
 2215  * Add or edit a group. If we're editing a group we only update user
 2216  * parameters such as rank, etc. if they are changed
 2217  */
 2218  function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow_desc_bbcode = false, $allow_desc_urls = false, $allow_desc_smilies = false)
 2219  {
 2220      global $phpbb_root_path, $config, $db, $user, $file_upload, $phpbb_container;
 2221   
 2222      $error = array();
 2223   
 2224      // Attributes which also affect the users table
 2225      $user_attribute_ary = array('group_colour', 'group_rank', 'group_avatar', 'group_avatar_type', 'group_avatar_width', 'group_avatar_height');
 2226   
 2227      // Check data. Limit group name length.
 2228      if (!utf8_strlen($name) || utf8_strlen($name) > 60)
 2229      {
 2230          $error[] = (!utf8_strlen($name)) ? $user->lang['GROUP_ERR_USERNAME'] : $user->lang['GROUP_ERR_USER_LONG'];
 2231      }
 2232   
 2233      $err = group_validate_groupname($group_id, $name);
 2234      if (!empty($err))
 2235      {
 2236          $error[] = $user->lang[$err];
 2237      }
 2238   
 2239      if (!in_array($type, array(GROUP_OPEN, GROUP_CLOSED, GROUP_HIDDEN, GROUP_SPECIAL, GROUP_FREE)))
 2240      {
 2241          $error[] = $user->lang['GROUP_ERR_TYPE'];
 2242      }
 2243   
 2244      $group_teampage = !empty($group_attributes['group_teampage']);
 2245      unset($group_attributes['group_teampage']);
 2246   
 2247      if (!sizeof($error))
 2248      {
 2249          $current_legend = \phpbb\groupposition\legend::GROUP_DISABLED;
 2250          $current_teampage = \phpbb\groupposition\teampage::GROUP_DISABLED;
 2251   
 2252          $legend = $phpbb_container->get('groupposition.legend');
 2253          $teampage = $phpbb_container->get('groupposition.teampage');
 2254          if ($group_id)
 2255          {
 2256              try
 2257              {
 2258                  $current_legend = $legend->get_group_value($group_id);
 2259                  $current_teampage = $teampage->get_group_value($group_id);
 2260              }
 2261              catch (\phpbb\groupposition\exception $exception)
 2262              {
 2263                  trigger_error($user->lang($exception->getMessage()));
 2264              }
 2265          }
 2266   
 2267          if (!empty($group_attributes['group_legend']))
 2268          {
 2269              if (($group_id && ($current_legend == \phpbb\groupposition\legend::GROUP_DISABLED)) || !$group_id)
 2270              {
 2271                  // Old group currently not in the legend or new group, add at the end.
 2272                  $group_attributes['group_legend'] = 1 + $legend->get_group_count();
 2273              }
 2274              else
 2275              {
 2276                  // Group stayes in the legend
 2277                  $group_attributes['group_legend'] = $current_legend;
 2278              }
 2279          }
 2280          else if ($group_id && ($current_legend != \phpbb\groupposition\legend::GROUP_DISABLED))
 2281          {
 2282              // Group is removed from the legend
 2283              try
 2284              {
 2285                  $legend->delete_group($group_id, true);
 2286              }
 2287              catch (\phpbb\groupposition\exception $exception)
 2288              {
 2289                  trigger_error($user->lang($exception->getMessage()));
 2290              }
 2291              $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
 2292          }
 2293          else
 2294          {
 2295              $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
 2296          }
 2297   
 2298          // Unset the objects, we don't need them anymore.
 2299          unset($legend);
 2300   
 2301          $user_ary = array();
 2302          $sql_ary = array(
 2303              'group_name'            => (string) $name,
 2304              'group_desc'            => (string) $desc,
 2305              'group_desc_uid'        => '',
 2306              'group_desc_bitfield'    => '',
 2307              'group_type'            => (int) $type,
 2308          );
 2309   
 2310          // Parse description
 2311          if ($desc)
 2312          {
 2313              generate_text_for_storage($sql_ary['group_desc'], $sql_ary['group_desc_uid'], $sql_ary['group_desc_bitfield'], $sql_ary['group_desc_options'], $allow_desc_bbcode, $allow_desc_urls, $allow_desc_smilies);
 2314          }
 2315   
 2316          if (sizeof($group_attributes))
 2317          {
 2318              // Merge them with $sql_ary to properly update the group
 2319              $sql_ary = array_merge($sql_ary, $group_attributes);
 2320          }
 2321   
 2322          // Setting the log message before we set the group id (if group gets added)
 2323          $log = ($group_id) ? 'LOG_GROUP_UPDATED' : 'LOG_GROUP_CREATED';
 2324   
 2325          $query = '';
 2326   
 2327          if ($group_id)
 2328          {
 2329              $sql = 'SELECT user_id
 2330                  FROM ' . USERS_TABLE . '
 2331                  WHERE group_id = ' . $group_id;
 2332              $result = $db->sql_query($sql);
 2333   
 2334              while ($row = $db->sql_fetchrow($result))
 2335              {
 2336                  $user_ary[] = $row['user_id'];
 2337              }
 2338              $db->sql_freeresult($result);
 2339   
 2340              if (isset($sql_ary['group_avatar']))
 2341              {
 2342                  remove_default_avatar($group_id, $user_ary);
 2343              }
 2344   
 2345              if (isset($sql_ary['group_rank']))
 2346              {
 2347                  remove_default_rank($group_id, $user_ary);
 2348              }
 2349   
 2350              $sql = 'UPDATE ' . GROUPS_TABLE . '
 2351                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
 2352                  WHERE group_id = $group_id";
 2353              $db->sql_query($sql);
 2354   
 2355              // Since we may update the name too, we need to do this on other tables too...
 2356              $sql = 'UPDATE ' . MODERATOR_CACHE_TABLE . "
 2357                  SET group_name = '" . $db->sql_escape($sql_ary['group_name']) . "'
 2358                  WHERE group_id = $group_id";
 2359              $db->sql_query($sql);
 2360   
 2361              // One special case is the group skip auth setting. If this was changed we need to purge permissions for this group
 2362              if (isset($group_attributes['group_skip_auth']))
 2363              {
 2364                  // Get users within this group...
 2365                  $sql = 'SELECT user_id
 2366                      FROM ' . USER_GROUP_TABLE . '
 2367                      WHERE group_id = ' . $group_id . '
 2368                          AND user_pending = 0';
 2369                  $result = $db->sql_query($sql);
 2370   
 2371                  $user_id_ary = array();
 2372                  while ($row = $db->sql_fetchrow($result))
 2373                  {
 2374                      $user_id_ary[] = $row['user_id'];
 2375                  }
 2376                  $db->sql_freeresult($result);
 2377   
 2378                  if (!empty($user_id_ary))
 2379                  {
 2380                      global $auth;
 2381   
 2382                      // Clear permissions cache of relevant users
 2383                      $auth->acl_clear_prefetch($user_id_ary);
 2384                  }
 2385              }
 2386          }
 2387          else
 2388          {
 2389              $sql = 'INSERT INTO ' . GROUPS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
 2390              $db->sql_query($sql);
 2391          }
 2392   
 2393          // Remove the group from the teampage, only if unselected and we are editing a group,
 2394          // which is currently displayed.
 2395          if (!$group_teampage && $group_id && $current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED)
 2396          {
 2397              try
 2398              {
 2399                  $teampage->delete_group($group_id);
 2400              }
 2401              catch (\phpbb\groupposition\exception $exception)
 2402              {
 2403                  trigger_error($user->lang($exception->getMessage()));
 2404              }
 2405          }
 2406   
 2407          if (!$group_id)
 2408          {
 2409              $group_id = $db->sql_nextid();
 2410   
 2411              if (isset($sql_ary['group_avatar_type']) && $sql_ary['group_avatar_type'] == 'avatar.driver.upload')
 2412              {
 2413                  group_correct_avatar($group_id, $sql_ary['group_avatar']);
 2414              }
 2415          }
 2416   
 2417          try
 2418          {
 2419              if ($group_teampage && $current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
 2420              {
 2421                  $teampage->add_group($group_id);
 2422              }
 2423   
 2424              if ($group_teampage)
 2425              {
 2426                  if ($current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
 2427                  {
 2428                      $teampage->add_group($group_id);
 2429                  }
 2430              }
 2431              else if ($group_id && ($current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED))
 2432              {
 2433                  $teampage->delete_group($group_id);
 2434              }
 2435          }
 2436          catch (\phpbb\groupposition\exception $exception)
 2437          {
 2438              trigger_error($user->lang($exception->getMessage()));
 2439          }
 2440          unset($teampage);
 2441   
 2442          // Set user attributes
 2443          $sql_ary = array();
 2444          if (sizeof($group_attributes))
 2445          {
 2446              // Go through the user attributes array, check if a group attribute matches it and then set it. ;)
 2447              foreach ($user_attribute_ary as $attribute)
 2448              {
 2449                  if (!isset($group_attributes[$attribute]))
 2450                  {
 2451                      continue;
 2452                  }
 2453   
 2454                  // If we are about to set an avatar, we will not overwrite user avatars if no group avatar is set...
 2455                  if (strpos($attribute, 'group_avatar') === 0 && !$group_attributes[$attribute])
 2456                  {
 2457                      continue;
 2458                  }
 2459   
 2460                  $sql_ary[$attribute] = $group_attributes[$attribute];
 2461              }
 2462          }
 2463   
 2464          if (sizeof($sql_ary) && sizeof($user_ary))
 2465          {
 2466              group_set_user_default($group_id, $user_ary, $sql_ary);
 2467          }
 2468   
 2469          $name = ($type == GROUP_SPECIAL) ? $user->lang['G_' . $name] : $name;
 2470          add_log('admin', $log, $name);
 2471   
 2472          group_update_listings($group_id);
 2473      }
 2474   
 2475      return (sizeof($error)) ? $error : false;
 2476  }
 2477   
 2478   
 2479  /**
 2480  * Changes a group avatar's filename to conform to the naming scheme
 2481  */
 2482  function group_correct_avatar($group_id, $old_entry)
 2483  {
 2484      global $config, $db, $phpbb_root_path;
 2485   
 2486      $group_id        = (int) $group_id;
 2487      $ext             = substr(strrchr($old_entry, '.'), 1);
 2488      $old_filename     = get_avatar_filename($old_entry);
 2489      $new_filename     = $config['avatar_salt'] . "_g$group_id.$ext";
 2490      $new_entry         = 'g' . $group_id . '_' . substr(time(), -5) . ".$ext";
 2491   
 2492      $avatar_path = $phpbb_root_path . $config['avatar_path'];
 2493      if (@rename($avatar_path . '/'. $old_filename, $avatar_path . '/' . $new_filename))
 2494      {
 2495          $sql = 'UPDATE ' . GROUPS_TABLE . '
 2496              SET group_avatar = \'' . $db->sql_escape($new_entry) . "'
 2497              WHERE group_id = $group_id";
 2498          $db->sql_query($sql);
 2499      }
 2500  }
 2501   
 2502   
 2503  /**
 2504  * Remove avatar also for users not having the group as default
 2505  */
 2506  function avatar_remove_db($avatar_name)
 2507  {
 2508      global $config, $db;
 2509   
 2510      $sql = 'UPDATE ' . USERS_TABLE . "
 2511          SET user_avatar = '',
 2512          user_avatar_type = ''
 2513          WHERE user_avatar = '" . $db->sql_escape($avatar_name) . '\'';
 2514      $db->sql_query($sql);
 2515  }
 2516   
 2517   
 2518  /**
 2519  * Group Delete
 2520  */
 2521  function group_delete($group_id, $group_name = false)
 2522  {
 2523      global $db, $cache, $auth, $user, $phpbb_root_path, $phpEx, $phpbb_dispatcher, $phpbb_container;
 2524   
 2525      if (!$group_name)
 2526      {
 2527          $group_name = get_group_name($group_id);
 2528      }
 2529   
 2530      $start = 0;
 2531   
 2532      do
 2533      {
 2534          $user_id_ary = $username_ary = array();
 2535   
 2536          // Batch query for group members, call group_user_del
 2537          $sql = 'SELECT u.user_id, u.username
 2538              FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u
 2539              WHERE ug.group_id = $group_id
 2540                  AND u.user_id = ug.user_id";
 2541          $result = $db->sql_query_limit($sql, 200, $start);
 2542   
 2543          if ($row = $db->sql_fetchrow($result))
 2544          {
 2545              do
 2546              {
 2547                  $user_id_ary[] = $row['user_id'];
 2548                  $username_ary[] = $row['username'];
 2549   
 2550                  $start++;
 2551              }
 2552              while ($row = $db->sql_fetchrow($result));
 2553   
 2554              group_user_del($group_id, $user_id_ary, $username_ary, $group_name);
 2555          }
 2556          else
 2557          {
 2558              $start = 0;
 2559          }
 2560          $db->sql_freeresult($result);
 2561      }
 2562      while ($start);
 2563   
 2564      // Delete group from legend and teampage
 2565      try
 2566      {
 2567          $legend = $phpbb_container->get('groupposition.legend');
 2568          $legend->delete_group($group_id);
 2569          unset($legend);
 2570      }
 2571      catch (\phpbb\groupposition\exception $exception)
 2572      {
 2573          // The group we want to delete does not exist.
 2574          // No reason to worry, we just continue the deleting process.
 2575          //trigger_error($user->lang($exception->getMessage()));
 2576      }
 2577   
 2578      try
 2579      {
 2580          $teampage = $phpbb_container->get('groupposition.teampage');
 2581          $teampage->delete_group($group_id);
 2582          unset($teampage);
 2583      }
 2584      catch (\phpbb\groupposition\exception $exception)
 2585      {
 2586          // The group we want to delete does not exist.
 2587          // No reason to worry, we just continue the deleting process.
 2588          //trigger_error($user->lang($exception->getMessage()));
 2589      }
 2590   
 2591      // Delete group
 2592      $sql = 'DELETE FROM ' . GROUPS_TABLE . "
 2593          WHERE group_id = $group_id";
 2594      $db->sql_query($sql);
 2595   
 2596      // Delete auth entries from the groups table
 2597      $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . "
 2598          WHERE group_id = $group_id";
 2599      $db->sql_query($sql);
 2600   
 2601      /**
 2602      * Event after a group is deleted
 2603      *
 2604      * @event core.delete_group_after
 2605      * @var    int        group_id    ID of the deleted group
 2606      * @var    string    group_name    Name of the deleted group
 2607      * @since 3.1.0-a1
 2608      */
 2609      $vars = array('group_id', 'group_name');
 2610      extract($phpbb_dispatcher->trigger_event('core.delete_group_after', compact($vars)));
 2611   
 2612      // Re-cache moderators
 2613      if (!function_exists('phpbb_cache_moderators'))
 2614      {
 2615          include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
 2616      }
 2617   
 2618      phpbb_cache_moderators($db, $cache, $auth);
 2619   
 2620      add_log('admin', 'LOG_GROUP_DELETE', $group_name);
 2621   
 2622      // Return false - no error
 2623      return false;
 2624  }
 2625   
 2626  /**
 2627  * Add user(s) to group
 2628  *
 2629  * @return mixed false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
 2630  */
 2631  function group_user_add($group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $default = false, $leader = 0, $pending = 0, $group_attributes = false)
 2632  {
 2633      global $db, $auth, $phpbb_container;
 2634   
 2635      // We need both username and user_id info
 2636      $result = user_get_id_name($user_id_ary, $username_ary);
 2637   
 2638      if (!sizeof($user_id_ary) || $result !== false)
 2639      {
 2640          return 'NO_USER';
 2641      }
 2642   
 2643      // Remove users who are already members of this group
 2644      $sql = 'SELECT user_id, group_leader
 2645          FROM ' . USER_GROUP_TABLE . '
 2646          WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . "
 2647              AND group_id = $group_id";
 2648      $result = $db->sql_query($sql);
 2649   
 2650      $add_id_ary = $update_id_ary = array();
 2651      while ($row = $db->sql_fetchrow($result))
 2652      {
 2653          $add_id_ary[] = (int) $row['user_id'];
 2654   
 2655          if ($leader && !$row['group_leader'])
 2656          {
 2657              $update_id_ary[] = (int) $row['user_id'];
 2658          }
 2659      }
 2660      $db->sql_freeresult($result);
 2661   
 2662      // Do all the users exist in this group?
 2663      $add_id_ary = array_diff($user_id_ary, $add_id_ary);
 2664   
 2665      // If we have no users
 2666      if (!sizeof($add_id_ary) && !sizeof($update_id_ary))
 2667      {
 2668          return 'GROUP_USERS_EXIST';
 2669      }
 2670   
 2671      $db->sql_transaction('begin');
 2672   
 2673      // Insert the new users
 2674      if (sizeof($add_id_ary))
 2675      {
 2676          $sql_ary = array();
 2677   
 2678          foreach ($add_id_ary as $user_id)
 2679          {
 2680              $sql_ary[] = array(
 2681                  'user_id'        => (int) $user_id,
 2682                  'group_id'        => (int) $group_id,
 2683                  'group_leader'    => (int) $leader,
 2684                  'user_pending'    => (int) $pending,
 2685              );
 2686          }
 2687   
 2688          $db->sql_multi_insert(USER_GROUP_TABLE, $sql_ary);
 2689      }
 2690   
 2691      if (sizeof($update_id_ary))
 2692      {
 2693          $sql = 'UPDATE ' . USER_GROUP_TABLE . '
 2694              SET group_leader = 1
 2695              WHERE ' . $db->sql_in_set('user_id', $update_id_ary) . "
 2696                  AND group_id = $group_id";
 2697          $db->sql_query($sql);
 2698      }
 2699   
 2700      if ($default)
 2701      {
 2702          group_user_attributes('default', $group_id, $user_id_ary, false, $group_name, $group_attributes);
 2703      }
 2704   
 2705      $db->sql_transaction('commit');
 2706   
 2707      // Clear permissions cache of relevant users
 2708      $auth->acl_clear_prefetch($user_id_ary);
 2709   
 2710      if (!$group_name)
 2711      {
 2712          $group_name = get_group_name($group_id);
 2713      }
 2714   
 2715      $log = ($leader) ? 'LOG_MODS_ADDED' : (($pending) ? 'LOG_USERS_PENDING' : 'LOG_USERS_ADDED');
 2716   
 2717      add_log('admin', $log, $group_name, implode(', ', $username_ary));
 2718   
 2719      group_update_listings($group_id);
 2720   
 2721      if ($pending)
 2722      {
 2723          $phpbb_notifications = $phpbb_container->get('notification_manager');
 2724   
 2725          foreach ($add_id_ary as $user_id)
 2726          {
 2727              $phpbb_notifications->add_notifications('notification.type.group_request', array(
 2728                  'group_id'        => $group_id,
 2729                  'user_id'        => $user_id,
 2730                  'group_name'    => $group_name,
 2731              ));
 2732          }
 2733      }
 2734   
 2735      // Return false - no error
 2736      return false;
 2737  }
 2738   
 2739  /**
 2740  * Remove a user/s from a given group. When we remove users we update their
 2741  * default group_id. We do this by examining which "special" groups they belong
 2742  * to. The selection is made based on a reasonable priority system
 2743  *
 2744  * @return false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
 2745  */
 2746  function group_user_del($group_id, $user_id_ary = false, $username_ary = false, $group_name = false)
 2747  {
 2748      global $db, $auth, $config, $phpbb_dispatcher, $phpbb_container;
 2749   
 2750      if ($config['coppa_enable'])
 2751      {
 2752          $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED_COPPA', 'REGISTERED', 'BOTS', 'GUESTS');
 2753      }
 2754      else
 2755      {
 2756          $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED', 'BOTS', 'GUESTS');
 2757      }
 2758   
 2759      // We need both username and user_id info
 2760      $result = user_get_id_name($user_id_ary, $username_ary);
 2761   
 2762      if (!sizeof($user_id_ary) || $result !== false)
 2763      {
 2764          return 'NO_USER';
 2765      }
 2766   
 2767      $sql = 'SELECT *
 2768          FROM ' . GROUPS_TABLE . '
 2769          WHERE ' . $db->sql_in_set('group_name', $group_order);
 2770      $result = $db->sql_query($sql);
 2771   
 2772      $group_order_id = $special_group_data = array();
 2773      while ($row = $db->sql_fetchrow($result))
 2774      {
 2775          $group_order_id[$row['group_name']] = $row['group_id'];
 2776   
 2777          $special_group_data[$row['group_id']] = array(
 2778              'group_colour'            => $row['group_colour'],
 2779              'group_rank'                => $row['group_rank'],
 2780          );
 2781   
 2782          // Only set the group avatar if one is defined...
 2783          if ($row['group_avatar'])
 2784          {
 2785              $special_group_data[$row['group_id']] = array_merge($special_group_data[$row['group_id']], array(
 2786                  'group_avatar'            => $row['group_avatar'],
 2787                  'group_avatar_type'        => $row['group_avatar_type'],
 2788                  'group_avatar_width'        => $row['group_avatar_width'],
 2789                  'group_avatar_height'    => $row['group_avatar_height'])
 2790              );
 2791          }
 2792      }
 2793      $db->sql_freeresult($result);
 2794   
 2795      // Get users default groups - we only need to reset default group membership if the group from which the user gets removed is set as default
 2796      $sql = 'SELECT user_id, group_id
 2797          FROM ' . USERS_TABLE . '
 2798          WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
 2799      $result = $db->sql_query($sql);
 2800   
 2801      $default_groups = array();
 2802      while ($row = $db->sql_fetchrow($result))
 2803      {
 2804          $default_groups[$row['user_id']] = $row['group_id'];
 2805      }
 2806      $db->sql_freeresult($result);
 2807   
 2808      // What special group memberships exist for these users?
 2809      $sql = 'SELECT g.group_id, g.group_name, ug.user_id
 2810          FROM ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
 2811          WHERE ' . $db->sql_in_set('ug.user_id', $user_id_ary) . "
 2812              AND g.group_id = ug.group_id
 2813              AND g.group_id <> $group_id
 2814              AND g.group_type = " . GROUP_SPECIAL . '
 2815          ORDER BY ug.user_id, g.group_id';
 2816      $result = $db->sql_query($sql);
 2817   
 2818      $temp_ary = array();
 2819      while ($row = $db->sql_fetchrow($result))
 2820      {
 2821          if ($default_groups[$row['user_id']] == $group_id && (!isset($temp_ary[$row['user_id']]) || $group_order_id[$row['group_name']] < $temp_ary[$row['user_id']]))
 2822          {
 2823              $temp_ary[$row['user_id']] = $row['group_id'];
 2824          }
 2825      }
 2826      $db->sql_freeresult($result);
 2827   
 2828      // sql_where_ary holds the new default groups and their users
 2829      $sql_where_ary = array();
 2830      foreach ($temp_ary as $uid => $gid)
 2831      {
 2832          $sql_where_ary[$gid][] = $uid;
 2833      }
 2834      unset($temp_ary);
 2835   
 2836      foreach ($special_group_data as $gid => $default_data_ary)
 2837      {
 2838          if (isset($sql_where_ary[$gid]) && sizeof($sql_where_ary[$gid]))
 2839          {
 2840              remove_default_rank($group_id, $sql_where_ary[$gid]);
 2841              remove_default_avatar($group_id, $sql_where_ary[$gid]);
 2842              group_set_user_default($gid, $sql_where_ary[$gid], $default_data_ary);
 2843          }
 2844      }
 2845      unset($special_group_data);
 2846   
 2847      /**
 2848      * Event before users are removed from a group
 2849      *
 2850      * @event core.group_delete_user_before
 2851      * @var    int        group_id        ID of the group from which users are deleted
 2852      * @var    string    group_name        Name of the group
 2853      * @var    array    user_id_ary        IDs of the users which are removed
 2854      * @var    array    username_ary    names of the users which are removed
 2855      * @since 3.1.0-a1
 2856      */
 2857      $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary');
 2858      extract($phpbb_dispatcher->trigger_event('core.group_delete_user_before', compact($vars)));
 2859   
 2860      $sql = 'DELETE FROM ' . USER_GROUP_TABLE . "
 2861          WHERE group_id = $group_id
 2862              AND " . $db->sql_in_set('user_id', $user_id_ary);
 2863      $db->sql_query($sql);
 2864   
 2865      // Clear permissions cache of relevant users
 2866      $auth->acl_clear_prefetch($user_id_ary);
 2867   
 2868      if (!$group_name)
 2869      {
 2870          $group_name = get_group_name($group_id);
 2871      }
 2872   
 2873      $log = 'LOG_GROUP_REMOVE';
 2874   
 2875      if ($group_name)
 2876      {
 2877          add_log('admin', $log, $group_name, implode(', ', $username_ary));
 2878      }
 2879   
 2880      group_update_listings($group_id);
 2881   
 2882      $phpbb_notifications = $phpbb_container->get('notification_manager');
 2883   
 2884      $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
 2885   
 2886      // Return false - no error
 2887      return false;
 2888  }
 2889   
 2890   
 2891  /**
 2892  * Removes the group avatar of the default group from the users in user_ids who have that group as default.
 2893  */
 2894  function remove_default_avatar($group_id, $user_ids)
 2895  {
 2896      global $db;
 2897   
 2898      if (!is_array($user_ids))
 2899      {
 2900          $user_ids = array($user_ids);
 2901      }
 2902      if (empty($user_ids))
 2903      {
 2904          return false;
 2905      }
 2906   
 2907      $user_ids = array_map('intval', $user_ids);
 2908   
 2909      $sql = 'SELECT *
 2910          FROM ' . GROUPS_TABLE . '
 2911          WHERE group_id = ' . (int) $group_id;
 2912      $result = $db->sql_query($sql);
 2913      if (!$row = $db->sql_fetchrow($result))
 2914      {
 2915          $db->sql_freeresult($result);
 2916          return false;
 2917      }
 2918      $db->sql_freeresult($result);
 2919   
 2920      $sql = 'UPDATE ' . USERS_TABLE . "
 2921          SET user_avatar = '',
 2922              user_avatar_type = '',
 2923              user_avatar_width = 0,
 2924              user_avatar_height = 0
 2925          WHERE group_id = " . (int) $group_id . "
 2926              AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
 2927              AND " . $db->sql_in_set('user_id', $user_ids);
 2928   
 2929      $db->sql_query($sql);
 2930  }
 2931   
 2932  /**
 2933  * Removes the group rank of the default group from the users in user_ids who have that group as default.
 2934  */
 2935  function remove_default_rank($group_id, $user_ids)
 2936  {
 2937      global $db;
 2938   
 2939      if (!is_array($user_ids))
 2940      {
 2941          $user_ids = array($user_ids);
 2942      }
 2943      if (empty($user_ids))
 2944      {
 2945          return false;
 2946      }
 2947   
 2948      $user_ids = array_map('intval', $user_ids);
 2949   
 2950      $sql = 'SELECT *
 2951          FROM ' . GROUPS_TABLE . '
 2952          WHERE group_id = ' . (int) $group_id;
 2953      $result = $db->sql_query($sql);
 2954      if (!$row = $db->sql_fetchrow($result))
 2955      {
 2956          $db->sql_freeresult($result);
 2957          return false;
 2958      }
 2959      $db->sql_freeresult($result);
 2960   
 2961      $sql = 'UPDATE ' . USERS_TABLE . '
 2962          SET user_rank = 0
 2963          WHERE group_id = ' . (int) $group_id . '
 2964              AND user_rank <> 0
 2965              AND user_rank = ' . (int) $row['group_rank'] . '
 2966              AND ' . $db->sql_in_set('user_id', $user_ids);
 2967      $db->sql_query($sql);
 2968  }
 2969   
 2970  /**
 2971  * This is used to promote (to leader), demote or set as default a member/s
 2972  */
 2973  function group_user_attributes($action, $group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $group_attributes = false)
 2974  {
 2975      global $db, $auth, $phpbb_root_path, $phpEx, $config, $phpbb_container;
 2976   
 2977      // We need both username and user_id info
 2978      $result = user_get_id_name($user_id_ary, $username_ary);
 2979   
 2980      if (!sizeof($user_id_ary) || $result !== false)
 2981      {
 2982          return 'NO_USERS';
 2983      }
 2984   
 2985      if (!$group_name)
 2986      {
 2987          $group_name = get_group_name($group_id);
 2988      }
 2989   
 2990      switch ($action)
 2991      {
 2992          case 'demote':
 2993          case 'promote':
 2994   
 2995              $sql = 'SELECT user_id
 2996                  FROM ' . USER_GROUP_TABLE . "
 2997                  WHERE group_id = $group_id
 2998                      AND user_pending = 1
 2999                      AND " . $db->sql_in_set('user_id', $user_id_ary);
 3000              $result = $db->sql_query_limit($sql, 1);
 3001              $not_empty = ($db->sql_fetchrow($result));
 3002              $db->sql_freeresult($result);
 3003              if ($not_empty)
 3004              {
 3005                  return 'NO_VALID_USERS';
 3006              }
 3007   
 3008              $sql = 'UPDATE ' . USER_GROUP_TABLE . '
 3009                  SET group_leader = ' . (($action == 'promote') ? 1 : 0) . "
 3010                  WHERE group_id = $group_id
 3011                      AND user_pending = 0
 3012                      AND " . $db->sql_in_set('user_id', $user_id_ary);
 3013              $db->sql_query($sql);
 3014   
 3015              $log = ($action == 'promote') ? 'LOG_GROUP_PROMOTED' : 'LOG_GROUP_DEMOTED';
 3016          break;
 3017   
 3018          case 'approve':
 3019              // Make sure we only approve those which are pending ;)
 3020              $sql = 'SELECT u.user_id, u.user_email, u.username, u.username_clean, u.user_notify_type, u.user_jabber, u.user_lang
 3021                  FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
 3022                  WHERE ug.group_id = ' . $group_id . '
 3023                      AND ug.user_pending = 1
 3024                      AND ug.user_id = u.user_id
 3025                      AND ' . $db->sql_in_set('ug.user_id', $user_id_ary);
 3026              $result = $db->sql_query($sql);
 3027   
 3028              $user_id_ary = array();
 3029              while ($row = $db->sql_fetchrow($result))
 3030              {
 3031                  $user_id_ary[] = $row['user_id'];
 3032              }
 3033              $db->sql_freeresult($result);
 3034   
 3035              if (!sizeof($user_id_ary))
 3036              {
 3037                  return false;
 3038              }
 3039   
 3040              $sql = 'UPDATE ' . USER_GROUP_TABLE . "
 3041                  SET user_pending = 0
 3042                  WHERE group_id = $group_id
 3043                      AND " . $db->sql_in_set('user_id', $user_id_ary);
 3044              $db->sql_query($sql);
 3045   
 3046              $phpbb_notifications = $phpbb_container->get('notification_manager');
 3047   
 3048              $phpbb_notifications->add_notifications('notification.type.group_request_approved', array(
 3049                  'user_ids'        => $user_id_ary,
 3050                  'group_id'        => $group_id,
 3051                  'group_name'    => $group_name,
 3052              ));
 3053              $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
 3054   
 3055              $log = 'LOG_USERS_APPROVED';
 3056          break;
 3057   
 3058          case 'default':
 3059              // We only set default group for approved members of the group
 3060              $sql = 'SELECT user_id
 3061                  FROM ' . USER_GROUP_TABLE . "
 3062                  WHERE group_id = $group_id
 3063                      AND user_pending = 0
 3064                      AND " . $db->sql_in_set('user_id', $user_id_ary);
 3065              $result = $db->sql_query($sql);
 3066   
 3067              $user_id_ary = $username_ary = array();
 3068              while ($row = $db->sql_fetchrow($result))
 3069              {
 3070                  $user_id_ary[] = $row['user_id'];
 3071              }
 3072              $db->sql_freeresult($result);
 3073   
 3074              $result = user_get_id_name($user_id_ary, $username_ary);
 3075              if (!sizeof($user_id_ary) || $result !== false)
 3076              {
 3077                  return 'NO_USERS';
 3078              }
 3079   
 3080              $sql = 'SELECT user_id, group_id
 3081                  FROM ' . USERS_TABLE . '
 3082                  WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
 3083              $result = $db->sql_query($sql);
 3084   
 3085              $groups = array();
 3086              while ($row = $db->sql_fetchrow($result))
 3087              {
 3088                  if (!isset($groups[$row['group_id']]))
 3089                  {
 3090                      $groups[$row['group_id']] = array();
 3091                  }
 3092                  $groups[$row['group_id']][] = $row['user_id'];
 3093              }
 3094              $db->sql_freeresult($result);
 3095   
 3096              foreach ($groups as $gid => $uids)
 3097              {
 3098                  remove_default_rank($gid, $uids);
 3099                  remove_default_avatar($gid, $uids);
 3100              }
 3101              group_set_user_default($group_id, $user_id_ary, $group_attributes);
 3102              $log = 'LOG_GROUP_DEFAULTS';
 3103          break;
 3104      }
 3105   
 3106      // Clear permissions cache of relevant users
 3107      $auth->acl_clear_prefetch($user_id_ary);
 3108   
 3109      add_log('admin', $log, $group_name, implode(', ', $username_ary));
 3110   
 3111      group_update_listings($group_id);
 3112   
 3113      return false;
 3114  }
 3115   
 3116  /**
 3117  * A small version of validate_username to check for a group name's existence. To be called directly.
 3118  */
 3119  function group_validate_groupname($group_id, $group_name)
 3120  {
 3121      global $config, $db;
 3122   
 3123      $group_name =  utf8_clean_string($group_name);
 3124   
 3125      if (!empty($group_id))
 3126      {
 3127          $sql = 'SELECT group_name
 3128              FROM ' . GROUPS_TABLE . '
 3129              WHERE group_id = ' . (int) $group_id;
 3130          $result = $db->sql_query($sql);
 3131          $row = $db->sql_fetchrow($result);
 3132          $db->sql_freeresult($result);
 3133   
 3134          if (!$row)
 3135          {
 3136              return false;
 3137          }
 3138   
 3139          $allowed_groupname = utf8_clean_string($row['group_name']);
 3140   
 3141          if ($allowed_groupname == $group_name)
 3142          {
 3143              return false;
 3144          }
 3145      }
 3146   
 3147      $sql = 'SELECT group_name
 3148          FROM ' . GROUPS_TABLE . "
 3149          WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($group_name)) . "'";
 3150      $result = $db->sql_query($sql);
 3151      $row = $db->sql_fetchrow($result);
 3152      $db->sql_freeresult($result);
 3153   
 3154      if ($row)
 3155      {
 3156          return 'GROUP_NAME_TAKEN';
 3157      }
 3158   
 3159      return false;
 3160  }
 3161   
 3162  /**
 3163  * Set users default group
 3164  *
 3165  * @access private
 3166  */
 3167  function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false)
 3168  {
 3169      global $phpbb_container, $db, $phpbb_dispatcher;
 3170   
 3171      if (empty($user_id_ary))
 3172      {
 3173          return;
 3174      }
 3175   
 3176      $attribute_ary = array(
 3177          'group_colour'            => 'string',
 3178          'group_rank'            => 'int',
 3179          'group_avatar'            => 'string',
 3180          'group_avatar_type'        => 'string',
 3181          'group_avatar_width'    => 'int',
 3182          'group_avatar_height'    => 'int',
 3183      );
 3184   
 3185      $sql_ary = array(
 3186          'group_id'        => $group_id
 3187      );
 3188   
 3189      // Were group attributes passed to the function? If not we need to obtain them
 3190      if ($group_attributes === false)
 3191      {
 3192          $sql = 'SELECT ' . implode(', ', array_keys($attribute_ary)) . '
 3193              FROM ' . GROUPS_TABLE . "
 3194              WHERE group_id = $group_id";
 3195          $result = $db->sql_query($sql);
 3196          $group_attributes = $db->sql_fetchrow($result);
 3197          $db->sql_freeresult($result);
 3198      }
 3199   
 3200      foreach ($attribute_ary as $attribute => $type)
 3201      {
 3202          if (isset($group_attributes[$attribute]))
 3203          {
 3204              // If we are about to set an avatar or rank, we will not overwrite with empty, unless we are not actually changing the default group
 3205              if ((strpos($attribute, 'group_avatar') === 0 || strpos($attribute, 'group_rank') === 0) && !$group_attributes[$attribute])
 3206              {
 3207                  continue;
 3208              }
 3209   
 3210              settype($group_attributes[$attribute], $type);
 3211              $sql_ary[str_replace('group_', 'user_', $attribute)] = $group_attributes[$attribute];
 3212          }
 3213      }
 3214   
 3215      $updated_sql_ary = $sql_ary;
 3216   
 3217      // Before we update the user attributes, we will update the rank for users that don't have a custom rank
 3218      if (isset($sql_ary['user_rank']))
 3219      {
 3220          $sql = 'UPDATE ' . USERS_TABLE . '
 3221              SET ' . $db->sql_build_array('UPDATE', array('user_rank' => $sql_ary['user_rank'])) . '
 3222              WHERE user_rank = 0
 3223                  AND ' . $db->sql_in_set('user_id', $user_id_ary);
 3224          $db->sql_query($sql);
 3225          unset($sql_ary['user_rank']);
 3226      }
 3227   
 3228      // Before we update the user attributes, we will update the avatar for users that don't have a custom avatar
 3229      $avatar_options = array('user_avatar', 'user_avatar_type', 'user_avatar_height', 'user_avatar_width');
 3230   
 3231      if (isset($sql_ary['user_avatar']))
 3232      {
 3233          $avatar_sql_ary = array();
 3234          foreach ($avatar_options as $avatar_option)
 3235          {
 3236              if (isset($sql_ary[$avatar_option]))
 3237              {
 3238                  $avatar_sql_ary[$avatar_option] = $sql_ary[$avatar_option];
 3239                  }
 3240              }
 3241   
 3242          $sql = 'UPDATE ' . USERS_TABLE . '
 3243              SET ' . $db->sql_build_array('UPDATE', $avatar_sql_ary) . "
 3244              WHERE user_avatar = ''
 3245                  AND " . $db->sql_in_set('user_id', $user_id_ary);
 3246          $db->sql_query($sql);
 3247      }
 3248   
 3249      // Remove the avatar options, as we already updated them
 3250      foreach ($avatar_options as $avatar_option)
 3251      {
 3252          unset($sql_ary[$avatar_option]);
 3253      }
 3254   
 3255      if (!empty($sql_ary))
 3256      {
 3257          $sql = 'UPDATE ' . USERS_TABLE . '
 3258              SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 3259              WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
 3260          $db->sql_query($sql);
 3261      }
 3262   
 3263      if (isset($sql_ary['user_colour']))
 3264      {
 3265          // Update any cached colour information for these users
 3266          $sql = 'UPDATE ' . FORUMS_TABLE . "
 3267              SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
 3268              WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
 3269          $db->sql_query($sql);
 3270   
 3271          $sql = 'UPDATE ' . TOPICS_TABLE . "
 3272              SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
 3273              WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
 3274          $db->sql_query($sql);
 3275   
 3276          $sql = 'UPDATE ' . TOPICS_TABLE . "
 3277              SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
 3278              WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
 3279          $db->sql_query($sql);
 3280   
 3281          global $config;
 3282   
 3283          if (in_array($config['newest_user_id'], $user_id_ary))
 3284          {
 3285              set_config('newest_user_colour', $sql_ary['user_colour'], true);
 3286          }
 3287      }
 3288   
 3289      // Make all values available for the event
 3290      $sql_ary = $updated_sql_ary;
 3291   
 3292      /**
 3293      * Event when the default group is set for an array of users
 3294      *
 3295      * @event core.user_set_default_group
 3296      * @var    int        group_id            ID of the group
 3297      * @var    array    user_id_ary            IDs of the users
 3298      * @var    array    group_attributes    Group attributes which were changed
 3299      * @var    array    update_listing        Update the list of moderators and foes
 3300      * @var    array    sql_ary                User attributes which were changed
 3301      * @since 3.1.0-a1
 3302      */
 3303      $vars = array('group_id', 'user_id_ary', 'group_attributes', 'update_listing', 'sql_ary');
 3304      extract($phpbb_dispatcher->trigger_event('core.user_set_default_group', compact($vars)));
 3305   
 3306      if ($update_listing)
 3307      {
 3308          group_update_listings($group_id);
 3309      }
 3310   
 3311      // Because some tables/caches use usercolour-specific data we need to purge this here.
 3312      $phpbb_container->get('cache.driver')->destroy('sql', MODERATOR_CACHE_TABLE);
 3313  }
 3314   
 3315  /**
 3316  * Get group name
 3317  */
 3318  function get_group_name($group_id)
 3319  {
 3320      global $db, $user;
 3321   
 3322      $sql = 'SELECT group_name, group_type
 3323          FROM ' . GROUPS_TABLE . '
 3324          WHERE group_id = ' . (int) $group_id;
 3325      $result = $db->sql_query($sql);
 3326      $row = $db->sql_fetchrow($result);
 3327      $db->sql_freeresult($result);
 3328   
 3329      if (!$row || ($row['group_type'] == GROUP_SPECIAL && empty($user->lang)))
 3330      {
 3331          return '';
 3332      }
 3333   
 3334      return ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'];
 3335  }
 3336   
 3337  /**
 3338  * Obtain either the members of a specified group, the groups the specified user is subscribed to
 3339  * or checking if a specified user is in a specified group. This function does not return pending memberships.
 3340  *
 3341  * Note: Never use this more than once... first group your users/groups
 3342  */
 3343  function group_memberships($group_id_ary = false, $user_id_ary = false, $return_bool = false)
 3344  {
 3345      global $db;
 3346   
 3347      if (!$group_id_ary && !$user_id_ary)
 3348      {
 3349          return true;
 3350      }
 3351   
 3352      if ($user_id_ary)
 3353      {
 3354          $user_id_ary = (!is_array($user_id_ary)) ? array($user_id_ary) : $user_id_ary;
 3355      }
 3356   
 3357      if ($group_id_ary)
 3358      {
 3359          $group_id_ary = (!is_array($group_id_ary)) ? array($group_id_ary) : $group_id_ary;
 3360      }
 3361   
 3362      $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email
 3363          FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
 3364          WHERE ug.user_id = u.user_id
 3365              AND ug.user_pending = 0 AND ';
 3366   
 3367      if ($group_id_ary)
 3368      {
 3369          $sql .= ' ' . $db->sql_in_set('ug.group_id', $group_id_ary);
 3370      }
 3371   
 3372      if ($user_id_ary)
 3373      {
 3374          $sql .= ($group_id_ary) ? ' AND ' : ' ';
 3375          $sql .= $db->sql_in_set('ug.user_id', $user_id_ary);
 3376      }
 3377   
 3378      $result = ($return_bool) ? $db->sql_query_limit($sql, 1) : $db->sql_query($sql);
 3379   
 3380      $row = $db->sql_fetchrow($result);
 3381   
 3382      if ($return_bool)
 3383      {
 3384          $db->sql_freeresult($result);
 3385          return ($row) ? true : false;
 3386      }
 3387   
 3388      if (!$row)
 3389      {
 3390          return false;
 3391      }
 3392   
 3393      $return = array();
 3394   
 3395      do
 3396      {
 3397          $return[] = $row;
 3398      }
 3399      while ($row = $db->sql_fetchrow($result));
 3400   
 3401      $db->sql_freeresult($result);
 3402   
 3403      return $return;
 3404  }
 3405   
 3406  /**
 3407  * Re-cache moderators and foes if group has a_ or m_ permissions
 3408  */
 3409  function group_update_listings($group_id)
 3410  {
 3411      global $db, $cache, $auth;
 3412   
 3413      $hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_'));
 3414   
 3415      if (!sizeof($hold_ary))
 3416      {
 3417          return;
 3418      }
 3419   
 3420      $mod_permissions = $admin_permissions = false;
 3421   
 3422      foreach ($hold_ary as $g_id => $forum_ary)
 3423      {
 3424          foreach ($forum_ary as $forum_id => $auth_ary)
 3425          {
 3426              foreach ($auth_ary as $auth_option => $setting)
 3427              {
 3428                  if ($mod_permissions && $admin_permissions)
 3429                  {
 3430                      break 3;
 3431                  }
 3432   
 3433                  if ($setting != ACL_YES)
 3434                  {
 3435                      continue;
 3436                  }
 3437   
 3438                  if ($auth_option == 'm_')
 3439                  {
 3440                      $mod_permissions = true;
 3441                  }
 3442   
 3443                  if ($auth_option == 'a_')
 3444                  {
 3445                      $admin_permissions = true;
 3446                  }
 3447              }
 3448          }
 3449      }
 3450   
 3451      if ($mod_permissions)
 3452      {
 3453          if (!function_exists('phpbb_cache_moderators'))
 3454          {
 3455              global $phpbb_root_path, $phpEx;
 3456              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
 3457          }
 3458          phpbb_cache_moderators($db, $cache, $auth);
 3459      }
 3460   
 3461      if ($mod_permissions || $admin_permissions)
 3462      {
 3463          if (!function_exists('phpbb_update_foes'))
 3464          {
 3465              global $phpbb_root_path, $phpEx;
 3466              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
 3467          }
 3468          phpbb_update_foes($db, $auth, array($group_id));
 3469      }
 3470  }
 3471   
 3472   
 3473   
 3474  /**
 3475  * Funtion to make a user leave the NEWLY_REGISTERED system group.
 3476  * @access public
 3477  * @param $user_id The id of the user to remove from the group
 3478  */
 3479  function remove_newly_registered($user_id, $user_data = false)
 3480  {
 3481      global $db;
 3482   
 3483      if ($user_data === false)
 3484      {
 3485          $sql = 'SELECT *
 3486              FROM ' . USERS_TABLE . '
 3487              WHERE user_id = ' . $user_id;
 3488          $result = $db->sql_query($sql);
 3489          $user_row = $db->sql_fetchrow($result);
 3490          $db->sql_freeresult($result);
 3491   
 3492          if (!$user_row)
 3493          {
 3494              return false;
 3495          }
 3496          else
 3497          {
 3498              $user_data  = $user_row;
 3499          }
 3500      }
 3501   
 3502      if (empty($user_data['user_new']))
 3503      {
 3504          return false;
 3505      }
 3506   
 3507      $sql = 'SELECT group_id
 3508          FROM ' . GROUPS_TABLE . "
 3509          WHERE group_name = 'NEWLY_REGISTERED'
 3510              AND group_type = " . GROUP_SPECIAL;
 3511      $result = $db->sql_query($sql);
 3512      $group_id = (int) $db->sql_fetchfield('group_id');
 3513      $db->sql_freeresult($result);
 3514   
 3515      if (!$group_id)
 3516      {
 3517          return false;
 3518      }
 3519   
 3520      // We need to call group_user_del here, because this function makes sure everything is correctly changed.
 3521      // A downside for a call within the session handler is that the language is not set up yet - so no log entry
 3522      group_user_del($group_id, $user_id);
 3523   
 3524      // Set user_new to 0 to let this not be triggered again
 3525      $sql = 'UPDATE ' . USERS_TABLE . '
 3526          SET user_new = 0
 3527          WHERE user_id = ' . $user_id;
 3528      $db->sql_query($sql);
 3529   
 3530      // The new users group was the users default group?
 3531      if ($user_data['group_id'] == $group_id)
 3532      {
 3533          // Which group is now the users default one?
 3534          $sql = 'SELECT group_id
 3535              FROM ' . USERS_TABLE . '
 3536              WHERE user_id = ' . $user_id;
 3537          $result = $db->sql_query($sql);
 3538          $user_data['group_id'] = $db->sql_fetchfield('group_id');
 3539          $db->sql_freeresult($result);
 3540      }
 3541   
 3542      return $user_data['group_id'];
 3543  }
 3544   
 3545  /**
 3546  * Gets user ids of currently banned registered users.
 3547  *
 3548  * @param array $user_ids Array of users' ids to check for banning,
 3549  *                        leave empty to get complete list of banned ids
 3550  * @param bool|int $ban_end Bool True to get users currently banned
 3551  *                         Bool False to only get permanently banned users
 3552  *                         Int Unix timestamp to get users banned until that time
 3553  * @return array    Array of banned users' ids if any, empty array otherwise
 3554  */
 3555  function phpbb_get_banned_user_ids($user_ids = array(), $ban_end = true)
 3556  {
 3557      global $db;
 3558   
 3559      $sql_user_ids = (!empty($user_ids)) ? $db->sql_in_set('ban_userid', $user_ids) : 'ban_userid <> 0';
 3560   
 3561      // Get banned User ID's
 3562      // Ignore stale bans which were not wiped yet
 3563      $banned_ids_list = array();
 3564      $sql = 'SELECT ban_userid
 3565          FROM ' . BANLIST_TABLE . "
 3566          WHERE $sql_user_ids
 3567              AND ban_exclude <> 1";
 3568   
 3569      if ($ban_end === true)
 3570      {
 3571          // Banned currently
 3572          $sql .= " AND (ban_end > " . time() . '
 3573                  OR ban_end = 0)';
 3574      }
 3575      else if ($ban_end === false)
 3576      {
 3577          // Permanently banned
 3578          $sql .= " AND ban_end = 0";
 3579      }
 3580      else
 3581      {
 3582          // Banned until a specified time
 3583          $sql .= " AND (ban_end > " . (int) $ban_end . '
 3584                  OR ban_end = 0)';
 3585      }
 3586   
 3587      $result = $db->sql_query($sql);
 3588      while ($row = $db->sql_fetchrow($result))
 3589      {
 3590          $user_id = (int) $row['ban_userid'];
 3591          $banned_ids_list[$user_id] = $user_id;
 3592      }
 3593      $db->sql_freeresult($result);
 3594   
 3595      return $banned_ids_list;
 3596  }
 3597   
 3598  /**
 3599  * Function for assigning a template var if the zebra module got included
 3600  */
 3601  function phpbb_module_zebra($mode, &$module_row)
 3602  {
 3603      global $template;
 3604   
 3605      $template->assign_var('S_ZEBRA_ENABLED', true);
 3606   
 3607      if ($mode == 'friends')
 3608      {
 3609          $template->assign_var('S_ZEBRA_FRIENDS_ENABLED', true);
 3610      }
 3611   
 3612      if ($mode == 'foes')
 3613      {
 3614          $template->assign_var('S_ZEBRA_FOES_ENABLED', true);
 3615      }
 3616  }
 3617