[ Index ]

PHP Cross Reference of phpBB-3.1.10-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 $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          set_config('newest_user_id', $row['user_id'], true);
 106          set_config('newest_username', $row['username'], true);
 107          set_config('newest_user_colour', $row['user_colour'], true);
 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          set_config('newest_username', $new_name, true);
 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, $user, $auth, $config, $phpbb_root_path, $phpEx;
 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          $cp = $phpbb_container->get('profilefields.manager');
 295          $sql = 'INSERT INTO ' . PROFILE_FIELDS_DATA_TABLE . ' ' .
 296              $db->sql_build_array('INSERT', $cp->build_insert_sql_array($cp_data));
 297          $db->sql_query($sql);
 298      }
 299  
 300      // Place into appropriate group...
 301      $sql = 'INSERT INTO ' . USER_GROUP_TABLE . ' ' . $db->sql_build_array('INSERT', array(
 302          'user_id'        => (int) $user_id,
 303          'group_id'        => (int) $user_row['group_id'],
 304          'user_pending'    => 0)
 305      );
 306      $db->sql_query($sql);
 307  
 308      // Now make it the users default group...
 309      group_set_user_default($user_row['group_id'], array($user_id), false);
 310  
 311      // Add to newly registered users group if user_new is 1
 312      if ($config['new_member_post_limit'] && $sql_ary['user_new'])
 313      {
 314          $sql = 'SELECT group_id
 315              FROM ' . GROUPS_TABLE . "
 316              WHERE group_name = 'NEWLY_REGISTERED'
 317                  AND group_type = " . GROUP_SPECIAL;
 318          $result = $db->sql_query($sql);
 319          $add_group_id = (int) $db->sql_fetchfield('group_id');
 320          $db->sql_freeresult($result);
 321  
 322          if ($add_group_id)
 323          {
 324              global $phpbb_log;
 325  
 326              // Because these actions only fill the log unneccessarily we skip the add_log() entry.
 327              $phpbb_log->disable('admin');
 328  
 329              // Add user to "newly registered users" group and set to default group if admin specified so.
 330              if ($config['new_member_group_default'])
 331              {
 332                  group_user_add($add_group_id, $user_id, false, false, true);
 333                  $user_row['group_id'] = $add_group_id;
 334              }
 335              else
 336              {
 337                  group_user_add($add_group_id, $user_id);
 338              }
 339  
 340              $phpbb_log->enable('admin');
 341          }
 342      }
 343  
 344      // set the newest user and adjust the user count if the user is a normal user and no activation mail is sent
 345      if ($user_row['user_type'] == USER_NORMAL || $user_row['user_type'] == USER_FOUNDER)
 346      {
 347          set_config('newest_user_id', $user_id, true);
 348          set_config('newest_username', $user_row['username'], true);
 349          set_config_count('num_users', 1, true);
 350  
 351          $sql = 'SELECT group_colour
 352              FROM ' . GROUPS_TABLE . '
 353              WHERE group_id = ' . (int) $user_row['group_id'];
 354          $result = $db->sql_query_limit($sql, 1);
 355          $row = $db->sql_fetchrow($result);
 356          $db->sql_freeresult($result);
 357  
 358          set_config('newest_user_colour', $row['group_colour'], true);
 359      }
 360  
 361      // Use default notifications settings if notifications_data is not set
 362      if ($notifications_data === null)
 363      {
 364          $notifications_data = array(
 365              array(
 366                  'item_type'    => 'notification.type.post',
 367                  'method'    => 'notification.method.email',
 368              ),
 369              array(
 370                  'item_type'    => 'notification.type.topic',
 371                  'method'    => 'notification.method.email',
 372              ),
 373          );
 374      }
 375  
 376      // Subscribe user to notifications if necessary
 377      if (!empty($notifications_data))
 378      {
 379          $phpbb_notifications = $phpbb_container->get('notification_manager');
 380          foreach ($notifications_data as $subscription)
 381          {
 382              $phpbb_notifications->add_subscription($subscription['item_type'], 0, $subscription['method'], $user_id);
 383          }
 384      }
 385  
 386      /**
 387      * Event that returns user id, user detals and user CPF of newly registared user
 388      *
 389      * @event core.user_add_after
 390      * @var int        user_id            User id of newly registared user
 391      * @var array    user_row        Array of user details submited to user_add
 392      * @var array    cp_data            Array of Custom profile fields submited to user_add
 393      * @since 3.1.0-b5
 394      */
 395      $vars = array('user_id', 'user_row', 'cp_data');
 396      extract($phpbb_dispatcher->trigger_event('core.user_add_after', compact($vars)));
 397  
 398      return $user_id;
 399  }
 400  
 401  /**
 402   * Remove User
 403   *
 404   * @param string    $mode        Either 'retain' or 'remove'
 405   * @param mixed        $user_ids    Either an array of integers or an integer
 406   * @param bool        $retain_username
 407   * @return bool
 408   */
 409  function user_delete($mode, $user_ids, $retain_username = true)
 410  {
 411      global $cache, $config, $db, $user, $phpbb_dispatcher, $phpbb_container;
 412      global $phpbb_root_path, $phpEx;
 413  
 414      $db->sql_transaction('begin');
 415  
 416      $user_rows = array();
 417      if (!is_array($user_ids))
 418      {
 419          $user_ids = array($user_ids);
 420      }
 421  
 422      $user_id_sql = $db->sql_in_set('user_id', $user_ids);
 423  
 424      $sql = 'SELECT *
 425          FROM ' . USERS_TABLE . '
 426          WHERE ' . $user_id_sql;
 427      $result = $db->sql_query($sql);
 428      while ($row = $db->sql_fetchrow($result))
 429      {
 430          $user_rows[(int) $row['user_id']] = $row;
 431      }
 432      $db->sql_freeresult($result);
 433  
 434      if (empty($user_rows))
 435      {
 436          return false;
 437      }
 438  
 439      /**
 440      * Event before a user is deleted
 441      *
 442      * @event core.delete_user_before
 443      * @var    string    mode        Mode of deletion (retain/delete posts)
 444      * @var    array    user_ids    IDs of the deleted user
 445      * @var    mixed    retain_username    True if username should be retained
 446      *                or false if not
 447      * @since 3.1.0-a1
 448      */
 449      $vars = array('mode', 'user_ids', 'retain_username');
 450      extract($phpbb_dispatcher->trigger_event('core.delete_user_before', compact($vars)));
 451  
 452      // Before we begin, we will remove the reports the user issued.
 453      $sql = 'SELECT r.post_id, p.topic_id
 454          FROM ' . REPORTS_TABLE . ' r, ' . POSTS_TABLE . ' p
 455          WHERE ' . $db->sql_in_set('r.user_id', $user_ids) . '
 456              AND p.post_id = r.post_id';
 457      $result = $db->sql_query($sql);
 458  
 459      $report_posts = $report_topics = array();
 460      while ($row = $db->sql_fetchrow($result))
 461      {
 462          $report_posts[] = $row['post_id'];
 463          $report_topics[] = $row['topic_id'];
 464      }
 465      $db->sql_freeresult($result);
 466  
 467      if (sizeof($report_posts))
 468      {
 469          $report_posts = array_unique($report_posts);
 470          $report_topics = array_unique($report_topics);
 471  
 472          // Get a list of topics that still contain reported posts
 473          $sql = 'SELECT DISTINCT topic_id
 474              FROM ' . POSTS_TABLE . '
 475              WHERE ' . $db->sql_in_set('topic_id', $report_topics) . '
 476                  AND post_reported = 1
 477                  AND ' . $db->sql_in_set('post_id', $report_posts, true);
 478          $result = $db->sql_query($sql);
 479  
 480          $keep_report_topics = array();
 481          while ($row = $db->sql_fetchrow($result))
 482          {
 483              $keep_report_topics[] = $row['topic_id'];
 484          }
 485          $db->sql_freeresult($result);
 486  
 487          if (sizeof($keep_report_topics))
 488          {
 489              $report_topics = array_diff($report_topics, $keep_report_topics);
 490          }
 491          unset($keep_report_topics);
 492  
 493          // Now set the flags back
 494          $sql = 'UPDATE ' . POSTS_TABLE . '
 495              SET post_reported = 0
 496              WHERE ' . $db->sql_in_set('post_id', $report_posts);
 497          $db->sql_query($sql);
 498  
 499          if (sizeof($report_topics))
 500          {
 501              $sql = 'UPDATE ' . TOPICS_TABLE . '
 502                  SET topic_reported = 0
 503                  WHERE ' . $db->sql_in_set('topic_id', $report_topics);
 504              $db->sql_query($sql);
 505          }
 506      }
 507  
 508      // Remove reports
 509      $db->sql_query('DELETE FROM ' . REPORTS_TABLE . ' WHERE ' . $user_id_sql);
 510  
 511      $num_users_delta = 0;
 512  
 513      // Get auth provider collection in case accounts might need to be unlinked
 514      $provider_collection = $phpbb_container->get('auth.provider_collection');
 515  
 516      // Some things need to be done in the loop (if the query changes based
 517      // on which user is currently being deleted)
 518      $added_guest_posts = 0;
 519      foreach ($user_rows as $user_id => $user_row)
 520      {
 521          if ($user_row['user_avatar'] && $user_row['user_avatar_type'] == 'avatar.driver.upload')
 522          {
 523              avatar_delete('user', $user_row);
 524          }
 525  
 526          // Unlink accounts
 527          foreach ($provider_collection as $provider_name => $auth_provider)
 528          {
 529              $provider_data = $auth_provider->get_auth_link_data($user_id);
 530  
 531              if ($provider_data !== null)
 532              {
 533                  $link_data = array(
 534                      'user_id' => $user_id,
 535                      'link_method' => 'user_delete',
 536                  );
 537  
 538                  // BLOCK_VARS might contain hidden fields necessary for unlinking accounts
 539                  if (isset($provider_data['BLOCK_VARS']) && is_array($provider_data['BLOCK_VARS']))
 540                  {
 541                      foreach ($provider_data['BLOCK_VARS'] as $provider_service)
 542                      {
 543                          if (!array_key_exists('HIDDEN_FIELDS', $provider_service))
 544                          {
 545                              $provider_service['HIDDEN_FIELDS'] = array();
 546                          }
 547  
 548                          $auth_provider->unlink_account(array_merge($link_data, $provider_service['HIDDEN_FIELDS']));
 549                      }
 550                  }
 551                  else
 552                  {
 553                      $auth_provider->unlink_account($link_data);
 554                  }
 555              }
 556          }
 557  
 558          // Decrement number of users if this user is active
 559          if ($user_row['user_type'] != USER_INACTIVE && $user_row['user_type'] != USER_IGNORE)
 560          {
 561              --$num_users_delta;
 562          }
 563  
 564          switch ($mode)
 565          {
 566              case 'retain':
 567                  if ($retain_username === false)
 568                  {
 569                      $post_username = $user->lang['GUEST'];
 570                  }
 571                  else
 572                  {
 573                      $post_username = $user_row['username'];
 574                  }
 575  
 576                  // If the user is inactive and newly registered
 577                  // we assume no posts from the user, and save
 578                  // the queries
 579                  if ($user_row['user_type'] != USER_INACTIVE || $user_row['user_inactive_reason'] != INACTIVE_REGISTER || $user_row['user_posts'])
 580                  {
 581                      // When we delete these users and retain the posts, we must assign all the data to the guest user
 582                      $sql = 'UPDATE ' . FORUMS_TABLE . '
 583                          SET forum_last_poster_id = ' . ANONYMOUS . ", forum_last_poster_name = '" . $db->sql_escape($post_username) . "', forum_last_poster_colour = ''
 584                          WHERE forum_last_poster_id = $user_id";
 585                      $db->sql_query($sql);
 586  
 587                      $sql = 'UPDATE ' . POSTS_TABLE . '
 588                          SET poster_id = ' . ANONYMOUS . ", post_username = '" . $db->sql_escape($post_username) . "'
 589                          WHERE poster_id = $user_id";
 590                      $db->sql_query($sql);
 591  
 592                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 593                          SET topic_poster = ' . ANONYMOUS . ", topic_first_poster_name = '" . $db->sql_escape($post_username) . "', topic_first_poster_colour = ''
 594                          WHERE topic_poster = $user_id";
 595                      $db->sql_query($sql);
 596  
 597                      $sql = 'UPDATE ' . TOPICS_TABLE . '
 598                          SET topic_last_poster_id = ' . ANONYMOUS . ", topic_last_poster_name = '" . $db->sql_escape($post_username) . "', topic_last_poster_colour = ''
 599                          WHERE topic_last_poster_id = $user_id";
 600                      $db->sql_query($sql);
 601  
 602                      // Since we change every post by this author, we need to count this amount towards the anonymous user
 603  
 604                      if ($user_row['user_posts'])
 605                      {
 606                          $added_guest_posts += $user_row['user_posts'];
 607                      }
 608                  }
 609              break;
 610  
 611              case 'remove':
 612                  // there is nothing variant specific to deleting posts
 613              break;
 614          }
 615      }
 616  
 617      if ($num_users_delta != 0)
 618      {
 619          set_config_count('num_users', $num_users_delta, true);
 620      }
 621  
 622      // Now do the invariant tasks
 623      // all queries performed in one call of this function are in a single transaction
 624      // so this is kosher
 625      if ($mode == 'retain')
 626      {
 627          // Assign more data to the Anonymous user
 628          $sql = 'UPDATE ' . ATTACHMENTS_TABLE . '
 629              SET poster_id = ' . ANONYMOUS . '
 630              WHERE ' . $db->sql_in_set('poster_id', $user_ids);
 631          $db->sql_query($sql);
 632  
 633          $sql = 'UPDATE ' . USERS_TABLE . '
 634              SET user_posts = user_posts + ' . $added_guest_posts . '
 635              WHERE user_id = ' . ANONYMOUS;
 636          $db->sql_query($sql);
 637      }
 638      else if ($mode == 'remove')
 639      {
 640          if (!function_exists('delete_posts'))
 641          {
 642              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
 643          }
 644  
 645          // Delete posts, attachments, etc.
 646          // delete_posts can handle any number of IDs in its second argument
 647          delete_posts('poster_id', $user_ids);
 648      }
 649  
 650      $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);
 651  
 652      // Delete the miscellaneous (non-post) data for the user
 653      foreach ($table_ary as $table)
 654      {
 655          $sql = "DELETE FROM $table
 656              WHERE " . $user_id_sql;
 657          $db->sql_query($sql);
 658      }
 659  
 660      $cache->destroy('sql', MODERATOR_CACHE_TABLE);
 661  
 662      // Change user_id to anonymous for posts edited by this user
 663      $sql = 'UPDATE ' . POSTS_TABLE . '
 664          SET post_edit_user = ' . ANONYMOUS . '
 665          WHERE ' . $db->sql_in_set('post_edit_user', $user_ids);
 666      $db->sql_query($sql);
 667  
 668      // Change user_id to anonymous for pms edited by this user
 669      $sql = 'UPDATE ' . PRIVMSGS_TABLE . '
 670          SET message_edit_user = ' . ANONYMOUS . '
 671          WHERE ' . $db->sql_in_set('message_edit_user', $user_ids);
 672      $db->sql_query($sql);
 673  
 674      // Change user_id to anonymous for posts deleted by this user
 675      $sql = 'UPDATE ' . POSTS_TABLE . '
 676          SET post_delete_user = ' . ANONYMOUS . '
 677          WHERE ' . $db->sql_in_set('post_delete_user', $user_ids);
 678      $db->sql_query($sql);
 679  
 680      // Change user_id to anonymous for topics deleted by this user
 681      $sql = 'UPDATE ' . TOPICS_TABLE . '
 682          SET topic_delete_user = ' . ANONYMOUS . '
 683          WHERE ' . $db->sql_in_set('topic_delete_user', $user_ids);
 684      $db->sql_query($sql);
 685  
 686      // Delete user log entries about this user
 687      $sql = 'DELETE FROM ' . LOG_TABLE . '
 688          WHERE ' . $db->sql_in_set('reportee_id', $user_ids);
 689      $db->sql_query($sql);
 690  
 691      // Change user_id to anonymous for this users triggered events
 692      $sql = 'UPDATE ' . LOG_TABLE . '
 693          SET user_id = ' . ANONYMOUS . '
 694          WHERE ' . $user_id_sql;
 695      $db->sql_query($sql);
 696  
 697      // Delete the user_id from the zebra table
 698      $sql = 'DELETE FROM ' . ZEBRA_TABLE . '
 699          WHERE ' . $user_id_sql . '
 700              OR ' . $db->sql_in_set('zebra_id', $user_ids);
 701      $db->sql_query($sql);
 702  
 703      // Delete the user_id from the banlist
 704      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 705          WHERE ' . $db->sql_in_set('ban_userid', $user_ids);
 706      $db->sql_query($sql);
 707  
 708      // Delete the user_id from the session table
 709      $sql = 'DELETE FROM ' . SESSIONS_TABLE . '
 710          WHERE ' . $db->sql_in_set('session_user_id', $user_ids);
 711      $db->sql_query($sql);
 712  
 713      // Clean the private messages tables from the user
 714      if (!function_exists('phpbb_delete_user_pms'))
 715      {
 716          include($phpbb_root_path . 'includes/functions_privmsgs.' . $phpEx);
 717      }
 718      phpbb_delete_users_pms($user_ids);
 719  
 720      $phpbb_notifications = $phpbb_container->get('notification_manager');
 721      $phpbb_notifications->delete_notifications('notification.type.admin_activate_user', $user_ids);
 722  
 723      $db->sql_transaction('commit');
 724  
 725      /**
 726      * Event after a user is deleted
 727      *
 728      * @event core.delete_user_after
 729      * @var    string    mode        Mode of deletion (retain/delete posts)
 730      * @var    array    user_ids    IDs of the deleted user
 731      * @var    mixed    retain_username    True if username should be retained
 732      *                or false if not
 733      * @since 3.1.0-a1
 734      */
 735      $vars = array('mode', 'user_ids', 'retain_username');
 736      extract($phpbb_dispatcher->trigger_event('core.delete_user_after', compact($vars)));
 737  
 738      // Reset newest user info if appropriate
 739      if (in_array($config['newest_user_id'], $user_ids))
 740      {
 741          update_last_username();
 742      }
 743  
 744      return false;
 745  }
 746  
 747  /**
 748  * Flips user_type from active to inactive and vice versa, handles group membership updates
 749  *
 750  * @param string $mode can be flip for flipping from active/inactive, activate or deactivate
 751  */
 752  function user_active_flip($mode, $user_id_ary, $reason = INACTIVE_MANUAL)
 753  {
 754      global $config, $db, $user, $auth, $phpbb_dispatcher;
 755  
 756      $deactivated = $activated = 0;
 757      $sql_statements = array();
 758  
 759      if (!is_array($user_id_ary))
 760      {
 761          $user_id_ary = array($user_id_ary);
 762      }
 763  
 764      if (!sizeof($user_id_ary))
 765      {
 766          return;
 767      }
 768  
 769      $sql = 'SELECT user_id, group_id, user_type, user_inactive_reason
 770          FROM ' . USERS_TABLE . '
 771          WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
 772      $result = $db->sql_query($sql);
 773  
 774      while ($row = $db->sql_fetchrow($result))
 775      {
 776          $sql_ary = array();
 777  
 778          if ($row['user_type'] == USER_IGNORE || $row['user_type'] == USER_FOUNDER ||
 779              ($mode == 'activate' && $row['user_type'] != USER_INACTIVE) ||
 780              ($mode == 'deactivate' && $row['user_type'] == USER_INACTIVE))
 781          {
 782              continue;
 783          }
 784  
 785          if ($row['user_type'] == USER_INACTIVE)
 786          {
 787              $activated++;
 788          }
 789          else
 790          {
 791              $deactivated++;
 792  
 793              // Remove the users session key...
 794              $user->reset_login_keys($row['user_id']);
 795          }
 796  
 797          $sql_ary += array(
 798              'user_type'                => ($row['user_type'] == USER_NORMAL) ? USER_INACTIVE : USER_NORMAL,
 799              'user_inactive_time'    => ($row['user_type'] == USER_NORMAL) ? time() : 0,
 800              'user_inactive_reason'    => ($row['user_type'] == USER_NORMAL) ? $reason : 0,
 801          );
 802  
 803          $sql_statements[$row['user_id']] = $sql_ary;
 804      }
 805      $db->sql_freeresult($result);
 806  
 807      /**
 808      * Check or modify activated/deactivated users data before submitting it to the database
 809      *
 810      * @event core.user_active_flip_before
 811      * @var    string    mode            User type changing mode, can be: flip|activate|deactivate
 812      * @var    int        reason            Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND
 813      * @var    int        activated        The number of users to be activated
 814      * @var    int        deactivated        The number of users to be deactivated
 815      * @var    array    user_id_ary        Array with user ids to change user type
 816      * @var    array    sql_statements    Array with users data to submit to the database, keys: user ids, values: arrays with user data
 817      * @since 3.1.4-RC1
 818      */
 819      $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements');
 820      extract($phpbb_dispatcher->trigger_event('core.user_active_flip_before', compact($vars)));
 821  
 822      if (sizeof($sql_statements))
 823      {
 824          foreach ($sql_statements as $user_id => $sql_ary)
 825          {
 826              $sql = 'UPDATE ' . USERS_TABLE . '
 827                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
 828                  WHERE user_id = ' . $user_id;
 829              $db->sql_query($sql);
 830          }
 831  
 832          $auth->acl_clear_prefetch(array_keys($sql_statements));
 833      }
 834  
 835      /**
 836      * Perform additional actions after the users have been activated/deactivated
 837      *
 838      * @event core.user_active_flip_after
 839      * @var    string    mode            User type changing mode, can be: flip|activate|deactivate
 840      * @var    int        reason            Reason for changing user type, can be: INACTIVE_REGISTER|INACTIVE_PROFILE|INACTIVE_MANUAL|INACTIVE_REMIND
 841      * @var    int        activated        The number of users to be activated
 842      * @var    int        deactivated        The number of users to be deactivated
 843      * @var    array    user_id_ary        Array with user ids to change user type
 844      * @var    array    sql_statements    Array with users data to submit to the database, keys: user ids, values: arrays with user data
 845      * @since 3.1.4-RC1
 846      */
 847      $vars = array('mode', 'reason', 'activated', 'deactivated', 'user_id_ary', 'sql_statements');
 848      extract($phpbb_dispatcher->trigger_event('core.user_active_flip_after', compact($vars)));
 849  
 850      if ($deactivated)
 851      {
 852          set_config_count('num_users', $deactivated * (-1), true);
 853      }
 854  
 855      if ($activated)
 856      {
 857          set_config_count('num_users', $activated, true);
 858      }
 859  
 860      // Update latest username
 861      update_last_username();
 862  }
 863  
 864  /**
 865  * Add a ban or ban exclusion to the banlist. Bans either a user, an IP or an email address
 866  *
 867  * @param string $mode Type of ban. One of the following: user, ip, email
 868  * @param mixed $ban Banned entity. Either string or array with usernames, ips or email addresses
 869  * @param int $ban_len Ban length in minutes
 870  * @param string $ban_len_other Ban length as a date (YYYY-MM-DD)
 871  * @param boolean $ban_exclude Exclude these entities from banning?
 872  * @param string $ban_reason String describing the reason for this ban
 873  * @return boolean
 874  */
 875  function user_ban($mode, $ban, $ban_len, $ban_len_other, $ban_exclude, $ban_reason, $ban_give_reason = '')
 876  {
 877      global $db, $user, $auth, $cache;
 878  
 879      // Delete stale bans
 880      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
 881          WHERE ban_end < ' . time() . '
 882              AND ban_end <> 0';
 883      $db->sql_query($sql);
 884  
 885      $ban_list = (!is_array($ban)) ? array_unique(explode("\n", $ban)) : $ban;
 886      $ban_list_log = implode(', ', $ban_list);
 887  
 888      $current_time = time();
 889  
 890      // Set $ban_end to the unix time when the ban should end. 0 is a permanent ban.
 891      if ($ban_len)
 892      {
 893          if ($ban_len != -1 || !$ban_len_other)
 894          {
 895              $ban_end = max($current_time, $current_time + ($ban_len) * 60);
 896          }
 897          else
 898          {
 899              $ban_other = explode('-', $ban_len_other);
 900              if (sizeof($ban_other) == 3 && ((int) $ban_other[0] < 9999) &&
 901                  (strlen($ban_other[0]) == 4) && (strlen($ban_other[1]) == 2) && (strlen($ban_other[2]) == 2))
 902              {
 903                  $ban_end = max($current_time, $user->create_datetime()
 904                      ->setDate((int) $ban_other[0], (int) $ban_other[1], (int) $ban_other[2])
 905                      ->setTime(0, 0, 0)
 906                      ->getTimestamp() + $user->timezone->getOffset(new DateTime('UTC')));
 907              }
 908              else
 909              {
 910                  trigger_error('LENGTH_BAN_INVALID', E_USER_WARNING);
 911              }
 912          }
 913      }
 914      else
 915      {
 916          $ban_end = 0;
 917      }
 918  
 919      $founder = $founder_names = array();
 920  
 921      if (!$ban_exclude)
 922      {
 923          // Create a list of founder...
 924          $sql = 'SELECT user_id, user_email, username_clean
 925              FROM ' . USERS_TABLE . '
 926              WHERE user_type = ' . USER_FOUNDER;
 927          $result = $db->sql_query($sql);
 928  
 929          while ($row = $db->sql_fetchrow($result))
 930          {
 931              $founder[$row['user_id']] = $row['user_email'];
 932              $founder_names[$row['user_id']] = $row['username_clean'];
 933          }
 934          $db->sql_freeresult($result);
 935      }
 936  
 937      $banlist_ary = array();
 938  
 939      switch ($mode)
 940      {
 941          case 'user':
 942              $type = 'ban_userid';
 943  
 944              // At the moment we do not support wildcard username banning
 945  
 946              // Select the relevant user_ids.
 947              $sql_usernames = array();
 948  
 949              foreach ($ban_list as $username)
 950              {
 951                  $username = trim($username);
 952                  if ($username != '')
 953                  {
 954                      $clean_name = utf8_clean_string($username);
 955                      if ($clean_name == $user->data['username_clean'])
 956                      {
 957                          trigger_error('CANNOT_BAN_YOURSELF', E_USER_WARNING);
 958                      }
 959                      if (in_array($clean_name, $founder_names))
 960                      {
 961                          trigger_error('CANNOT_BAN_FOUNDER', E_USER_WARNING);
 962                      }
 963                      $sql_usernames[] = $clean_name;
 964                  }
 965              }
 966  
 967              // Make sure we have been given someone to ban
 968              if (!sizeof($sql_usernames))
 969              {
 970                  trigger_error('NO_USER_SPECIFIED', E_USER_WARNING);
 971              }
 972  
 973              $sql = 'SELECT user_id
 974                  FROM ' . USERS_TABLE . '
 975                  WHERE ' . $db->sql_in_set('username_clean', $sql_usernames);
 976  
 977              // Do not allow banning yourself, the guest account, or founders.
 978              $non_bannable = array($user->data['user_id'], ANONYMOUS);
 979              if (sizeof($founder))
 980              {
 981                  $sql .= ' AND ' . $db->sql_in_set('user_id', array_merge(array_keys($founder), $non_bannable), true);
 982              }
 983              else
 984              {
 985                  $sql .= ' AND ' . $db->sql_in_set('user_id', $non_bannable, true);
 986              }
 987  
 988              $result = $db->sql_query($sql);
 989  
 990              if ($row = $db->sql_fetchrow($result))
 991              {
 992                  do
 993                  {
 994                      $banlist_ary[] = (int) $row['user_id'];
 995                  }
 996                  while ($row = $db->sql_fetchrow($result));
 997              }
 998              else
 999              {
1000                  $db->sql_freeresult($result);
1001                  trigger_error('NO_USERS', E_USER_WARNING);
1002              }
1003              $db->sql_freeresult($result);
1004          break;
1005  
1006          case 'ip':
1007              $type = 'ban_ip';
1008  
1009              foreach ($ban_list as $ban_item)
1010              {
1011                  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))
1012                  {
1013                      // This is an IP range
1014                      // Don't ask about all this, just don't ask ... !
1015                      $ip_1_counter = $ip_range_explode[1];
1016                      $ip_1_end = $ip_range_explode[5];
1017  
1018                      while ($ip_1_counter <= $ip_1_end)
1019                      {
1020                          $ip_2_counter = ($ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[2] : 0;
1021                          $ip_2_end = ($ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[6];
1022  
1023                          if ($ip_2_counter == 0 && $ip_2_end == 254)
1024                          {
1025                              $ip_2_counter = 256;
1026                              $ip_2_fragment = 256;
1027  
1028                              $banlist_ary[] = "$ip_1_counter.*";
1029                          }
1030  
1031                          while ($ip_2_counter <= $ip_2_end)
1032                          {
1033                              $ip_3_counter = ($ip_2_counter == $ip_range_explode[2] && $ip_1_counter == $ip_range_explode[1]) ? $ip_range_explode[3] : 0;
1034                              $ip_3_end = ($ip_2_counter < $ip_2_end || $ip_1_counter < $ip_1_end) ? 254 : $ip_range_explode[7];
1035  
1036                              if ($ip_3_counter == 0 && $ip_3_end == 254)
1037                              {
1038                                  $ip_3_counter = 256;
1039                                  $ip_3_fragment = 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                                      $ip_4_fragment = 256;
1053  
1054                                      $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.*";
1055                                  }
1056  
1057                                  while ($ip_4_counter <= $ip_4_end)
1058                                  {
1059                                      $banlist_ary[] = "$ip_1_counter.$ip_2_counter.$ip_3_counter.$ip_4_counter";
1060                                      $ip_4_counter++;
1061                                  }
1062                                  $ip_3_counter++;
1063                              }
1064                              $ip_2_counter++;
1065                          }
1066                          $ip_1_counter++;
1067                      }
1068                  }
1069                  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)))
1070                  {
1071                      // Normal IP address
1072                      $banlist_ary[] = trim($ban_item);
1073                  }
1074                  else if (preg_match('#^\*$#', trim($ban_item)))
1075                  {
1076                      // Ban all IPs
1077                      $banlist_ary[] = '*';
1078                  }
1079                  else if (preg_match('#^([\w\-_]\.?){2,}$#is', trim($ban_item)))
1080                  {
1081                      // hostname
1082                      $ip_ary = gethostbynamel(trim($ban_item));
1083  
1084                      if (!empty($ip_ary))
1085                      {
1086                          foreach ($ip_ary as $ip)
1087                          {
1088                              if ($ip)
1089                              {
1090                                  if (strlen($ip) > 40)
1091                                  {
1092                                      continue;
1093                                  }
1094  
1095                                  $banlist_ary[] = $ip;
1096                              }
1097                          }
1098                      }
1099                  }
1100  
1101                  if (empty($banlist_ary))
1102                  {
1103                      trigger_error('NO_IPS_DEFINED', E_USER_WARNING);
1104                  }
1105              }
1106          break;
1107  
1108          case 'email':
1109              $type = 'ban_email';
1110  
1111              foreach ($ban_list as $ban_item)
1112              {
1113                  $ban_item = trim($ban_item);
1114  
1115                  if (preg_match('#^.*?@*|(([a-z0-9\-]+\.)+([a-z]{2,3}))$#i', $ban_item))
1116                  {
1117                      if (strlen($ban_item) > 100)
1118                      {
1119                          continue;
1120                      }
1121  
1122                      if (!sizeof($founder) || !in_array($ban_item, $founder))
1123                      {
1124                          $banlist_ary[] = $ban_item;
1125                      }
1126                  }
1127              }
1128  
1129              if (sizeof($ban_list) == 0)
1130              {
1131                  trigger_error('NO_EMAILS_DEFINED', E_USER_WARNING);
1132              }
1133          break;
1134  
1135          default:
1136              trigger_error('NO_MODE', E_USER_WARNING);
1137          break;
1138      }
1139  
1140      // Fetch currently set bans of the specified type and exclude state. Prevent duplicate bans.
1141      $sql_where = ($type == 'ban_userid') ? 'ban_userid <> 0' : "$type <> ''";
1142  
1143      $sql = "SELECT $type
1144          FROM " . BANLIST_TABLE . "
1145          WHERE $sql_where
1146              AND ban_exclude = " . (int) $ban_exclude;
1147      $result = $db->sql_query($sql);
1148  
1149      // Reset $sql_where, because we use it later...
1150      $sql_where = '';
1151  
1152      if ($row = $db->sql_fetchrow($result))
1153      {
1154          $banlist_ary_tmp = array();
1155          do
1156          {
1157              switch ($mode)
1158              {
1159                  case 'user':
1160                      $banlist_ary_tmp[] = $row['ban_userid'];
1161                  break;
1162  
1163                  case 'ip':
1164                      $banlist_ary_tmp[] = $row['ban_ip'];
1165                  break;
1166  
1167                  case 'email':
1168                      $banlist_ary_tmp[] = $row['ban_email'];
1169                  break;
1170              }
1171          }
1172          while ($row = $db->sql_fetchrow($result));
1173  
1174          $banlist_ary_tmp = array_intersect($banlist_ary, $banlist_ary_tmp);
1175  
1176          if (sizeof($banlist_ary_tmp))
1177          {
1178              // One or more entities are already banned/excluded, delete the existing bans, so they can be re-inserted with the given new length
1179              $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1180                  WHERE ' . $db->sql_in_set($type, $banlist_ary_tmp) . '
1181                      AND ban_exclude = ' . (int) $ban_exclude;
1182              $db->sql_query($sql);
1183          }
1184  
1185          unset($banlist_ary_tmp);
1186      }
1187      $db->sql_freeresult($result);
1188  
1189      // We have some entities to ban
1190      if (sizeof($banlist_ary))
1191      {
1192          $sql_ary = array();
1193  
1194          foreach ($banlist_ary as $ban_entry)
1195          {
1196              $sql_ary[] = array(
1197                  $type                => $ban_entry,
1198                  'ban_start'            => (int) $current_time,
1199                  'ban_end'            => (int) $ban_end,
1200                  'ban_exclude'        => (int) $ban_exclude,
1201                  'ban_reason'        => (string) $ban_reason,
1202                  'ban_give_reason'    => (string) $ban_give_reason,
1203              );
1204          }
1205  
1206          $db->sql_multi_insert(BANLIST_TABLE, $sql_ary);
1207  
1208          // If we are banning we want to logout anyone matching the ban
1209          if (!$ban_exclude)
1210          {
1211              switch ($mode)
1212              {
1213                  case 'user':
1214                      $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $banlist_ary);
1215                  break;
1216  
1217                  case 'ip':
1218                      $sql_where = 'WHERE ' . $db->sql_in_set('session_ip', $banlist_ary);
1219                  break;
1220  
1221                  case 'email':
1222                      $banlist_ary_sql = array();
1223  
1224                      foreach ($banlist_ary as $ban_entry)
1225                      {
1226                          $banlist_ary_sql[] = (string) str_replace('*', '%', $ban_entry);
1227                      }
1228  
1229                      $sql = 'SELECT user_id
1230                          FROM ' . USERS_TABLE . '
1231                          WHERE ' . $db->sql_in_set('user_email', $banlist_ary_sql);
1232                      $result = $db->sql_query($sql);
1233  
1234                      $sql_in = array();
1235  
1236                      if ($row = $db->sql_fetchrow($result))
1237                      {
1238                          do
1239                          {
1240                              $sql_in[] = $row['user_id'];
1241                          }
1242                          while ($row = $db->sql_fetchrow($result));
1243  
1244                          $sql_where = 'WHERE ' . $db->sql_in_set('session_user_id', $sql_in);
1245                      }
1246                      $db->sql_freeresult($result);
1247                  break;
1248              }
1249  
1250              if (isset($sql_where) && $sql_where)
1251              {
1252                  $sql = 'DELETE FROM ' . SESSIONS_TABLE . "
1253                      $sql_where";
1254                  $db->sql_query($sql);
1255  
1256                  if ($mode == 'user')
1257                  {
1258                      $sql = 'DELETE FROM ' . SESSIONS_KEYS_TABLE . ' ' . ((in_array('*', $banlist_ary)) ? '' : 'WHERE ' . $db->sql_in_set('user_id', $banlist_ary));
1259                      $db->sql_query($sql);
1260                  }
1261              }
1262          }
1263  
1264          // Update log
1265          $log_entry = ($ban_exclude) ? 'LOG_BAN_EXCLUDE_' : 'LOG_BAN_';
1266  
1267          // Add to admin log, moderator log and user notes
1268          add_log('admin', $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
1269          add_log('mod', 0, 0, $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
1270          if ($mode == 'user')
1271          {
1272              foreach ($banlist_ary as $user_id)
1273              {
1274                  add_log('user', $user_id, $log_entry . strtoupper($mode), $ban_reason, $ban_list_log);
1275              }
1276          }
1277  
1278          $cache->destroy('sql', BANLIST_TABLE);
1279  
1280          return true;
1281      }
1282  
1283      // There was nothing to ban/exclude. But destroying the cache because of the removal of stale bans.
1284      $cache->destroy('sql', BANLIST_TABLE);
1285  
1286      return false;
1287  }
1288  
1289  /**
1290  * Unban User
1291  */
1292  function user_unban($mode, $ban)
1293  {
1294      global $db, $user, $auth, $cache;
1295  
1296      // Delete stale bans
1297      $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1298          WHERE ban_end < ' . time() . '
1299              AND ban_end <> 0';
1300      $db->sql_query($sql);
1301  
1302      if (!is_array($ban))
1303      {
1304          $ban = array($ban);
1305      }
1306  
1307      $unban_sql = array_map('intval', $ban);
1308  
1309      if (sizeof($unban_sql))
1310      {
1311          // Grab details of bans for logging information later
1312          switch ($mode)
1313          {
1314              case 'user':
1315                  $sql = 'SELECT u.username AS unban_info, u.user_id
1316                      FROM ' . USERS_TABLE . ' u, ' . BANLIST_TABLE . ' b
1317                      WHERE ' . $db->sql_in_set('b.ban_id', $unban_sql) . '
1318                          AND u.user_id = b.ban_userid';
1319              break;
1320  
1321              case 'email':
1322                  $sql = 'SELECT ban_email AS unban_info
1323                      FROM ' . BANLIST_TABLE . '
1324                      WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1325              break;
1326  
1327              case 'ip':
1328                  $sql = 'SELECT ban_ip AS unban_info
1329                      FROM ' . BANLIST_TABLE . '
1330                      WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1331              break;
1332          }
1333          $result = $db->sql_query($sql);
1334  
1335          $l_unban_list = '';
1336          $user_ids_ary = array();
1337          while ($row = $db->sql_fetchrow($result))
1338          {
1339              $l_unban_list .= (($l_unban_list != '') ? ', ' : '') . $row['unban_info'];
1340              if ($mode == 'user')
1341              {
1342                  $user_ids_ary[] = $row['user_id'];
1343              }
1344          }
1345          $db->sql_freeresult($result);
1346  
1347          $sql = 'DELETE FROM ' . BANLIST_TABLE . '
1348              WHERE ' . $db->sql_in_set('ban_id', $unban_sql);
1349          $db->sql_query($sql);
1350  
1351          // Add to moderator log, admin log and user notes
1352          add_log('admin', 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
1353          add_log('mod', 0, 0, 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
1354          if ($mode == 'user')
1355          {
1356              foreach ($user_ids_ary as $user_id)
1357              {
1358                  add_log('user', $user_id, 'LOG_UNBAN_' . strtoupper($mode), $l_unban_list);
1359              }
1360          }
1361      }
1362  
1363      $cache->destroy('sql', BANLIST_TABLE);
1364  
1365      return false;
1366  }
1367  
1368  /**
1369  * Internet Protocol Address Whois
1370  * RFC3912: WHOIS Protocol Specification
1371  *
1372  * @param string $ip        Ip address, either IPv4 or IPv6.
1373  *
1374  * @return string        Empty string if not a valid ip address.
1375  *                        Otherwise make_clickable()'ed whois result.
1376  */
1377  function user_ipwhois($ip)
1378  {
1379      if (empty($ip))
1380      {
1381          return '';
1382      }
1383  
1384      if (preg_match(get_preg_expression('ipv4'), $ip))
1385      {
1386          // IPv4 address
1387          $whois_host = 'whois.arin.net.';
1388      }
1389      else if (preg_match(get_preg_expression('ipv6'), $ip))
1390      {
1391          // IPv6 address
1392          $whois_host = 'whois.sixxs.net.';
1393      }
1394      else
1395      {
1396          return '';
1397      }
1398  
1399      $ipwhois = '';
1400  
1401      if (($fsk = @fsockopen($whois_host, 43)))
1402      {
1403          // CRLF as per RFC3912
1404          fputs($fsk, "$ip\r\n");
1405          while (!feof($fsk))
1406          {
1407              $ipwhois .= fgets($fsk, 1024);
1408          }
1409          @fclose($fsk);
1410      }
1411  
1412      $match = array();
1413  
1414      // Test for referrals from $whois_host to other whois databases, roll on rwhois
1415      if (preg_match('#ReferralServer:[\x20]*whois://(.+)#im', $ipwhois, $match))
1416      {
1417          if (strpos($match[1], ':') !== false)
1418          {
1419              $pos    = strrpos($match[1], ':');
1420              $server    = substr($match[1], 0, $pos);
1421              $port    = (int) substr($match[1], $pos + 1);
1422              unset($pos);
1423          }
1424          else
1425          {
1426              $server    = $match[1];
1427              $port    = 43;
1428          }
1429  
1430          $buffer = '';
1431  
1432          if (($fsk = @fsockopen($server, $port)))
1433          {
1434              fputs($fsk, "$ip\r\n");
1435              while (!feof($fsk))
1436              {
1437                  $buffer .= fgets($fsk, 1024);
1438              }
1439              @fclose($fsk);
1440          }
1441  
1442          // Use the result from $whois_host if we don't get any result here
1443          $ipwhois = (empty($buffer)) ? $ipwhois : $buffer;
1444      }
1445  
1446      $ipwhois = htmlspecialchars($ipwhois);
1447  
1448      // Magic URL ;)
1449      return trim(make_clickable($ipwhois, false, ''));
1450  }
1451  
1452  /**
1453  * Data validation ... used primarily but not exclusively by ucp modules
1454  *
1455  * "Master" function for validating a range of data types
1456  */
1457  function validate_data($data, $val_ary)
1458  {
1459      global $user;
1460  
1461      $error = array();
1462  
1463      foreach ($val_ary as $var => $val_seq)
1464      {
1465          if (!is_array($val_seq[0]))
1466          {
1467              $val_seq = array($val_seq);
1468          }
1469  
1470          foreach ($val_seq as $validate)
1471          {
1472              $function = array_shift($validate);
1473              array_unshift($validate, $data[$var]);
1474  
1475              if (is_array($function))
1476              {
1477                  $result = call_user_func_array(array($function[0], 'validate_' . $function[1]), $validate);
1478              }
1479              else
1480              {
1481                  $function_prefix = (function_exists('phpbb_validate_' . $function)) ? 'phpbb_validate_' : 'validate_';
1482                  $result = call_user_func_array($function_prefix . $function, $validate);
1483              }
1484  
1485              if ($result)
1486              {
1487                  // Since errors are checked later for their language file existence, we need to make sure custom errors are not adjusted.
1488                  $error[] = (empty($user->lang[$result . '_' . strtoupper($var)])) ? $result : $result . '_' . strtoupper($var);
1489              }
1490          }
1491      }
1492  
1493      return $error;
1494  }
1495  
1496  /**
1497  * Validate String
1498  *
1499  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1500  */
1501  function validate_string($string, $optional = false, $min = 0, $max = 0)
1502  {
1503      if (empty($string) && $optional)
1504      {
1505          return false;
1506      }
1507  
1508      if ($min && utf8_strlen(htmlspecialchars_decode($string)) < $min)
1509      {
1510          return 'TOO_SHORT';
1511      }
1512      else if ($max && utf8_strlen(htmlspecialchars_decode($string)) > $max)
1513      {
1514          return 'TOO_LONG';
1515      }
1516  
1517      return false;
1518  }
1519  
1520  /**
1521  * Validate Number
1522  *
1523  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1524  */
1525  function validate_num($num, $optional = false, $min = 0, $max = 1E99)
1526  {
1527      if (empty($num) && $optional)
1528      {
1529          return false;
1530      }
1531  
1532      if ($num < $min)
1533      {
1534          return 'TOO_SMALL';
1535      }
1536      else if ($num > $max)
1537      {
1538          return 'TOO_LARGE';
1539      }
1540  
1541      return false;
1542  }
1543  
1544  /**
1545  * Validate Date
1546  * @param String $string a date in the dd-mm-yyyy format
1547  * @return    boolean
1548  */
1549  function validate_date($date_string, $optional = false)
1550  {
1551      $date = explode('-', $date_string);
1552      if ((empty($date) || sizeof($date) != 3) && $optional)
1553      {
1554          return false;
1555      }
1556      else if ($optional)
1557      {
1558          for ($field = 0; $field <= 1; $field++)
1559          {
1560              $date[$field] = (int) $date[$field];
1561              if (empty($date[$field]))
1562              {
1563                  $date[$field] = 1;
1564              }
1565          }
1566          $date[2] = (int) $date[2];
1567          // assume an arbitrary leap year
1568          if (empty($date[2]))
1569          {
1570              $date[2] = 1980;
1571          }
1572      }
1573  
1574      if (sizeof($date) != 3 || !checkdate($date[1], $date[0], $date[2]))
1575      {
1576          return 'INVALID';
1577      }
1578  
1579      return false;
1580  }
1581  
1582  
1583  /**
1584  * Validate Match
1585  *
1586  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1587  */
1588  function validate_match($string, $optional = false, $match = '')
1589  {
1590      if (empty($string) && $optional)
1591      {
1592          return false;
1593      }
1594  
1595      if (empty($match))
1596      {
1597          return false;
1598      }
1599  
1600      if (!preg_match($match, $string))
1601      {
1602          return 'WRONG_DATA';
1603      }
1604  
1605      return false;
1606  }
1607  
1608  /**
1609  * Validate Language Pack ISO Name
1610  *
1611  * Tests whether a language name is valid and installed
1612  *
1613  * @param string $lang_iso    The language string to test
1614  *
1615  * @return bool|string        Either false if validation succeeded or
1616  *                            a string which will be used as the error message
1617  *                            (with the variable name appended)
1618  */
1619  function validate_language_iso_name($lang_iso)
1620  {
1621      global $db;
1622  
1623      $sql = 'SELECT lang_id
1624          FROM ' . LANG_TABLE . "
1625          WHERE lang_iso = '" . $db->sql_escape($lang_iso) . "'";
1626      $result = $db->sql_query($sql);
1627      $lang_id = (int) $db->sql_fetchfield('lang_id');
1628      $db->sql_freeresult($result);
1629  
1630      return ($lang_id) ? false : 'WRONG_DATA';
1631  }
1632  
1633  /**
1634  * Validate Timezone Name
1635  *
1636  * Tests whether a timezone name is valid
1637  *
1638  * @param string $timezone    The timezone string to test
1639  *
1640  * @return bool|string        Either false if validation succeeded or
1641  *                            a string which will be used as the error message
1642  *                            (with the variable name appended)
1643  */
1644  function phpbb_validate_timezone($timezone)
1645  {
1646      return (in_array($timezone, phpbb_get_timezone_identifiers($timezone))) ? false : 'TIMEZONE_INVALID';
1647  }
1648  
1649  /**
1650  * Check to see if the username has been taken, or if it is disallowed.
1651  * Also checks if it includes the " character, which we don't allow in usernames.
1652  * Used for registering, changing names, and posting anonymously with a username
1653  *
1654  * @param string $username The username to check
1655  * @param string $allowed_username An allowed username, default being $user->data['username']
1656  *
1657  * @return    mixed    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1658  */
1659  function validate_username($username, $allowed_username = false)
1660  {
1661      global $config, $db, $user, $cache;
1662  
1663      $clean_username = utf8_clean_string($username);
1664      $allowed_username = ($allowed_username === false) ? $user->data['username_clean'] : utf8_clean_string($allowed_username);
1665  
1666      if ($allowed_username == $clean_username)
1667      {
1668          return false;
1669      }
1670  
1671      // ... fast checks first.
1672      if (strpos($username, '&quot;') !== false || strpos($username, '"') !== false || empty($clean_username))
1673      {
1674          return 'INVALID_CHARS';
1675      }
1676  
1677      $mbstring = $pcre = false;
1678  
1679      // generic UTF-8 character types supported?
1680      if (phpbb_pcre_utf8_support())
1681      {
1682          $pcre = true;
1683      }
1684      else if (function_exists('mb_ereg_match'))
1685      {
1686          mb_regex_encoding('UTF-8');
1687          $mbstring = true;
1688      }
1689  
1690      switch ($config['allow_name_chars'])
1691      {
1692          case 'USERNAME_CHARS_ANY':
1693              $pcre = true;
1694              $regex = '.+';
1695          break;
1696  
1697          case 'USERNAME_ALPHA_ONLY':
1698              $pcre = true;
1699              $regex = '[A-Za-z0-9]+';
1700          break;
1701  
1702          case 'USERNAME_ALPHA_SPACERS':
1703              $pcre = true;
1704              $regex = '[A-Za-z0-9-[\]_+ ]+';
1705          break;
1706  
1707          case 'USERNAME_LETTER_NUM':
1708              if ($pcre)
1709              {
1710                  $regex = '[\p{Lu}\p{Ll}\p{N}]+';
1711              }
1712              else if ($mbstring)
1713              {
1714                  $regex = '[[:upper:][:lower:][:digit:]]+';
1715              }
1716              else
1717              {
1718                  $pcre = true;
1719                  $regex = '[a-zA-Z0-9]+';
1720              }
1721          break;
1722  
1723          case 'USERNAME_LETTER_NUM_SPACERS':
1724              if ($pcre)
1725              {
1726                  $regex = '[-\]_+ [\p{Lu}\p{Ll}\p{N}]+';
1727              }
1728              else if ($mbstring)
1729              {
1730                  $regex = '[-\]_+ \[[:upper:][:lower:][:digit:]]+';
1731              }
1732              else
1733              {
1734                  $pcre = true;
1735                  $regex = '[-\]_+ [a-zA-Z0-9]+';
1736              }
1737          break;
1738  
1739          case 'USERNAME_ASCII':
1740          default:
1741              $pcre = true;
1742              $regex = '[\x01-\x7F]+';
1743          break;
1744      }
1745  
1746      if ($pcre)
1747      {
1748          if (!preg_match('#^' . $regex . '$#u', $username))
1749          {
1750              return 'INVALID_CHARS';
1751          }
1752      }
1753      else if ($mbstring)
1754      {
1755          mb_ereg_search_init($username, '^' . $regex . '$');
1756          if (!mb_ereg_search())
1757          {
1758              return 'INVALID_CHARS';
1759          }
1760      }
1761  
1762      $sql = 'SELECT username
1763          FROM ' . USERS_TABLE . "
1764          WHERE username_clean = '" . $db->sql_escape($clean_username) . "'";
1765      $result = $db->sql_query($sql);
1766      $row = $db->sql_fetchrow($result);
1767      $db->sql_freeresult($result);
1768  
1769      if ($row)
1770      {
1771          return 'USERNAME_TAKEN';
1772      }
1773  
1774      $sql = 'SELECT group_name
1775          FROM ' . GROUPS_TABLE . "
1776          WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($username)) . "'";
1777      $result = $db->sql_query($sql);
1778      $row = $db->sql_fetchrow($result);
1779      $db->sql_freeresult($result);
1780  
1781      if ($row)
1782      {
1783          return 'USERNAME_TAKEN';
1784      }
1785  
1786      $bad_usernames = $cache->obtain_disallowed_usernames();
1787  
1788      foreach ($bad_usernames as $bad_username)
1789      {
1790          if (preg_match('#^' . $bad_username . '$#', $clean_username))
1791          {
1792              return 'USERNAME_DISALLOWED';
1793          }
1794      }
1795  
1796      return false;
1797  }
1798  
1799  /**
1800  * Check to see if the password meets the complexity settings
1801  *
1802  * @return    boolean|string    Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1803  */
1804  function validate_password($password)
1805  {
1806      global $config;
1807  
1808      if ($password === '' || $config['pass_complex'] === 'PASS_TYPE_ANY')
1809      {
1810          // Password empty or no password complexity required.
1811          return false;
1812      }
1813  
1814      $pcre = $mbstring = false;
1815  
1816      // generic UTF-8 character types supported?
1817      if (phpbb_pcre_utf8_support())
1818      {
1819          $upp = '\p{Lu}';
1820          $low = '\p{Ll}';
1821          $num = '\p{N}';
1822          $sym = '[^\p{Lu}\p{Ll}\p{N}]';
1823          $pcre = true;
1824      }
1825      else if (function_exists('mb_ereg_match'))
1826      {
1827          mb_regex_encoding('UTF-8');
1828          $upp = '[[:upper:]]';
1829          $low = '[[:lower:]]';
1830          $num = '[[:digit:]]';
1831          $sym = '[^[:upper:][:lower:][:digit:]]';
1832          $mbstring = true;
1833      }
1834      else
1835      {
1836          $upp = '[A-Z]';
1837          $low = '[a-z]';
1838          $num = '[0-9]';
1839          $sym = '[^A-Za-z0-9]';
1840          $pcre = true;
1841      }
1842  
1843      $chars = array();
1844  
1845      switch ($config['pass_complex'])
1846      {
1847          // No break statements below ...
1848          // We require strong passwords in case pass_complex is not set or is invalid
1849          default:
1850  
1851          // Require mixed case letters, numbers and symbols
1852          case 'PASS_TYPE_SYMBOL':
1853              $chars[] = $sym;
1854  
1855          // Require mixed case letters and numbers
1856          case 'PASS_TYPE_ALPHA':
1857              $chars[] = $num;
1858  
1859          // Require mixed case letters
1860          case 'PASS_TYPE_CASE':
1861              $chars[] = $low;
1862              $chars[] = $upp;
1863      }
1864  
1865      if ($pcre)
1866      {
1867          foreach ($chars as $char)
1868          {
1869              if (!preg_match('#' . $char . '#u', $password))
1870              {
1871                  return 'INVALID_CHARS';
1872              }
1873          }
1874      }
1875      else if ($mbstring)
1876      {
1877          foreach ($chars as $char)
1878          {
1879              if (mb_ereg($char, $password) === false)
1880              {
1881                  return 'INVALID_CHARS';
1882              }
1883          }
1884      }
1885  
1886      return false;
1887  }
1888  
1889  /**
1890  * Check to see if email address is a valid address and contains a MX record
1891  *
1892  * @param string $email The email to check
1893  *
1894  * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1895  */
1896  function phpbb_validate_email($email, $config = null)
1897  {
1898      if ($config === null)
1899      {
1900          global $config;
1901      }
1902  
1903      $email = strtolower($email);
1904  
1905      if (!preg_match('/^' . get_preg_expression('email') . '$/i', $email))
1906      {
1907          return 'EMAIL_INVALID';
1908      }
1909  
1910      // Check MX record.
1911      // The idea for this is from reading the UseBB blog/announcement. :)
1912      if ($config['email_check_mx'])
1913      {
1914          list(, $domain) = explode('@', $email);
1915  
1916          if (phpbb_checkdnsrr($domain, 'A') === false && phpbb_checkdnsrr($domain, 'MX') === false)
1917          {
1918              return 'DOMAIN_NO_MX_RECORD';
1919          }
1920      }
1921  
1922      return false;
1923  }
1924  
1925  /**
1926  * Check to see if email address is banned or already present in the DB
1927  *
1928  * @param string $email The email to check
1929  * @param string $allowed_email An allowed email, default being $user->data['user_email']
1930  *
1931  * @return mixed Either false if validation succeeded or a string which will be used as the error message (with the variable name appended)
1932  */
1933  function validate_user_email($email, $allowed_email = false)
1934  {
1935      global $config, $db, $user;
1936  
1937      $email = strtolower($email);
1938      $allowed_email = ($allowed_email === false) ? strtolower($user->data['user_email']) : strtolower($allowed_email);
1939  
1940      if ($allowed_email == $email)
1941      {
1942          return false;
1943      }
1944  
1945      $validate_email = phpbb_validate_email($email, $config);
1946      if ($validate_email)
1947      {
1948          return $validate_email;
1949      }
1950  
1951      if (($ban_reason = $user->check_ban(false, false, $email, true)) !== false)
1952      {
1953          return ($ban_reason === true) ? 'EMAIL_BANNED' : $ban_reason;
1954      }
1955  
1956      if (!$config['allow_emailreuse'])
1957      {
1958          $sql = 'SELECT user_email_hash
1959              FROM ' . USERS_TABLE . "
1960              WHERE user_email_hash = " . $db->sql_escape(phpbb_email_hash($email));
1961          $result = $db->sql_query($sql);
1962          $row = $db->sql_fetchrow($result);
1963          $db->sql_freeresult($result);
1964  
1965          if ($row)
1966          {
1967              return 'EMAIL_TAKEN';
1968          }
1969      }
1970  
1971      return false;
1972  }
1973  
1974  /**
1975  * Validate jabber address
1976  * Taken from the jabber class within flyspray (see author notes)
1977  *
1978  * @author flyspray.org
1979  */
1980  function validate_jabber($jid)
1981  {
1982      if (!$jid)
1983      {
1984          return false;
1985      }
1986  
1987      $separator_pos = strpos($jid, '@');
1988  
1989      if ($separator_pos === false)
1990      {
1991          return 'WRONG_DATA';
1992      }
1993  
1994      $username = substr($jid, 0, $separator_pos);
1995      $realm = substr($jid, $separator_pos + 1);
1996  
1997      if (strlen($username) == 0 || strlen($realm) < 3)
1998      {
1999          return 'WRONG_DATA';
2000      }
2001  
2002      $arr = explode('.', $realm);
2003  
2004      if (sizeof($arr) == 0)
2005      {
2006          return 'WRONG_DATA';
2007      }
2008  
2009      foreach ($arr as $part)
2010      {
2011          if (substr($part, 0, 1) == '-' || substr($part, -1, 1) == '-')
2012          {
2013              return 'WRONG_DATA';
2014          }
2015  
2016          if (!preg_match("@^[a-zA-Z0-9-.]+$@", $part))
2017          {
2018              return 'WRONG_DATA';
2019          }
2020      }
2021  
2022      $boundary = array(array(0, 127), array(192, 223), array(224, 239), array(240, 247), array(248, 251), array(252, 253));
2023  
2024      // Prohibited Characters RFC3454 + RFC3920
2025      $prohibited = array(
2026          // Table C.1.1
2027          array(0x0020, 0x0020),        // SPACE
2028          // Table C.1.2
2029          array(0x00A0, 0x00A0),        // NO-BREAK SPACE
2030          array(0x1680, 0x1680),        // OGHAM SPACE MARK
2031          array(0x2000, 0x2001),        // EN QUAD
2032          array(0x2001, 0x2001),        // EM QUAD
2033          array(0x2002, 0x2002),        // EN SPACE
2034          array(0x2003, 0x2003),        // EM SPACE
2035          array(0x2004, 0x2004),        // THREE-PER-EM SPACE
2036          array(0x2005, 0x2005),        // FOUR-PER-EM SPACE
2037          array(0x2006, 0x2006),        // SIX-PER-EM SPACE
2038          array(0x2007, 0x2007),        // FIGURE SPACE
2039          array(0x2008, 0x2008),        // PUNCTUATION SPACE
2040          array(0x2009, 0x2009),        // THIN SPACE
2041          array(0x200A, 0x200A),        // HAIR SPACE
2042          array(0x200B, 0x200B),        // ZERO WIDTH SPACE
2043          array(0x202F, 0x202F),        // NARROW NO-BREAK SPACE
2044          array(0x205F, 0x205F),        // MEDIUM MATHEMATICAL SPACE
2045          array(0x3000, 0x3000),        // IDEOGRAPHIC SPACE
2046          // Table C.2.1
2047          array(0x0000, 0x001F),        // [CONTROL CHARACTERS]
2048          array(0x007F, 0x007F),        // DELETE
2049          // Table C.2.2
2050          array(0x0080, 0x009F),        // [CONTROL CHARACTERS]
2051          array(0x06DD, 0x06DD),        // ARABIC END OF AYAH
2052          array(0x070F, 0x070F),        // SYRIAC ABBREVIATION MARK
2053          array(0x180E, 0x180E),        // MONGOLIAN VOWEL SEPARATOR
2054          array(0x200C, 0x200C),         // ZERO WIDTH NON-JOINER
2055          array(0x200D, 0x200D),        // ZERO WIDTH JOINER
2056          array(0x2028, 0x2028),        // LINE SEPARATOR
2057          array(0x2029, 0x2029),        // PARAGRAPH SEPARATOR
2058          array(0x2060, 0x2060),        // WORD JOINER
2059          array(0x2061, 0x2061),        // FUNCTION APPLICATION
2060          array(0x2062, 0x2062),        // INVISIBLE TIMES
2061          array(0x2063, 0x2063),        // INVISIBLE SEPARATOR
2062          array(0x206A, 0x206F),        // [CONTROL CHARACTERS]
2063          array(0xFEFF, 0xFEFF),        // ZERO WIDTH NO-BREAK SPACE
2064          array(0xFFF9, 0xFFFC),        // [CONTROL CHARACTERS]
2065          array(0x1D173, 0x1D17A),    // [MUSICAL CONTROL CHARACTERS]
2066          // Table C.3
2067          array(0xE000, 0xF8FF),        // [PRIVATE USE, PLANE 0]
2068          array(0xF0000, 0xFFFFD),    // [PRIVATE USE, PLANE 15]
2069          array(0x100000, 0x10FFFD),    // [PRIVATE USE, PLANE 16]
2070          // Table C.4
2071          array(0xFDD0, 0xFDEF),        // [NONCHARACTER CODE POINTS]
2072          array(0xFFFE, 0xFFFF),        // [NONCHARACTER CODE POINTS]
2073          array(0x1FFFE, 0x1FFFF),    // [NONCHARACTER CODE POINTS]
2074          array(0x2FFFE, 0x2FFFF),    // [NONCHARACTER CODE POINTS]
2075          array(0x3FFFE, 0x3FFFF),    // [NONCHARACTER CODE POINTS]
2076          array(0x4FFFE, 0x4FFFF),    // [NONCHARACTER CODE POINTS]
2077          array(0x5FFFE, 0x5FFFF),    // [NONCHARACTER CODE POINTS]
2078          array(0x6FFFE, 0x6FFFF),    // [NONCHARACTER CODE POINTS]
2079          array(0x7FFFE, 0x7FFFF),    // [NONCHARACTER CODE POINTS]
2080          array(0x8FFFE, 0x8FFFF),    // [NONCHARACTER CODE POINTS]
2081          array(0x9FFFE, 0x9FFFF),    // [NONCHARACTER CODE POINTS]
2082          array(0xAFFFE, 0xAFFFF),    // [NONCHARACTER CODE POINTS]
2083          array(0xBFFFE, 0xBFFFF),    // [NONCHARACTER CODE POINTS]
2084          array(0xCFFFE, 0xCFFFF),    // [NONCHARACTER CODE POINTS]
2085          array(0xDFFFE, 0xDFFFF),    // [NONCHARACTER CODE POINTS]
2086          array(0xEFFFE, 0xEFFFF),    // [NONCHARACTER CODE POINTS]
2087          array(0xFFFFE, 0xFFFFF),    // [NONCHARACTER CODE POINTS]
2088          array(0x10FFFE, 0x10FFFF),    // [NONCHARACTER CODE POINTS]
2089          // Table C.5
2090          array(0xD800, 0xDFFF),        // [SURROGATE CODES]
2091          // Table C.6
2092          array(0xFFF9, 0xFFF9),        // INTERLINEAR ANNOTATION ANCHOR
2093          array(0xFFFA, 0xFFFA),        // INTERLINEAR ANNOTATION SEPARATOR
2094          array(0xFFFB, 0xFFFB),        // INTERLINEAR ANNOTATION TERMINATOR
2095          array(0xFFFC, 0xFFFC),        // OBJECT REPLACEMENT CHARACTER
2096          array(0xFFFD, 0xFFFD),        // REPLACEMENT CHARACTER
2097          // Table C.7
2098          array(0x2FF0, 0x2FFB),        // [IDEOGRAPHIC DESCRIPTION CHARACTERS]
2099          // Table C.8
2100          array(0x0340, 0x0340),        // COMBINING GRAVE TONE MARK
2101          array(0x0341, 0x0341),        // COMBINING ACUTE TONE MARK
2102          array(0x200E, 0x200E),        // LEFT-TO-RIGHT MARK
2103          array(0x200F, 0x200F),        // RIGHT-TO-LEFT MARK
2104          array(0x202A, 0x202A),        // LEFT-TO-RIGHT EMBEDDING
2105          array(0x202B, 0x202B),        // RIGHT-TO-LEFT EMBEDDING
2106          array(0x202C, 0x202C),        // POP DIRECTIONAL FORMATTING
2107          array(0x202D, 0x202D),        // LEFT-TO-RIGHT OVERRIDE
2108          array(0x202E, 0x202E),        // RIGHT-TO-LEFT OVERRIDE
2109          array(0x206A, 0x206A),        // INHIBIT SYMMETRIC SWAPPING
2110          array(0x206B, 0x206B),        // ACTIVATE SYMMETRIC SWAPPING
2111          array(0x206C, 0x206C),        // INHIBIT ARABIC FORM SHAPING
2112          array(0x206D, 0x206D),        // ACTIVATE ARABIC FORM SHAPING
2113          array(0x206E, 0x206E),        // NATIONAL DIGIT SHAPES
2114          array(0x206F, 0x206F),        // NOMINAL DIGIT SHAPES
2115          // Table C.9
2116          array(0xE0001, 0xE0001),    // LANGUAGE TAG
2117          array(0xE0020, 0xE007F),    // [TAGGING CHARACTERS]
2118          // RFC3920
2119          array(0x22, 0x22),            // "
2120          array(0x26, 0x26),            // &
2121          array(0x27, 0x27),            // '
2122          array(0x2F, 0x2F),            // /
2123          array(0x3A, 0x3A),            // :
2124          array(0x3C, 0x3C),            // <
2125          array(0x3E, 0x3E),            // >
2126          array(0x40, 0x40)            // @
2127      );
2128  
2129      $pos = 0;
2130      $result = true;
2131  
2132      while ($pos < strlen($username))
2133      {
2134          $len = $uni = 0;
2135          for ($i = 0; $i <= 5; $i++)
2136          {
2137              if (ord($username[$pos]) >= $boundary[$i][0] && ord($username[$pos]) <= $boundary[$i][1])
2138              {
2139                  $len = $i + 1;
2140                  $uni = (ord($username[$pos]) - $boundary[$i][0]) * pow(2, $i * 6);
2141  
2142                  for ($k = 1; $k < $len; $k++)
2143                  {
2144                      $uni += (ord($username[$pos + $k]) - 128) * pow(2, ($i - $k) * 6);
2145                  }
2146  
2147                  break;
2148              }
2149          }
2150  
2151          if ($len == 0)
2152          {
2153              return 'WRONG_DATA';
2154          }
2155  
2156          foreach ($prohibited as $pval)
2157          {
2158              if ($uni >= $pval[0] && $uni <= $pval[1])
2159              {
2160                  $result = false;
2161                  break 2;
2162              }
2163          }
2164  
2165          $pos = $pos + $len;
2166      }
2167  
2168      if (!$result)
2169      {
2170          return 'WRONG_DATA';
2171      }
2172  
2173      return false;
2174  }
2175  
2176  /**
2177  * Validate hex colour value
2178  *
2179  * @param string $colour The hex colour value
2180  * @param bool $optional Whether the colour value is optional. True if an empty
2181  *            string will be accepted as correct input, false if not.
2182  * @return bool|string Error message if colour value is incorrect, false if it
2183  *            fits the hex colour code
2184  */
2185  function phpbb_validate_hex_colour($colour, $optional = false)
2186  {
2187      if ($colour === '')
2188      {
2189          return (($optional) ? false : 'WRONG_DATA');
2190      }
2191  
2192      if (!preg_match('/^([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/', $colour))
2193      {
2194          return 'WRONG_DATA';
2195      }
2196  
2197      return false;
2198  }
2199  
2200  /**
2201  * Verifies whether a style ID corresponds to an active style.
2202  *
2203  * @param int $style_id The style_id of a style which should be checked if activated or not.
2204  * @return boolean
2205  */
2206  function phpbb_style_is_active($style_id)
2207  {
2208      global $db;
2209  
2210      $sql = 'SELECT style_active
2211          FROM ' . STYLES_TABLE . '
2212          WHERE style_id = '. (int) $style_id;
2213      $result = $db->sql_query($sql);
2214  
2215      $style_is_active = (bool) $db->sql_fetchfield('style_active');
2216      $db->sql_freeresult($result);
2217  
2218      return $style_is_active;
2219  }
2220  
2221  /**
2222  * Remove avatar
2223  */
2224  function avatar_delete($mode, $row, $clean_db = false)
2225  {
2226      global $phpbb_root_path, $config, $db, $user;
2227  
2228      // Check if the users avatar is actually *not* a group avatar
2229      if ($mode == 'user')
2230      {
2231          if (strpos($row['user_avatar'], 'g') === 0 || (((int) $row['user_avatar'] !== 0) && ((int) $row['user_avatar'] !== (int) $row['user_id'])))
2232          {
2233              return false;
2234          }
2235      }
2236  
2237      if ($clean_db)
2238      {
2239          avatar_remove_db($row[$mode . '_avatar']);
2240      }
2241      $filename = get_avatar_filename($row[$mode . '_avatar']);
2242  
2243      if (file_exists($phpbb_root_path . $config['avatar_path'] . '/' . $filename))
2244      {
2245          @unlink($phpbb_root_path . $config['avatar_path'] . '/' . $filename);
2246          return true;
2247      }
2248  
2249      return false;
2250  }
2251  
2252  /**
2253  * Generates avatar filename from the database entry
2254  */
2255  function get_avatar_filename($avatar_entry)
2256  {
2257      global $config;
2258  
2259      if ($avatar_entry[0] === 'g')
2260      {
2261          $avatar_group = true;
2262          $avatar_entry = substr($avatar_entry, 1);
2263      }
2264      else
2265      {
2266          $avatar_group = false;
2267      }
2268      $ext             = substr(strrchr($avatar_entry, '.'), 1);
2269      $avatar_entry    = intval($avatar_entry);
2270      return $config['avatar_salt'] . '_' . (($avatar_group) ? 'g' : '') . $avatar_entry . '.' . $ext;
2271  }
2272  
2273  /**
2274  * Returns an explanation string with maximum avatar settings
2275  *
2276  * @return string
2277  */
2278  function phpbb_avatar_explanation_string()
2279  {
2280      global $config, $user;
2281  
2282      return $user->lang('AVATAR_EXPLAIN',
2283          $user->lang('PIXELS', (int) $config['avatar_max_width']),
2284          $user->lang('PIXELS', (int) $config['avatar_max_height']),
2285          round($config['avatar_filesize'] / 1024));
2286  }
2287  
2288  //
2289  // Usergroup functions
2290  //
2291  
2292  /**
2293  * Add or edit a group. If we're editing a group we only update user
2294  * parameters such as rank, etc. if they are changed
2295  */
2296  function group_create(&$group_id, $type, $name, $desc, $group_attributes, $allow_desc_bbcode = false, $allow_desc_urls = false, $allow_desc_smilies = false)
2297  {
2298      global $phpbb_root_path, $config, $db, $user, $file_upload, $phpbb_container;
2299  
2300      $error = array();
2301  
2302      // Attributes which also affect the users table
2303      $user_attribute_ary = array('group_colour', 'group_rank', 'group_avatar', 'group_avatar_type', 'group_avatar_width', 'group_avatar_height');
2304  
2305      // Check data. Limit group name length.
2306      if (!utf8_strlen($name) || utf8_strlen($name) > 60)
2307      {
2308          $error[] = (!utf8_strlen($name)) ? $user->lang['GROUP_ERR_USERNAME'] : $user->lang['GROUP_ERR_USER_LONG'];
2309      }
2310  
2311      $err = group_validate_groupname($group_id, $name);
2312      if (!empty($err))
2313      {
2314          $error[] = $user->lang[$err];
2315      }
2316  
2317      if (!in_array($type, array(GROUP_OPEN, GROUP_CLOSED, GROUP_HIDDEN, GROUP_SPECIAL, GROUP_FREE)))
2318      {
2319          $error[] = $user->lang['GROUP_ERR_TYPE'];
2320      }
2321  
2322      $group_teampage = !empty($group_attributes['group_teampage']);
2323      unset($group_attributes['group_teampage']);
2324  
2325      if (!sizeof($error))
2326      {
2327          $current_legend = \phpbb\groupposition\legend::GROUP_DISABLED;
2328          $current_teampage = \phpbb\groupposition\teampage::GROUP_DISABLED;
2329  
2330          $legend = $phpbb_container->get('groupposition.legend');
2331          $teampage = $phpbb_container->get('groupposition.teampage');
2332          if ($group_id)
2333          {
2334              try
2335              {
2336                  $current_legend = $legend->get_group_value($group_id);
2337                  $current_teampage = $teampage->get_group_value($group_id);
2338              }
2339              catch (\phpbb\groupposition\exception $exception)
2340              {
2341                  trigger_error($user->lang($exception->getMessage()));
2342              }
2343          }
2344  
2345          if (!empty($group_attributes['group_legend']))
2346          {
2347              if (($group_id && ($current_legend == \phpbb\groupposition\legend::GROUP_DISABLED)) || !$group_id)
2348              {
2349                  // Old group currently not in the legend or new group, add at the end.
2350                  $group_attributes['group_legend'] = 1 + $legend->get_group_count();
2351              }
2352              else
2353              {
2354                  // Group stayes in the legend
2355                  $group_attributes['group_legend'] = $current_legend;
2356              }
2357          }
2358          else if ($group_id && ($current_legend != \phpbb\groupposition\legend::GROUP_DISABLED))
2359          {
2360              // Group is removed from the legend
2361              try
2362              {
2363                  $legend->delete_group($group_id, true);
2364              }
2365              catch (\phpbb\groupposition\exception $exception)
2366              {
2367                  trigger_error($user->lang($exception->getMessage()));
2368              }
2369              $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
2370          }
2371          else
2372          {
2373              $group_attributes['group_legend'] = \phpbb\groupposition\legend::GROUP_DISABLED;
2374          }
2375  
2376          // Unset the objects, we don't need them anymore.
2377          unset($legend);
2378  
2379          $user_ary = array();
2380          $sql_ary = array(
2381              'group_name'            => (string) $name,
2382              'group_desc'            => (string) $desc,
2383              'group_desc_uid'        => '',
2384              'group_desc_bitfield'    => '',
2385              'group_type'            => (int) $type,
2386          );
2387  
2388          // Parse description
2389          if ($desc)
2390          {
2391              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);
2392          }
2393  
2394          if (sizeof($group_attributes))
2395          {
2396              // Merge them with $sql_ary to properly update the group
2397              $sql_ary = array_merge($sql_ary, $group_attributes);
2398          }
2399  
2400          // Setting the log message before we set the group id (if group gets added)
2401          $log = ($group_id) ? 'LOG_GROUP_UPDATED' : 'LOG_GROUP_CREATED';
2402  
2403          $query = '';
2404  
2405          if ($group_id)
2406          {
2407              $sql = 'SELECT user_id
2408                  FROM ' . USERS_TABLE . '
2409                  WHERE group_id = ' . $group_id;
2410              $result = $db->sql_query($sql);
2411  
2412              while ($row = $db->sql_fetchrow($result))
2413              {
2414                  $user_ary[] = $row['user_id'];
2415              }
2416              $db->sql_freeresult($result);
2417  
2418              if (isset($sql_ary['group_avatar']))
2419              {
2420                  remove_default_avatar($group_id, $user_ary);
2421              }
2422  
2423              if (isset($sql_ary['group_rank']))
2424              {
2425                  remove_default_rank($group_id, $user_ary);
2426              }
2427  
2428              $sql = 'UPDATE ' . GROUPS_TABLE . '
2429                  SET ' . $db->sql_build_array('UPDATE', $sql_ary) . "
2430                  WHERE group_id = $group_id";
2431              $db->sql_query($sql);
2432  
2433              // Since we may update the name too, we need to do this on other tables too...
2434              $sql = 'UPDATE ' . MODERATOR_CACHE_TABLE . "
2435                  SET group_name = '" . $db->sql_escape($sql_ary['group_name']) . "'
2436                  WHERE group_id = $group_id";
2437              $db->sql_query($sql);
2438  
2439              // One special case is the group skip auth setting. If this was changed we need to purge permissions for this group
2440              if (isset($group_attributes['group_skip_auth']))
2441              {
2442                  // Get users within this group...
2443                  $sql = 'SELECT user_id
2444                      FROM ' . USER_GROUP_TABLE . '
2445                      WHERE group_id = ' . $group_id . '
2446                          AND user_pending = 0';
2447                  $result = $db->sql_query($sql);
2448  
2449                  $user_id_ary = array();
2450                  while ($row = $db->sql_fetchrow($result))
2451                  {
2452                      $user_id_ary[] = $row['user_id'];
2453                  }
2454                  $db->sql_freeresult($result);
2455  
2456                  if (!empty($user_id_ary))
2457                  {
2458                      global $auth;
2459  
2460                      // Clear permissions cache of relevant users
2461                      $auth->acl_clear_prefetch($user_id_ary);
2462                  }
2463              }
2464          }
2465          else
2466          {
2467              $sql = 'INSERT INTO ' . GROUPS_TABLE . ' ' . $db->sql_build_array('INSERT', $sql_ary);
2468              $db->sql_query($sql);
2469          }
2470  
2471          // Remove the group from the teampage, only if unselected and we are editing a group,
2472          // which is currently displayed.
2473          if (!$group_teampage && $group_id && $current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED)
2474          {
2475              try
2476              {
2477                  $teampage->delete_group($group_id);
2478              }
2479              catch (\phpbb\groupposition\exception $exception)
2480              {
2481                  trigger_error($user->lang($exception->getMessage()));
2482              }
2483          }
2484  
2485          if (!$group_id)
2486          {
2487              $group_id = $db->sql_nextid();
2488  
2489              if (isset($sql_ary['group_avatar_type']) && $sql_ary['group_avatar_type'] == 'avatar.driver.upload')
2490              {
2491                  group_correct_avatar($group_id, $sql_ary['group_avatar']);
2492              }
2493          }
2494  
2495          try
2496          {
2497              if ($group_teampage && $current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
2498              {
2499                  $teampage->add_group($group_id);
2500              }
2501  
2502              if ($group_teampage)
2503              {
2504                  if ($current_teampage == \phpbb\groupposition\teampage::GROUP_DISABLED)
2505                  {
2506                      $teampage->add_group($group_id);
2507                  }
2508              }
2509              else if ($group_id && ($current_teampage != \phpbb\groupposition\teampage::GROUP_DISABLED))
2510              {
2511                  $teampage->delete_group($group_id);
2512              }
2513          }
2514          catch (\phpbb\groupposition\exception $exception)
2515          {
2516              trigger_error($user->lang($exception->getMessage()));
2517          }
2518          unset($teampage);
2519  
2520          // Set user attributes
2521          $sql_ary = array();
2522          if (sizeof($group_attributes))
2523          {
2524              // Go through the user attributes array, check if a group attribute matches it and then set it. ;)
2525              foreach ($user_attribute_ary as $attribute)
2526              {
2527                  if (!isset($group_attributes[$attribute]))
2528                  {
2529                      continue;
2530                  }
2531  
2532                  // If we are about to set an avatar, we will not overwrite user avatars if no group avatar is set...
2533                  if (strpos($attribute, 'group_avatar') === 0 && !$group_attributes[$attribute])
2534                  {
2535                      continue;
2536                  }
2537  
2538                  $sql_ary[$attribute] = $group_attributes[$attribute];
2539              }
2540          }
2541  
2542          if (sizeof($sql_ary) && sizeof($user_ary))
2543          {
2544              group_set_user_default($group_id, $user_ary, $sql_ary);
2545          }
2546  
2547          $name = ($type == GROUP_SPECIAL) ? $user->lang['G_' . $name] : $name;
2548          add_log('admin', $log, $name);
2549  
2550          group_update_listings($group_id);
2551      }
2552  
2553      return (sizeof($error)) ? $error : false;
2554  }
2555  
2556  
2557  /**
2558  * Changes a group avatar's filename to conform to the naming scheme
2559  */
2560  function group_correct_avatar($group_id, $old_entry)
2561  {
2562      global $config, $db, $phpbb_root_path;
2563  
2564      $group_id        = (int) $group_id;
2565      $ext             = substr(strrchr($old_entry, '.'), 1);
2566      $old_filename     = get_avatar_filename($old_entry);
2567      $new_filename     = $config['avatar_salt'] . "_g$group_id.$ext";
2568      $new_entry         = 'g' . $group_id . '_' . substr(time(), -5) . ".$ext";
2569  
2570      $avatar_path = $phpbb_root_path . $config['avatar_path'];
2571      if (@rename($avatar_path . '/'. $old_filename, $avatar_path . '/' . $new_filename))
2572      {
2573          $sql = 'UPDATE ' . GROUPS_TABLE . '
2574              SET group_avatar = \'' . $db->sql_escape($new_entry) . "'
2575              WHERE group_id = $group_id";
2576          $db->sql_query($sql);
2577      }
2578  }
2579  
2580  
2581  /**
2582  * Remove avatar also for users not having the group as default
2583  */
2584  function avatar_remove_db($avatar_name)
2585  {
2586      global $config, $db;
2587  
2588      $sql = 'UPDATE ' . USERS_TABLE . "
2589          SET user_avatar = '',
2590          user_avatar_type = ''
2591          WHERE user_avatar = '" . $db->sql_escape($avatar_name) . '\'';
2592      $db->sql_query($sql);
2593  }
2594  
2595  
2596  /**
2597  * Group Delete
2598  */
2599  function group_delete($group_id, $group_name = false)
2600  {
2601      global $db, $cache, $auth, $user, $phpbb_root_path, $phpEx, $phpbb_dispatcher, $phpbb_container;
2602  
2603      if (!$group_name)
2604      {
2605          $group_name = get_group_name($group_id);
2606      }
2607  
2608      $start = 0;
2609  
2610      do
2611      {
2612          $user_id_ary = $username_ary = array();
2613  
2614          // Batch query for group members, call group_user_del
2615          $sql = 'SELECT u.user_id, u.username
2616              FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . " u
2617              WHERE ug.group_id = $group_id
2618                  AND u.user_id = ug.user_id";
2619          $result = $db->sql_query_limit($sql, 200, $start);
2620  
2621          if ($row = $db->sql_fetchrow($result))
2622          {
2623              do
2624              {
2625                  $user_id_ary[] = $row['user_id'];
2626                  $username_ary[] = $row['username'];
2627  
2628                  $start++;
2629              }
2630              while ($row = $db->sql_fetchrow($result));
2631  
2632              group_user_del($group_id, $user_id_ary, $username_ary, $group_name);
2633          }
2634          else
2635          {
2636              $start = 0;
2637          }
2638          $db->sql_freeresult($result);
2639      }
2640      while ($start);
2641  
2642      // Delete group from legend and teampage
2643      try
2644      {
2645          $legend = $phpbb_container->get('groupposition.legend');
2646          $legend->delete_group($group_id);
2647          unset($legend);
2648      }
2649      catch (\phpbb\groupposition\exception $exception)
2650      {
2651          // The group we want to delete does not exist.
2652          // No reason to worry, we just continue the deleting process.
2653          //trigger_error($user->lang($exception->getMessage()));
2654      }
2655  
2656      try
2657      {
2658          $teampage = $phpbb_container->get('groupposition.teampage');
2659          $teampage->delete_group($group_id);
2660          unset($teampage);
2661      }
2662      catch (\phpbb\groupposition\exception $exception)
2663      {
2664          // The group we want to delete does not exist.
2665          // No reason to worry, we just continue the deleting process.
2666          //trigger_error($user->lang($exception->getMessage()));
2667      }
2668  
2669      // Delete group
2670      $sql = 'DELETE FROM ' . GROUPS_TABLE . "
2671          WHERE group_id = $group_id";
2672      $db->sql_query($sql);
2673  
2674      // Delete auth entries from the groups table
2675      $sql = 'DELETE FROM ' . ACL_GROUPS_TABLE . "
2676          WHERE group_id = $group_id";
2677      $db->sql_query($sql);
2678  
2679      /**
2680      * Event after a group is deleted
2681      *
2682      * @event core.delete_group_after
2683      * @var    int        group_id    ID of the deleted group
2684      * @var    string    group_name    Name of the deleted group
2685      * @since 3.1.0-a1
2686      */
2687      $vars = array('group_id', 'group_name');
2688      extract($phpbb_dispatcher->trigger_event('core.delete_group_after', compact($vars)));
2689  
2690      // Re-cache moderators
2691      if (!function_exists('phpbb_cache_moderators'))
2692      {
2693          include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
2694      }
2695  
2696      phpbb_cache_moderators($db, $cache, $auth);
2697  
2698      add_log('admin', 'LOG_GROUP_DELETE', $group_name);
2699  
2700      // Return false - no error
2701      return false;
2702  }
2703  
2704  /**
2705  * Add user(s) to group
2706  *
2707  * @return mixed false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2708  */
2709  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)
2710  {
2711      global $db, $auth, $phpbb_container, $phpbb_dispatcher;
2712  
2713      // We need both username and user_id info
2714      $result = user_get_id_name($user_id_ary, $username_ary);
2715  
2716      if (!sizeof($user_id_ary) || $result !== false)
2717      {
2718          return 'NO_USER';
2719      }
2720  
2721      // Remove users who are already members of this group
2722      $sql = 'SELECT user_id, group_leader
2723          FROM ' . USER_GROUP_TABLE . '
2724          WHERE ' . $db->sql_in_set('user_id', $user_id_ary) . "
2725              AND group_id = $group_id";
2726      $result = $db->sql_query($sql);
2727  
2728      $add_id_ary = $update_id_ary = array();
2729      while ($row = $db->sql_fetchrow($result))
2730      {
2731          $add_id_ary[] = (int) $row['user_id'];
2732  
2733          if ($leader && !$row['group_leader'])
2734          {
2735              $update_id_ary[] = (int) $row['user_id'];
2736          }
2737      }
2738      $db->sql_freeresult($result);
2739  
2740      // Do all the users exist in this group?
2741      $add_id_ary = array_diff($user_id_ary, $add_id_ary);
2742  
2743      // If we have no users
2744      if (!sizeof($add_id_ary) && !sizeof($update_id_ary))
2745      {
2746          return 'GROUP_USERS_EXIST';
2747      }
2748  
2749      $db->sql_transaction('begin');
2750  
2751      // Insert the new users
2752      if (sizeof($add_id_ary))
2753      {
2754          $sql_ary = array();
2755  
2756          foreach ($add_id_ary as $user_id)
2757          {
2758              $sql_ary[] = array(
2759                  'user_id'        => (int) $user_id,
2760                  'group_id'        => (int) $group_id,
2761                  'group_leader'    => (int) $leader,
2762                  'user_pending'    => (int) $pending,
2763              );
2764          }
2765  
2766          $db->sql_multi_insert(USER_GROUP_TABLE, $sql_ary);
2767      }
2768  
2769      if (sizeof($update_id_ary))
2770      {
2771          $sql = 'UPDATE ' . USER_GROUP_TABLE . '
2772              SET group_leader = 1
2773              WHERE ' . $db->sql_in_set('user_id', $update_id_ary) . "
2774                  AND group_id = $group_id";
2775          $db->sql_query($sql);
2776      }
2777  
2778      if ($default)
2779      {
2780          group_user_attributes('default', $group_id, $user_id_ary, false, $group_name, $group_attributes);
2781      }
2782  
2783      $db->sql_transaction('commit');
2784  
2785      // Clear permissions cache of relevant users
2786      $auth->acl_clear_prefetch($user_id_ary);
2787  
2788      /**
2789      * Event after users are added to a group
2790      *
2791      * @event core.group_add_user_after
2792      * @var    int    group_id        ID of the group to which users are added
2793      * @var    string group_name        Name of the group
2794      * @var    array    user_id_ary        IDs of the users which are added
2795      * @var    array    username_ary    names of the users which are added
2796      * @var    int        pending            Pending setting, 1 if user(s) added are pending
2797      * @since 3.1.7-RC1
2798      */
2799      $vars = array(
2800          'group_id',
2801          'group_name',
2802          'user_id_ary',
2803          'username_ary',
2804          'pending',
2805      );
2806      extract($phpbb_dispatcher->trigger_event('core.group_add_user_after', compact($vars)));
2807  
2808      if (!$group_name)
2809      {
2810          $group_name = get_group_name($group_id);
2811      }
2812  
2813      $log = ($leader) ? 'LOG_MODS_ADDED' : (($pending) ? 'LOG_USERS_PENDING' : 'LOG_USERS_ADDED');
2814  
2815      add_log('admin', $log, $group_name, implode(', ', $username_ary));
2816  
2817      group_update_listings($group_id);
2818  
2819      if ($pending)
2820      {
2821          $phpbb_notifications = $phpbb_container->get('notification_manager');
2822  
2823          foreach ($add_id_ary as $user_id)
2824          {
2825              $phpbb_notifications->add_notifications('notification.type.group_request', array(
2826                  'group_id'        => $group_id,
2827                  'user_id'        => $user_id,
2828                  'group_name'    => $group_name,
2829              ));
2830          }
2831      }
2832  
2833      // Return false - no error
2834      return false;
2835  }
2836  
2837  /**
2838  * Remove a user/s from a given group. When we remove users we update their
2839  * default group_id. We do this by examining which "special" groups they belong
2840  * to. The selection is made based on a reasonable priority system
2841  *
2842  * @return false if no errors occurred, else the user lang string for the relevant error, for example 'NO_USER'
2843  */
2844  function group_user_del($group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $log_action = true)
2845  {
2846      global $db, $auth, $config, $phpbb_dispatcher, $phpbb_container;
2847  
2848      if ($config['coppa_enable'])
2849      {
2850          $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED_COPPA', 'REGISTERED', 'BOTS', 'GUESTS');
2851      }
2852      else
2853      {
2854          $group_order = array('ADMINISTRATORS', 'GLOBAL_MODERATORS', 'NEWLY_REGISTERED', 'REGISTERED', 'BOTS', 'GUESTS');
2855      }
2856  
2857      // We need both username and user_id info
2858      $result = user_get_id_name($user_id_ary, $username_ary);
2859  
2860      if (!sizeof($user_id_ary) || $result !== false)
2861      {
2862          return 'NO_USER';
2863      }
2864  
2865      $sql = 'SELECT *
2866          FROM ' . GROUPS_TABLE . '
2867          WHERE ' . $db->sql_in_set('group_name', $group_order);
2868      $result = $db->sql_query($sql);
2869  
2870      $group_order_id = $special_group_data = array();
2871      while ($row = $db->sql_fetchrow($result))
2872      {
2873          $group_order_id[$row['group_name']] = $row['group_id'];
2874  
2875          $special_group_data[$row['group_id']] = array(
2876              'group_colour'            => $row['group_colour'],
2877              'group_rank'                => $row['group_rank'],
2878          );
2879  
2880          // Only set the group avatar if one is defined...
2881          if ($row['group_avatar'])
2882          {
2883              $special_group_data[$row['group_id']] = array_merge($special_group_data[$row['group_id']], array(
2884                  'group_avatar'            => $row['group_avatar'],
2885                  'group_avatar_type'        => $row['group_avatar_type'],
2886                  'group_avatar_width'        => $row['group_avatar_width'],
2887                  'group_avatar_height'    => $row['group_avatar_height'])
2888              );
2889          }
2890      }
2891      $db->sql_freeresult($result);
2892  
2893      // 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
2894      $sql = 'SELECT user_id, group_id
2895          FROM ' . USERS_TABLE . '
2896          WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
2897      $result = $db->sql_query($sql);
2898  
2899      $default_groups = array();
2900      while ($row = $db->sql_fetchrow($result))
2901      {
2902          $default_groups[$row['user_id']] = $row['group_id'];
2903      }
2904      $db->sql_freeresult($result);
2905  
2906      // What special group memberships exist for these users?
2907      $sql = 'SELECT g.group_id, g.group_name, ug.user_id
2908          FROM ' . USER_GROUP_TABLE . ' ug, ' . GROUPS_TABLE . ' g
2909          WHERE ' . $db->sql_in_set('ug.user_id', $user_id_ary) . "
2910              AND g.group_id = ug.group_id
2911              AND g.group_id <> $group_id
2912              AND g.group_type = " . GROUP_SPECIAL . '
2913          ORDER BY ug.user_id, g.group_id';
2914      $result = $db->sql_query($sql);
2915  
2916      $temp_ary = array();
2917      while ($row = $db->sql_fetchrow($result))
2918      {
2919          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']]))
2920          {
2921              $temp_ary[$row['user_id']] = $row['group_id'];
2922          }
2923      }
2924      $db->sql_freeresult($result);
2925  
2926      // sql_where_ary holds the new default groups and their users
2927      $sql_where_ary = array();
2928      foreach ($temp_ary as $uid => $gid)
2929      {
2930          $sql_where_ary[$gid][] = $uid;
2931      }
2932      unset($temp_ary);
2933  
2934      foreach ($special_group_data as $gid => $default_data_ary)
2935      {
2936          if (isset($sql_where_ary[$gid]) && sizeof($sql_where_ary[$gid]))
2937          {
2938              remove_default_rank($group_id, $sql_where_ary[$gid]);
2939              remove_default_avatar($group_id, $sql_where_ary[$gid]);
2940              group_set_user_default($gid, $sql_where_ary[$gid], $default_data_ary);
2941          }
2942      }
2943      unset($special_group_data);
2944  
2945      /**
2946      * Event before users are removed from a group
2947      *
2948      * @event core.group_delete_user_before
2949      * @var    int        group_id        ID of the group from which users are deleted
2950      * @var    string    group_name        Name of the group
2951      * @var    array    user_id_ary        IDs of the users which are removed
2952      * @var    array    username_ary    names of the users which are removed
2953      * @since 3.1.0-a1
2954      */
2955      $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary');
2956      extract($phpbb_dispatcher->trigger_event('core.group_delete_user_before', compact($vars)));
2957  
2958      $sql = 'DELETE FROM ' . USER_GROUP_TABLE . "
2959          WHERE group_id = $group_id
2960              AND " . $db->sql_in_set('user_id', $user_id_ary);
2961      $db->sql_query($sql);
2962  
2963      // Clear permissions cache of relevant users
2964      $auth->acl_clear_prefetch($user_id_ary);
2965  
2966      /**
2967      * Event after users are removed from a group
2968      *
2969      * @event core.group_delete_user_after
2970      * @var    int        group_id        ID of the group from which users are deleted
2971      * @var    string    group_name        Name of the group
2972      * @var    array    user_id_ary        IDs of the users which are removed
2973      * @var    array    username_ary    names of the users which are removed
2974      * @since 3.1.7-RC1
2975      */
2976      $vars = array('group_id', 'group_name', 'user_id_ary', 'username_ary');
2977      extract($phpbb_dispatcher->trigger_event('core.group_delete_user_after', compact($vars)));
2978  
2979      if ($log_action)
2980      {
2981          if (!$group_name)
2982          {
2983              $group_name = get_group_name($group_id);
2984          }
2985  
2986          $log = 'LOG_GROUP_REMOVE';
2987  
2988          if ($group_name)
2989          {
2990              add_log('admin', $log, $group_name, implode(', ', $username_ary));
2991          }
2992      }
2993  
2994      group_update_listings($group_id);
2995  
2996      $phpbb_notifications = $phpbb_container->get('notification_manager');
2997  
2998      $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
2999  
3000      // Return false - no error
3001      return false;
3002  }
3003  
3004  
3005  /**
3006  * Removes the group avatar of the default group from the users in user_ids who have that group as default.
3007  */
3008  function remove_default_avatar($group_id, $user_ids)
3009  {
3010      global $db;
3011  
3012      if (!is_array($user_ids))
3013      {
3014          $user_ids = array($user_ids);
3015      }
3016      if (empty($user_ids))
3017      {
3018          return false;
3019      }
3020  
3021      $user_ids = array_map('intval', $user_ids);
3022  
3023      $sql = 'SELECT *
3024          FROM ' . GROUPS_TABLE . '
3025          WHERE group_id = ' . (int) $group_id;
3026      $result = $db->sql_query($sql);
3027      if (!$row = $db->sql_fetchrow($result))
3028      {
3029          $db->sql_freeresult($result);
3030          return false;
3031      }
3032      $db->sql_freeresult($result);
3033  
3034      $sql = 'UPDATE ' . USERS_TABLE . "
3035          SET user_avatar = '',
3036              user_avatar_type = '',
3037              user_avatar_width = 0,
3038              user_avatar_height = 0
3039          WHERE group_id = " . (int) $group_id . "
3040              AND user_avatar = '" . $db->sql_escape($row['group_avatar']) . "'
3041              AND " . $db->sql_in_set('user_id', $user_ids);
3042  
3043      $db->sql_query($sql);
3044  }
3045  
3046  /**
3047  * Removes the group rank of the default group from the users in user_ids who have that group as default.
3048  */
3049  function remove_default_rank($group_id, $user_ids)
3050  {
3051      global $db;
3052  
3053      if (!is_array($user_ids))
3054      {
3055          $user_ids = array($user_ids);
3056      }
3057      if (empty($user_ids))
3058      {
3059          return false;
3060      }
3061  
3062      $user_ids = array_map('intval', $user_ids);
3063  
3064      $sql = 'SELECT *
3065          FROM ' . GROUPS_TABLE . '
3066          WHERE group_id = ' . (int) $group_id;
3067      $result = $db->sql_query($sql);
3068      if (!$row = $db->sql_fetchrow($result))
3069      {
3070          $db->sql_freeresult($result);
3071          return false;
3072      }
3073      $db->sql_freeresult($result);
3074  
3075      $sql = 'UPDATE ' . USERS_TABLE . '
3076          SET user_rank = 0
3077          WHERE group_id = ' . (int) $group_id . '
3078              AND user_rank <> 0
3079              AND user_rank = ' . (int) $row['group_rank'] . '
3080              AND ' . $db->sql_in_set('user_id', $user_ids);
3081      $db->sql_query($sql);
3082  }
3083  
3084  /**
3085  * This is used to promote (to leader), demote or set as default a member/s
3086  */
3087  function group_user_attributes($action, $group_id, $user_id_ary = false, $username_ary = false, $group_name = false, $group_attributes = false)
3088  {
3089      global $db, $auth, $phpbb_root_path, $phpEx, $config, $phpbb_container, $phpbb_dispatcher;
3090  
3091      // We need both username and user_id info
3092      $result = user_get_id_name($user_id_ary, $username_ary);
3093  
3094      if (!sizeof($user_id_ary) || $result !== false)
3095      {
3096          return 'NO_USERS';
3097      }
3098  
3099      if (!$group_name)
3100      {
3101          $group_name = get_group_name($group_id);
3102      }
3103  
3104      switch ($action)
3105      {
3106          case 'demote':
3107          case 'promote':
3108  
3109              $sql = 'SELECT user_id
3110                  FROM ' . USER_GROUP_TABLE . "
3111                  WHERE group_id = $group_id
3112                      AND user_pending = 1
3113                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3114              $result = $db->sql_query_limit($sql, 1);
3115              $not_empty = ($db->sql_fetchrow($result));
3116              $db->sql_freeresult($result);
3117              if ($not_empty)
3118              {
3119                  return 'NO_VALID_USERS';
3120              }
3121  
3122              $sql = 'UPDATE ' . USER_GROUP_TABLE . '
3123                  SET group_leader = ' . (($action == 'promote') ? 1 : 0) . "
3124                  WHERE group_id = $group_id
3125                      AND user_pending = 0
3126                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3127              $db->sql_query($sql);
3128  
3129              $log = ($action == 'promote') ? 'LOG_GROUP_PROMOTED' : 'LOG_GROUP_DEMOTED';
3130          break;
3131  
3132          case 'approve':
3133              // Make sure we only approve those which are pending ;)
3134              $sql = 'SELECT u.user_id, u.user_email, u.username, u.username_clean, u.user_notify_type, u.user_jabber, u.user_lang
3135                  FROM ' . USERS_TABLE . ' u, ' . USER_GROUP_TABLE . ' ug
3136                  WHERE ug.group_id = ' . $group_id . '
3137                      AND ug.user_pending = 1
3138                      AND ug.user_id = u.user_id
3139                      AND ' . $db->sql_in_set('ug.user_id', $user_id_ary);
3140              $result = $db->sql_query($sql);
3141  
3142              $user_id_ary = array();
3143              while ($row = $db->sql_fetchrow($result))
3144              {
3145                  $user_id_ary[] = $row['user_id'];
3146              }
3147              $db->sql_freeresult($result);
3148  
3149              if (!sizeof($user_id_ary))
3150              {
3151                  return false;
3152              }
3153  
3154              $sql = 'UPDATE ' . USER_GROUP_TABLE . "
3155                  SET user_pending = 0
3156                  WHERE group_id = $group_id
3157                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3158              $db->sql_query($sql);
3159  
3160              $phpbb_notifications = $phpbb_container->get('notification_manager');
3161  
3162              $phpbb_notifications->add_notifications('notification.type.group_request_approved', array(
3163                  'user_ids'        => $user_id_ary,
3164                  'group_id'        => $group_id,
3165                  'group_name'    => $group_name,
3166              ));
3167              $phpbb_notifications->delete_notifications('notification.type.group_request', $user_id_ary, $group_id);
3168  
3169              $log = 'LOG_USERS_APPROVED';
3170          break;
3171  
3172          case 'default':
3173              // We only set default group for approved members of the group
3174              $sql = 'SELECT user_id
3175                  FROM ' . USER_GROUP_TABLE . "
3176                  WHERE group_id = $group_id
3177                      AND user_pending = 0
3178                      AND " . $db->sql_in_set('user_id', $user_id_ary);
3179              $result = $db->sql_query($sql);
3180  
3181              $user_id_ary = $username_ary = array();
3182              while ($row = $db->sql_fetchrow($result))
3183              {
3184                  $user_id_ary[] = $row['user_id'];
3185              }
3186              $db->sql_freeresult($result);
3187  
3188              $result = user_get_id_name($user_id_ary, $username_ary);
3189              if (!sizeof($user_id_ary) || $result !== false)
3190              {
3191                  return 'NO_USERS';
3192              }
3193  
3194              $sql = 'SELECT user_id, group_id
3195                  FROM ' . USERS_TABLE . '
3196                  WHERE ' . $db->sql_in_set('user_id', $user_id_ary, false, true);
3197              $result = $db->sql_query($sql);
3198  
3199              $groups = array();
3200              while ($row = $db->sql_fetchrow($result))
3201              {
3202                  if (!isset($groups[$row['group_id']]))
3203                  {
3204                      $groups[$row['group_id']] = array();
3205                  }
3206                  $groups[$row['group_id']][] = $row['user_id'];
3207              }
3208              $db->sql_freeresult($result);
3209  
3210              foreach ($groups as $gid => $uids)
3211              {
3212                  remove_default_rank($gid, $uids);
3213                  remove_default_avatar($gid, $uids);
3214              }
3215              group_set_user_default($group_id, $user_id_ary, $group_attributes);
3216              $log = 'LOG_GROUP_DEFAULTS';
3217          break;
3218      }
3219  
3220      /**
3221      * Event to perform additional actions on setting user group attributes
3222      *
3223      * @event core.user_set_group_attributes
3224      * @var    int        group_id            ID of the group
3225      * @var    string    group_name            Name of the group
3226      * @var    array    user_id_ary            IDs of the users to set group attributes
3227      * @var    array    username_ary        Names of the users to set group attributes
3228      * @var    array    group_attributes    Group attributes which were changed
3229      * @var    string    action                Action to perform over the group members
3230      * @since 3.1.10-RC1
3231      */
3232      $vars = array(
3233          'group_id',
3234          'group_name',
3235          'user_id_ary',
3236          'username_ary',
3237          'group_attributes',
3238          'action',
3239      );
3240      extract($phpbb_dispatcher->trigger_event('core.user_set_group_attributes', compact($vars)));
3241  
3242      // Clear permissions cache of relevant users
3243      $auth->acl_clear_prefetch($user_id_ary);
3244  
3245      add_log('admin', $log, $group_name, implode(', ', $username_ary));
3246  
3247      group_update_listings($group_id);
3248  
3249      return false;
3250  }
3251  
3252  /**
3253  * A small version of validate_username to check for a group name's existence. To be called directly.
3254  */
3255  function group_validate_groupname($group_id, $group_name)
3256  {
3257      global $config, $db;
3258  
3259      $group_name =  utf8_clean_string($group_name);
3260  
3261      if (!empty($group_id))
3262      {
3263          $sql = 'SELECT group_name
3264              FROM ' . GROUPS_TABLE . '
3265              WHERE group_id = ' . (int) $group_id;
3266          $result = $db->sql_query($sql);
3267          $row = $db->sql_fetchrow($result);
3268          $db->sql_freeresult($result);
3269  
3270          if (!$row)
3271          {
3272              return false;
3273          }
3274  
3275          $allowed_groupname = utf8_clean_string($row['group_name']);
3276  
3277          if ($allowed_groupname == $group_name)
3278          {
3279              return false;
3280          }
3281      }
3282  
3283      $sql = 'SELECT group_name
3284          FROM ' . GROUPS_TABLE . "
3285          WHERE LOWER(group_name) = '" . $db->sql_escape(utf8_strtolower($group_name)) . "'";
3286      $result = $db->sql_query($sql);
3287      $row = $db->sql_fetchrow($result);
3288      $db->sql_freeresult($result);
3289  
3290      if ($row)
3291      {
3292          return 'GROUP_NAME_TAKEN';
3293      }
3294  
3295      return false;
3296  }
3297  
3298  /**
3299  * Set users default group
3300  *
3301  * @access private
3302  */
3303  function group_set_user_default($group_id, $user_id_ary, $group_attributes = false, $update_listing = false)
3304  {
3305      global $phpbb_container, $db, $phpbb_dispatcher;
3306  
3307      if (empty($user_id_ary))
3308      {
3309          return;
3310      }
3311  
3312      $attribute_ary = array(
3313          'group_colour'            => 'string',
3314          'group_rank'            => 'int',
3315          'group_avatar'            => 'string',
3316          'group_avatar_type'        => 'string',
3317          'group_avatar_width'    => 'int',
3318          'group_avatar_height'    => 'int',
3319      );
3320  
3321      $sql_ary = array(
3322          'group_id'        => $group_id
3323      );
3324  
3325      // Were group attributes passed to the function? If not we need to obtain them
3326      if ($group_attributes === false)
3327      {
3328          $sql = 'SELECT ' . implode(', ', array_keys($attribute_ary)) . '
3329              FROM ' . GROUPS_TABLE . "
3330              WHERE group_id = $group_id";
3331          $result = $db->sql_query($sql);
3332          $group_attributes = $db->sql_fetchrow($result);
3333          $db->sql_freeresult($result);
3334      }
3335  
3336      foreach ($attribute_ary as $attribute => $type)
3337      {
3338          if (isset($group_attributes[$attribute]))
3339          {
3340              // 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
3341              if ((strpos($attribute, 'group_avatar') === 0 || strpos($attribute, 'group_rank') === 0) && !$group_attributes[$attribute])
3342              {
3343                  continue;
3344              }
3345  
3346              settype($group_attributes[$attribute], $type);
3347              $sql_ary[str_replace('group_', 'user_', $attribute)] = $group_attributes[$attribute];
3348          }
3349      }
3350  
3351      $updated_sql_ary = $sql_ary;
3352  
3353      // Before we update the user attributes, we will update the rank for users that don't have a custom rank
3354      if (isset($sql_ary['user_rank']))
3355      {
3356          $sql = 'UPDATE ' . USERS_TABLE . '
3357              SET ' . $db->sql_build_array('UPDATE', array('user_rank' => $sql_ary['user_rank'])) . '
3358              WHERE user_rank = 0
3359                  AND ' . $db->sql_in_set('user_id', $user_id_ary);
3360          $db->sql_query($sql);
3361          unset($sql_ary['user_rank']);
3362      }
3363  
3364      // Before we update the user attributes, we will update the avatar for users that don't have a custom avatar
3365      $avatar_options = array('user_avatar', 'user_avatar_type', 'user_avatar_height', 'user_avatar_width');
3366  
3367      if (isset($sql_ary['user_avatar']))
3368      {
3369          $avatar_sql_ary = array();
3370          foreach ($avatar_options as $avatar_option)
3371          {
3372              if (isset($sql_ary[$avatar_option]))
3373              {
3374                  $avatar_sql_ary[$avatar_option] = $sql_ary[$avatar_option];
3375                  }
3376              }
3377  
3378          $sql = 'UPDATE ' . USERS_TABLE . '
3379              SET ' . $db->sql_build_array('UPDATE', $avatar_sql_ary) . "
3380              WHERE user_avatar = ''
3381                  AND " . $db->sql_in_set('user_id', $user_id_ary);
3382          $db->sql_query($sql);
3383      }
3384  
3385      // Remove the avatar options, as we already updated them
3386      foreach ($avatar_options as $avatar_option)
3387      {
3388          unset($sql_ary[$avatar_option]);
3389      }
3390  
3391      if (!empty($sql_ary))
3392      {
3393          $sql = 'UPDATE ' . USERS_TABLE . '
3394              SET ' . $db->sql_build_array('UPDATE', $sql_ary) . '
3395              WHERE ' . $db->sql_in_set('user_id', $user_id_ary);
3396          $db->sql_query($sql);
3397      }
3398  
3399      if (isset($sql_ary['user_colour']))
3400      {
3401          // Update any cached colour information for these users
3402          $sql = 'UPDATE ' . FORUMS_TABLE . "
3403              SET forum_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3404              WHERE " . $db->sql_in_set('forum_last_poster_id', $user_id_ary);
3405          $db->sql_query($sql);
3406  
3407          $sql = 'UPDATE ' . TOPICS_TABLE . "
3408              SET topic_first_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3409              WHERE " . $db->sql_in_set('topic_poster', $user_id_ary);
3410          $db->sql_query($sql);
3411  
3412          $sql = 'UPDATE ' . TOPICS_TABLE . "
3413              SET topic_last_poster_colour = '" . $db->sql_escape($sql_ary['user_colour']) . "'
3414              WHERE " . $db->sql_in_set('topic_last_poster_id', $user_id_ary);
3415          $db->sql_query($sql);
3416  
3417          global $config;
3418  
3419          if (in_array($config['newest_user_id'], $user_id_ary))
3420          {
3421              set_config('newest_user_colour', $sql_ary['user_colour'], true);
3422          }
3423      }
3424  
3425      // Make all values available for the event
3426      $sql_ary = $updated_sql_ary;
3427  
3428      /**
3429      * Event when the default group is set for an array of users
3430      *
3431      * @event core.user_set_default_group
3432      * @var    int        group_id            ID of the group
3433      * @var    array    user_id_ary            IDs of the users
3434      * @var    array    group_attributes    Group attributes which were changed
3435      * @var    array    update_listing        Update the list of moderators and foes
3436      * @var    array    sql_ary                User attributes which were changed
3437      * @since 3.1.0-a1
3438      */
3439      $vars = array('group_id', 'user_id_ary', 'group_attributes', 'update_listing', 'sql_ary');
3440      extract($phpbb_dispatcher->trigger_event('core.user_set_default_group', compact($vars)));
3441  
3442      if ($update_listing)
3443      {
3444          group_update_listings($group_id);
3445      }
3446  
3447      // Because some tables/caches use usercolour-specific data we need to purge this here.
3448      $phpbb_container->get('cache.driver')->destroy('sql', MODERATOR_CACHE_TABLE);
3449  }
3450  
3451  /**
3452  * Get group name
3453  */
3454  function get_group_name($group_id)
3455  {
3456      global $db, $user;
3457  
3458      $sql = 'SELECT group_name, group_type
3459          FROM ' . GROUPS_TABLE . '
3460          WHERE group_id = ' . (int) $group_id;
3461      $result = $db->sql_query($sql);
3462      $row = $db->sql_fetchrow($result);
3463      $db->sql_freeresult($result);
3464  
3465      if (!$row || ($row['group_type'] == GROUP_SPECIAL && empty($user->lang)))
3466      {
3467          return '';
3468      }
3469  
3470      return ($row['group_type'] == GROUP_SPECIAL) ? $user->lang['G_' . $row['group_name']] : $row['group_name'];
3471  }
3472  
3473  /**
3474  * Obtain either the members of a specified group, the groups the specified user is subscribed to
3475  * or checking if a specified user is in a specified group. This function does not return pending memberships.
3476  *
3477  * Note: Never use this more than once... first group your users/groups
3478  */
3479  function group_memberships($group_id_ary = false, $user_id_ary = false, $return_bool = false)
3480  {
3481      global $db;
3482  
3483      if (!$group_id_ary && !$user_id_ary)
3484      {
3485          return true;
3486      }
3487  
3488      if ($user_id_ary)
3489      {
3490          $user_id_ary = (!is_array($user_id_ary)) ? array($user_id_ary) : $user_id_ary;
3491      }
3492  
3493      if ($group_id_ary)
3494      {
3495          $group_id_ary = (!is_array($group_id_ary)) ? array($group_id_ary) : $group_id_ary;
3496      }
3497  
3498      $sql = 'SELECT ug.*, u.username, u.username_clean, u.user_email
3499          FROM ' . USER_GROUP_TABLE . ' ug, ' . USERS_TABLE . ' u
3500          WHERE ug.user_id = u.user_id
3501              AND ug.user_pending = 0 AND ';
3502  
3503      if ($group_id_ary)
3504      {
3505          $sql .= ' ' . $db->sql_in_set('ug.group_id', $group_id_ary);
3506      }
3507  
3508      if ($user_id_ary)
3509      {
3510          $sql .= ($group_id_ary) ? ' AND ' : ' ';
3511          $sql .= $db->sql_in_set('ug.user_id', $user_id_ary);
3512      }
3513  
3514      $result = ($return_bool) ? $db->sql_query_limit($sql, 1) : $db->sql_query($sql);
3515  
3516      $row = $db->sql_fetchrow($result);
3517  
3518      if ($return_bool)
3519      {
3520          $db->sql_freeresult($result);
3521          return ($row) ? true : false;
3522      }
3523  
3524      if (!$row)
3525      {
3526          return false;
3527      }
3528  
3529      $return = array();
3530  
3531      do
3532      {
3533          $return[] = $row;
3534      }
3535      while ($row = $db->sql_fetchrow($result));
3536  
3537      $db->sql_freeresult($result);
3538  
3539      return $return;
3540  }
3541  
3542  /**
3543  * Re-cache moderators and foes if group has a_ or m_ permissions
3544  */
3545  function group_update_listings($group_id)
3546  {
3547      global $db, $cache, $auth;
3548  
3549      $hold_ary = $auth->acl_group_raw_data($group_id, array('a_', 'm_'));
3550  
3551      if (!sizeof($hold_ary))
3552      {
3553          return;
3554      }
3555  
3556      $mod_permissions = $admin_permissions = false;
3557  
3558      foreach ($hold_ary as $g_id => $forum_ary)
3559      {
3560          foreach ($forum_ary as $forum_id => $auth_ary)
3561          {
3562              foreach ($auth_ary as $auth_option => $setting)
3563              {
3564                  if ($mod_permissions && $admin_permissions)
3565                  {
3566                      break 3;
3567                  }
3568  
3569                  if ($setting != ACL_YES)
3570                  {
3571                      continue;
3572                  }
3573  
3574                  if ($auth_option == 'm_')
3575                  {
3576                      $mod_permissions = true;
3577                  }
3578  
3579                  if ($auth_option == 'a_')
3580                  {
3581                      $admin_permissions = true;
3582                  }
3583              }
3584          }
3585      }
3586  
3587      if ($mod_permissions)
3588      {
3589          if (!function_exists('phpbb_cache_moderators'))
3590          {
3591              global $phpbb_root_path, $phpEx;
3592              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
3593          }
3594          phpbb_cache_moderators($db, $cache, $auth);
3595      }
3596  
3597      if ($mod_permissions || $admin_permissions)
3598      {
3599          if (!function_exists('phpbb_update_foes'))
3600          {
3601              global $phpbb_root_path, $phpEx;
3602              include($phpbb_root_path . 'includes/functions_admin.' . $phpEx);
3603          }
3604          phpbb_update_foes($db, $auth, array($group_id));
3605      }
3606  }
3607  
3608  
3609  
3610  /**
3611  * Funtion to make a user leave the NEWLY_REGISTERED system group.
3612  * @access public
3613  * @param $user_id The id of the user to remove from the group
3614  */
3615  function remove_newly_registered($user_id, $user_data = false)
3616  {
3617      global $db;
3618  
3619      if ($user_data === false)
3620      {
3621          $sql = 'SELECT *
3622              FROM ' . USERS_TABLE . '
3623              WHERE user_id = ' . $user_id;
3624          $result = $db->sql_query($sql);
3625          $user_row = $db->sql_fetchrow($result);
3626          $db->sql_freeresult($result);
3627  
3628          if (!$user_row)
3629          {
3630              return false;
3631          }
3632          else
3633          {
3634              $user_data  = $user_row;
3635          }
3636      }
3637  
3638      if (empty($user_data['user_new']))
3639      {
3640          return false;
3641      }
3642  
3643      $sql = 'SELECT group_id
3644          FROM ' . GROUPS_TABLE . "
3645          WHERE group_name = 'NEWLY_REGISTERED'
3646              AND group_type = " . GROUP_SPECIAL;
3647      $result = $db->sql_query($sql);
3648      $group_id = (int) $db->sql_fetchfield('group_id');
3649      $db->sql_freeresult($result);
3650  
3651      if (!$group_id)
3652      {
3653          return false;
3654      }
3655  
3656      // We need to call group_user_del here, because this function makes sure everything is correctly changed.
3657      // Force function to not log the removal of users from newly registered users group
3658      group_user_del($group_id, $user_id, false, false, false);
3659  
3660      // Set user_new to 0 to let this not be triggered again
3661      $sql = 'UPDATE ' . USERS_TABLE . '
3662          SET user_new = 0
3663          WHERE user_id = ' . $user_id;
3664      $db->sql_query($sql);
3665  
3666      // The new users group was the users default group?
3667      if ($user_data['group_id'] == $group_id)
3668      {
3669          // Which group is now the users default one?
3670          $sql = 'SELECT group_id
3671              FROM ' . USERS_TABLE . '
3672              WHERE user_id = ' . $user_id;
3673          $result = $db->sql_query($sql);
3674          $user_data['group_id'] = $db->sql_fetchfield('group_id');
3675          $db->sql_freeresult($result);
3676      }
3677  
3678      return $user_data['group_id'];
3679  }
3680  
3681  /**
3682  * Gets user ids of currently banned registered users.
3683  *
3684  * @param array $user_ids Array of users' ids to check for banning,
3685  *                        leave empty to get complete list of banned ids
3686  * @param bool|int $ban_end Bool True to get users currently banned
3687  *                         Bool False to only get permanently banned users
3688  *                         Int Unix timestamp to get users banned until that time
3689  * @return array    Array of banned users' ids if any, empty array otherwise
3690  */
3691  function phpbb_get_banned_user_ids($user_ids = array(), $ban_end = true)
3692  {
3693      global $db;
3694  
3695      $sql_user_ids = (!empty($user_ids)) ? $db->sql_in_set('ban_userid', $user_ids) : 'ban_userid <> 0';
3696  
3697      // Get banned User ID's
3698      // Ignore stale bans which were not wiped yet
3699      $banned_ids_list = array();
3700      $sql = 'SELECT ban_userid
3701          FROM ' . BANLIST_TABLE . "
3702          WHERE $sql_user_ids
3703              AND ban_exclude <> 1";
3704  
3705      if ($ban_end === true)
3706      {
3707          // Banned currently
3708          $sql .= " AND (ban_end > " . time() . '
3709                  OR ban_end = 0)';
3710      }
3711      else if ($ban_end === false)
3712      {
3713          // Permanently banned
3714          $sql .= " AND ban_end = 0";
3715      }
3716      else
3717      {
3718          // Banned until a specified time
3719          $sql .= " AND (ban_end > " . (int) $ban_end . '
3720                  OR ban_end = 0)';
3721      }
3722  
3723      $result = $db->sql_query($sql);
3724      while ($row = $db->sql_fetchrow($result))
3725      {
3726          $user_id = (int) $row['ban_userid'];
3727          $banned_ids_list[$user_id] = $user_id;
3728      }
3729      $db->sql_freeresult($result);
3730  
3731      return $banned_ids_list;
3732  }
3733  
3734  /**
3735  * Function for assigning a template var if the zebra module got included
3736  */
3737  function phpbb_module_zebra($mode, &$module_row)
3738  {
3739      global $template;
3740  
3741      $template->assign_var('S_ZEBRA_ENABLED', true);
3742  
3743      if ($mode == 'friends')
3744      {
3745          $template->assign_var('S_ZEBRA_FRIENDS_ENABLED', true);
3746      }
3747  
3748      if ($mode == 'foes')
3749      {
3750          $template->assign_var('S_ZEBRA_FOES_ENABLED', true);
3751      }
3752  }


Generated: Sun Feb 19 19:52:41 2017 Cross-referenced by PHPXref 0.7.1