Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


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

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

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_user.php

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


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