Verzeichnisstruktur phpBB-3.2.0


Veröffentlicht
06.01.2017

So funktioniert es


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

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

      (Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

functions_user.php

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


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