Verzeichnisstruktur phpBB-3.3.15


Veröffentlicht
28.08.2024

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: 02.04.2025, 15:01 - Dateigröße: 100.72 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      if (preg_match('#ReferralServer:[\x20]*whois://(.+)#im', $ipwhois, $match))
1486      {
1487          if (strpos($match[1], ':') !== false)
1488          {
1489              $pos    = strrpos($match[1], ':');
1490              $server    = substr($match[1], 0, $pos);
1491              $port    = (int) substr($match[1], $pos + 1);
1492              unset($pos);
1493          }
1494          else
1495          {
1496              $server    = $match[1];
1497              $port    = 43;
1498          }
1499   
1500          $buffer = '';
1501   
1502          if (($fsk = @fsockopen($server, $port)))
1503          {
1504              fputs($fsk, "$ip\r\n");
1505              while (!feof($fsk))
1506              {
1507                  $buffer .= fgets($fsk, 1024);
1508              }
1509              @fclose($fsk);
1510          }
1511   
1512          // Use the result from $whois_host if we don't get any result here
1513          $ipwhois = (empty($buffer)) ? $ipwhois : $buffer;
1514      }
1515   
1516      $ipwhois = htmlspecialchars($ipwhois, ENT_COMPAT);
1517   
1518      // Magic URL ;)
1519      return trim(make_clickable($ipwhois, false, ''));
1520  }
1521   
1522  /**
1523  * Data validation ... used primarily but not exclusively by ucp modules
1524  *
1525  * "Master" function for validating a range of data types
1526  */
1527  function validate_data($data, $val_ary)
1528  {
1529      global $user;
1530   
1531      $error = array();
1532   
1533      foreach ($val_ary as $var => $val_seq)
1534      {
1535          if (!is_array($val_seq[0]))
1536          {
1537              $val_seq = array($val_seq);
1538          }
1539   
1540          foreach ($val_seq as $validate)
1541          {
1542              $function = array_shift($validate);
1543              array_unshift($validate, $data[$var]);
1544   
1545              if (is_array($function))
1546              {
1547                  $result = call_user_func_array(array($function[0], 'validate_' . $function[1]), $validate);
1548              }
1549              else
1550              {
1551                  $function_prefix = (function_exists('phpbb_validate_' . $function)) ? 'phpbb_validate_' : 'validate_';
1552                  $result = call_user_func_array($function_prefix . $function, $validate);
1553              }
1554   
1555              if ($result)
1556              {
1557                  // Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted.
1558                  $error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var);
1559              }
1560          }
1561      }
1562   
1563      return $error;
1564  }
1565   
1566  /**
1567  * Validate String
1568  *
1569  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1570  */
1571  function validate_string($string, $optional = false, $min = 0, $max = 0)
1572  {
1573      if (empty($string) && $optional)
1574      {
1575          return false;
1576      }
1577   
1578      if ($min && utf8_strlen(html_entity_decode($string, ENT_COMPAT)) < $min)
1579      {
1580          return 'TOO_SHORT';
1581      }
1582      else if ($max && utf8_strlen(html_entity_decode($string, ENT_COMPAT)) > $max)
1583      {
1584          return 'TOO_LONG';
1585      }
1586   
1587      return false;
1588  }
1589   
1590  /**
1591  * Validate Number
1592  *
1593  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1594  */
1595  function validate_num($num, $optional = false, $min = 0, $max = 1E99)
1596  {
1597      if (empty($num) && $optional)
1598      {
1599          return false;
1600      }
1601   
1602      if ($num < $min)
1603      {
1604          return 'TOO_SMALL';
1605      }
1606      else if ($num > $max)
1607      {
1608          return 'TOO_LARGE';
1609      }
1610   
1611      return false;
1612  }
1613   
1614  /**
1615  * Validate Date
1616  * @param    string $date_string a date in the dd-mm-yyyy format
1617  * @param    bool $optional
1618  * @return    boolean
1619  */
1620  function validate_date($date_string, $optional = false)
1621  {
1622      $date = explode('-', $date_string);
1623      if ((empty($date) || count($date) != 3) && $optional)
1624      {
1625          return false;
1626      }
1627      else if ($optional)
1628      {
1629          for ($field = 0; $field <= 1; $field++)
1630          {
1631              $date[$field] = (int) $date[$field];
1632              if (empty($date[$field]))
1633              {
1634                  $date[$field] = 1;
1635              }
1636          }
1637          $date[2] = (int) $date[2];
1638          // assume an arbitrary leap year
1639          if (empty($date[2]))
1640          {
1641              $date[2] = 1980;
1642          }
1643      }
1644   
1645      if (count($date) != 3 || !checkdate($date[1], $date[0], $date[2]))
1646      {
1647          return 'INVALID';
1648      }
1649   
1650      return false;
1651  }
1652   
1653   
1654  /**
1655  * Validate Match
1656  *
1657  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1658  */
1659  function validate_match($string, $optional = false, $match = '')
1660  {
1661      if (empty($string) && $optional)
1662      {
1663          return false;
1664      }
1665   
1666      if (empty($match))
1667      {
1668          return false;
1669      }
1670   
1671      if (!preg_match($match, $string))
1672      {
1673          return 'WRONG_DATA';
1674      }
1675   
1676      return false;
1677  }
1678   
1679  /**
1680  * Validate Language Pack ISO Name
1681  *
1682  * Tests whether a language name is valid and installed
1683  *
1684  * @param string $lang_iso    The language string to test
1685  *
1686  * @return bool|string        Either false if validation succeeded or
1687  *                            a string which will be used as the error message
1688  *                            (with the variable name appended)
1689  */
1690  function validate_language_iso_name($lang_iso)
1691  {
1692      global $db;
1693   
1694      $sql = 'SELECT lang_id
1695          FROM ' . LANG_TABLE . "
1696          WHERE lang_iso = '" . $db->sql_escape($lang_iso) . "'";
1697      $result = $db->sql_query($sql);
1698      $lang_id = (int) $db->sql_fetchfield('lang_id');
1699      $db->sql_freeresult($result);
1700   
1701      return ($lang_id) ? false : 'WRONG_DATA';
1702  }
1703   
1704  /**
1705  * Validate Timezone Name
1706  *
1707  * Tests whether a timezone name is valid
1708  *
1709  * @param string $timezone    The timezone string to test
1710  *
1711  * @return bool|string        Either false if validation succeeded or
1712  *                            a string which will be used as the error message
1713  *                            (with the variable name appended)
1714  */
1715  function phpbb_validate_timezone($timezone)
1716  {
1717      return (in_array($timezone, phpbb_get_timezone_identifiers($timezone))) ? false : 'TIMEZONE_INVALID';
1718  }
1719   
1720  /***
1721   * Validate Username
1722   *
1723   * Check to see if the username has been taken, or if it is disallowed.
1724   * Also checks if it includes the " character or the 4-bytes Unicode ones
1725   * (aka emojis) which we don't allow in usernames.
1726   * Used for registering, changing names, and posting anonymously with a username
1727   *
1728   * @param string    $username                The username to check
1729   * @param string    $allowed_username        An allowed username, default being $user->data['username']
1730   *
1731   * @return mixed                            Either false if validation succeeded or a string which will be
1732   *                                            used as the error message (with the variable name appended)
1733   */
1734  function validate_username($username, $allowed_username = false, $allow_all_names = false)
1735  {
1736      global $config, $db, $user, $cache;
1737   
1738      $clean_username = utf8_clean_string($username);
1739      $allowed_username = ($allowed_username === false) ? $user->data['username_clean'] : utf8_clean_string($allowed_username);
1740   
1741      if ($allowed_username == $clean_username)
1742      {
1743          return false;
1744      }
1745   
1746      // The very first check is for
1747      // out-of-bounds characters that are currently
1748      // not supported by utf8_bin in MySQL
1749      if (preg_match('/[\x{10000}-\x{10FFFF}]/u', $username))
1750      {
1751          return 'INVALID_EMOJIS';
1752      }
1753   
1754      // ... fast checks first.
1755      if (strpos($username, '&quot;') !== false || strpos($username, '"') !== false || empty($clean_username)
1756          || preg_match('/[\x{180E}\x{2005}-\x{200D}\x{202F}\x{205F}\x{2060}\x{FEFF}]/u', $username))
1757      {
1758          return 'INVALID_CHARS';
1759      }
1760   
1761      switch ($config['allow_name_chars'])
1762      {
1763          case 'USERNAME_CHARS_ANY':
1764              $regex = '.+';
1765          break;
1766   
1767          case 'USERNAME_ALPHA_ONLY':
1768              $regex = '[A-Za-z0-9]+';
1769          break;
1770   
1771          case 'USERNAME_ALPHA_SPACERS':
1772              $regex = '[A-Za-z0-9-[\]_+ ]+';
1773          break;
1774   
1775          case 'USERNAME_LETTER_NUM':
1776              $regex = '[\p{Lu}\p{Ll}\p{N}]+';
1777          break;
1778   
1779          case 'USERNAME_LETTER_NUM_SPACERS':
1780              $regex = '[-\]_+ [\p{Lu}\p{Ll}\p{N}]+';
1781          break;
1782   
1783          case 'USERNAME_ASCII':
1784          default:
1785              $regex = '[\x01-\x7F]+';
1786          break;
1787      }
1788   
1789      if (!preg_match('#^' . $regex . '$#u', $username))
1790      {
1791          return 'INVALID_CHARS';
1792      }
1793   
1794      $sql = 'SELECT username
1795          FROM ' . USERS_TABLE . "
1796          WHERE username_clean = '" . $db->sql_escape($clean_username) . "'";
1797      $result = $db->sql_query($sql);
1798      $row = $db->sql_fetchrow($result);
1799      $db->sql_freeresult($result);
1800   
1801      if ($row)
1802      {
1803          return 'USERNAME_TAKEN';
1804      }
1805   
1806      $sql = 'SELECT group_name
1807          FROM ' . GROUPS_TABLE . "
1808          WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($username)) . "'";
1809      $result = $db->sql_query($sql);
1810      $row = $db->sql_fetchrow($result);
1811      $db->sql_freeresult($result);
1812   
1813      if ($row)
1814      {
1815          return 'USERNAME_TAKEN';
1816      }
1817   
1818      if (!$allow_all_names)
1819      {
1820          $bad_usernames = $cache->obtain_disallowed_usernames();
1821   
1822          foreach ($bad_usernames as $bad_username)
1823          {
1824              if (preg_match('#^' . $bad_username . '$#', $clean_username))
1825              {
1826                  return 'USERNAME_DISALLOWED';
1827              }
1828          }
1829      }
1830   
1831      return false;
1832  }
1833   
1834  /**
1835  * Check to see if the password meets the complexity settings
1836  *
1837  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1838  */
1839  function validate_password($password)
1840  {
1841      global $config;
1842   
1843      if ($password === '' || $config['pass_complex'] === 'PASS_TYPE_ANY')
1844      {
1845          // Password empty or no password complexity required.
1846          return false;
1847      }
1848   
1849      $upp = '\p{Lu}';
1850      $low = '\p{Ll}';
1851      $num = '\p{N}';
1852      $sym = '[^\p{Lu}\p{Ll}\p{N}]';
1853      $chars = array();
1854   
1855      switch ($config['pass_complex'])
1856      {
1857          // No break statements below ...
1858          // We require strong passwords in case pass_complex is not set or is invalid
1859          default:
1860   
1861          // Require mixed case letters, numbers and symbols
1862          case 'PASS_TYPE_SYMBOL':
1863              $chars[] = $sym;
1864   
1865          // Require mixed case letters and numbers
1866          case 'PASS_TYPE_ALPHA':
1867              $chars[] = $num;
1868   
1869          // Require mixed case letters
1870          case 'PASS_TYPE_CASE':
1871              $chars[] = $low;
1872              $chars[] = $upp;
1873      }
1874   
1875      foreach ($chars as $char)
1876      {
1877          if (!preg_match('#' . $char . '#u', $password))
1878          {
1879              return 'INVALID_CHARS';
1880          }
1881      }
1882   
1883      return false;
1884  }
1885   
1886  /**
1887  * Check to see if email address is a valid address and contains a MX record
1888  *
1889  * @param string $email The email to check
1890  * @param $config
1891  *
1892  * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1893  */
1894  function phpbb_validate_email($email, $config = null)
1895  {
1896      if ($config === null)
1897      {
1898          global $config;
1899      }
1900   
1901      $email = strtolower($email);
1902   
1903      if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
1904      {
1905          return 'EMAIL_INVALID';
1906      }
1907   
1908      // Check MX record.
1909      // The idea for this is from reading the UseBB blog/announcement. :)
1910      if ($config['email_check_mx'])
1911      {
1912          list(, $domain) = explode('@', $email);
1913   
1914          if (checkdnsrr($domain, 'A') === false && checkdnsrr($domain, 'MX') === false)
1915          {
1916              return 'DOMAIN_NO_MX_RECORD';
1917          }
1918      }
1919   
1920      return false;
1921  }
1922   
1923  /**
1924  * Check to see if email address is banned or already present in the DB
1925  *
1926  * @param string $email The email to check
1927  * @param string $allowed_email An allowed email, default being $user->data['user_email']
1928  *
1929  * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1930  */
1931  function validate_user_email($email, $allowed_email = false)
1932  {
1933      global $config, $db, $user;
1934   
1935      $email = strtolower($email);
1936      $allowed_email = ($allowed_email === false) ? strtolower($user->data['user_email']) : strtolower($allowed_email);
1937   
1938      if ($allowed_email == $email)
1939      {
1940          return false;
1941      }
1942   
1943      $validate_email = phpbb_validate_email($email, $config);
1944      if ($validate_email)
1945      {
1946          return $validate_email;
1947      }
1948   
1949      $ban = $user->check_ban(false, false, $email, true);
1950      if (!empty($ban))
1951      {
1952          return !empty($ban['ban_give_reason']) ? $ban['ban_give_reason'] : 'EMAIL_BANNED';
1953      }
1954   
1955      if (!$config['allow_emailreuse'])
1956      {
1957          $sql = 'SELECT user_email
1958              FROM ' . USERS_TABLE . "
1959              WHERE user_email = '" . $db->sql_escape($email) . "'";
1960          $result = $db->sql_query($sql);
1961          $row = $db->sql_fetchrow($result);
1962          $db->sql_freeresult($result);
1963   
1964          if ($row)
1965          {
1966              return 'EMAIL_TAKEN';
1967          }
1968      }
1969   
1970      return false;
1971  }
1972   
1973  /**
1974  * Validate jabber address
1975  * Taken from the jabber class within flyspray (see author notes)
1976  *
1977  * @author flyspray.org
1978  */
1979  function validate_jabber($jid)
1980  {
1981      if (!$jid)
1982      {
1983          return false;
1984      }
1985   
1986      $separator_pos = strpos($jid, '@');
1987   
1988      if ($separator_pos === false)
1989      {
1990          return 'WRONG_DATA';
1991      }
1992   
1993      $username = substr($jid, 0, $separator_pos);
1994      $realm = substr($jid, $separator_pos + 1);
1995   
1996      if (strlen($username) == 0 || strlen($realm) < 3)
1997      {
1998          return 'WRONG_DATA';
1999      }
2000   
2001      $arr = explode('.', $realm);
2002   
2003      if (count($arr) == 0)
2004      {
2005          return 'WRONG_DATA';
2006      }
2007   
2008      foreach ($arr as $part)
2009      {
2010          if (substr($part, 0, 1) == '-' || substr($part, -1, 1) == '-')
2011          {
2012              return 'WRONG_DATA';
2013          }
2014   
2015          if (!preg_match("@^[a-zA-Z0-9-.]+$@", $part))
2016          {
2017              return 'WRONG_DATA';
2018          }
2019      }
2020   
2021      $boundary = array(array(0, 127), array(192, 223), array(224, 239), array(240, 247), array(248, 251), array(252, 253));
2022   
2023      // Prohibited Characters RFC3454 + RFC3920
2024      $prohibited = array(
2025          // Table C.1.1
2026          array(0x0020, 0x0020),        // SPACE
2027          // Table C.1.2
2028          array(0x00A0, 0x00A0),        // NO-BREAK SPACE
2029          array(0x1680, 0x1680),        // OGHAM SPACE MARK
2030          array(0x2000, 0x2001),        // EN QUAD
2031          array(0x2001, 0x2001),        // EM QUAD
2032          array(0x2002, 0x2002),        // EN SPACE
2033          array(0x2003, 0x2003),        // EM SPACE
2034          array(0x2004, 0x2004),        // THREE-PER-EM SPACE
2035          array(0x2005, 0x2005),        // FOUR-PER-EM SPACE
2036          array(0x2006, 0x2006),        // SIX-PER-EM SPACE
2037          array(0x2007, 0x2007),        // FIGURE SPACE
2038          array(0x2008, 0x2008),        // PUNCTUATION SPACE
2039          array(0x2009, 0x2009),        // THIN SPACE
2040          array(0x200A, 0x200A),        // HAIR SPACE
2041          array(0x200B, 0x200B),        // ZERO WIDTH SPACE
2042          array(0x202F, 0x202F),        // NARROW NO-BREAK SPACE
2043          array(0x205F, 0x205F),        // MEDIUM MATHEMATICAL SPACE
2044          array(0x3000, 0x3000),        // IDEOGRAPHIC SPACE
2045          // Table C.2.1
2046          array(0x0000, 0x001F),        // [CONTROL CHARACTERS]
2047          array(0x007F, 0x007F),        // DELETE
2048          // Table C.2.2
2049          array(0x0080, 0x009F),        // [CONTROL CHARACTERS]
2050          array(0x06DD, 0x06DD),        // ARABIC END OF AYAH
2051          array(0x070F, 0x070F),        // SYRIAC ABBREVIATION MARK
2052          array(0x180E, 0x180E),        // MONGOLIAN VOWEL SEPARATOR
2053          array(0x200C, 0x200C),         // ZERO WIDTH NON-JOINER
2054          array(0x200D, 0x200D),        // ZERO WIDTH JOINER
2055          array(0x2028, 0x2028),        // LINE SEPARATOR
2056          array(0x2029, 0x2029),        // PARAGRAPH SEPARATOR
2057          array(0x2060, 0x2060),        // WORD JOINER
2058          array(0x2061, 0x2061),        // FUNCTION APPLICATION
2059          array(0x2062, 0x2062),        // INVISIBLE TIMES
2060          array(0x2063, 0x2063),        // INVISIBLE SEPARATOR
2061          array(0x206A, 0x206F),        // [CONTROL CHARACTERS]
2062          array(0xFEFF, 0xFEFF),        // ZERO WIDTH NO-BREAK SPACE
2063          array(0xFFF9, 0xFFFC),        // [CONTROL CHARACTERS]
2064          array(0x1D173, 0x1D17A),    // [MUSICAL CONTROL CHARACTERS]
2065          // Table C.3
2066          array(0xE000, 0xF8FF),        // [PRIVATE USE, PLANE 0]
2067          array(0xF0000, 0xFFFFD),    // [PRIVATE USE, PLANE 15]
2068          array(0x100000, 0x10FFFD),    // [PRIVATE USE, PLANE 16]
2069          // Table C.4
2070          array(0xFDD0, 0xFDEF),        // [NONCHARACTER CODE POINTS]
2071          array(0xFFFE, 0xFFFF),        // [NONCHARACTER CODE POINTS]
2072          array(0x1FFFE, 0x1FFFF),    // [NONCHARACTER CODE POINTS]
2073          array(0x2FFFE, 0x2FFFF),    // [NONCHARACTER CODE POINTS]
2074          array(0x3FFFE, 0x3FFFF),    // [NONCHARACTER CODE POINTS]
2075          array(0x4FFFE, 0x4FFFF),    // [NONCHARACTER CODE POINTS]
2076          array(0x5FFFE, 0x5FFFF),    // [NONCHARACTER CODE POINTS]
2077          array(0x6FFFE, 0x6FFFF),    // [NONCHARACTER CODE POINTS]
2078          array(0x7FFFE, 0x7FFFF),    // [NONCHARACTER CODE POINTS]
2079          array(0x8FFFE, 0x8FFFF),    // [NONCHARACTER CODE POINTS]
2080          array(0x9FFFE, 0x9FFFF),    // [NONCHARACTER CODE POINTS]
2081          array(0xAFFFE, 0xAFFFF),    // [NONCHARACTER CODE POINTS]
2082          array(0xBFFFE, 0xBFFFF),    // [NONCHARACTER CODE POINTS]
2083          array(0xCFFFE, 0xCFFFF),    // [NONCHARACTER CODE POINTS]
2084          array(0xDFFFE, 0xDFFFF),    // [NONCHARACTER CODE POINTS]
2085          array(0xEFFFE, 0xEFFFF),    // [NONCHARACTER CODE POINTS]
2086          array(0xFFFFE, 0xFFFFF),    // [NONCHARACTER CODE POINTS]
2087          array(0x10FFFE, 0x10FFFF),    // [NONCHARACTER CODE POINTS]
2088          // Table C.5
2089          array(0xD800, 0xDFFF),        // [SURROGATE CODES]
2090          // Table C.6
2091          array(0xFFF9, 0xFFF9),        // INTERLINEAR ANNOTATION ANCHOR
2092          array(0xFFFA, 0xFFFA),        // INTERLINEAR ANNOTATION SEPARATOR
2093          array(0xFFFB, 0xFFFB),        // INTERLINEAR ANNOTATION TERMINATOR
2094          array(0xFFFC, 0xFFFC),        // OBJECT REPLACEMENT CHARACTER
2095          array(0xFFFD, 0xFFFD),        // REPLACEMENT CHARACTER
2096          // Table C.7
2097          array(0x2FF0, 0x2FFB),        // [IDEOGRAPHIC DESCRIPTION CHARACTERS]
2098          // Table C.8
2099          array(0x0340, 0x0340),        // COMBINING GRAVE TONE MARK
2100          array(0x0341, 0x0341),        // COMBINING ACUTE TONE MARK
2101          array(0x200E, 0x200E),        // LEFT-TO-RIGHT MARK
2102          array(0x200F, 0x200F),        // RIGHT-TO-LEFT MARK
2103          array(0x202A, 0x202A),        // LEFT-TO-RIGHT EMBEDDING
2104          array(0x202B, 0x202B),        // RIGHT-TO-LEFT EMBEDDING
2105          array(0x202C, 0x202C),        // POP DIRECTIONAL FORMATTING
2106          array(0x202D, 0x202D),        // LEFT-TO-RIGHT OVERRIDE
2107          array(0x202E, 0x202E),        // RIGHT-TO-LEFT OVERRIDE
2108          array(0x206A, 0x206A),        // INHIBIT SYMMETRIC SWAPPING
2109          array(0x206B, 0x206B),        // ACTIVATE SYMMETRIC SWAPPING
2110          array(0x206C, 0x206C),        // INHIBIT ARABIC FORM SHAPING
2111          array(0x206D, 0x206D),        // ACTIVATE ARABIC FORM SHAPING
2112          array(0x206E, 0x206E),        // NATIONAL DIGIT SHAPES
2113          array(0x206F, 0x206F),        // NOMINAL DIGIT SHAPES
2114          // Table C.9
2115          array(0xE0001, 0xE0001),    // LANGUAGE TAG
2116          array(0xE0020, 0xE007F),    // [TAGGING CHARACTERS]
2117          // RFC3920
2118          array(0x22, 0x22),            // "
2119          array(0x26, 0x26),            // &
2120          array(0x27, 0x27),            // '
2121          array(0x2F, 0x2F),            // /
2122          array(0x3A, 0x3A),            // :
2123          array(0x3C, 0x3C),            // <
2124          array(0x3E, 0x3E),            // >
2125          array(0x40, 0x40)            // @
2126      );
2127   
2128      $pos = 0;
2129      $result = true;
2130   
2131      while ($pos < strlen($username))
2132      {
2133          $len = $uni = 0;
2134          for ($i = 0; $i <= 5; $i++)
2135          {
2136              if (ord($username[$pos]) >= $boundary[$i][0] && ord($username[$pos]) <= $boundary[$i][1])
2137              {
2138                  $len = $i + 1;
2139                  $uni = (ord($username[$pos]) - $boundary[$i][0]) * pow(2, $i * 6);
2140   
2141                  for ($k = 1; $k < $len; $k++)
2142                  {
2143                      $uni += (ord($username[$pos + $k]) - 128) * pow(2, ($i - $k) * 6);
2144                  }
2145   
2146                  break;
2147              }
2148          }
2149   
2150          if ($len == 0)
2151          {
2152              return 'WRONG_DATA';
2153          }
2154   
2155          foreach ($prohibited as $pval)
2156          {
2157              if ($uni >= $pval[0] && $uni <= $pval[1])
2158              {
2159                  $result = false;
2160                  break 2;
2161              }
2162          }
2163   
2164          $pos = $pos + $len;
2165      }
2166   
2167      if (!$result)
2168      {
2169          return 'WRONG_DATA';
2170      }
2171   
2172      return false;
2173  }
2174   
2175  /**
2176  * Validate hex colour value
2177  *
2178  * @param string $colour The hex colour value
2179  * @param bool $optional Whether the colour value is optional. True if an empty
2180  *            string will be accepted as correct input, false if not.
2181  * @return bool|string Error message if colour value is incorrect, false if it
2182  *            fits the hex colour code
2183  */
2184  function phpbb_validate_hex_colour($colour, $optional = false)
2185  {
2186      if ($colour === '')
2187      {
2188          return (($optional) ? false : 'WRONG_DATA');
2189      }
2190   
2191      if (!preg_match('/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/', $colour))
2192      {
2193          return 'WRONG_DATA';
2194      }
2195   
2196      return false;
2197  }
2198   
2199  /**
2200  * Verifies whether a style ID corresponds to an active style.
2201  *
2202  * @param int $style_id The style_id of a style which should be checked if activated or not.
2203  * @return boolean
2204  */
2205  function phpbb_style_is_active($style_id)
2206  {
2207      global $db;
2208   
2209      $sql = 'SELECT style_active
2210          FROM ' . STYLES_TABLE . '
2211          WHERE style_id = '. (int) $style_id;
2212      $result = $db->sql_query($sql);
2213   
2214      $style_is_active = (bool) $db->sql_fetchfield('style_active');
2215      $db->sql_freeresult($result);
2216   
2217      return $style_is_active;
2218  }
2219   
2220  /**
2221  * Remove avatar
2222  */
2223  function avatar_delete($mode, $row, $clean_db = false)
2224  {
2225      global $phpbb_root_path, $config;
2226   
2227      // Check if the users avatar is actually *not* a group avatar
2228      if ($mode == 'user')
2229      {
2230          if (strpos($row['user_avatar'], 'g') === 0 || (((int) $row['user_avatar'] !== 0) && ((int) $row['user_avatar'] !== (int) $row['user_id'])))
2231          {
2232              return false;
2233          }
2234      }
2235   
2236      if ($clean_db)
2237      {
2238          avatar_remove_db($row[$mode . '_avatar']);
2239      }
2240      $filename = get_avatar_filename($row[$mode . '_avatar']);
2241   
2242      if (file_exists($phpbb_root_path . $config['avatar_path'] . '/' . $filename))
2243      {
2244          @unlink($phpbb_root_path . $config['avatar_path'] . '/' . $filename);
2245          return true;
2246      }
2247   
2248      return false;
2249  }
2250   
2251  /**
2252  * Generates avatar filename from the database entry
2253  */
2254  function get_avatar_filename($avatar_entry)
2255  {
2256      global $config;
2257   
2258      if ($avatar_entry[0] === 'g')
2259      {
2260          $avatar_group = true;
2261          $avatar_entry = substr($avatar_entry, 1);
2262      }
2263      else
2264      {
2265          $avatar_group = false;
2266      }
2267      $ext             = substr(strrchr($avatar_entry, '.'), 1);
2268      $avatar_entry    = intval($avatar_entry);
2269      return $config['avatar_salt'] . '_' . (($avatar_group) ? 'g' : '') . $avatar_entry . '.' . $ext;
2270  }
2271   
2272  /**
2273  * Returns an explanation string with maximum avatar settings
2274  *
2275  * @return string
2276  */
2277  function phpbb_avatar_explanation_string()
2278  {
2279      global $config, $user;
2280   
2281      return $user->lang(($config['avatar_filesize'] == 0) ? 'AVATAR_EXPLAIN_NO_FILESIZE' : 'AVATAR_EXPLAIN',
2282          $user->lang('PIXELS', (int) $config['avatar_max_width']),
2283          $user->lang('PIXELS', (int) $config['avatar_max_height']),
2284          round($config['avatar_filesize'] / 1024));
2285  }
2286   
2287  //
2288  // Usergroup functions
2289  //
2290   
2291  /**
2292  * Add or edit a group. If we're editing a group we only update user
2293  * parameters such as rank, etc. if they are changed
2294  */
2295  function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow_desc_bbcode = false, $allow_desc_urls = false, $allow_desc_smilies = false)
2296  {
2297      global $db, $user, $phpbb_container, $phpbb_log;
2298   
2299      /** @var \phpbb\group\helper $group_helper */
2300      $group_helper = $phpbb_container->get('group_helper');
2301   
2302      $error = array();
2303   
2304      // Attributes which also affect the users table
2305      $user_attribute_ary = array('group_colour', 'group_rank', 'group_avatar', 'group_avatar_type', 'group_avatar_width', 'group_avatar_height');
2306   
2307      // Check data. Limit group name length.
2308      if (!utf8_strlen($name) || utf8_strlen($name) > 60)
2309      {
2310          $error[] = (!utf8_strlen($name)) ? $user->lang['GROUP_ERR_USERNAME'] : $user->lang['GROUP_ERR_USER_LONG'];
2311      }
2312   
2313      $err = group_validate_groupname($group_id, $name);
2314      if (!empty($err))
2315      {
2316          $error[] = $user->lang[$err];
2317      }
2318   
2319      if (!in_array($type, array(GROUP_OPEN, GROUP_CLOSED, GROUP_HIDDEN, GROUP_SPECIAL, GROUP_FREE)))
2320      {
2321          $error[] = $user->lang['GROUP_ERR_TYPE'];
2322      }
2323   
2324      $group_teampage = !empty($group_attributes['group_teampage']);
2325      unset($group_attributes['group_teampage']);
2326   
2327      if (!count($error))
2328      {
2329          $current_legend = \phpbb\groupposition\legend::GROUP_DISABLED;
2330          $current_teampage = \phpbb\groupposition\teampage::GROUP_DISABLED;
2331   
2332          /* @var $legend \phpbb\groupposition\legend */
2333          $legend = $phpbb_container->get('groupposition.legend');
2334   
2335          /* @var $teampage \phpbb\groupposition\teampage */
2336          $teampage = $phpbb_container->get('groupposition.teampage');
2337   
2338          if ($group_id)
2339          {
2340              try
2341              {
2342                  $current_legend = $legend->get_group_value($group_id);
2343                  $current_teampage = $teampage->get_group_value($group_id);
2344              }
2345              catch (\phpbb\groupposition\exception $exception)
2346              {
2347                  trigger_error($user->lang($exception->getMessage()));
2348              }
2349          }
2350   
2351          if (!empty($group_attributes['group_legend']))
2352          {
2353              if (($group_id && ($current_legend == \phpbb\groupposition\legend::GROUP_DISABLED)) || !$group_id)
2354              {
2355                  // Old group currently not in the legend or new group, add at the end.
2356                  $group_attributes['group_legend'] = 1 + $legend->get_group_count();
2357              }
2358              else
2359              {
2360                  // Group stayes in the legend
2361                  $group_attributes['group_legend'] = $current_legend;
2362              }
2363          }
2364          else if ($group_id && ($current_legend != \phpbb\groupposition\legend::GROUP_DISABLED))
2365          {
2366              // Group is removed from the legend
2367              try
2368              {
2369                  $legend->delete_group($group_id, true);
2370              }
2371              catch (\phpbb\groupposition\exception $exception)
2372              {
2373                  trigger_error($user->lang($exception->getMessage()));
2374              }
2375              $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
2376          }
2377          else
2378          {
2379              $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
2380          }
2381   
2382          // Unset the objects, we don't need them anymore.
2383          unset($legend);
2384   
2385          $user_ary = array();
2386          $sql_ary = array(
2387              'group_name'            => (string) $name,
2388              'group_desc'            => (string) $desc,
2389              'group_desc_uid'        => '',
2390              'group_desc_bitfield'    => '',
2391              'group_type'            => (int) $type,
2392          );
2393   
2394          // Parse description
2395          if ($desc)
2396          {
2397              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);
2398          }
2399   
2400          if (count($group_attributes))
2401          {
2402              // Merge them with $sql_ary to properly update the group
2403              $sql_ary = array_merge($sql_ary, $group_attributes);
2404          }
2405   
2406          // Setting the log message before we set the group id (if group gets added)
2407          $log = ($group_id) ? 'LOG_GROUP_UPDATED' : 'LOG_GROUP_CREATED';
2408   
2409          if ($group_id)
2410          {
2411              $sql = 'SELECT user_id
2412                  FROM ' . USERS_TABLE . '
2413                  WHERE group_id = ' . $group_id;
2414              $result = $db->sql_query($sql);
2415   
2416              while ($row = $db->sql_fetchrow($result))
2417              {
2418                  $user_ary[] = $row['user_id'];
2419              }
2420              $db->sql_freeresult($result);
2421   
2422              if (isset($sql_ary['group_avatar']))
2423              {
2424                  remove_default_avatar($group_id, $user_ary);
2425              }
2426   
2427              if (isset($sql_ary['group_rank']))
2428              {
2429                  remove_default_rank($group_id, $user_ary);
2430              }
2431   
2432              $sql = 'UPDATE ' . GROUPS_TABLE . '
2433                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
2434                  WHERE group_id = $group_id";
2435              $db->sql_query($sql);
2436   
2437              // Since we may update the name too, we need to do this on other tables too...
2438              $sql = 'UPDATE ' . MODERATOR_CACHE_TABLE . "
2439                  SET group_name = '" . $db->sql_escape($sql_ary['group_name']) . "'
2440                  WHERE group_id = $group_id";
2441              $db->sql_query($sql);
2442   
2443              // One special case is the group skip auth setting. If this was changed we need to purge permissions for this group
2444              if (isset($group_attributes['group_skip_auth']))
2445              {
2446                  // Get users within this group...
2447                  $sql = 'SELECT user_id
2448                      FROM ' . USER_GROUP_TABLE . '
2449                      WHERE group_id = ' . $group_id . '
2450                          AND user_pending = 0';
2451                  $result = $db->sql_query($sql);
2452   
2453                  $user_id_ary = array();
2454                  while ($row = $db->sql_fetchrow($result))
2455                  {
2456                      $user_id_ary[] = $row['user_id'];
2457                  }
2458                  $db->sql_freeresult($result);
2459   
2460                  if (!empty($user_id_ary))
2461                  {
2462                      global $auth;
2463   
2464                      // Clear permissions cache of relevant users
2465                      $auth->acl_clear_prefetch($user_id_ary);
2466                  }
2467              }
2468          }
2469          else
2470          {
2471              $sql = 'INSERT INTO ' . GROUPS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
2472              $db->sql_query($sql);
2473          }
2474   
2475          // Remove the group from the teampage, only if unselected and we are editing a group,
2476          // which is currently displayed.
2477          if (!$group_teampage && $group_id && $current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED)
2478          {
2479              try
2480              {
2481                  $teampage->delete_group($group_id);
2482              }
2483              catch (\phpbb\groupposition\exception $exception)
2484              {
2485                  trigger_error($user->lang($exception->getMessage()));
2486              }
2487          }
2488   
2489          if (!$group_id)
2490          {
2491              $group_id = $db->sql_nextid();
2492   
2493              if (isset($sql_ary['group_avatar_type']) && $sql_ary['group_avatar_type'] == 'avatar.driver.upload')
2494              {
2495                  group_correct_avatar($group_id, $sql_ary['group_avatar']);
2496              }
2497          }
2498   
2499          try
2500          {
2501              if ($group_teampage && $current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
2502              {
2503                  $teampage->add_group($group_id);
2504              }
2505   
2506              if ($group_teampage)
2507              {
2508                  if ($current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
2509                  {
2510                      $teampage->add_group($group_id);
2511                  }
2512              }
2513              else if ($group_id && ($current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED))
2514              {
2515                  $teampage->delete_group($group_id);
2516              }
2517          }
2518          catch (\phpbb\groupposition\exception $exception)
2519          {
2520              trigger_error($user->lang($exception->getMessage()));
2521          }
2522          unset($teampage);
2523   
2524          // Set user attributes
2525          $sql_ary = array();
2526          if (count($group_attributes))
2527          {
2528              // Go through the user attributes array, check if a group attribute matches it and then set it. ;)
2529              foreach ($user_attribute_ary as $attribute)
2530              {
2531                  if (!isset($group_attributes[$attribute]))
2532                  {
2533                      continue;
2534                  }
2535   
2536                  // If we are about to set an avatar, we will not overwrite user avatars if no group avatar is set...
2537                  if (strpos($attribute, 'group_avatar') === 0 && !$group_attributes[$attribute])
2538                  {
2539                      continue;
2540                  }
2541   
2542                  $sql_ary[$attribute] = $group_attributes[$attribute];
2543              }
2544          }
2545   
2546          if (count($sql_ary) && count($user_ary))
2547          {
2548              group_set_user_default($group_id, $user_ary, $sql_ary);
2549          }
2550   
2551          $name = $group_helper->get_name($name);
2552          $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($name));
2553   
2554          group_update_listings($group_id);
2555      }
2556   
2557      return (count($error)) ? $error : false;
2558  }
2559   
2560   
2561  /**
2562  * Changes a group avatar's filename to conform to the naming scheme
2563  */
2564  function group_correct_avatar($group_id, $old_entry)
2565  {
2566      global $config, $db, $phpbb_root_path;
2567   
2568      $group_id        = (int) $group_id;
2569      $ext             = substr(strrchr($old_entry, '.'), 1);
2570      $old_filename     = get_avatar_filename($old_entry);
2571      $new_filename     = $config['avatar_salt'] . "_g$group_id.$ext";
2572      $new_entry         = 'g' . $group_id . '_' . substr(time(), -5) . ".$ext";
2573   
2574      $avatar_path = $phpbb_root_path . $config['avatar_path'];
2575      if (@rename($avatar_path . '/'. $old_filename, $avatar_path . '/' . $new_filename))
2576      {
2577          $sql = 'UPDATE ' . GROUPS_TABLE . '
2578              SET group_avatar = \'' . $db->sql_escape($new_entry) . "'
2579              WHERE group_id = $group_id";
2580          $db->sql_query($sql);
2581      }
2582  }
2583   
2584   
2585  /**
2586  * Remove avatar also for users not having the group as default
2587  */
2588  function avatar_remove_db($avatar_name)
2589  {
2590      global $db;
2591   
2592      $sql = 'UPDATE ' . USERS_TABLE . "
2593          SET user_avatar = '',
2594          user_avatar_type = ''
2595          WHERE user_avatar = '" . $db->sql_escape($avatar_name) . '\'';
2596      $db->sql_query($sql);
2597  }
2598   
2599   
2600  /**
2601  * Group Delete
2602  */
2603  function group_delete($group_id, $group_name = false)
2604  {
2605      global $db, $cache, $auth, $user, $phpbb_root_path, $phpEx, $phpbb_dispatcher, $phpbb_container, $phpbb_log;
2606   
2607      if (!$group_name)
2608      {
2609          $group_name = get_group_name($group_id);
2610      }
2611   
2612      $start = 0;
2613   
2614      do
2615      {
2616          $user_id_ary = $username_ary = array();
2617   
2618          // Batch query for group members, call group_user_del
2619          $sql = 'SELECT u.user_id, u.username
2620              FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u
2621              WHERE ug.group_id = $group_id
2622                  AND u.user_id = ug.user_id";
2623          $result = $db->sql_query_limit($sql, 200, $start);
2624   
2625          if ($row = $db->sql_fetchrow($result))
2626          {
2627              do
2628              {
2629                  $user_id_ary[] = $row['user_id'];
2630                  $username_ary[] = $row['username'];
2631   
2632                  $start++;
2633              }
2634              while ($row = $db->sql_fetchrow($result));
2635   
2636              group_user_del($group_id, $user_id_ary, $username_ary, $group_name);
2637          }
2638          else
2639          {
2640              $start = 0;
2641          }
2642          $db->sql_freeresult($result);
2643      }
2644      while ($start);
2645   
2646      // Delete group from legend and teampage
2647      try
2648      {
2649          /* @var $legend \phpbb\groupposition\legend */
2650          $legend = $phpbb_container->get('groupposition.legend');
2651          $legend->delete_group($group_id);
2652          unset($legend);
2653      }
2654      catch (\phpbb\groupposition\exception $exception)
2655      {
2656          // The group we want to delete does not exist.
2657          // No reason to worry, we just continue the deleting process.
2658          //trigger_error($user->lang($exception->getMessage()));
2659      }
2660   
2661      try
2662      {
2663          /* @var $teampage \phpbb\groupposition\teampage */
2664          $teampage = $phpbb_container->get('groupposition.teampage');
2665          $teampage->delete_group($group_id);
2666          unset($teampage);
2667      }
2668      catch (\phpbb\groupposition\exception $exception)
2669      {
2670          // The group we want to delete does not exist.
2671          // No reason to worry, we just continue the deleting process.
2672          //trigger_error($user->lang($exception->getMessage()));
2673      }
2674   
2675      // Delete group
2676      $sql = 'DELETE FROM ' . GROUPS_TABLE . "
2677          WHERE group_id = $group_id";
2678      $db->sql_query($sql);
2679   
2680      // Delete auth entries from the groups table
2681      $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . "
2682          WHERE group_id = $group_id";
2683      $db->sql_query($sql);
2684   
2685      /**
2686      * Event after a group is deleted
2687      *
2688      * @event core.delete_group_after
2689      * @var    int        group_id    ID of the deleted group
2690      * @var    string    group_name    Name of the deleted group
2691      * @since 3.1.0-a1
2692      */
2693      $vars = array('group_id', 'group_name');
2694      extract($phpbb_dispatcher->trigger_event('core.delete_group_after', compact($vars)));
2695   
2696      // Re-cache moderators
2697      if (!function_exists('phpbb_cache_moderators'))
2698      {
2699          include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
2700      }
2701   
2702      phpbb_cache_moderators($db, $cache, $auth);
2703   
2704      $phpbb_log->add('admin', $user->data['user_id'], $user->ip, 'LOG_GROUP_DELETE', false, array($group_name));
2705   
2706      // Return false - no error
2707      return false;
2708  }
2709   
2710  /**
2711  * Add user(s) to group
2712  *
2713  * @return mixed false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2714  */
2715  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)
2716  {
2717      global $db, $auth, $user, $phpbb_container, $phpbb_log, $phpbb_dispatcher;
2718   
2719      // We need both username and user_id info
2720      $result = user_get_id_name($user_id_ary, $username_ary);
2721   
2722      if (empty($user_id_ary) || $result !== false)
2723      {
2724          return 'NO_USER';
2725      }
2726   
2727      // Because the item that gets passed into the previous function is unset, the reference is lost and our original
2728      // array is retained - so we know there's a problem if there's a different number of ids to usernames now.
2729      if (count($user_id_ary) != count($username_ary))
2730      {
2731          return 'GROUP_USERS_INVALID';
2732      }
2733   
2734      // Remove users who are already members of this group
2735      $sql = 'SELECT user_id, group_leader
2736          FROM ' . USER_GROUP_TABLE . '
2737          WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . "
2738              AND group_id = $group_id";
2739      $result = $db->sql_query($sql);
2740   
2741      $add_id_ary = $update_id_ary = array();
2742      while ($row = $db->sql_fetchrow($result))
2743      {
2744          $add_id_ary[] = (int) $row['user_id'];
2745   
2746          if ($leader && !$row['group_leader'])
2747          {
2748              $update_id_ary[] = (int) $row['user_id'];
2749          }
2750      }
2751      $db->sql_freeresult($result);
2752   
2753      // Do all the users exist in this group?
2754      $add_id_ary = array_diff($user_id_ary, $add_id_ary);
2755   
2756      // If we have no users
2757      if (!count($add_id_ary) && !count($update_id_ary))
2758      {
2759          return 'GROUP_USERS_EXIST';
2760      }
2761   
2762      /**
2763       * Event before users are added to a group
2764       *
2765       * @event core.group_add_user_before
2766       * @var    int        group_id        ID of the group to which users are added
2767       * @var    string     group_name        Name of the group
2768       * @var    array    user_id_ary        IDs of the users to be added
2769       * @var    array    username_ary    Names of the users to be added
2770       * @var    int        pending            Pending setting, 1 if user(s) added are pending
2771       * @var    array    add_id_ary        IDs of the users to be added who are not members yet
2772       * @since 3.3.15-RC1
2773       */
2774      $vars = array(
2775          'group_id',
2776          'group_name',
2777          'user_id_ary',
2778          'username_ary',
2779          'pending',
2780          'add_id_ary',
2781      );
2782      extract($phpbb_dispatcher->trigger_event('core.group_add_user_before', compact($vars)));
2783   
2784      $db->sql_transaction('begin');
2785   
2786      // Insert the new users
2787      if (count($add_id_ary))
2788      {
2789          $sql_ary = array();
2790   
2791          foreach ($add_id_ary as $user_id)
2792          {
2793              $sql_ary[] = array(
2794                  'user_id'        => (int) $user_id,
2795                  'group_id'        => (int) $group_id,
2796                  'group_leader'    => (int) $leader,
2797                  'user_pending'    => (int) $pending,
2798              );
2799          }
2800   
2801          $db->sql_multi_insert(USER_GROUP_TABLE, $sql_ary);
2802      }
2803   
2804      if (count($update_id_ary))
2805      {
2806          $sql = 'UPDATE ' . USER_GROUP_TABLE . '
2807              SET group_leader = 1
2808              WHERE ' . $db->sql_in_set('user_id', $update_id_ary) . "
2809                  AND group_id = $group_id";
2810          $db->sql_query($sql);
2811      }
2812   
2813      if ($default)
2814      {
2815          group_user_attributes('default', $group_id, $user_id_ary, false, $group_name, $group_attributes);
2816      }
2817   
2818      $db->sql_transaction('commit');
2819   
2820      // Clear permissions cache of relevant users
2821      $auth->acl_clear_prefetch($user_id_ary);
2822   
2823      /**
2824      * Event after users are added to a group
2825      *
2826      * @event core.group_add_user_after
2827      * @var    int    group_id        ID of the group to which users are added
2828      * @var    string group_name        Name of the group
2829      * @var    array    user_id_ary        IDs of the users which are added
2830      * @var    array    username_ary    names of the users which are added
2831      * @var    int        pending            Pending setting, 1 if user(s) added are pending
2832      * @since 3.1.7-RC1
2833      */
2834      $vars = array(
2835          'group_id',
2836          'group_name',
2837          'user_id_ary',
2838          'username_ary',
2839          'pending',
2840      );
2841      extract($phpbb_dispatcher->trigger_event('core.group_add_user_after', compact($vars)));
2842   
2843      if (!$group_name)
2844      {
2845          $group_name = get_group_name($group_id);
2846      }
2847   
2848      $log = ($leader) ? 'LOG_MODS_ADDED' : (($pending) ? 'LOG_USERS_PENDING' : 'LOG_USERS_ADDED');
2849   
2850      $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary)));
2851   
2852      group_update_listings($group_id);
2853   
2854      if ($pending)
2855      {
2856          /* @var $phpbb_notifications \phpbb\notification\manager */
2857          $phpbb_notifications = $phpbb_container->get('notification_manager');
2858   
2859          foreach ($add_id_ary as $user_id)
2860          {
2861              $phpbb_notifications->add_notifications('notification.type.group_request', array(
2862                  'group_id'        => $group_id,
2863                  'user_id'        => $user_id,
2864                  'group_name'    => $group_name,
2865              ));
2866          }
2867      }
2868   
2869      // Return false - no error
2870      return false;
2871  }
2872   
2873  /**
2874  * Remove a user/s from a given group. When we remove users we update their
2875  * default group_id. We do this by examining which "special" groups they belong
2876  * to. The selection is made based on a reasonable priority system
2877  *
2878  * @return false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2879  */
2880  function group_user_del($group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $log_action = true)
2881  {
2882      global $db, $auth, $config, $user, $phpbb_dispatcher, $phpbb_container, $phpbb_log;
2883   
2884      if ($config['coppa_enable'])
2885      {
2886          $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED_COPPA', 'REGISTERED', 'BOTS', 'GUESTS');
2887      }
2888      else
2889      {
2890          $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED', 'BOTS', 'GUESTS');
2891      }
2892   
2893      // We need both username and user_id info
2894      $result = user_get_id_name($user_id_ary, $username_ary);
2895   
2896      if (empty($user_id_ary) || $result !== false)
2897      {
2898          return 'NO_USER';
2899      }
2900   
2901      $sql = 'SELECT *
2902          FROM ' . GROUPS_TABLE . '
2903          WHERE ' . $db->sql_in_set('group_name', $group_order);
2904      $result = $db->sql_query($sql);
2905   
2906      $group_order_id = $special_group_data = array();
2907      while ($row = $db->sql_fetchrow($result))
2908      {
2909          $group_order_id[$row['group_name']] = $row['group_id'];
2910   
2911          $special_group_data[$row['group_id']] = array(
2912              'group_colour'            => $row['group_colour'],
2913              'group_rank'                => $row['group_rank'],
2914          );
2915   
2916          // Only set the group avatar if one is defined...
2917          if ($row['group_avatar'])
2918          {
2919              $special_group_data[$row['group_id']] = array_merge($special_group_data[$row['group_id']], array(
2920                  'group_avatar'            => $row['group_avatar'],
2921                  'group_avatar_type'        => $row['group_avatar_type'],
2922                  'group_avatar_width'        => $row['group_avatar_width'],
2923                  'group_avatar_height'    => $row['group_avatar_height'])
2924              );
2925          }
2926      }
2927      $db->sql_freeresult($result);
2928   
2929      // 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
2930      $sql = 'SELECT user_id, group_id
2931          FROM ' . USERS_TABLE . '
2932          WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
2933      $result = $db->sql_query($sql);
2934   
2935      $default_groups = array();
2936      while ($row = $db->sql_fetchrow($result))
2937      {
2938          $default_groups[$row['user_id']] = $row['group_id'];
2939      }
2940      $db->sql_freeresult($result);
2941   
2942      // What special group memberships exist for these users?
2943      $sql = 'SELECT g.group_id, g.group_name, ug.user_id
2944          FROM ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
2945          WHERE ' . $db->sql_in_set('ug.user_id', $user_id_ary) . "
2946              AND g.group_id = ug.group_id
2947              AND g.group_id <> $group_id
2948              AND g.group_type = " . GROUP_SPECIAL . '
2949          ORDER BY ug.user_id, g.group_id';
2950      $result = $db->sql_query($sql);
2951   
2952      $temp_ary = array();
2953      while ($row = $db->sql_fetchrow($result))
2954      {
2955          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']]))
2956          {
2957              $temp_ary[$row['user_id']] = $row['group_id'];
2958          }
2959      }
2960      $db->sql_freeresult($result);
2961   
2962      // sql_where_ary holds the new default groups and their users
2963      $sql_where_ary = array();
2964      foreach ($temp_ary as $uid => $gid)
2965      {
2966          $sql_where_ary[$gid][] = $uid;
2967      }
2968      unset($temp_ary);
2969   
2970      foreach ($special_group_data as $gid => $default_data_ary)
2971      {
2972          if (isset($sql_where_ary[$gid]) && count($sql_where_ary[$gid]))
2973          {
2974              remove_default_rank($group_id, $sql_where_ary[$gid]);
2975              remove_default_avatar($group_id, $sql_where_ary[$gid]);
2976              group_set_user_default($gid, $sql_where_ary[$gid], $default_data_ary);
2977          }
2978      }
2979      unset($special_group_data);
2980   
2981      /**
2982      * Event before users are removed from a group
2983      *
2984      * @event core.group_delete_user_before
2985      * @var    int        group_id        ID of the group from which users are deleted
2986      * @var    string    group_name        Name of the group
2987      * @var    array    user_id_ary        IDs of the users which are removed
2988      * @var    array    username_ary    names of the users which are removed
2989      * @since 3.1.0-a1
2990      */
2991      $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary');
2992      extract($phpbb_dispatcher->trigger_event('core.group_delete_user_before', compact($vars)));
2993   
2994      $sql = 'DELETE FROM ' . USER_GROUP_TABLE . "
2995          WHERE group_id = $group_id
2996              AND " . $db->sql_in_set('user_id', $user_id_ary);
2997      $db->sql_query($sql);
2998   
2999      // Clear permissions cache of relevant users
3000      $auth->acl_clear_prefetch($user_id_ary);
3001   
3002      /**
3003      * Event after users are removed from a group
3004      *
3005      * @event core.group_delete_user_after
3006      * @var    int        group_id        ID of the group from which users are deleted
3007      * @var    string    group_name        Name of the group
3008      * @var    array    user_id_ary        IDs of the users which are removed
3009      * @var    array    username_ary    names of the users which are removed
3010      * @since 3.1.7-RC1
3011      */
3012      $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary');
3013      extract($phpbb_dispatcher->trigger_event('core.group_delete_user_after', compact($vars)));
3014   
3015      if ($log_action)
3016      {
3017          if (!$group_name)
3018          {
3019              $group_name = get_group_name($group_id);
3020          }
3021   
3022          $log = 'LOG_GROUP_REMOVE';
3023   
3024          if ($group_name)
3025          {
3026              $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary)));
3027          }
3028      }
3029   
3030      group_update_listings($group_id);
3031   
3032      /* @var $phpbb_notifications \phpbb\notification\manager */
3033      $phpbb_notifications = $phpbb_container->get('notification_manager');
3034   
3035      $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
3036   
3037      // Return false - no error
3038      return false;
3039  }
3040   
3041   
3042  /**
3043  * Removes the group avatar of the default group from the users in user_ids who have that group as default.
3044  */
3045  function remove_default_avatar($group_id, $user_ids)
3046  {
3047      global $db;
3048   
3049      if (!is_array($user_ids))
3050      {
3051          $user_ids = array($user_ids);
3052      }
3053      if (empty($user_ids))
3054      {
3055          return false;
3056      }
3057   
3058      $user_ids = array_map('intval', $user_ids);
3059   
3060      $sql = 'SELECT *
3061          FROM ' . GROUPS_TABLE . '
3062          WHERE group_id = ' . (int) $group_id;
3063      $result = $db->sql_query($sql);
3064      if (!$row = $db->sql_fetchrow($result))
3065      {
3066          $db->sql_freeresult($result);
3067          return false;
3068      }
3069      $db->sql_freeresult($result);
3070   
3071      $sql = 'UPDATE ' . USERS_TABLE . "
3072          SET user_avatar = '',
3073              user_avatar_type = '',
3074              user_avatar_width = 0,
3075              user_avatar_height = 0
3076          WHERE group_id = " . (int) $group_id . "
3077              AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
3078              AND " . $db->sql_in_set('user_id', $user_ids);
3079   
3080      $db->sql_query($sql);
3081  }
3082   
3083  /**
3084  * Removes the group rank of the default group from the users in user_ids who have that group as default.
3085  */
3086  function remove_default_rank($group_id, $user_ids)
3087  {
3088      global $db;
3089   
3090      if (!is_array($user_ids))
3091      {
3092          $user_ids = array($user_ids);
3093      }
3094      if (empty($user_ids))
3095      {
3096          return false;
3097      }
3098   
3099      $user_ids = array_map('intval', $user_ids);
3100   
3101      $sql = 'SELECT *
3102          FROM ' . GROUPS_TABLE . '
3103          WHERE group_id = ' . (int) $group_id;
3104      $result = $db->sql_query($sql);
3105      if (!$row = $db->sql_fetchrow($result))
3106      {
3107          $db->sql_freeresult($result);
3108          return false;
3109      }
3110      $db->sql_freeresult($result);
3111   
3112      $sql = 'UPDATE ' . USERS_TABLE . '
3113          SET user_rank = 0
3114          WHERE group_id = ' . (int) $group_id . '
3115              AND user_rank <> 0
3116              AND user_rank = ' . (int) $row['group_rank'] . '
3117              AND ' . $db->sql_in_set('user_id', $user_ids);
3118      $db->sql_query($sql);
3119  }
3120   
3121  /**
3122  * This is used to promote (to leader), demote or set as default a member/s
3123  */
3124  function group_user_attributes($action, $group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $group_attributes = false)
3125  {
3126      global $db, $auth, $user, $phpbb_container, $phpbb_log, $phpbb_dispatcher;
3127   
3128      // We need both username and user_id info
3129      $result = user_get_id_name($user_id_ary, $username_ary);
3130   
3131      if (empty($user_id_ary) || $result !== false)
3132      {
3133          return 'NO_USERS';
3134      }
3135   
3136      if (!$group_name)
3137      {
3138          $group_name = get_group_name($group_id);
3139      }
3140   
3141      switch ($action)
3142      {
3143          case 'demote':
3144          case 'promote':
3145   
3146              $sql = 'SELECT user_id
3147                  FROM ' . USER_GROUP_TABLE . "
3148                  WHERE group_id = $group_id
3149                      AND user_pending = 1
3150                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3151              $result = $db->sql_query_limit($sql, 1);
3152              $not_empty = ($db->sql_fetchrow($result));
3153              $db->sql_freeresult($result);
3154              if ($not_empty)
3155              {
3156                  return 'NO_VALID_USERS';
3157              }
3158   
3159              $sql = 'UPDATE ' . USER_GROUP_TABLE . '
3160                  SET group_leader = ' . (($action == 'promote') ? 1 : 0) . "
3161                  WHERE group_id = $group_id
3162                      AND user_pending = 0
3163                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3164              $db->sql_query($sql);
3165   
3166              $log = ($action == 'promote') ? 'LOG_GROUP_PROMOTED' : 'LOG_GROUP_DEMOTED';
3167          break;
3168   
3169          case 'approve':
3170              // Make sure we only approve those which are pending ;)
3171              $sql = 'SELECT u.user_id, u.user_email, u.username, u.username_clean, u.user_notify_type, u.user_jabber, u.user_lang
3172                  FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
3173                  WHERE ug.group_id = ' . $group_id . '
3174                      AND ug.user_pending = 1
3175                      AND ug.user_id = u.user_id
3176                      AND ' . $db->sql_in_set('ug.user_id', $user_id_ary);
3177              $result = $db->sql_query($sql);
3178   
3179              $user_id_ary = array();
3180              while ($row = $db->sql_fetchrow($result))
3181              {
3182                  $user_id_ary[] = $row['user_id'];
3183              }
3184              $db->sql_freeresult($result);
3185   
3186              if (!count($user_id_ary))
3187              {
3188                  return false;
3189              }
3190   
3191              $sql = 'UPDATE ' . USER_GROUP_TABLE . "
3192                  SET user_pending = 0
3193                  WHERE group_id = $group_id
3194                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3195              $db->sql_query($sql);
3196   
3197              /* @var $phpbb_notifications \phpbb\notification\manager */
3198              $phpbb_notifications = $phpbb_container->get('notification_manager');
3199   
3200              $phpbb_notifications->add_notifications('notification.type.group_request_approved', array(
3201                  'user_ids'        => $user_id_ary,
3202                  'group_id'        => $group_id,
3203                  'group_name'    => $group_name,
3204              ));
3205              $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
3206   
3207              $log = 'LOG_USERS_APPROVED';
3208          break;
3209   
3210          case 'default':
3211              // We only set default group for approved members of the group
3212              $sql = 'SELECT user_id
3213                  FROM ' . USER_GROUP_TABLE . "
3214                  WHERE group_id = $group_id
3215                      AND user_pending = 0
3216                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3217              $result = $db->sql_query($sql);
3218   
3219              $user_id_ary = $username_ary = array();
3220              while ($row = $db->sql_fetchrow($result))
3221              {
3222                  $user_id_ary[] = $row['user_id'];
3223              }
3224              $db->sql_freeresult($result);
3225   
3226              $result = user_get_id_name($user_id_ary, $username_ary);
3227              if (!count($user_id_ary) || $result !== false)
3228              {
3229                  return 'NO_USERS';
3230              }
3231   
3232              $sql = 'SELECT user_id, group_id
3233                  FROM ' . USERS_TABLE . '
3234                  WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
3235              $result = $db->sql_query($sql);
3236   
3237              $groups = array();
3238              while ($row = $db->sql_fetchrow($result))
3239              {
3240                  if (!isset($groups[$row['group_id']]))
3241                  {
3242                      $groups[$row['group_id']] = array();
3243                  }
3244                  $groups[$row['group_id']][] = $row['user_id'];
3245              }
3246              $db->sql_freeresult($result);
3247   
3248              foreach ($groups as $gid => $uids)
3249              {
3250                  remove_default_rank($gid, $uids);
3251                  remove_default_avatar($gid, $uids);
3252              }
3253              group_set_user_default($group_id, $user_id_ary, $group_attributes);
3254              $log = 'LOG_GROUP_DEFAULTS';
3255          break;
3256      }
3257   
3258      /**
3259      * Event to perform additional actions on setting user group attributes
3260      *
3261      * @event core.user_set_group_attributes
3262      * @var    int        group_id            ID of the group
3263      * @var    string    group_name            Name of the group
3264      * @var    array    user_id_ary            IDs of the users to set group attributes
3265      * @var    array    username_ary        Names of the users to set group attributes
3266      * @var    array    group_attributes    Group attributes which were changed
3267      * @var    string    action                Action to perform over the group members
3268      * @since 3.1.10-RC1
3269      */
3270      $vars = array(
3271          'group_id',
3272          'group_name',
3273          'user_id_ary',
3274          'username_ary',
3275          'group_attributes',
3276          'action',
3277      );
3278      extract($phpbb_dispatcher->trigger_event('core.user_set_group_attributes', compact($vars)));
3279   
3280      // Clear permissions cache of relevant users
3281      $auth->acl_clear_prefetch($user_id_ary);
3282   
3283      $phpbb_log->add('admin', $user->data['user_id'], $user->ip, $log, false, array($group_name, implode(', ', $username_ary)));
3284   
3285      group_update_listings($group_id);
3286   
3287      return false;
3288  }
3289   
3290  /**
3291  * A small version of validate_username to check for a group name's existence. To be called directly.
3292  */
3293  function group_validate_groupname($group_id, $group_name)
3294  {
3295      global $db;
3296   
3297      $group_name =  utf8_clean_string($group_name);
3298   
3299      if (!empty($group_id))
3300      {
3301          $sql = 'SELECT group_name
3302              FROM ' . GROUPS_TABLE . '
3303              WHERE group_id = ' . (int) $group_id;
3304          $result = $db->sql_query($sql);
3305          $row = $db->sql_fetchrow($result);
3306          $db->sql_freeresult($result);
3307   
3308          if (!$row)
3309          {
3310              return false;
3311          }
3312   
3313          $allowed_groupname = utf8_clean_string($row['group_name']);
3314   
3315          if ($allowed_groupname == $group_name)
3316          {
3317              return false;
3318          }
3319      }
3320   
3321      $sql = 'SELECT group_name
3322          FROM ' . GROUPS_TABLE . "
3323          WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($group_name)) . "'";
3324      $result = $db->sql_query($sql);
3325      $row = $db->sql_fetchrow($result);
3326      $db->sql_freeresult($result);
3327   
3328      if ($row)
3329      {
3330          return 'GROUP_NAME_TAKEN';
3331      }
3332   
3333      return false;
3334  }
3335   
3336  /**
3337  * Set users default group
3338  *
3339  * @access private
3340  */
3341  function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false)
3342  {
3343      global $config, $phpbb_container, $db, $phpbb_dispatcher;
3344   
3345      if (empty($user_id_ary))
3346      {
3347          return;
3348      }
3349   
3350      $attribute_ary = array(
3351          'group_colour'            => 'string',
3352          'group_rank'            => 'int',
3353          'group_avatar'            => 'string',
3354          'group_avatar_type'        => 'string',
3355          'group_avatar_width'    => 'int',
3356          'group_avatar_height'    => 'int',
3357      );
3358   
3359      $sql_ary = array(
3360          'group_id'        => $group_id
3361      );
3362   
3363      // Were group attributes passed to the function? If not we need to obtain them
3364      if ($group_attributes === false)
3365      {
3366          $sql = 'SELECT ' . implode(', ', array_keys($attribute_ary)) . '
3367              FROM ' . GROUPS_TABLE . "
3368              WHERE group_id = $group_id";
3369          $result = $db->sql_query($sql);
3370          $group_attributes = $db->sql_fetchrow($result);
3371          $db->sql_freeresult($result);
3372      }
3373   
3374      foreach ($attribute_ary as $attribute => $type)
3375      {
3376          if (isset($group_attributes[$attribute]))
3377          {
3378              // 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
3379              if ((strpos($attribute, 'group_avatar') === 0 || strpos($attribute, 'group_rank') === 0) && !$group_attributes[$attribute])
3380              {
3381                  continue;
3382              }
3383   
3384              settype($group_attributes[$attribute], $type);
3385              $sql_ary[str_replace('group_', 'user_', $attribute)] = $group_attributes[$attribute];
3386          }
3387      }
3388   
3389      $updated_sql_ary = $sql_ary;
3390   
3391      // Before we update the user attributes, we will update the rank for users that don't have a custom rank
3392      if (isset($sql_ary['user_rank']))
3393      {
3394          $sql = 'UPDATE ' . USERS_TABLE . '
3395              SET ' . $db->sql_build_array('UPDATE', array('user_rank' => $sql_ary['user_rank'])) . '
3396              WHERE user_rank = 0
3397                  AND ' . $db->sql_in_set('user_id', $user_id_ary);
3398          $db->sql_query($sql);
3399          unset($sql_ary['user_rank']);
3400      }
3401   
3402      // Before we update the user attributes, we will update the avatar for users that don't have a custom avatar
3403      $avatar_options = array('user_avatar', 'user_avatar_type', 'user_avatar_height', 'user_avatar_width');
3404   
3405      if (isset($sql_ary['user_avatar']))
3406      {
3407          $avatar_sql_ary = array();
3408          foreach ($avatar_options as $avatar_option)
3409          {
3410              if (isset($sql_ary[$avatar_option]))
3411              {
3412                  $avatar_sql_ary[$avatar_option] = $sql_ary[$avatar_option];
3413              }
3414          }
3415   
3416          $sql = 'UPDATE ' . USERS_TABLE . '
3417              SET ' . $db->sql_build_array('UPDATE', $avatar_sql_ary) . "
3418              WHERE user_avatar = ''
3419                  AND " . $db->sql_in_set('user_id', $user_id_ary);
3420          $db->sql_query($sql);
3421      }
3422   
3423      // Remove the avatar options, as we already updated them
3424      foreach ($avatar_options as $avatar_option)
3425      {
3426          unset($sql_ary[$avatar_option]);
3427      }
3428   
3429      if (!empty($sql_ary))
3430      {
3431          $sql = 'UPDATE ' . USERS_TABLE . '
3432              SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
3433              WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
3434          $db->sql_query($sql);
3435      }
3436   
3437      if (isset($sql_ary['user_colour']))
3438      {
3439          // Update any cached colour information for these users
3440          $sql = 'UPDATE ' . FORUMS_TABLE . "
3441              SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3442              WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
3443          $db->sql_query($sql);
3444   
3445          $sql = 'UPDATE ' . TOPICS_TABLE . "
3446              SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3447              WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
3448          $db->sql_query($sql);
3449   
3450          $sql = 'UPDATE ' . TOPICS_TABLE . "
3451              SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3452              WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
3453          $db->sql_query($sql);
3454   
3455          if (in_array($config['newest_user_id'], $user_id_ary))
3456          {
3457              $config->set('newest_user_colour', $sql_ary['user_colour'], false);
3458          }
3459      }
3460   
3461      // Make all values available for the event
3462      $sql_ary = $updated_sql_ary;
3463   
3464      /**
3465      * Event when the default group is set for an array of users
3466      *
3467      * @event core.user_set_default_group
3468      * @var    int        group_id            ID of the group
3469      * @var    array    user_id_ary            IDs of the users
3470      * @var    array    group_attributes    Group attributes which were changed
3471      * @var    array    update_listing        Update the list of moderators and foes
3472      * @var    array    sql_ary                User attributes which were changed
3473      * @since 3.1.0-a1
3474      */
3475      $vars = array('group_id', 'user_id_ary', 'group_attributes', 'update_listing', 'sql_ary');
3476      extract($phpbb_dispatcher->trigger_event('core.user_set_default_group', compact($vars)));
3477   
3478      if ($update_listing)
3479      {
3480          group_update_listings($group_id);
3481      }
3482   
3483      // Because some tables/caches use usercolour-specific data we need to purge this here.
3484      $phpbb_container->get('cache.driver')->destroy('sql', MODERATOR_CACHE_TABLE);
3485  }
3486   
3487  /**
3488  * Get group name
3489  */
3490  function get_group_name($group_id)
3491  {
3492      global $db, $phpbb_container;
3493   
3494      $sql = 'SELECT group_name, group_type
3495          FROM ' . GROUPS_TABLE . '
3496          WHERE group_id = ' . (int) $group_id;
3497      $result = $db->sql_query($sql);
3498      $row = $db->sql_fetchrow($result);
3499      $db->sql_freeresult($result);
3500   
3501      if (!$row)
3502      {
3503          return '';
3504      }
3505   
3506      /** @var \phpbb\group\helper $group_helper */
3507      $group_helper = $phpbb_container->get('group_helper');
3508   
3509      return $group_helper->get_name($row['group_name']);
3510  }
3511   
3512  /**
3513  * Obtain either the members of a specified group, the groups the specified user is subscribed to
3514  * or checking if a specified user is in a specified group. This function does not return pending memberships.
3515  *
3516  * Note: Never use this more than once... first group your users/groups
3517  */
3518  function group_memberships($group_id_ary = false, $user_id_ary = false, $return_bool = false)
3519  {
3520      global $db;
3521   
3522      if (!$group_id_ary && !$user_id_ary)
3523      {
3524          return true;
3525      }
3526   
3527      if ($user_id_ary)
3528      {
3529          $user_id_ary = (!is_array($user_id_ary)) ? array($user_id_ary) : $user_id_ary;
3530      }
3531   
3532      if ($group_id_ary)
3533      {
3534          $group_id_ary = (!is_array($group_id_ary)) ? array($group_id_ary) : $group_id_ary;
3535      }
3536   
3537      $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email
3538          FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
3539          WHERE ug.user_id = u.user_id
3540              AND ug.user_pending = 0 AND ';
3541   
3542      if ($group_id_ary)
3543      {
3544          $sql .= ' ' . $db->sql_in_set('ug.group_id', $group_id_ary);
3545      }
3546   
3547      if ($user_id_ary)
3548      {
3549          $sql .= ($group_id_ary) ? ' AND ' : ' ';
3550          $sql .= $db->sql_in_set('ug.user_id', $user_id_ary);
3551      }
3552   
3553      $result = ($return_bool) ? $db->sql_query_limit($sql, 1) : $db->sql_query($sql);
3554   
3555      $row = $db->sql_fetchrow($result);
3556   
3557      if ($return_bool)
3558      {
3559          $db->sql_freeresult($result);
3560          return ($row) ? true : false;
3561      }
3562   
3563      if (!$row)
3564      {
3565          return false;
3566      }
3567   
3568      $return = array();
3569   
3570      do
3571      {
3572          $return[] = $row;
3573      }
3574      while ($row = $db->sql_fetchrow($result));
3575   
3576      $db->sql_freeresult($result);
3577   
3578      return $return;
3579  }
3580   
3581  /**
3582  * Re-cache moderators and foes if group has a_ or m_ permissions
3583  */
3584  function group_update_listings($group_id)
3585  {
3586      global $db, $cache, $auth;
3587   
3588      $hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_'));
3589   
3590      if (empty($hold_ary))
3591      {
3592          return;
3593      }
3594   
3595      $mod_permissions = $admin_permissions = false;
3596   
3597      foreach ($hold_ary as $g_id => $forum_ary)
3598      {
3599          foreach ($forum_ary as $forum_id => $auth_ary)
3600          {
3601              foreach ($auth_ary as $auth_option => $setting)
3602              {
3603                  if ($mod_permissions && $admin_permissions)
3604                  {
3605                      break 3;
3606                  }
3607   
3608                  if ($setting != ACL_YES)
3609                  {
3610                      continue;
3611                  }
3612   
3613                  if ($auth_option == 'm_')
3614                  {
3615                      $mod_permissions = true;
3616                  }
3617   
3618                  if ($auth_option == 'a_')
3619                  {
3620                      $admin_permissions = true;
3621                  }
3622              }
3623          }
3624      }
3625   
3626      if ($mod_permissions)
3627      {
3628          if (!function_exists('phpbb_cache_moderators'))
3629          {
3630              global $phpbb_root_path, $phpEx;
3631              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
3632          }
3633          phpbb_cache_moderators($db, $cache, $auth);
3634      }
3635   
3636      if ($mod_permissions || $admin_permissions)
3637      {
3638          if (!function_exists('phpbb_update_foes'))
3639          {
3640              global $phpbb_root_path, $phpEx;
3641              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
3642          }
3643          phpbb_update_foes($db, $auth, array($group_id));
3644      }
3645  }
3646   
3647   
3648   
3649  /**
3650  * Funtion to make a user leave the NEWLY_REGISTERED system group.
3651  * @access public
3652  * @param int $user_id The id of the user to remove from the group
3653  * @param mixed $user_data The id of the user to remove from the group
3654  */
3655  function remove_newly_registered($user_id, $user_data = false)
3656  {
3657      global $db;
3658   
3659      if ($user_data === false)
3660      {
3661          $sql = 'SELECT *
3662              FROM ' . USERS_TABLE . '
3663              WHERE user_id = ' . $user_id;
3664          $result = $db->sql_query($sql);
3665          $user_row = $db->sql_fetchrow($result);
3666          $db->sql_freeresult($result);
3667   
3668          if (!$user_row)
3669          {
3670              return false;
3671          }
3672          else
3673          {
3674              $user_data  = $user_row;
3675          }
3676      }
3677   
3678      $sql = 'SELECT group_id
3679          FROM ' . GROUPS_TABLE . "
3680          WHERE group_name = 'NEWLY_REGISTERED'
3681              AND group_type = " . GROUP_SPECIAL;
3682      $result = $db->sql_query($sql);
3683      $group_id = (int) $db->sql_fetchfield('group_id');
3684      $db->sql_freeresult($result);
3685   
3686      if (!$group_id)
3687      {
3688          return false;
3689      }
3690   
3691      // We need to call group_user_del here, because this function makes sure everything is correctly changed.
3692      // Force function to not log the removal of users from newly registered users group
3693      group_user_del($group_id, $user_id, false, false, false);
3694   
3695      // Set user_new to 0 to let this not be triggered again
3696      $sql = 'UPDATE ' . USERS_TABLE . '
3697          SET user_new = 0
3698          WHERE user_id = ' . $user_id;
3699      $db->sql_query($sql);
3700   
3701      // The new users group was the users default group?
3702      if ($user_data['group_id'] == $group_id)
3703      {
3704          // Which group is now the users default one?
3705          $sql = 'SELECT group_id
3706              FROM ' . USERS_TABLE . '
3707              WHERE user_id = ' . $user_id;
3708          $result = $db->sql_query($sql);
3709          $user_data['group_id'] = $db->sql_fetchfield('group_id');
3710          $db->sql_freeresult($result);
3711      }
3712   
3713      return $user_data['group_id'];
3714  }
3715   
3716  /**
3717  * Gets user ids of currently banned registered users.
3718  *
3719  * @param array $user_ids Array of users' ids to check for banning,
3720  *                        leave empty to get complete list of banned ids
3721  * @param bool|int $ban_end Bool True to get users currently banned
3722  *                         Bool False to only get permanently banned users
3723  *                         Int Unix timestamp to get users banned until that time
3724  * @return array    Array of banned users' ids if any, empty array otherwise
3725  */
3726  function phpbb_get_banned_user_ids($user_ids = array(), $ban_end = true)
3727  {
3728      global $db;
3729   
3730      $sql_user_ids = (!empty($user_ids)) ? $db->sql_in_set('ban_userid', $user_ids) : 'ban_userid <> 0';
3731   
3732      // Get banned User ID's
3733      // Ignore stale bans which were not wiped yet
3734      $banned_ids_list = array();
3735      $sql = 'SELECT ban_userid
3736          FROM ' . BANLIST_TABLE . "
3737          WHERE $sql_user_ids
3738              AND ban_exclude <> 1";
3739   
3740      if ($ban_end === true)
3741      {
3742          // Banned currently
3743          $sql .= " AND (ban_end > " . time() . '
3744                  OR ban_end = 0)';
3745      }
3746      else if ($ban_end === false)
3747      {
3748          // Permanently banned
3749          $sql .= " AND ban_end = 0";
3750      }
3751      else
3752      {
3753          // Banned until a specified time
3754          $sql .= " AND (ban_end > " . (int) $ban_end . '
3755                  OR ban_end = 0)';
3756      }
3757   
3758      $result = $db->sql_query($sql);
3759      while ($row = $db->sql_fetchrow($result))
3760      {
3761          $user_id = (int) $row['ban_userid'];
3762          $banned_ids_list[$user_id] = $user_id;
3763      }
3764      $db->sql_freeresult($result);
3765   
3766      return $banned_ids_list;
3767  }
3768   
3769  /**
3770  * Function for assigning a template var if the zebra module got included
3771  */
3772  function phpbb_module_zebra($mode, &$module_row)
3773  {
3774      global $template;
3775   
3776      $template->assign_var('S_ZEBRA_ENABLED', true);
3777   
3778      if ($mode == 'friends')
3779      {
3780          $template->assign_var('S_ZEBRA_FRIENDS_ENABLED', true);
3781      }
3782   
3783      if ($mode == 'foes')
3784      {
3785          $template->assign_var('S_ZEBRA_FOES_ENABLED', true);
3786      }
3787  }
3788