Verzeichnisstruktur phpBB-3.3.16


Veröffentlicht
27.04.2026

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