Verzeichnisstruktur phpBB-3.1.0


Veröffentlicht
27.10.2014

So funktioniert es


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

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

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_user.php

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


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