[ Index ]

PHP Cross Reference of phpBB-3.2.0-deutsch

title

Body

[close]

/includes/ -> functions_user.php (source)

   1  <?php
   2  /**
   3  *
   4  * This file is part of the phpBB Forum Software package.
   5  *
   6  * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7  * @license GNU General Public License, version 2 (GPL-2.0)
   8  *
   9  * For full copyright and license information, please see
  10  * the docs/CREDITS.txt file.
  11  *
  12  */
  13  
  14  /**
  15  * @ignore
  16  */
  17  if (!defined('IN_PHPBB'))
  18  {
  19      exit;
  20  }
  21  
  22  /**
  23  * Obtain user_ids from usernames or vice versa. Returns false on
  24  * success else the error string
  25  *
  26  * @param array &$user_id_ary The user ids to check or empty if usernames used
  27  * @param array &$username_ary The usernames to check or empty if user ids used
  28  * @param mixed $user_type Array of user types to check, false if not restricting by user type
  29  */
  30  function user_get_id_name(&$user_id_ary, &$username_ary, $user_type = false)
  31  {
  32      global $db;
  33  
  34      // Are both arrays already filled? Yep, return else
  35      // are neither array filled?
  36      if ($user_id_ary && $username_ary)
  37      {
  38          return false;
  39      }
  40      else if (!$user_id_ary && !$username_ary)
  41      {
  42          return 'NO_USERS';
  43      }
  44  
  45      $which_ary = ($user_id_ary) ? 'user_id_ary' : 'username_ary';
  46  
  47      if (${$which_ary} && !is_array(${$which_ary}))
  48      {
  49          ${$which_ary} = array(${$which_ary});
  50      }
  51  
  52      $sql_in = ($which_ary == 'user_id_ary') ? array_map('intval', ${$which_ary}) : array_map('utf8_clean_string', ${$which_ary});
  53      unset(${$which_ary});
  54  
  55      $user_id_ary = $username_ary = array();
  56  
  57      // Grab the user id/username records
  58      $sql_where = ($which_ary == 'user_id_ary') ? 'user_id' : 'username_clean';
  59      $sql = 'SELECT user_id, username
  60          FROM ' . USERS_TABLE . '
  61          WHERE ' . $db->sql_in_set($sql_where, $sql_in);
  62  
  63      if ($user_type !== false && !empty($user_type))
  64      {
  65          $sql .= ' AND ' . $db->sql_in_set('user_type', $user_type);
  66      }
  67  
  68      $result = $db->sql_query($sql);
  69  
  70      if (!($row = $db->sql_fetchrow($result)))
  71      {
  72          $db->sql_freeresult($result);
  73          return 'NO_USERS';
  74      }
  75  
  76      do
  77      {
  78          $username_ary[$row['user_id']] = $row['username'];
  79          $user_id_ary[] = $row['user_id'];
  80      }
  81      while ($row = $db->sql_fetchrow($result));
  82      $db->sql_freeresult($result);
  83  
  84      return false;
  85  }
  86  
  87  /**
  88  * Get latest registered username and update database to reflect it
  89  */
  90  function update_last_username()
  91  {
  92      global $config, $db;
  93  
  94      // Get latest username
  95      $sql = 'SELECT user_id, username, user_colour
  96          FROM ' . USERS_TABLE . '
  97          WHERE user_type IN (' . USER_NORMAL . ', ' . USER_FOUNDER . ')
  98          ORDER BY user_id DESC';
  99      $result = $db->sql_query_limit($sql, 1);
 100      $row = $db->sql_fetchrow($result);
 101      $db->sql_freeresult($result);
 102  
 103      if ($row)
 104      {
 105          $config->set('newest_user_id', $row['user_id'], false);
 106          $config->set('newest_username', $row['username'], false);
 107          $config->set('newest_user_colour', $row['user_colour'], false);
 108      }
 109  }
 110  
 111  /**
 112  * Updates a username across all relevant tables/fields
 113  *
 114  * @param string $old_name the old/current username
 115  * @param string $new_name the new username
 116  */
 117  function user_update_name($old_name, $new_name)
 118  {
 119      global $config, $db, $cache, $phpbb_dispatcher;
 120  
 121      $update_ary = array(
 122          FORUMS_TABLE            => array(
 123              'forum_last_poster_id'    => 'forum_last_poster_name',
 124          ),
 125          MODERATOR_CACHE_TABLE    => array(
 126              'user_id'    => 'username',
 127          ),
 128          POSTS_TABLE                => array(
 129              'poster_id'    => 'post_username',
 130          ),
 131          TOPICS_TABLE            => array(
 132              'topic_poster'            => 'topic_first_poster_name',
 133              'topic_last_poster_id'    => 'topic_last_poster_name',
 134          ),
 135      );
 136  
 137      foreach ($update_ary as $table => $field_ary)
 138      {
 139          foreach ($field_ary as $id_field => $name_field)
 140          {
 141              $sql = "UPDATE $table
 142                  SET $name_field = '" . $db->sql_escape($new_name) . "'
 143                  WHERE $name_field = '" . $db->sql_escape($old_name) . "'
 144                      AND $id_field <> " . ANONYMOUS;
 145              $db->sql_query($sql);
 146          }
 147      }
 148  
 149      if ($config['newest_username'] == $old_name)
 150      {
 151          $config->set('newest_username', $new_name, false);
 152      }
 153  
 154      /**
 155      * Update a username when it is changed
 156      *
 157      * @event core.update_username
 158      * @var    string    old_name    The old username that is replaced
 159      * @var    string    new_name    The new username
 160      * @since 3.1.0-a1
 161      */
 162      $vars = array('old_name', 'new_name');
 163      extract($phpbb_dispatcher->trigger_event('core.update_username', compact($vars)));
 164  
 165      // Because some tables/caches use username-specific data we need to purge this here.
 166      $cache->destroy('sql', MODERATOR_CACHE_TABLE);
 167  }
 168  
 169  /**
 170  * Adds an user
 171  *
 172  * @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.
 173  * @param string $cp_data custom profile fields, see custom_profile::build_insert_sql_array
 174  * @param array $notifications_data The notifications settings for the new user
 175  * @return the new user's ID.
 176  */
 177  function user_add($user_row, $cp_data = false, $notifications_data = null)
 178  {
 179      global $db, $config;
 180      global $phpbb_dispatcher, $phpbb_container;
 181  
 182      if (empty($user_row['username']) || !isset($user_row['group_id']) || !isset($user_row['user_email']) || !isset($user_row['user_type']))
 183      {
 184          return false;
 185      }
 186  
 187      $username_clean = utf8_clean_string($user_row['username']);
 188  
 189      if (empty($username_clean))
 190      {
 191          return false;
 192      }
 193  
 194      $sql_ary = array(
 195          'username'            => $user_row['username'],
 196          'username_clean'    => $username_clean,
 197          'user_password'        => (isset($user_row['user_password'])) ? $user_row['user_password'] : '',
 198          'user_email'        => strtolower($user_row['user_email']),
 199          'user_email_hash'    => phpbb_email_hash($user_row['user_email']),
 200          'group_id'            => $user_row['group_id'],
 201          'user_type'            => $user_row['user_type'],
 202      );
 203  
 204      // These are the additional vars able to be specified
 205      $additional_vars = array(
 206          'user_permissions'    => '',
 207          'user_timezone'        => $config['board_timezone'],
 208          'user_dateformat'    => $config['default_dateformat'],
 209          'user_lang'            => $config['default_lang'],
 210          'user_style'        => (int) $config['default_style'],
 211          'user_actkey'        => '',
 212          'user_ip'            => '',
 213          'user_regdate'        => time(),
 214          'user_passchg'        => time(),
 215          'user_options'        => 230271,
 216          // We do not set the new flag here - registration scripts need to specify it
 217          'user_new'            => 0,
 218  
 219          'user_inactive_reason'    => 0,
 220          'user_inactive_time'    => 0,
 221          'user_lastmark'            => time(),
 222          'user_lastvisit'        => 0,
 223          'user_lastpost_time'    => 0,
 224          'user_lastpage'            => '',
 225          'user_posts'            => 0,
 226          'user_colour'            => '',
 227          'user_avatar'            => '',
 228          'user_avatar_type'        => '',
 229          'user_avatar_width'        => 0,
 230          'user_avatar_height'    => 0,
 231          'user_new_privmsg'        => 0,
 232          'user_unread_privmsg'    => 0,
 233          'user_last_privmsg'        => 0,
 234          'user_message_rules'    => 0,
 235          'user_full_folder'        => PRIVMSGS_NO_BOX,
 236          'user_emailtime'        => 0,
 237  
 238          'user_notify'            => 0,
 239          'user_notify_pm'        => 1,
 240          'user_notify_type'        => NOTIFY_EMAIL,
 241          'user_allow_pm'            => 1,
 242          'user_allow_viewonline'    => 1,
 243          'user_allow_viewemail'    => 1,
 244          'user_allow_massemail'    => 1,
 245  
 246          'user_sig'                    => '',
 247          'user_sig_bbcode_uid'        => '',
 248          'user_sig_bbcode_bitfield'    => '',
 249  
 250          'user_form_salt'            => unique_id(),
 251      );
 252  
 253      // Now fill the sql array with not required variables
 254      foreach ($additional_vars as $key => $default_value)
 255      {
 256          $sql_ary[$key] = (isset($user_row[$key])) ? $user_row[$key] : $default_value;
 257      }
 258  
 259      // Any additional variables in $user_row not covered above?
 260      $remaining_vars = array_diff(array_keys($user_row), array_keys($sql_ary));
 261  
 262      // Now fill our sql array with the remaining vars
 263      if (sizeof($remaining_vars))
 264      {
 265          foreach ($remaining_vars as $key)
 266          {
 267              $sql_ary[$key] = $user_row[$key];
 268          }
 269      }
 270  
 271      /**
 272      * Use this event to modify the values to be inserted when a user is added
 273      *
 274      * @event core.user_add_modify_data
 275      * @var array    user_row        Array of user details submited to user_add
 276      * @var array    cp_data            Array of Custom profile fields submited to user_add
 277      * @var array    sql_ary        Array of data to be inserted when a user is added
 278      * @since 3.1.0-a1
 279      * @change 3.1.0-b5
 280      */
 281      $vars = array('user_row', 'cp_data', 'sql_ary');
 282      extract($phpbb_dispatcher->trigger_event('core.user_add_modify_data', compact($vars)));
 283  
 284      $sql = 'INSERT INTO ' . USERS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
 285      $db->sql_query($sql);
 286  
 287      $user_id = $db->sql_nextid();
 288  
 289      // Insert Custom Profile Fields
 290      if ($cp_data !== false && sizeof($cp_data))
 291      {
 292          $cp_data['user_id'] = (int) $user_id;
 293  
 294          /* @var $cp \phpbb\profilefields\manager */
 295          $cp = $phpbb_container->get('profilefields.manager');
 296          $sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' .
 297              $db->sql_build_array('INSERT', $cp->build_insert_sql_array($cp_data));
 298          $db->sql_query($sql);
 299      }
 300  
 301      // Place into appropriate group...
 302      $sql = 'INSERT INTO ' . USER_GROUP_TABLE . ' ' . $db->sql_build_array('INSERT', array(
 303          'user_id'        => (int) $user_id,
 304          'group_id'        => (int) $user_row['group_id'],
 305          'user_pending'    => 0)
 306      );
 307      $db->sql_query($sql);
 308  
 309      // Now make it the users default group...
 310      group_set_user_default($user_row['group_id'], array($user_id), false);
 311  
 312      // Add to newly registered users group if user_new is 1
 313      if ($config['new_member_post_limit'] && $sql_ary['user_new'])
 314      {
 315          $sql = 'SELECT group_id
 316              FROM ' . GROUPS_TABLE . "
 317              WHERE group_name = 'NEWLY_REGISTERED'
 318                  AND group_type = " . GROUP_SPECIAL;
 319          $result = $db->sql_query($sql);
 320          $add_group_id = (int) $db->sql_fetchfield('group_id');
 321          $db->sql_freeresult($result);
 322  
 323          if ($add_group_id)
 324          {
 325              global $phpbb_log;
 326  
 327              // Because these actions only fill the log unnecessarily, we disable it
 328              $phpbb_log->disable('admin');
 329  
 330              // Add user to "newly registered users" group and set to default group if admin specified so.
 331              if ($config['new_member_group_default'])
 332              {
 333                  group_user_add($add_group_id, $user_id, false, false, true);
 334                  $user_row['group_id'] = $add_group_id;
 335              }
 336              else
 337              {
 338                  group_user_add($add_group_id, $user_id);
 339              }
 340  
 341              $phpbb_log->enable('admin');
 342          }
 343      }
 344  
 345      // set the newest user and adjust the user count if the user is a normal user and no activation mail is sent
 346      if ($user_row['user_type'] == USER_NORMAL || $user_row['user_type'] == USER_FOUNDER)
 347      {
 348          $config->set('newest_user_id', $user_id, false);
 349          $config->set('newest_username', $user_row['username'], false);
 350          $config->increment('num_users', 1, false);
 351  
 352          $sql = 'SELECT group_colour
 353              FROM ' . GROUPS_TABLE . '
 354              WHERE group_id = ' . (int) $user_row['group_id'];
 355          $result = $db->sql_query_limit($sql, 1);
 356          $row = $db->sql_fetchrow($result);
 357          $db->sql_freeresult($result);
 358  
 359          $config->set('newest_user_colour', $row['group_colour'], false);
 360      }
 361  
 362      // Use default notifications settings if notifications_data is not set
 363      if ($notifications_data === null)
 364      {
 365          $notifications_data = array(
 366              array(
 367                  'item_type'    => 'notification.type.post',
 368                  'method'    => 'notification.method.email',
 369              ),
 370              array(
 371                  'item_type'    => 'notification.type.topic',
 372                  'method'    => 'notification.method.email',
 373              ),
 374          );
 375      }
 376  
 377      // Subscribe user to notifications if necessary
 378      if (!empty($notifications_data))
 379      {
 380          /* @var $phpbb_notifications \phpbb\notification\manager */
 381          $phpbb_notifications = $phpbb_container->get('notification_manager');
 382          foreach ($notifications_data as $subscription)
 383          {
 384              $phpbb_notifications->add_subscription($subscription['item_type'], 0, $subscription['method'], $user_id);
 385          }
 386      }
 387  
 388      /**
 389      * Event that returns user id, user detals and user CPF of newly registared user
 390      *
 391      * @event core.user_add_after
 392      * @var int        user_id            User id of newly registared user
 393      * @var array    user_row        Array of user details submited to user_add
 394      * @var array    cp_data            Array of Custom profile fields submited to user_add
 395      * @since 3.1.0-b5
 396      */
 397      $vars = array('user_id', 'user_row', 'cp_data');
 398      extract($phpbb_dispatcher->trigger_event('core.user_add_after', compact($vars)));
 399  
 400      return $user_id;
 401  }
 402  
 403  /**
 404   * Remove User
 405   *
 406   * @param string    $mode        Either 'retain' or 'remove'
 407   * @param mixed        $user_ids    Either an array of integers or an integer
 408   * @param bool        $retain_username
 409   * @return bool
 410   */
 411  function user_delete($mode, $user_ids, $retain_username = true)
 412  {
 413      global $cache, $config, $db, $user, $phpbb_dispatcher, $phpbb_container;
 414      global $phpbb_root_path, $phpEx;
 415  
 416      $db->sql_transaction('begin');
 417  
 418      $user_rows = array();
 419      if (!is_array($user_ids))
 420      {
 421          $user_ids = array($user_ids);
 422      }
 423  
 424      $user_id_sql = $db->sql_in_set('user_id', $user_ids);
 425  
 426      $sql = 'SELECT *
 427          FROM ' . USERS_TABLE . '
 428          WHERE ' . $user_id_sql;
 429      $result = $db->sql_query($sql);
 430      while ($row = $db->sql_fetchrow($result))
 431      {
 432          $user_rows[(int) $row['user_id']] = $row;
 433      }
 434      $db->sql_freeresult($result);
 435  
 436      if (empty($user_rows))
 437      {
 438          return false;
 439      }
 440  
 441      /**
 442      * Event before a user is deleted
 443      *
 444      * @event core.delete_user_before
 445      * @var    string    mode        Mode of deletion (retain/delete posts)
 446      * @var    array    user_ids    IDs of the deleted user
 447      * @var    mixed    retain_username    True if username should be retained
 448      *                or false if not
 449      * @since 3.1.0-a1
 450      */
 451      $vars = array('mode', 'user_ids', 'retain_username');
 452      extract($phpbb_dispatcher->trigger_event('core.delete_user_before', compact($vars)));
 453  
 454      // Before we begin, we will remove the reports the user issued.
 455      $sql = 'SELECT r.post_id, p.topic_id
 456          FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p
 457          WHERE ' . $db->sql_in_set('r.user_id', $user_ids) . '
 458              AND p.post_id = r.post_id';
 459      $result = $db->sql_query($sql);
 460  
 461      $report_posts = $report_topics = array();
 462      while ($row = $db->sql_fetchrow($result))
 463      {
 464          $report_posts[] = $row['post_id'];
 465          $report_topics[] = $row['topic_id'];
 466      }
 467      $db->sql_freeresult($result);
 468  
 469      if (sizeof($report_posts))
 470      {
 471          $report_posts = array_unique($report_posts);
 472          $report_topics = array_unique($report_topics);
 473  
 474          // Get a list of topics that still contain reported posts
 475          $sql = 'SELECT DISTINCT topic_id
 476              FROM ' . POSTS_TABLE . '
 477              WHERE ' . $db->sql_in_set('topic_id', $report_topics) . '
 478                  AND post_reported = 1
 479                  AND ' . $db->sql_in_set('post_id', $report_posts, true);
 480          $result = $db->sql_query($sql);
 481  
 482          $keep_report_topics = array();
 483          while ($row = $db->sql_fetchrow($result))
 484          {
 485              $keep_report_topics[] = $row['topic_id'];
 486          }
 487          $db->sql_freeresult($result);
 488  
 489          if (sizeof($keep_report_topics))
 490          {
 491              $report_topics = array_diff($report_topics, $keep_report_topics);
 492          }
 493          unset($keep_report_topics);
 494  
 495          // Now set the flags back
 496          $sql = 'UPDATE ' . POSTS_TABLE . '
 497              SET post_reported = 0
 498              WHERE ' . $db->sql_in_set('post_id', $report_posts);
 499          $db->sql_query($sql);
 500  
 501          if (sizeof($report_topics))
 502          {
 503              $sql = 'UPDATE ' . TOPICS_TABLE . '
 504                  SET topic_reported = 0
 505                  WHERE ' . $db->sql_in_set('topic_id', $report_topics);
 506              $db->sql_query($sql);
 507          }
 508      }
 509  
 510      // Remove reports
 511      $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE ' . $user_id_sql);
 512  
 513      $num_users_delta = 0;
 514  
 515      // Get auth provider collection in case accounts might need to be unlinked
 516      $provider_collection = $phpbb_container->get('auth.provider_collection');
 517  
 518      // Some things need to be done in the loop (if the query changes based
 519      // on which user is currently being deleted)
 520      $added_guest_posts = 0;
 521      foreach ($user_rows as $user_id => $user_row)
 522      {
 523          if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == 'avatar.driver.upload')
 524          {
 525              avatar_delete('user', $user_row);
 526          }
 527  
 528          // Unlink accounts
 529          foreach ($provider_collection as $provider_name => $auth_provider)
 530          {
 531              $provider_data = $auth_provider->get_auth_link_data($user_id);
 532  
 533              if ($provider_data !== null)
 534              {
 535                  $link_data = array(
 536                      'user_id' => $user_id,
 537                      'link_method' => 'user_delete',
 538                  );
 539  
 540                  // BLOCK_VARS might contain hidden fields necessary for unlinking accounts
 541                  if (isset($provider_data['BLOCK_VARS']) && is_array($provider_data['BLOCK_VARS']))
 542                  {
 543                      foreach ($provider_data['BLOCK_VARS'] as $provider_service)
 544                      {
 545                          if (!array_key_exists('HIDDEN_FIELDS', $provider_service))
 546                          {
 547                              $provider_service['HIDDEN_FIELDS'] = array();
 548                          }
 549  
 550                          $auth_provider->unlink_account(array_merge($link_data, $provider_service['HIDDEN_FIELDS']));
 551                      }
 552                  }
 553                  else
 554                  {
 555                      $auth_provider->unlink_account($link_data);
 556                  }
 557              }
 558          }
 559  
 560          // Decrement number of users if this user is active
 561          if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
 562          {
 563              --$num_users_delta;
 564          }
 565  
 566          switch ($mode)
 567          {
 568              case 'retain':
 569                  if ($retain_username === false)
 570                  {
 571                      $post_username = $user->lang['GUEST'];
 572                  }
 573                  else
 574                  {
 575                      $post_username = $user_row['username'];
 576                  }
 577  
 578                  // If the user is inactive and newly registered
 579                  // we assume no posts from the user, and save
 580                  // the queries
 581                  if ($user_row['user_type'] != USER_INACTIVE || $user_row['user_inactive_reason'] != INACTIVE_REGISTER || $user_row['user_posts'])
 582                  {
 583                      // When we delete these users and retain the posts, we must assign all the data to the guest user
 584                      $sql = 'UPDATE ' . FORUMS_TABLE . '
 585                          SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = ''
 586                          WHERE forum_last_poster_id = $user_id";
 587                      $db->sql_query($sql);
 588  
 589                      $sql = 'UPDATE ' . POSTS_TABLE . '
 590                          SET poster_id = ' . ANONYMOUS . ", post_username = '" . $db->sql_escape($post_username) . "'
 591                          WHERE poster_id = $user_id";
 592                      $db->sql_query($sql);
 593  
 594                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 595                          SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = ''
 596                          WHERE topic_poster = $user_id";
 597                      $db->sql_query($sql);
 598  
 599                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 600                          SET topic_last_poster_id = ' . ANONYMOUS . ", topic_last_poster_name = '" . $db->sql_escape($post_username) . "', topic_last_poster_colour = ''
 601                          WHERE topic_last_poster_id = $user_id";
 602                      $db->sql_query($sql);
 603  
 604                      // Since we change every post by this author, we need to count this amount towards the anonymous user
 605  
 606                      if ($user_row['user_posts'])
 607                      {
 608                          $added_guest_posts += $user_row['user_posts'];
 609                      }
 610                  }
 611              break;
 612  
 613              case 'remove':
 614                  // there is nothing variant specific to deleting posts
 615              break;
 616          }
 617      }
 618  
 619      if ($num_users_delta != 0)
 620      {
 621          $config->increment('num_users', $num_users_delta, false);
 622      }
 623  
 624      // Now do the invariant tasks
 625      // all queries performed in one call of this function are in a single transaction
 626      // so this is kosher
 627      if ($mode == 'retain')
 628      {
 629          // Assign more data to the Anonymous user
 630          $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
 631              SET poster_id = ' . ANONYMOUS . '
 632              WHERE ' . $db->sql_in_set('poster_id', $user_ids);
 633          $db->sql_query($sql);
 634  
 635          $sql = 'UPDATE ' . USERS_TABLE . '
 636              SET user_posts = user_posts + ' . $added_guest_posts . '
 637              WHERE user_id = ' . ANONYMOUS;
 638          $db->sql_query($sql);
 639      }
 640      else if ($mode == 'remove')
 641      {
 642          if (!function_exists('delete_posts'))
 643          {
 644              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
 645          }
 646  
 647          // Delete posts, attachments, etc.
 648          // delete_posts can handle any number of IDs in its second argument
 649          delete_posts('poster_id', $user_ids);
 650      }
 651  
 652      $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);
 653  
 654      // Delete the miscellaneous (non-post) data for the user
 655      foreach ($table_ary as $table)
 656      {
 657          $sql = "DELETE FROM $table
 658              WHERE " . $user_id_sql;
 659          $db->sql_query($sql);
 660      }
 661  
 662      $cache->destroy('sql', MODERATOR_CACHE_TABLE);
 663  
 664      // Change user_id to anonymous for posts edited by this user
 665      $sql = 'UPDATE ' . POSTS_TABLE . '
 666          SET post_edit_user = ' . ANONYMOUS . '
 667          WHERE ' . $db->sql_in_set('post_edit_user', $user_ids);
 668      $db->sql_query($sql);
 669  
 670      // Change user_id to anonymous for pms edited by this user
 671      $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
 672          SET message_edit_user = ' . ANONYMOUS . '
 673          WHERE ' . $db->sql_in_set('message_edit_user', $user_ids);
 674      $db->sql_query($sql);
 675  
 676      // Change user_id to anonymous for posts deleted by this user
 677      $sql = 'UPDATE ' . POSTS_TABLE . '
 678          SET post_delete_user = ' . ANONYMOUS . '
 679          WHERE ' . $db->sql_in_set('post_delete_user', $user_ids);
 680      $db->sql_query($sql);
 681  
 682      // Change user_id to anonymous for topics deleted by this user
 683      $sql = 'UPDATE ' . TOPICS_TABLE . '
 684          SET topic_delete_user = ' . ANONYMOUS . '
 685          WHERE ' . $db->sql_in_set('topic_delete_user', $user_ids);
 686      $db->sql_query($sql);
 687  
 688      // Delete user log entries about this user
 689      $sql = 'DELETE FROM ' . LOG_TABLE . '
 690          WHERE ' . $db->sql_in_set('reportee_id', $user_ids);
 691      $db->sql_query($sql);
 692  
 693      // Change user_id to anonymous for this users triggered events
 694      $sql = 'UPDATE ' . LOG_TABLE . '
 695          SET user_id = ' . ANONYMOUS . '
 696          WHERE ' . $user_id_sql;
 697      $db->sql_query($sql);
 698  
 699      // Delete the user_id from the zebra table
 700      $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
 701          WHERE ' . $user_id_sql . '
 702              OR ' . $db->sql_in_set('zebra_id', $user_ids);
 703      $db->sql_query($sql);
 704  
 705      // Delete the user_id from the banlist
 706      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 707          WHERE ' . $db->sql_in_set('ban_userid', $user_ids);
 708      $db->sql_query($sql);
 709  
 710      // Delete the user_id from the session table
 711      $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
 712          WHERE ' . $db->sql_in_set('session_user_id', $user_ids);
 713      $db->sql_query($sql);
 714  
 715      // Clean the private messages tables from the user
 716      if (!function_exists('phpbb_delete_user_pms'))
 717      {
 718          include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx);
 719      }
 720      phpbb_delete_users_pms($user_ids);
 721  
 722      $phpbb_notifications = $phpbb_container->get('notification_manager');
 723      $phpbb_notifications->delete_notifications('notification.type.admin_activate_user', $user_ids);
 724  
 725      $db->sql_transaction('commit');
 726  
 727      /**
 728      * Event after a user is deleted
 729      *
 730      * @event core.delete_user_after
 731      * @var    string    mode        Mode of deletion (retain/delete posts)
 732      * @var    array    user_ids    IDs of the deleted user
 733      * @var    mixed    retain_username    True if username should be retained
 734      *                or false if not
 735      * @since 3.1.0-a1
 736      */
 737      $vars = array('mode', 'user_ids', 'retain_username');
 738      extract($phpbb_dispatcher->trigger_event('core.delete_user_after', compact($vars)));
 739  
 740      // Reset newest user info if appropriate
 741      if (in_array($config['newest_user_id'], $user_ids))
 742      {
 743          update_last_username();
 744      }
 745  
 746      return false;
 747  }
 748  
 749  /**
 750  * Flips user_type from active to inactive and vice versa, handles group membership updates
 751  *
 752  * @param string $mode can be flip for flipping from active/inactive, activate or deactivate
 753  */
 754  function user_active_flip($mode, $user_id_ary, $reason = INACTIVE_MANUAL)
 755  {
 756      global $config, $db, $user, $auth, $phpbb_dispatcher;
 757  
 758      $deactivated = $activated = 0;
 759      $sql_statements = array();
 760  
 761      if (!is_array($user_id_ary))
 762      {
 763          $user_id_ary = array($user_id_ary);
 764      }
 765  
 766      if (!sizeof($user_id_ary))
 767      {
 768          return;
 769      }
 770  
 771      $sql = 'SELECT user_id, group_id, user_type, user_inactive_reason
 772          FROM ' . USERS_TABLE . '
 773          WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
 774      $result = $db->sql_query($sql);
 775  
 776      while ($row = $db->sql_fetchrow($result))
 777      {
 778          $sql_ary = array();
 779  
 780          if ($row['user_type'] == USER_IGNORE || $row['user_type'] == USER_FOUNDER ||
 781              ($mode == 'activate' && $row['user_type'] != USER_INACTIVE) ||
 782              ($mode == 'deactivate' && $row['user_type'] == USER_INACTIVE))
 783          {
 784              continue;
 785          }
 786  
 787          if ($row['user_type'] == USER_INACTIVE)
 788          {
 789              $activated++;
 790          }
 791          else
 792          {
 793              $deactivated++;
 794  
 795              // Remove the users session key...
 796              $user->reset_login_keys($row['user_id']);
 797          }
 798  
 799          $sql_ary += array(
 800              'user_type'                => ($row['user_type'] == USER_NORMAL) ? USER_INACTIVE : USER_NORMAL,
 801              'user_inactive_time'    => ($row['user_type'] == USER_NORMAL) ? time() : 0,
 802              'user_inactive_reason'    => ($row['user_type'] == USER_NORMAL) ? $reason : 0,
 803          );
 804  
 805          $sql_statements[$row['user_id']] = $sql_ary;
 806      }
 807      $db->sql_freeresult($result);
 808  
 809      /**
 810      * Check or modify activated/deactivated users data before submitting it to the database
 811      *
 812      * @event core.user_active_flip_before
 813      * @var    string    mode            User type changing mode, can be: flip|activate|deactivate
 814      * @var    int        reason            Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND
 815      * @var    int        activated        The number of users to be activated
 816      * @var    int        deactivated        The number of users to be deactivated
 817      * @var    array    user_id_ary        Array with user ids to change user type
 818      * @var    array    sql_statements    Array with users data to submit to the database, keys: user ids, values: arrays with user data
 819      * @since 3.1.4-RC1
 820      */
 821      $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements');
 822      extract($phpbb_dispatcher->trigger_event('core.user_active_flip_before', compact($vars)));
 823  
 824      if (sizeof($sql_statements))
 825      {
 826          foreach ($sql_statements as $user_id => $sql_ary)
 827          {
 828              $sql = 'UPDATE ' . USERS_TABLE . '
 829                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 830                  WHERE user_id = ' . $user_id;
 831              $db->sql_query($sql);
 832          }
 833  
 834          $auth->acl_clear_prefetch(array_keys($sql_statements));
 835      }
 836  
 837      /**
 838      * Perform additional actions after the users have been activated/deactivated
 839      *
 840      * @event core.user_active_flip_after
 841      * @var    string    mode            User type changing mode, can be: flip|activate|deactivate
 842      * @var    int        reason            Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND
 843      * @var    int        activated        The number of users to be activated
 844      * @var    int        deactivated        The number of users to be deactivated
 845      * @var    array    user_id_ary        Array with user ids to change user type
 846      * @var    array    sql_statements    Array with users data to submit to the database, keys: user ids, values: arrays with user data
 847      * @since 3.1.4-RC1
 848      */
 849      $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements');
 850      extract($phpbb_dispatcher->trigger_event('core.user_active_flip_after', compact($vars)));
 851  
 852      if ($deactivated)
 853      {
 854          $config->increment('num_users', $deactivated * (-1), false);
 855      }
 856  
 857      if ($activated)
 858      {
 859          $config->increment('num_users', $activated, false);
 860      }
 861  
 862      // Update latest username
 863      update_last_username();
 864  }
 865  
 866  /**
 867  * Add a ban or ban exclusion to the banlist. Bans either a user, an IP or an email address
 868  *
 869  * @param string $mode Type of ban. One of the following: user, ip, email
 870  * @param mixed $ban Banned entity. Either string or array with usernames, ips or email addresses
 871  * @param int $ban_len Ban length in minutes
 872  * @param string $ban_len_other Ban length as a date (YYYY-MM-DD)
 873  * @param boolean $ban_exclude Exclude these entities from banning?
 874  * @param string $ban_reason String describing the reason for this ban
 875  * @return boolean
 876  */
 877  function user_ban($mode, $ban, $ban_len, $ban_len_other, $ban_exclude, $ban_reason, $ban_give_reason = '')
 878  {
 879      global $db, $user, $cache, $phpbb_log;
 880  
 881      // Delete stale bans
 882      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 883          WHERE ban_end < ' . time() . '
 884              AND ban_end <> 0';
 885      $db->sql_query($sql);
 886  
 887      $ban_list = (!is_array($ban)) ? array_unique(explode("\n", $ban)) : $ban;
 888      $ban_list_log = implode(', ', $ban_list);
 889  
 890      $current_time = time();
 891  
 892      // Set $ban_end to the unix time when the ban should end. 0 is a permanent ban.
 893      if ($ban_len)
 894      {
 895          if ($ban_len != -1 || !$ban_len_other)
 896          {
 897              $ban_end = max($current_time, $current_time + ($ban_len) * 60);
 898          }
 899          else
 900          {
 901              $ban_other = explode('-', $ban_len_other);
 902              if (sizeof($ban_other) == 3 && ((int) $ban_other[0] < 9999) &&
 903                  (strlen($ban_other[0]) == 4) && (strlen($ban_other[1]) == 2) && (strlen($ban_other[2]) == 2))
 904              {
 905                  $ban_end = max($current_time, $user->create_datetime()
 906                      ->setDate((int) $ban_other[0], (int) $ban_other[1], (int) $ban_other[2])
 907                      ->setTime(0, 0, 0)
 908                      ->getTimestamp() + $user->timezone->getOffset(new DateTime('UTC')));
 909              }
 910              else
 911              {
 912                  trigger_error('LENGTH_BAN_INVALID', E_USER_WARNING);
 913              }
 914          }
 915      }
 916      else
 917      {
 918          $ban_end = 0;
 919      }
 920  
 921      $founder = $founder_names = array();
 922  
 923      if (!$ban_exclude)
 924      {
 925          // Create a list of founder...
 926          $sql = 'SELECT user_id, user_email, username_clean
 927              FROM ' . USERS_TABLE . '
 928              WHERE user_type = ' . USER_FOUNDER;
 929          $result = $db->sql_query($sql);
 930  
 931          while ($row = $db->sql_fetchrow($result))
 932          {
 933              $founder[$row['user_id']] = $row['user_email'];
 934              $founder_names[$row['user_id']] = $row['username_clean'];
 935          }
 936          $db->sql_freeresult($result);
 937      }
 938  
 939      $banlist_ary = array();
 940  
 941      switch ($mode)
 942      {
 943          case 'user':
 944              $type = 'ban_userid';
 945  
 946              // At the moment we do not support wildcard username banning
 947  
 948              // Select the relevant user_ids.
 949              $sql_usernames = array();
 950  
 951              foreach ($ban_list as $username)
 952              {
 953                  $username = trim($username);
 954                  if ($username != '')
 955                  {
 956                      $clean_name = utf8_clean_string($username);
 957                      if ($clean_name == $user->data['username_clean'])
 958                      {
 959                          trigger_error('CANNOT_BAN_YOURSELF', E_USER_WARNING);
 960                      }
 961                      if (in_array($clean_name, $founder_names))
 962                      {
 963                          trigger_error('CANNOT_BAN_FOUNDER', E_USER_WARNING);
 964                      }
 965                      $sql_usernames[] = $clean_name;
 966                  }
 967              }
 968  
 969              // Make sure we have been given someone to ban
 970              if (!sizeof($sql_usernames))
 971              {
 972                  trigger_error('NO_USER_SPECIFIED', E_USER_WARNING);
 973              }
 974  
 975              $sql = 'SELECT user_id
 976                  FROM ' . USERS_TABLE . '
 977                  WHERE ' . $db->sql_in_set('username_clean', $sql_usernames);
 978  
 979              // Do not allow banning yourself, the guest account, or founders.
 980              $non_bannable = array($user->data['user_id'], ANONYMOUS);
 981              if (sizeof($founder))
 982              {
 983                  $sql .= ' AND ' . $db->sql_in_set('user_id', array_merge(array_keys($founder), $non_bannable), true);
 984              }
 985              else
 986              {
 987                  $sql .= ' AND ' . $db->sql_in_set('user_id', $non_bannable, true);
 988              }
 989  
 990              $result = $db->sql_query($sql);
 991  
 992              if ($row = $db->sql_fetchrow($result))
 993              {
 994                  do
 995                  {
 996                      $banlist_ary[] = (int) $row['user_id'];
 997                  }
 998                  while ($row = $db->sql_fetchrow($result));
 999              }
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  }


Generated: Sun Feb 19 19:47:08 2017 Cross-referenced by PHPXref 0.7.1