[ PHPXref.com ] [ Generated: Sat Aug 9 15:45:03 2008 ] [ FluxBB 1.3 ]
[ Index ]     [ Variables ]     [ Functions ]     [ Classes ]     [ Constants ]     [ Statistics ]

title

Body

[close]

/include/ -> functions.php (source)

   1  <?php
   2  /**
   3   * Loads common functions used throughout the site.
   4   *
   5   * @copyright Copyright (C) 2008 FluxBB.org, based on code copyright (C) 2002-2008 PunBB.org
   6   * @license http://www.gnu.org/licenses/gpl.html GPL version 2 or higher
   7   * @package FluxBB
   8   */
   9  
  10  
  11  //
  12  // Return all code blocks that hook into $hook_id
  13  //
  14  function get_hook($hook_id)
  15  {
  16      global $forum_hooks;
  17  
  18      if (defined('FORUM_USE_INCLUDE'))
  19          return !defined('FORUM_DISABLE_HOOKS') && file_exists(FORUM_ROOT.'/cache/cache_hook_'.$hook_id.'.php') ? FORUM_ROOT.'/cache/cache_hook_'.$hook_id.'.php' : false;
  20      else
  21          return !defined('FORUM_DISABLE_HOOKS') && isset($forum_hooks[$hook_id]) ? implode("\n", $forum_hooks[$hook_id]) : false;
  22  }
  23  
  24  
  25  //
  26  // Authenticates the provided username and password against the user database
  27  // $user can be either a user ID (integer) or a username (string)
  28  // $password can be either a plaintext password or a password hash including salt ($password_is_hash must be set accordingly)
  29  //
  30  function authenticate_user($user, $password, $password_is_hash = false)
  31  {
  32      global $forum_db, $forum_user;
  33  
  34      $return = ($hook = get_hook('fn_authenticate_user_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
  35      if ($return != null)
  36          return;
  37  
  38      // Check if there's a user matching $user and $password
  39      $query = array(
  40          'SELECT'    => 'u.*, g.*, o.logged, o.idle, o.csrf_token, o.prev_url',
  41          'FROM'        => 'users AS u',
  42          'JOINS'        => array(
  43              array(
  44                  'INNER JOIN'    => 'groups AS g',
  45                  'ON'            => 'g.g_id=u.group_id'
  46              ),
  47              array(
  48                  'LEFT JOIN'        => 'online AS o',
  49                  'ON'            => 'o.user_id=u.id'
  50              )
  51          )
  52      );
  53  
  54      // Are we looking for a user ID or a username?
  55      $query['WHERE'] = is_int($user) ? 'u.id='.intval($user) : 'u.username=\''.$forum_db->escape($user).'\'';
  56  
  57      ($hook = get_hook('fn_authenticate_user_qr_get_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
  58      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
  59      $forum_user = $forum_db->fetch_assoc($result);
  60  
  61      if (!isset($forum_user['id']) ||
  62          ($password_is_hash && $password != $forum_user['password']) ||
  63          (!$password_is_hash && forum_hash($password, $forum_user['salt']) != $forum_user['password']))
  64          set_default_user();
  65  
  66      ($hook = get_hook('fn_authenticate_user_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
  67  }
  68  
  69  
  70  //
  71  // Attempt to login with the user ID and password hash from the cookie
  72  //
  73  function cookie_login(&$forum_user)
  74  {
  75      global $forum_db, $db_type, $forum_config, $cookie_name, $cookie_path, $cookie_domain, $cookie_secure, $forum_time_formats, $forum_date_formats;
  76  
  77      $now = time();
  78      $expire = $now + 1209600;    // The cookie expires after 14 days
  79  
  80      // We assume it's a guest
  81      $cookie = array('user_id' => 1, 'password_hash' => 'Guest', 'expiration_time' => 0, 'expire_hash' => 'Guest');
  82  
  83      $return = ($hook = get_hook('fn_cookie_login_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
  84      if ($return != null)
  85          return;
  86  
  87      // If a cookie is set, we get the user_id and password hash from it
  88      if (isset($_COOKIE[$cookie_name]))
  89          @list($cookie['user_id'], $cookie['password_hash'], $cookie['expiration_time'], $cookie['expire_hash']) = @explode('|', base64_decode($_COOKIE[$cookie_name]));
  90  
  91      ($hook = get_hook('fn_cookie_login_fetch_cookie')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
  92  
  93      // If this a cookie for a logged in user and it shouldn't have already expired
  94      if (intval($cookie['user_id']) > 1 && intval($cookie['expiration_time']) > $now)
  95      {
  96          authenticate_user(intval($cookie['user_id']), $cookie['password_hash'], true);
  97  
  98          // We now validate the cookie hash
  99          if ($cookie['expire_hash'] !== sha1($forum_user['salt'].$forum_user['password'].forum_hash(intval($cookie['expiration_time']), $forum_user['salt'])))
 100              set_default_user();
 101  
 102          // If we got back the default user, the login failed
 103          if ($forum_user['id'] == '1')
 104          {
 105              forum_setcookie($cookie_name, base64_encode('1|'.random_key(8, false, true).'|'.$expire.'|'.random_key(8, false, true)), $expire);
 106              return;
 107          }
 108  
 109          // Send a new, updated cookie with a new expiration timestamp
 110          $expire = (intval($cookie['expiration_time']) > $now + $forum_config['o_timeout_visit']) ? $now + 1209600 : $now + $forum_config['o_timeout_visit'];
 111          forum_setcookie($cookie_name, base64_encode($forum_user['id'].'|'.$forum_user['password'].'|'.$expire.'|'.sha1($forum_user['salt'].$forum_user['password'].forum_hash($expire, $forum_user['salt']))), $expire);
 112  
 113          // Set a default language if the user selected language no longer exists
 114          if (!file_exists(FORUM_ROOT.'lang/'.$forum_user['language'].'/common.php'))
 115              $forum_user['language'] = $forum_config['o_default_lang'];
 116  
 117          // Set a default style if the user selected style no longer exists
 118          if (!file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/'.$forum_user['style'].'.php'))
 119              $forum_user['style'] = $forum_config['o_default_style'];
 120  
 121          if (!$forum_user['disp_topics'])
 122              $forum_user['disp_topics'] = $forum_config['o_disp_topics_default'];
 123          if (!$forum_user['disp_posts'])
 124              $forum_user['disp_posts'] = $forum_config['o_disp_posts_default'];
 125  
 126          // Check user has a valid date and time format
 127          if (!isset($forum_time_formats[$forum_user['time_format']]))
 128              $forum_user['time_format'] = 0;
 129          if (!isset($forum_date_formats[$forum_user['date_format']]))
 130              $forum_user['date_format'] = 0;
 131  
 132          // Define this if you want this visit to affect the online list and the users last visit data
 133          if (!defined('FORUM_QUIET_VISIT'))
 134          {
 135              // Update the online list
 136              if (!$forum_user['logged'])
 137              {
 138                  $forum_user['logged'] = $now;
 139                  $forum_user['csrf_token'] = random_key(40, false, true);
 140                  $forum_user['prev_url'] = get_current_url(255);
 141  
 142                  // REPLACE INTO avoids a user having two rows in the online table
 143                  $query = array(
 144                      'REPLACE'    => 'user_id, ident, logged, csrf_token',
 145                      'INTO'        => 'online',
 146                      'VALUES'    => $forum_user['id'].', \''.$forum_db->escape($forum_user['username']).'\', '.$forum_user['logged'].', \''.$forum_user['csrf_token'].'\'',
 147                      'UNIQUE'    => 'user_id='.$forum_user['id']
 148                  );
 149  
 150                  if ($forum_user['prev_url'] != null)
 151                  {
 152                      $query['REPLACE'] .= ', prev_url';
 153                      $query['VALUES'] .= ', \''.$forum_db->escape($forum_user['prev_url']).'\'';
 154                  }
 155  
 156                  ($hook = get_hook('fn_cookie_login_qr_add_online_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 157                  $forum_db->query_build($query) or error(__FILE__, __LINE__);
 158  
 159                  // Reset tracked topics
 160                  set_tracked_topics(null);
 161              }
 162              else
 163              {
 164                  // Special case: We've timed out, but no other user has browsed the forums since we timed out
 165                  if ($forum_user['logged'] < ($now-$forum_config['o_timeout_visit']))
 166                  {
 167                      $query = array(
 168                          'UPDATE'    => 'users',
 169                          'SET'        => 'last_visit='.$forum_user['logged'],
 170                          'WHERE'        => 'id='.$forum_user['id']
 171                      );
 172  
 173                      ($hook = get_hook('fn_cookie_login_qr_update_user_visit')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 174                      $forum_db->query_build($query) or error(__FILE__, __LINE__);
 175  
 176                      $forum_user['last_visit'] = $forum_user['logged'];
 177                  }
 178  
 179                  // Now update the logged time and save the current URL in the online list
 180                  $query = array(
 181                      'UPDATE'    => 'online',
 182                      'SET'        => 'logged='.$now,
 183                      'WHERE'        => 'user_id='.$forum_user['id']
 184                  );
 185  
 186                  $current_url = get_current_url(255);
 187                  if ($current_url != null)
 188                      $query['SET'] .= ', prev_url=\''.$forum_db->escape($current_url).'\'';
 189  
 190                  if ($forum_user['idle'] == '1')
 191                      $query['SET'] .= ', idle=0';
 192  
 193                  ($hook = get_hook('fn_cookie_login_qr_update_online_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 194                  $forum_db->query_build($query) or error(__FILE__, __LINE__);
 195  
 196                  // Update tracked topics with the current expire time
 197                  if (isset($_COOKIE[$cookie_name.'_track']))
 198                      forum_setcookie($cookie_name.'_track', $_COOKIE[$cookie_name.'_track'], $now + $forum_config['o_timeout_visit']);
 199              }
 200          }
 201  
 202          $forum_user['is_guest'] = false;
 203          $forum_user['is_admmod'] = $forum_user['g_id'] == FORUM_ADMIN || $forum_user['g_moderator'] == '1';
 204      }
 205      else
 206          set_default_user();
 207  
 208      ($hook = get_hook('fn_cookie_login_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 209  }
 210  
 211  
 212  //
 213  // Fill $forum_user with default values (for guests)
 214  //
 215  function set_default_user()
 216  {
 217      global $forum_db, $db_type, $forum_user, $forum_config;
 218  
 219      $remote_addr = get_remote_address();
 220  
 221      $return = ($hook = get_hook('fn_set_default_user_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 222      if ($return != null)
 223          return;
 224  
 225      // Fetch guest user
 226      $query = array(
 227          'SELECT'    => 'u.*, g.*, o.logged, o.csrf_token, o.prev_url',
 228          'FROM'        => 'users AS u',
 229          'JOINS'        => array(
 230              array(
 231                  'INNER JOIN'    => 'groups AS g',
 232                  'ON'            => 'g.g_id=u.group_id'
 233              ),
 234              array(
 235                  'LEFT JOIN'        => 'online AS o',
 236                  'ON'            => 'o.ident=\''.$forum_db->escape($remote_addr).'\''
 237              )
 238          ),
 239          'WHERE'        => 'u.id=1'
 240      );
 241  
 242      ($hook = get_hook('fn_set_default_user_qr_get_default_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 243      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 244      if (!$forum_db->num_rows($result))
 245          exit('Unable to fetch guest information. The table \''.$forum_db->prefix.'users\' must contain an entry with id = 1 that represents anonymous users.');
 246  
 247      $forum_user = $forum_db->fetch_assoc($result);
 248  
 249      // Update online list
 250      if (!$forum_user['logged'])
 251      {
 252          $forum_user['logged'] = time();
 253          $forum_user['csrf_token'] = random_key(40, false, true);
 254          $forum_user['prev_url'] = get_current_url(255);
 255  
 256          // REPLACE INTO avoids a user having two rows in the online table
 257          $query = array(
 258              'REPLACE'    => 'user_id, ident, logged, csrf_token',
 259              'INTO'        => 'online',
 260              'VALUES'    => '1, \''.$forum_db->escape($remote_addr).'\', '.$forum_user['logged'].', \''.$forum_user['csrf_token'].'\'',
 261              'UNIQUE'    => 'user_id=1 AND ident=\''.$forum_db->escape($remote_addr).'\''
 262          );
 263  
 264          if ($forum_user['prev_url'] != null)
 265          {
 266              $query['REPLACE'] .= ', prev_url';
 267              $query['VALUES'] .= ', \''.$forum_db->escape($forum_user['prev_url']).'\'';
 268          }
 269  
 270          ($hook = get_hook('fn_set_default_user_qr_add_online_guest_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 271          $forum_db->query_build($query) or error(__FILE__, __LINE__);
 272      }
 273      else
 274      {
 275          $query = array(
 276              'UPDATE'    => 'online',
 277              'SET'        => 'logged='.time(),
 278              'WHERE'        => 'ident=\''.$forum_db->escape($remote_addr).'\''
 279          );
 280  
 281          $current_url = get_current_url(255);
 282          if ($current_url != null)
 283              $query['SET'] .= ', prev_url=\''.$forum_db->escape($current_url).'\'';
 284  
 285          ($hook = get_hook('fn_set_default_user_qr_update_online_guest_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 286          $forum_db->query_build($query) or error(__FILE__, __LINE__);
 287      }
 288  
 289      $forum_user['disp_topics'] = $forum_config['o_disp_topics_default'];
 290      $forum_user['disp_posts'] = $forum_config['o_disp_posts_default'];
 291      $forum_user['timezone'] = $forum_config['o_default_timezone'];
 292      $forum_user['language'] = $forum_config['o_default_lang'];
 293      $forum_user['style'] = $forum_config['o_default_style'];
 294      $forum_user['is_guest'] = true;
 295      $forum_user['is_admmod'] = false;
 296  }
 297  
 298  
 299  //
 300  // Set a cookie, FluxBB style!
 301  //
 302  function forum_setcookie($name, $value, $expire)
 303  {
 304      global $cookie_path, $cookie_domain, $cookie_secure;
 305  
 306      $return = ($hook = get_hook('fn_forum_setcookie_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 307      if ($return != null)
 308          return;
 309  
 310      // Enable sending of a P3P header
 311      @header('P3P: CP="CUR ADM"');
 312  
 313      if (version_compare(PHP_VERSION, '5.2.0', '>='))
 314          setcookie($name, $value, $expire, $cookie_path, $cookie_domain, $cookie_secure, true);
 315      else
 316          setcookie($name, $value, $expire, $cookie_path.'; HttpOnly', $cookie_domain, $cookie_secure);
 317  }
 318  
 319  
 320  //
 321  // Check whether the connecting user is banned (and delete any expired bans while we're at it)
 322  //
 323  function check_bans()
 324  {
 325      global $forum_db, $forum_config, $lang_common, $forum_user, $forum_bans;
 326  
 327      $return = ($hook = get_hook('fn_check_bans_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 328      if ($return != null)
 329          return;
 330  
 331      // Admins aren't affected
 332      if (defined('FORUM_ADMIN') && $forum_user['g_id'] == FORUM_ADMIN || !$forum_bans)
 333          return;
 334  
 335      // Add a dot or a colon (depending on IPv4/IPv6) at the end of the IP address to prevent banned address
 336      // 192.168.0.5 from matching e.g. 192.168.0.50
 337      $user_ip = get_remote_address();
 338      $user_ip .= (strpos($user_ip, '.') !== false) ? '.' : ':';
 339  
 340      $bans_altered = false;
 341      $is_banned = false;
 342  
 343      foreach ($forum_bans as $cur_ban)
 344      {
 345          // Has this ban expired?
 346          if ($cur_ban['expire'] != '' && $cur_ban['expire'] <= time())
 347          {
 348              $query = array(
 349                  'DELETE'    => 'bans',
 350                  'WHERE'        => 'id='.$cur_ban['id']
 351              );
 352  
 353              ($hook = get_hook('fn_check_bans_qr_delete_expired_ban')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 354              $forum_db->query_build($query) or error(__FILE__, __LINE__);
 355  
 356              $bans_altered = true;
 357              continue;
 358          }
 359  
 360          if ($cur_ban['username'] != '' && utf8_strtolower($forum_user['username']) == utf8_strtolower($cur_ban['username']))
 361              $is_banned = true;
 362  
 363          if ($cur_ban['ip'] != '')
 364          {
 365              $cur_ban_ips = explode(' ', $cur_ban['ip']);
 366  
 367              $num_ips = count($cur_ban_ips);
 368              for ($i = 0; $i < $num_ips; ++$i)
 369              {
 370                  // Add the proper ending to the ban
 371                  if (strpos($user_ip, '.') !== false)
 372                      $cur_ban_ips[$i] = $cur_ban_ips[$i].'.';
 373                  else
 374                      $cur_ban_ips[$i] = $cur_ban_ips[$i].':';
 375  
 376                  if (substr($user_ip, 0, strlen($cur_ban_ips[$i])) == $cur_ban_ips[$i])
 377                  {
 378                      $is_banned = true;
 379                      break;
 380                  }
 381              }
 382          }
 383  
 384          if ($is_banned)
 385          {
 386              $query = array(
 387                  'DELETE'    => 'online',
 388                  'WHERE'        => 'ident=\''.$forum_db->escape($forum_user['username']).'\''
 389              );
 390  
 391              ($hook = get_hook('fn_check_bans_qr_delete_online_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 392              $forum_db->query_build($query) or error(__FILE__, __LINE__);
 393  
 394              message($lang_common['Ban message'].(($cur_ban['expire'] != '') ? ' '.sprintf($lang_common['Ban message 2'], format_time($cur_ban['expire'], 1, null, null, true)) : '').(($cur_ban['message'] != '') ? ' '.$lang_common['Ban message 3'].'</p><p><strong>'.forum_htmlencode($cur_ban['message']).'</strong></p>' : '</p>').'<p>'.sprintf($lang_common['Ban message 4'], '<a href="mailto:'.forum_htmlencode($forum_config['o_admin_email']).'">'.forum_htmlencode($forum_config['o_admin_email']).'</a>'));
 395          }
 396      }
 397  
 398      // If we removed any expired bans during our run-through, we need to regenerate the bans cache
 399      if ($bans_altered)
 400      {
 401          if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
 402              require  FORUM_ROOT.'include/cache.php';
 403  
 404          generate_bans_cache();
 405      }
 406  }
 407  
 408  
 409  //
 410  // Update "Users online"
 411  //
 412  function update_users_online()
 413  {
 414      global $forum_db, $forum_config, $forum_user;
 415  
 416      $now = time();
 417  
 418      $return = ($hook = get_hook('fn_update_users_online_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 419      if ($return != null)
 420          return;
 421  
 422      // Fetch all online list entries that are older than "o_timeout_online"
 423      $query = array(
 424          'SELECT'    => 'o.*',
 425          'FROM'        => 'online AS o',
 426          'WHERE'        => 'o.logged<'.($now-$forum_config['o_timeout_online'])
 427      );
 428  
 429      ($hook = get_hook('fn_update_users_online_qr_get_old_online_users')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 430      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 431      while ($cur_user = $forum_db->fetch_assoc($result))
 432      {
 433          // If the entry is a guest, delete it
 434          if ($cur_user['user_id'] == '1')
 435          {
 436              $query = array(
 437                  'DELETE'    => 'online',
 438                  'WHERE'        => 'ident=\''.$forum_db->escape($cur_user['ident']).'\''
 439              );
 440  
 441              ($hook = get_hook('fn_update_users_online_qr_delete_online_guest_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 442              $forum_db->query_build($query) or error(__FILE__, __LINE__);
 443          }
 444          else
 445          {
 446              // If the entry is older than "o_timeout_visit", update last_visit for the user in question, then delete him/her from the online list
 447              if ($cur_user['logged'] < ($now-$forum_config['o_timeout_visit']))
 448              {
 449                  $query = array(
 450                      'UPDATE'    => 'users',
 451                      'SET'        => 'last_visit='.$cur_user['logged'],
 452                      'WHERE'        => 'id='.$cur_user['user_id']
 453                  );
 454  
 455                  ($hook = get_hook('fn_update_users_online_qr_update_user_visit')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 456                  $forum_db->query_build($query) or error(__FILE__, __LINE__);
 457  
 458                  $query = array(
 459                      'DELETE'    => 'online',
 460                      'WHERE'        => 'user_id='.$cur_user['user_id']
 461                  );
 462  
 463                  ($hook = get_hook('fn_update_users_online_qr_delete_online_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 464                  $forum_db->query_build($query) or error(__FILE__, __LINE__);
 465              }
 466              else if ($cur_user['idle'] == '0')
 467              {
 468                  $query = array(
 469                      'UPDATE'    => 'online',
 470                      'SET'        => 'idle=1',
 471                      'WHERE'        => 'user_id='.$cur_user['user_id']
 472                  );
 473  
 474                  ($hook = get_hook('fn_update_users_online_qr_update_user_idle')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 475                  $forum_db->query_build($query) or error(__FILE__, __LINE__);
 476              }
 477          }
 478      }
 479  
 480      ($hook = get_hook('fn_update_users_online_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 481  }
 482  
 483  
 484  //
 485  // Generate the "navigator" that appears at the top of every page
 486  //
 487  function generate_navlinks()
 488  {
 489      global $forum_config, $lang_common, $forum_url, $forum_user;
 490  
 491      $return = ($hook = get_hook('fn_generate_navlinks_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 492      if ($return != null)
 493          return $return;
 494  
 495      // Index should always be displayed
 496      $links['index'] = '<li id="navindex"'.((FORUM_PAGE == 'index') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['index']).'"><span>'.$lang_common['Index'].'</span></a></li>';
 497  
 498      if ($forum_user['g_read_board'] == '1' && $forum_user['g_view_users'] == '1')
 499          $links['userlist'] = '<li id="navuserlist"'.((FORUM_PAGE == 'userlist') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['users']).'"><span>'.$lang_common['User list'].'</span></a></li>';
 500  
 501      if ($forum_config['o_rules'] == '1' && (!$forum_user['is_guest'] || $forum_user['g_read_board'] == '1' || $forum_config['o_regs_allow'] == '1'))
 502          $links['rules'] = '<li id="navrules"'.((FORUM_PAGE == 'rules') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['rules']).'"><span>'.$lang_common['Rules'].'</span></a></li>';
 503  
 504      if ($forum_user['is_guest'])
 505      {
 506          if ($forum_user['g_read_board'] == '1' && $forum_user['g_search'] == '1')
 507              $links['search'] = '<li id="navsearch"'.((FORUM_PAGE == 'search') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['search']).'"><span>'.$lang_common['Search'].'</span></a></li>';
 508  
 509          $links['register'] = '<li id="navregister"'.((FORUM_PAGE == 'register') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['register']).'"><span>'.$lang_common['Register'].'</span></a></li>';
 510          $links['login'] = '<li id="navlogin"'.((FORUM_PAGE == 'login') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['login']).'"><span>'.$lang_common['Login'].'</span></a></li>';
 511      }
 512      else
 513      {
 514          if (!$forum_user['is_admmod'])
 515          {
 516              if ($forum_user['g_read_board'] == '1' && $forum_user['g_search'] == '1')
 517                  $links['search'] = '<li id="navsearch"'.((FORUM_PAGE == 'search') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['search']).'"><span>'.$lang_common['Search'].'</span></a></li>';
 518  
 519              $links['profile'] = '<li id="navprofile"'.((substr(FORUM_PAGE, 0, 7) == 'profile') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['user'], $forum_user['id']).'"><span>'.$lang_common['Profile'].'</span></a></li>';
 520              $links['logout'] = '<li id="navlogout"><a href="'.forum_link($forum_url['logout'], array($forum_user['id'], generate_form_token('logout'.$forum_user['id']))).'"><span>'.$lang_common['Logout'].'</span></a></li>';
 521          }
 522          else
 523          {
 524              $links['search'] = '<li id="navsearch"'.((FORUM_PAGE == 'search') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['search']).'"><span>'.$lang_common['Search'].'</span></a></li>';
 525              $links['profile'] = '<li id="navprofile"'.((FORUM_PAGE == 'editprofile' || FORUM_PAGE == 'viewprofile') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['user'], $forum_user['id']).'"><span>'.$lang_common['Profile'].'</span></a></li>';
 526              $links['logout'] = '<li id="navlogout"><a href="'.forum_link($forum_url['logout'], array($forum_user['id'], generate_form_token('logout'.$forum_user['id']))).'"><span>'.$lang_common['Logout'].'</span></a></li>';
 527              $links['admin'] = '<li id="navadmin"'.((substr(FORUM_PAGE, 0, 5) == 'admin') ? ' class="isactive"' : '').'><a href="'.forum_link($forum_url['admin_index']).'"><span>'.$lang_common['Admin'].'</span></a></li>';
 528          }
 529      }
 530  
 531      // Are there any additional navlinks we should insert into the array before imploding it?
 532      if ($forum_config['o_additional_navlinks'] != '')
 533      {
 534          if (preg_match_all('#([0-9]+)\s*=\s*(.*?)\n#s', $forum_config['o_additional_navlinks']."\n", $extra_links))
 535          {
 536              // Insert any additional links into the $links array (at the correct index)
 537              $num_links = count($extra_links[1]);
 538              for ($i = 0; $i < $num_links; ++$i)
 539                  array_insert($links, (int)$extra_links[1][$i], '<li id="navextra'.($i + 1).'">'.$extra_links[2][$i].'</li>');
 540          }
 541      }
 542  
 543      ($hook = get_hook('fn_generate_navlinks_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 544  
 545      return implode("\n\t\t", $links);
 546  }
 547  
 548  
 549  //
 550  // Outputs markup to display a user's avatar
 551  //
 552  function generate_avatar_markup($user_id)
 553  {
 554      global $forum_config, $base_url;
 555  
 556      $filetypes = array('jpg', 'gif', 'png');
 557      $avatar_markup = '';
 558  
 559      $return = ($hook = get_hook('fn_generate_avatar_markup_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 560      if ($return != null)
 561          return $return;
 562  
 563      foreach ($filetypes as $cur_type)
 564      {
 565          $path = $forum_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type;
 566  
 567          if (file_exists(FORUM_ROOT.$path) && $img_size = @getimagesize(FORUM_ROOT.$path))
 568          {
 569              $avatar_markup = '<img src="'.$base_url.'/'.$path.'?m='.filemtime(FORUM_ROOT.$path).'" '.$img_size[3].' alt="" />';
 570              break;
 571          }
 572      }
 573  
 574      ($hook = get_hook('fn_generate_avatar_markup_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 575  
 576      return $avatar_markup;
 577  }
 578  
 579  
 580  //
 581  // Generate breadcrumb navigation
 582  //
 583  function generate_crumbs($reverse)
 584  {
 585      global $lang_common, $forum_url, $forum_config, $forum_page;
 586  
 587      $return = ($hook = get_hook('fn_generate_crumbs_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 588      if ($return != null)
 589          return $return;
 590  
 591      if (empty($forum_page['crumbs']))
 592          $forum_page['crumbs'][0] = $forum_config['o_board_title'];
 593  
 594      $crumbs = '';
 595      $num_crumbs = count($forum_page['crumbs']);
 596  
 597      if ($reverse)
 598      {
 599          for ($i = ($num_crumbs - 1); $i >= 0; --$i)
 600              $crumbs .= (is_array($forum_page['crumbs'][$i]) ? forum_htmlencode($forum_page['crumbs'][$i][0]) : forum_htmlencode($forum_page['crumbs'][$i])).((isset($forum_page['page']) && $i == ($num_crumbs - 1)) ? ' ('.$lang_common['Page'].' '.forum_number_format($forum_page['page']).')' : '').($i > 0 ? $lang_common['Title separator'] : '');
 601      }
 602      else
 603      {
 604          for ($i = 0; $i < $num_crumbs; ++$i)
 605          {
 606              if ($i < ($num_crumbs - 1))
 607                  $crumbs .= '<span class="crumb'.(($i == 0) ? ' crumbfirst' : '').'">'.(($i >= 1) ? '<span>'.$lang_common['Crumb separator'].'</span>' : '').(is_array($forum_page['crumbs'][$i]) ? '<a href="'.$forum_page['crumbs'][$i][1].'">'.forum_htmlencode($forum_page['crumbs'][$i][0]).'</a>' : forum_htmlencode($forum_page['crumbs'][$i])).'</span> ';
 608              else
 609                  $crumbs .= '<span class="crumb crumblast'.(($i == 0) ? ' crumbfirst' : '').'">'.(($i >= 1) ? '<span>'.$lang_common['Crumb separator'].'</span>' : '').(is_array($forum_page['crumbs'][$i]) ? '<a href="'.$forum_page['crumbs'][$i][1].'">'.forum_htmlencode($forum_page['crumbs'][$i][0]).'</a>' : forum_htmlencode($forum_page['crumbs'][$i])).'</span> ';
 610          }
 611      }
 612  
 613      ($hook = get_hook('fn_generate_crumbs_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 614  
 615      return $crumbs;
 616  }
 617  
 618  
 619  //
 620  // Save array of tracked topics in cookie
 621  //
 622  function set_tracked_topics($tracked_topics)
 623  {
 624      global $cookie_name, $cookie_path, $cookie_domain, $cookie_secure, $forum_config;
 625  
 626      $return = ($hook = get_hook('fn_set_tracked_topics_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 627      if ($return != null)
 628          return;
 629  
 630      $cookie_data = '';
 631      if (!empty($tracked_topics))
 632      {
 633          // Sort the arrays (latest read first)
 634          arsort($tracked_topics['topics'], SORT_NUMERIC);
 635          arsort($tracked_topics['forums'], SORT_NUMERIC);
 636  
 637          // Homebrew serialization (to avoid having to run unserialize() on cookie data)
 638          foreach ($tracked_topics['topics'] as $id => $timestamp)
 639              $cookie_data .= 't'.$id.'='.$timestamp.';';
 640          foreach ($tracked_topics['forums'] as $id => $timestamp)
 641              $cookie_data .= 'f'.$id.'='.$timestamp.';';
 642  
 643          // Enforce a 4048 byte size limit (4096 minus some space for the cookie name)
 644          if (strlen($cookie_data) > 4048)
 645          {
 646              $cookie_data = substr($cookie_data, 0, 4048);
 647              $cookie_data = substr($cookie_data, 0, strrpos($cookie_data, ';')).';';
 648          }
 649      }
 650  
 651      forum_setcookie($cookie_name.'_track', $cookie_data, time() + $forum_config['o_timeout_visit']);
 652      $_COOKIE[$cookie_name.'_track'] = $cookie_data;    // Set it directly in $_COOKIE as well
 653  }
 654  
 655  
 656  //
 657  // Extract array of tracked topics from cookie
 658  //
 659  function get_tracked_topics()
 660  {
 661      global $cookie_name;
 662  
 663      $return = ($hook = get_hook('fn_get_tracked_topics_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 664      if ($return != null)
 665          return $return;
 666  
 667      $cookie_data = isset($_COOKIE[$cookie_name.'_track']) ? $_COOKIE[$cookie_name.'_track'] : false;
 668      if (!$cookie_data)
 669          return array('topics' => array(), 'forums' => array());
 670  
 671      if (strlen($cookie_data) > 4048)
 672          return array('topics' => array(), 'forums' => array());
 673  
 674      // Unserialize data from cookie
 675      $tracked_topics = array('topics' => array(), 'forums' => array());
 676      $temp = explode(';', $cookie_data);
 677      foreach ($temp as $t)
 678      {
 679          $type = substr($t, 0, 1) == 'f' ? 'forums' : 'topics';
 680          $id = intval(substr($t, 1));
 681          $timestamp = intval(@substr($t, strpos($t, '=') + 1));
 682          if ($id > 0 && $timestamp > 0)
 683              $tracked_topics[$type][$id] = $timestamp;
 684      }
 685  
 686      ($hook = get_hook('fn_get_tracked_topics_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 687  
 688      return $tracked_topics;
 689  }
 690  
 691  
 692  //
 693  // Update posts, topics, last_post, last_post_id and last_poster for a forum
 694  //
 695  function sync_forum($forum_id)
 696  {
 697      global $forum_db;
 698  
 699      $return = ($hook = get_hook('fn_sync_forum_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 700      if ($return != null)
 701          return;
 702  
 703      // Get topic and post count for forum
 704      $query = array(
 705          'SELECT'    => 'COUNT(t.id), SUM(t.num_replies)',
 706          'FROM'        => 'topics AS t',
 707          'WHERE'        => 't.forum_id='.$forum_id
 708      );
 709  
 710      ($hook = get_hook('fn_sync_forum_qr_get_forum_stats')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 711      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 712      list($num_topics, $num_posts) = $forum_db->fetch_row($result);
 713  
 714      $num_posts = $num_posts + $num_topics;        // $num_posts is only the sum of all replies (we have to add the topic posts)
 715  
 716      // Get last_post, last_post_id and last_poster for forum (if any)
 717      $query = array(
 718          'SELECT'    => 't.last_post, t.last_post_id, t.last_poster',
 719          'FROM'        => 'topics AS t',
 720          'WHERE'        => 't.forum_id='.$forum_id.' AND t.moved_to is NULL',
 721          'ORDER BY'    => 't.last_post DESC',
 722          'LIMIT'        => '1'
 723      );
 724  
 725      ($hook = get_hook('fn_sync_forum_qr_get_forum_last_post_data')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 726      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 727      if ($forum_db->num_rows($result))
 728      {
 729          list($last_post, $last_post_id, $last_poster) = $forum_db->fetch_row($result);
 730          $last_poster = '\''.$forum_db->escape($last_poster).'\'';
 731      }
 732      else
 733          $last_post = $last_post_id = $last_poster = 'NULL';
 734  
 735      // Now update the forum
 736      $query = array(
 737          'UPDATE'    => 'forums',
 738          'SET'        => 'num_topics='.$num_topics.', num_posts='.$num_posts.', last_post='.$last_post.', last_post_id='.$last_post_id.', last_poster='.$last_poster,
 739          'WHERE'        => 'id='.$forum_id
 740      );
 741  
 742      ($hook = get_hook('fn_sync_forum_qr_update_forum')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 743      $forum_db->query_build($query) or error(__FILE__, __LINE__);
 744  
 745      ($hook = get_hook('fn_sync_forum_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 746  }
 747  
 748  
 749  //
 750  // Update replies, last_post, last_post_id and last_poster for a topic
 751  //
 752  function sync_topic($topic_id)
 753  {
 754      global $forum_db;
 755  
 756      $return = ($hook = get_hook('fn_sync_topic_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 757      if ($return != null)
 758          return;
 759  
 760      // Count number of replies in the topic
 761      $query = array(
 762          'SELECT'    => 'COUNT(p.id)',
 763          'FROM'        => 'posts AS p',
 764          'WHERE'        => 'p.topic_id='.$topic_id
 765      );
 766  
 767      ($hook = get_hook('fn_sync_topic_qr_get_topic_reply_count')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 768      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 769      $num_replies = $forum_db->result($result, 0) - 1;
 770  
 771      // Get last_post, last_post_id and last_poster
 772      $query = array(
 773          'SELECT'    => 'p.posted, p.id, p.poster',
 774          'FROM'        => 'posts AS p',
 775          'WHERE'        => 'p.topic_id='.$topic_id,
 776          'ORDER BY'    => 'p.id DESC',
 777          'LIMIT'        => '1'
 778      );
 779  
 780      ($hook = get_hook('fn_sync_topic_qr_get_topic_last_post_data')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 781      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 782      list($last_post, $last_post_id, $last_poster) = $forum_db->fetch_row($result);
 783  
 784      // Now update the topic
 785      $query = array(
 786          'UPDATE'    => 'topics',
 787          'SET'        => 'num_replies='.$num_replies.', last_post='.$last_post.', last_post_id='.$last_post_id.', last_poster=\''.$forum_db->escape($last_poster).'\'',
 788          'WHERE'        => 'id='.$topic_id
 789      );
 790  
 791      ($hook = get_hook('fn_sync_topic_qr_update_topic')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 792      $forum_db->query_build($query) or error(__FILE__, __LINE__);
 793  
 794      ($hook = get_hook('fn_sync_topic_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 795  }
 796  
 797  
 798  //
 799  // Verifies that the provided username is OK for insertion into the database
 800  //
 801  function validate_username($username, $exclude_id = null)
 802  {
 803      global $lang_common, $lang_register, $lang_profile, $forum_config;
 804  
 805      $errors = array();
 806  
 807      $return = ($hook = get_hook('fn_validate_username_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 808      if ($return != null)
 809          return $return;
 810  
 811      // Convert multiple whitespace characters into one (to prevent people from registering with indistinguishable usernames)
 812      $username = preg_replace('#\s+#s', ' ', $username);
 813  
 814      // Validate username
 815      if (utf8_strlen($username) < 2)
 816          $errors[] = $lang_profile['Username too short'];
 817      else if (utf8_strlen($username) > 25)
 818          $errors[] = $lang_profile['Username too long'];
 819      else if (strtolower($username) == 'guest' || utf8_strtolower($username) == utf8_strtolower($lang_common['Guest']))
 820          $errors[] = $lang_profile['Username guest'];
 821      else if (preg_match('/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/', $username) || preg_match('/((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))/', $username))
 822          $errors[] = $lang_profile['Username IP'];
 823      else if ((strpos($username, '[') !== false || strpos($username, ']') !== false) && strpos($username, '\'') !== false && strpos($username, '"') !== false)
 824          $errors[] = $lang_profile['Username reserved chars'];
 825      else if (preg_match('/(?:\[\/?(?:b|u|i|h|colou?r|quote|code|img|url|email|list)\]|\[(?:code|quote|list)=)/i', $username))
 826          $errors[] = $lang_profile['Username BBCode'];
 827  
 828      // Check username for any censored words
 829      if ($forum_config['o_censoring'] == '1' && censor_words($username) != $username)
 830          $errors[] = $lang_profile['Username censor'];
 831  
 832      // Check for username dupe
 833      $dupe = check_username_dupe($username, $exclude_id);
 834      if ($dupe !== false)
 835          $errors[] = sprintf($lang_profile['Username dupe'], forum_htmlencode($dupe));
 836  
 837      ($hook = get_hook('fn_validate_username_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 838  
 839      return $errors;
 840  }
 841  
 842  
 843  //
 844  // Adds a new user. The username must be passed through validate_username() first.
 845  //
 846  function add_user($user_info, &$new_uid)
 847  {
 848      global $forum_db, $base_url, $lang_common, $forum_config, $forum_user, $forum_url;
 849  
 850      $return = ($hook = get_hook('fn_add_user_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 851      if ($return != null)
 852          return;
 853  
 854      // Add the user
 855      $query = array(
 856          'INSERT'    => 'username, group_id, password, email, email_setting, timezone, dst, language, style, registered, registration_ip, last_visit, salt, activate_key',
 857          'INTO'        => 'users',
 858          'VALUES'    => '\''.$forum_db->escape($user_info['username']).'\', '.$user_info['group_id'].', \''.$forum_db->escape($user_info['password_hash']).'\', \''.$forum_db->escape($user_info['email']).'\', '.$user_info['email_setting'].', '.floatval($user_info['timezone']).', '.$user_info['dst'].', \''.$forum_db->escape($user_info['language']).'\', \''.$forum_db->escape($user_info['style']).'\', '.$user_info['registered'].', \''.$forum_db->escape($user_info['registration_ip']).'\', '.$user_info['registered'].', \''.$forum_db->escape($user_info['salt']).'\', '.$user_info['activate_key'].''
 859      );
 860  
 861      ($hook = get_hook('fn_add_user_qr_insert_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 862      $forum_db->query_build($query) or error(__FILE__, __LINE__);
 863      $new_uid = $forum_db->insert_id();
 864  
 865      // Must the user verify the registration?
 866      if ($user_info['require_verification'])
 867      {
 868          // Load the "welcome" template
 869          $mail_tpl = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$forum_user['language'].'/mail_templates/welcome.tpl'));
 870  
 871          // The first row contains the subject
 872          $first_crlf = strpos($mail_tpl, "\n");
 873          $mail_subject = forum_trim(substr($mail_tpl, 8, $first_crlf-8));
 874          $mail_message = forum_trim(substr($mail_tpl, $first_crlf));
 875  
 876          $mail_subject = str_replace('<board_title>', $forum_config['o_board_title'], $mail_subject);
 877          $mail_message = str_replace('<base_url>', $base_url.'/', $mail_message);
 878          $mail_message = str_replace('<username>', $user_info['username'], $mail_message);
 879          $mail_message = str_replace('<activation_url>', str_replace('&amp;', '&', forum_link($forum_url['change_password_key'], array($new_uid, substr($user_info['activate_key'], 1, -1)))), $mail_message);
 880          $mail_message = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message);
 881  
 882          ($hook = get_hook('fn_add_user_send_verification')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 883  
 884          forum_mail($user_info['email'], $mail_subject, $mail_message);
 885      }
 886  
 887      // Should we alert people on the admin mailing list that a new user has registered?
 888      if ($user_info['notify_admins'] && $forum_config['o_mailing_list'] != '')
 889      {
 890          $mail_subject = 'Alert - New registration';
 891          $mail_message = 'User \''.$user_info['username'].'\' registered in the forums at '.$base_url.'/'."\n\n".'User profile: '.forum_link($forum_url['user'], $new_uid)."\n\n".'-- '."\n".'Forum Mailer'."\n".'(Do not reply to this message)';
 892  
 893          forum_mail($forum_config['o_mailing_list'], $mail_subject, $mail_message);
 894      }
 895  
 896      ($hook = get_hook('fn_add_user_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 897  }
 898  
 899  
 900  //
 901  // Delete a user and all information associated with it
 902  //
 903  function delete_user($user_id, $delete_posts = false)
 904  {
 905      global $forum_db, $db_type, $forum_config;
 906  
 907      $return = ($hook = get_hook('fn_delete_user_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 908      if ($return != null)
 909          return;
 910  
 911      // First we need to get some data on the user
 912      $query = array(
 913          'SELECT'    => 'u.username, u.group_id, g.g_moderator',
 914          'FROM'        => 'users AS u',
 915          'JOINS'        => array(
 916              array(
 917                  'INNER JOIN'    => 'groups AS g',
 918                  'ON'            => 'g.g_id=u.group_id'
 919              )
 920          ),
 921          'WHERE'        => 'u.id='.$user_id
 922      );
 923  
 924      ($hook = get_hook('fn_delete_user_qr_get_user_data')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 925      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 926      $user = $forum_db->fetch_assoc($result);
 927  
 928      // Delete any subscriptions
 929      $query = array(
 930          'DELETE'    => 'subscriptions',
 931          'WHERE'        => 'user_id='.$user_id
 932      );
 933  
 934      ($hook = get_hook('fn_delete_user_qr_delete_subscriptions')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 935      $forum_db->query_build($query) or error(__FILE__, __LINE__);
 936  
 937      // Remove him/her from the online list (if they happen to be logged in)
 938      $query = array(
 939          'DELETE'    => 'online',
 940          'WHERE'        => 'user_id='.$user_id
 941      );
 942  
 943      ($hook = get_hook('fn_delete_user_qr_delete_online')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 944      $forum_db->query_build($query) or error(__FILE__, __LINE__);
 945  
 946      // Should we delete all posts made by this user?
 947      if ($delete_posts)
 948      {
 949          @set_time_limit(0);
 950  
 951          // Find all posts made by this user
 952          $query = array(
 953              'SELECT'    => 'p.id, p.topic_id, t.forum_id, t.first_post_id',
 954              'FROM'        => 'posts AS p',
 955              'JOINS'        => array(
 956                  array(
 957                      'INNER JOIN'    => 'topics AS t',
 958                      'ON'            => 't.id=p.topic_id'
 959                  )
 960              ),
 961              'WHERE'        => 'p.poster_id='.$user_id
 962          );
 963  
 964          ($hook = get_hook('fn_delete_user_qr_get_user_posts')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 965          $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
 966          while ($cur_post = $forum_db->fetch_assoc($result))
 967          {
 968              if ($cur_post['first_post_id'] == $cur_post['id'])
 969                  delete_topic($cur_post['topic_id'], $cur_post['forum_id']);
 970              else
 971                  delete_post($cur_post['id'], $cur_post['topic_id'], $cur_post['forum_id']);
 972          }
 973      }
 974      else
 975      {
 976          // Set all his/her posts to guest
 977          $query = array(
 978              'UPDATE'    => 'posts',
 979              'SET'        => 'poster_id=1',
 980              'WHERE'        => 'poster_id='.$user_id
 981          );
 982  
 983          ($hook = get_hook('fn_delete_user_qr_reset_user_posts')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 984          $forum_db->query_build($query) or error(__FILE__, __LINE__);
 985      }
 986  
 987      // Delete the user
 988      $query = array(
 989          'DELETE'    => 'users',
 990          'WHERE'        => 'id='.$user_id
 991      );
 992  
 993      ($hook = get_hook('fn_delete_user_qr_delete_user')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
 994      $forum_db->query_build($query) or error(__FILE__, __LINE__);
 995  
 996      // Delete user avatar
 997      delete_avatar($user_id);
 998  
 999      // If the user is a moderator or an administrator, we remove him/her from the moderator list in all forums
1000      // and regenerate the bans cache (in case he/she created any bans)
1001      if ($user['group_id'] == FORUM_ADMIN || $user['g_moderator'] == '1')
1002      {
1003          clean_forum_moderators();
1004  
1005          // Regenerate the bans cache
1006          if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
1007              require  FORUM_ROOT.'include/cache.php';
1008  
1009          generate_bans_cache();
1010      }
1011  
1012      ($hook = get_hook('fn_delete_user_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1013  }
1014  
1015  
1016  //
1017  // Iterates through all forum moderator lists and removes any erroneous entries
1018  //
1019  function clean_forum_moderators()
1020  {
1021      global $forum_db;
1022  
1023      $return = ($hook = get_hook('fn_clean_forum_moderators_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1024      if ($return != null)
1025          return;
1026  
1027      // Get a list of forums and their respective lists of moderators
1028      $query = array(
1029          'SELECT'    => 'f.id, f.moderators',
1030          'FROM'        => 'forums AS f',
1031          'WHERE'        => 'f.moderators IS NOT NULL'
1032      );
1033  
1034      ($hook = get_hook('fn_clean_forum_moderators_qr_get_forum_moderators')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1035      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1036  
1037      while ($cur_forum = $forum_db->fetch_assoc($result))
1038      {
1039          $cur_moderators = unserialize($cur_forum['moderators']);
1040          $new_moderators = $cur_moderators;
1041  
1042          // Iterate through each user in the list and check if he/she is in a moderator or admin group
1043          foreach ($cur_moderators as $username => $user_id)
1044          {
1045              $query = array(
1046                  'SELECT'    => '1',
1047                  'FROM'        => 'users AS u',
1048                  'JOINS'        => array(
1049                      array(
1050                          'INNER JOIN'    => 'groups AS g',
1051                          'ON'            => 'g.g_id=u.group_id'
1052                      )
1053                  ),
1054                  'WHERE'        => '(g.g_moderator=1 OR u.group_id=1) AND u.id='.$user_id
1055              );
1056  
1057              ($hook = get_hook('fn_clean_forum_moderators_qr_check_user_in_moderator_group')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1058              $result2 = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1059  
1060              if (!$forum_db->num_rows($result2))    // If the user isn't in a moderator or admin group, remove him/her from the list
1061                  unset($new_moderators[$username]);
1062          }
1063  
1064          // If we changed anything, update the forum
1065          if ($cur_moderators != $new_moderators)
1066          {
1067              $new_moderators = (!empty($new_moderators)) ? '\''.$forum_db->escape(serialize($new_moderators)).'\'' : 'NULL';
1068  
1069              $query = array(
1070                  'UPDATE'    => 'forums',
1071                  'SET'        => 'moderators='.$new_moderators,
1072                  'WHERE'        => 'id='.$cur_forum['id']
1073              );
1074  
1075              ($hook = get_hook('fn_qr_clean_forum_moderators_set_forum_moderators')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1076              $forum_db->query_build($query) or error(__FILE__, __LINE__);
1077          }
1078      }
1079  
1080      ($hook = get_hook('fn_clean_forum_moderators_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1081  }
1082  
1083  
1084  //
1085  // Delete every .php file in the forum's cache directory
1086  //
1087  function forum_clear_cache()
1088  {
1089      $return = ($hook = get_hook('fn_forum_clear_cache_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1090      if ($return != null)
1091          return;
1092  
1093      $d = dir(FORUM_CACHE_DIR);
1094      while (($entry = $d->read()) !== false)
1095      {
1096          if (substr($entry, strlen($entry)-4) == '.php')
1097              @unlink(FORUM_CACHE_DIR.$entry);
1098      }
1099      $d->close();
1100  }
1101  
1102  
1103  //
1104  // Deletes any avatars owned by the specified user ID
1105  //
1106  function delete_avatar($user_id)
1107  {
1108      global $forum_config;
1109  
1110      $filetypes = array('jpg', 'gif', 'png');
1111  
1112      $return = ($hook = get_hook('fn_delete_avatar_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1113      if ($return != null)
1114          return;
1115  
1116      // Delete user avatar
1117      foreach ($filetypes as $cur_type)
1118      {
1119          if (file_exists(FORUM_ROOT.$forum_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type))
1120              @unlink(FORUM_ROOT.$forum_config['o_avatars_dir'].'/'.$user_id.'.'.$cur_type);
1121      }
1122  }
1123  
1124  
1125  //
1126  // Locate and delete any orphaned redirect topics
1127  //
1128  function delete_orphans()
1129  {
1130      global $forum_db;
1131  
1132      $return = ($hook = get_hook('fn_delete_orphans_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1133      if ($return != null)
1134          return;
1135  
1136      // Locate any orphaned redirect topics
1137      $query = array(
1138          'SELECT'    => 't1.id',
1139          'FROM'        => 'topics AS t1',
1140          'JOINS'        => array(
1141              array(
1142                  'LEFT JOIN'        => 'topics AS t2',
1143                  'ON'            => 't1.moved_to=t2.id'
1144              )
1145          ),
1146          'WHERE'        => 't2.id IS NULL AND t1.moved_to IS NOT NULL'
1147      );
1148  
1149      ($hook = get_hook('fn_delete_orphans_qr_get_orphans')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1150      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1151      $num_orphans = $forum_db->num_rows($result);
1152  
1153      if ($num_orphans)
1154      {
1155          for ($i = 0; $i < $num_orphans; ++$i)
1156              $orphans[] = $forum_db->result($result, $i);
1157  
1158          // Delete the orphan
1159          $query = array(
1160              'DELETE'    => 'topics',
1161              'WHERE'        => 'id IN('.implode(',', $orphans).')'
1162          );
1163  
1164          ($hook = get_hook('fn_delete_orphans_qr_delete_orphan')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1165          $forum_db->query_build($query) or error(__FILE__, __LINE__);
1166      }
1167  
1168      ($hook = get_hook('fn_delete_orphans_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1169  }
1170  
1171  
1172  //
1173  // Delete a topic and all of it's posts
1174  //
1175  function delete_topic($topic_id, $forum_id)
1176  {
1177      global $forum_db, $db_type;
1178  
1179      $return = ($hook = get_hook('fn_delete_topic_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1180      if ($return != null)
1181          return;
1182  
1183      // Create an array of forum IDs that need to be synced
1184      $forum_ids = array($forum_id);
1185      $query = array(
1186          'SELECT'    => 't.forum_id',
1187          'FROM'        => 'topics AS t',
1188          'WHERE'        => 't.moved_to='.$topic_id
1189      );
1190  
1191      ($hook = get_hook('fn_delete_topic_qr_get_forums_to_sync')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1192      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1193      while ($row = $forum_db->fetch_row($result))
1194          $forum_ids[] = $row[0];
1195  
1196      // Delete the topic and any redirect topics
1197      $query = array(
1198          'DELETE'    => 'topics',
1199          'WHERE'        => 'id='.$topic_id.' OR moved_to='.$topic_id
1200      );
1201  
1202      ($hook = get_hook('fn_delete_topic_qr_delete_topic')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1203      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1204  
1205      // Create a list of the post ID's in this topic
1206      $post_ids = '';
1207      $query = array(
1208          'SELECT'    => 'p.id',
1209          'FROM'        => 'posts AS p',
1210          'WHERE'        => 'p.topic_id='.$topic_id
1211      );
1212  
1213      ($hook = get_hook('fn_delete_topic_qr_get_posts_to_delete')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1214      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1215      while ($row = $forum_db->fetch_row($result))
1216          $post_ids .= ($post_ids != '') ? ','.$row[0] : $row[0];
1217  
1218      // Make sure we have a list of post ID's
1219      if ($post_ids != '')
1220      {
1221          // Delete posts in topic
1222          $query = array(
1223              'DELETE'    => 'posts',
1224              'WHERE'        => 'topic_id='.$topic_id
1225          );
1226  
1227          ($hook = get_hook('fn_delete_topic_qr_delete_topic_posts')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1228          $forum_db->query_build($query) or error(__FILE__, __LINE__);
1229  
1230          if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED'))
1231              require  FORUM_ROOT.'include/search_idx.php';
1232  
1233          strip_search_index($post_ids);
1234      }
1235  
1236      // Delete any subscriptions for this topic
1237      $query = array(
1238          'DELETE'    => 'subscriptions',
1239          'WHERE'        => 'topic_id='.$topic_id
1240      );
1241  
1242      ($hook = get_hook('fn_delete_topic_qr_delete_topic_subscriptions')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1243      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1244  
1245      foreach ($forum_ids as $cur_forum_id)
1246          sync_forum($cur_forum_id);
1247  
1248      ($hook = get_hook('fn_delete_topic_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1249  }
1250  
1251  
1252  //
1253  // Delete a single post
1254  //
1255  function delete_post($post_id, $topic_id, $forum_id)
1256  {
1257      global $forum_db, $db_type;
1258  
1259      $return = ($hook = get_hook('fn_delete_post_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1260      if ($return != null)
1261          return;
1262  
1263      // Delete the post
1264      $query = array(
1265          'DELETE'    => 'posts',
1266          'WHERE'        => 'id='.$post_id
1267      );
1268  
1269      ($hook = get_hook('fn_delete_post_qr_delete_post')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1270      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1271  
1272      if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED'))
1273          require  FORUM_ROOT.'include/search_idx.php';
1274  
1275      strip_search_index($post_id);
1276  
1277      sync_topic($topic_id);
1278      sync_forum($forum_id);
1279  
1280      ($hook = get_hook('fn_delete_post_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1281  }
1282  
1283  
1284  //
1285  // Creates a new topic with its first post
1286  //
1287  function add_topic($post_info, &$new_tid, &$new_pid)
1288  {
1289      global $forum_db, $db_type, $forum_config, $lang_common;
1290  
1291      $return = ($hook = get_hook('fn_add_topic_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1292      if ($return != null)
1293          return;
1294  
1295      // Add the topic
1296      $query = array(
1297          'INSERT'    => 'poster, subject, posted, last_post, last_poster, forum_id',
1298          'INTO'        => 'topics',
1299          'VALUES'    => '\''.$forum_db->escape($post_info['poster']).'\', \''.$forum_db->escape($post_info['subject']).'\', '.$post_info['posted'].', '.$post_info['posted'].', \''.$forum_db->escape($post_info['poster']).'\', '.$post_info['forum_id']
1300      );
1301  
1302      ($hook = get_hook('fn_add_topic_qr_add_topic')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1303      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1304      $new_tid = $forum_db->insert_id();
1305  
1306      // To subscribe or not to subscribe, that ...
1307      if (!$post_info['is_guest'] && $post_info['subscribe'])
1308      {
1309          $query = array(
1310              'INSERT'    => 'user_id, topic_id',
1311              'INTO'        => 'subscriptions',
1312              'VALUES'    => $post_info['poster_id'].' ,'.$new_tid
1313          );
1314  
1315          ($hook = get_hook('fn_add_topic_qr_add_subscription')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1316          $forum_db->query_build($query) or error(__FILE__, __LINE__);
1317      }
1318  
1319      // Create the post ("topic post")
1320      $query = array(
1321          'INSERT'    => 'poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id',
1322          'INTO'        => 'posts',
1323          'VALUES'    => '\''.$forum_db->escape($post_info['poster']).'\', '.$post_info['poster_id'].', \''.$forum_db->escape(get_remote_address()).'\', \''.$forum_db->escape($post_info['message']).'\', '.$post_info['hide_smilies'].', '.$post_info['posted'].', '.$new_tid
1324      );
1325  
1326      // If it's a guest post, there might be an e-mail address we need to include
1327      if ($post_info['is_guest'] && $post_info['poster_email'] != null)
1328      {
1329          $query['INSERT'] .= ', poster_email';
1330          $query['VALUES'] .= ', \''.$forum_db->escape($post_info['poster_email']).'\'';
1331      }
1332  
1333      ($hook = get_hook('fn_add_topic_qr_add_topic_post')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1334      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1335      $new_pid = $forum_db->insert_id();
1336  
1337      // Update the topic with last_post_id and first_post_id
1338      $query = array(
1339          'UPDATE'    => 'topics',
1340          'SET'        => 'last_post_id='.$new_pid.', first_post_id='.$new_pid,
1341          'WHERE'        => 'id='.$new_tid
1342      );
1343  
1344      ($hook = get_hook('fn_add_topic_qr_update_topic')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1345      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1346  
1347      if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED'))
1348          require  FORUM_ROOT.'include/search_idx.php';
1349  
1350      update_search_index('post', $new_pid, $post_info['message'], $post_info['subject']);
1351  
1352      sync_forum($post_info['forum_id']);
1353  
1354      // If the posting user is logged in...
1355      if (!$post_info['is_guest'])
1356      {
1357          // ...increment his/her post count
1358          if (isset($post_info['update_user']) && $post_info['update_user'])
1359          {
1360              $query = array(
1361                  'UPDATE'    => 'users',
1362                  'SET'        => 'num_posts=num_posts+1, last_post='.$post_info['posted'],
1363                  'WHERE'        => 'id='.$post_info['poster_id']
1364              );
1365  
1366              ($hook = get_hook('fn_add_topic_qr_increment_num_posts')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1367              $forum_db->query_build($query) or error(__FILE__, __LINE__);
1368          }
1369  
1370          // ...update his/her unread indicator
1371          if (isset($post_info['update_unread']) && $post_info['update_unread'])
1372          {
1373              $tracked_topics = get_tracked_topics();
1374              $tracked_topics['topics'][$new_tid] = time();
1375              set_tracked_topics($tracked_topics);
1376          }
1377      }
1378  
1379      ($hook = get_hook('fn_add_topic_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1380  }
1381  
1382  
1383  //
1384  // Creates a new post
1385  //
1386  function add_post($post_info, &$new_pid)
1387  {
1388      global $forum_db, $db_type, $forum_config, $lang_common;
1389  
1390      $return = ($hook = get_hook('fn_add_post_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1391      if ($return != null)
1392          return;
1393  
1394      // Add the post
1395      $query = array(
1396          'INSERT'    => 'poster, poster_id, poster_ip, message, hide_smilies, posted, topic_id',
1397          'INTO'        => 'posts',
1398          'VALUES'    => '\''.$forum_db->escape($post_info['poster']).'\', '.$post_info['poster_id'].', \''.$forum_db->escape(get_remote_address()).'\', \''.$forum_db->escape($post_info['message']).'\', '.$post_info['hide_smilies'].', '.$post_info['posted'].', '.$post_info['topic_id']
1399      );
1400  
1401      // If it's a guest post, there might be an e-mail address we need to include
1402      if ($post_info['is_guest'] && $post_info['poster_email'] != null)
1403      {
1404          $query['INSERT'] .= ', poster_email';
1405          $query['VALUES'] .= ', \''.$forum_db->escape($post_info['poster_email']).'\'';
1406      }
1407  
1408      ($hook = get_hook('fn_add_post_qr_add_post')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1409      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1410      $new_pid = $forum_db->insert_id();
1411  
1412      if (!$post_info['is_guest'])
1413      {
1414          // Subscribe or unsubscribe?
1415          if ($post_info['subscr_action'] == 1)
1416          {
1417              $query = array(
1418                  'INSERT'    => 'user_id, topic_id',
1419                  'INTO'        => 'subscriptions',
1420                  'VALUES'    => $post_info['poster_id'].' ,'.$post_info['topic_id']
1421              );
1422  
1423              ($hook = get_hook('fn_add_post_qr_add_subscription')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1424              $forum_db->query_build($query) or error(__FILE__, __LINE__);
1425          }
1426          else if ($post_info['subscr_action'] == 2)
1427          {
1428              $query = array(
1429                  'DELETE'    => 'subscriptions',
1430                  'WHERE'        => 'topic_id='.$post_info['topic_id'].' AND user_id='.$post_info['poster_id']
1431              );
1432  
1433              ($hook = get_hook('fn_add_post_qr_delete_subscription')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1434              $forum_db->query_build($query) or error(__FILE__, __LINE__);
1435          }
1436      }
1437  
1438      // Count number of replies in the topic
1439      $query = array(
1440          'SELECT'    => 'COUNT(p.id)',
1441          'FROM'        => 'posts AS p',
1442          'WHERE'        => 'p.topic_id='.$post_info['topic_id']
1443      );
1444  
1445      ($hook = get_hook('fn_add_post_qr_get_topic_reply_count')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1446      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1447      $num_replies = $forum_db->result($result, 0) - 1;
1448  
1449      // Update topic
1450      $query = array(
1451          'UPDATE'    => 'topics',
1452          'SET'        => 'num_replies='.$num_replies.', last_post='.$post_info['posted'].', last_post_id='.$new_pid.', last_poster=\''.$forum_db->escape($post_info['poster']).'\'',
1453          'WHERE'        => 'id='.$post_info['topic_id']
1454      );
1455  
1456      ($hook = get_hook('fn_add_post_qr_update_topic')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1457      $forum_db->query_build($query) or error(__FILE__, __LINE__);
1458  
1459      sync_forum($post_info['forum_id']);
1460  
1461      if (!defined('FORUM_SEARCH_IDX_FUNCTIONS_LOADED'))
1462          require  FORUM_ROOT.'include/search_idx.php';
1463  
1464      update_search_index('post', $new_pid, $post_info['message']);
1465  
1466      send_subscriptions($post_info, $new_pid);
1467  
1468      // If the posting user is logged in...
1469      if (!$post_info['is_guest'])
1470      {
1471          // ...increment his/her post count
1472          if (isset($post_info['update_user']))
1473          {
1474              $query = array(
1475                  'UPDATE'    => 'users',
1476                  'SET'        => 'num_posts=num_posts+1, last_post='.$post_info['posted'],
1477                  'WHERE'        => 'id='.$post_info['poster_id']
1478              );
1479  
1480              ($hook = get_hook('fn_add_post_qr_increment_num_posts')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1481              $forum_db->query_build($query) or error(__FILE__, __LINE__);
1482          }
1483  
1484          // ...update his/her unread indicator
1485          if (isset($post_info['update_unread']))
1486          {
1487              $tracked_topics = get_tracked_topics();
1488              $tracked_topics['topics'][$post_info['topic_id']] = time();
1489              set_tracked_topics($tracked_topics);
1490          }
1491      }
1492  
1493      ($hook = get_hook('fn_add_post_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1494  }
1495  
1496  
1497  //
1498  // Send out subscription emails
1499  //
1500  function send_subscriptions($post_info, $new_pid)
1501  {
1502      global $forum_config, $forum_db, $forum_url, $lang_common;
1503  
1504      $return = ($hook = get_hook('fn_send_subscriptions_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1505      if ($return != null)
1506          return;
1507  
1508      if ($forum_config['o_subscriptions'] != '1')
1509          return;
1510  
1511      // Get the post time for the previous post in this topic
1512      $query = array(
1513          'SELECT'    => 'p.posted',
1514          'FROM'        => 'posts AS p',
1515          'WHERE'        => 'p.topic_id='.$post_info['topic_id'],
1516          'ORDER BY'    => 'p.id DESC',
1517          'LIMIT'        => '1, 1'
1518      );
1519  
1520      ($hook = get_hook('fn_send_subscriptions_qr_get_previous_post_time')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1521      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1522      $previous_post_time = $forum_db->result($result);
1523  
1524      // Get any subscribed users that should be notified (banned users are excluded)
1525      $query = array(
1526          'SELECT'    => 'u.id, u.email, u.notify_with_post, u.language',
1527          'FROM'        => 'users AS u',
1528          'JOINS'        => array(
1529              array(
1530                  'INNER JOIN'    => 'subscriptions AS s',
1531                  'ON'            => 'u.id=s.user_id'
1532              ),
1533              array(
1534                  'LEFT JOIN'        => 'forum_perms AS fp',
1535                  'ON'            => '(fp.forum_id='.$post_info['forum_id'].' AND fp.group_id=u.group_id)'
1536              ),
1537              array(
1538                  'LEFT JOIN'        => 'online AS o',
1539                  'ON'            => 'u.id=o.user_id'
1540              ),
1541              array(
1542                  'LEFT JOIN'        => 'bans AS b',
1543                  'ON'            => 'u.username=b.username'
1544              ),
1545          ),
1546          'WHERE'        => 'b.username IS NULL AND COALESCE(o.logged, u.last_visit)>'.$previous_post_time.' AND (fp.read_forum IS NULL OR fp.read_forum=1) AND s.topic_id='.$post_info['topic_id'].' AND u.id!='.$post_info['poster_id']
1547      );
1548  
1549      ($hook = get_hook('fn_send_subscriptions_qr_get_users_to_notify')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1550      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1551  
1552      if ($forum_db->num_rows($result))
1553      {
1554          if (!defined('FORUM_EMAIL_FUNCTIONS_LOADED'))
1555              require  FORUM_ROOT.'include/email.php';
1556  
1557          $notification_emails = array();
1558  
1559          // Loop through subscribed users and send e-mails
1560          while ($cur_subscriber = $forum_db->fetch_assoc($result))
1561          {
1562              // Is the subscription e-mail for $cur_subscriber['language'] cached or not?
1563              if (!isset($notification_emails[$cur_subscriber['language']]))
1564              {
1565                  if (file_exists(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl'))
1566                  {
1567                      // Load the "new reply" template
1568                      $mail_tpl = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply.tpl'));
1569  
1570                      // Load the "new reply full" template (with post included)
1571                      $mail_tpl_full = forum_trim(file_get_contents(FORUM_ROOT.'lang/'.$cur_subscriber['language'].'/mail_templates/new_reply_full.tpl'));
1572  
1573                      // The first row contains the subject (it also starts with "Subject:")
1574                      $first_crlf = strpos($mail_tpl, "\n");
1575                      $mail_subject = forum_trim(substr($mail_tpl, 8, $first_crlf-8));
1576                      $mail_message = forum_trim(substr($mail_tpl, $first_crlf));
1577  
1578                      $first_crlf = strpos($mail_tpl_full, "\n");
1579                      $mail_subject_full = forum_trim(substr($mail_tpl_full, 8, $first_crlf-8));
1580                      $mail_message_full = forum_trim(substr($mail_tpl_full, $first_crlf));
1581  
1582                      $mail_subject = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_subject);
1583                      $mail_message = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_message);
1584                      $mail_message = str_replace('<replier>', $post_info['poster'], $mail_message);
1585                      $mail_message = str_replace('<post_url>', forum_link($forum_url['post'], $new_pid), $mail_message);
1586                      $mail_message = str_replace('<unsubscribe_url>', forum_link($forum_url['unsubscribe'], array($post_info['topic_id'], generate_form_token('unsubscribe'.$post_info['topic_id'].$cur_subscriber['id']))), $mail_message);
1587                      $mail_message = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message);
1588  
1589                      $mail_subject_full = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_subject_full);
1590                      $mail_message_full = str_replace('<topic_subject>', '\''.$post_info['subject'].'\'', $mail_message_full);
1591                      $mail_message_full = str_replace('<replier>', $post_info['poster'], $mail_message_full);
1592                      $mail_message_full = str_replace('<message>', $post_info['message'], $mail_message_full);
1593                      $mail_message_full = str_replace('<post_url>', forum_link($forum_url['post'], $new_pid), $mail_message_full);
1594                      $mail_message_full = str_replace('<unsubscribe_url>', forum_link($forum_url['unsubscribe'], array($post_info['topic_id'], generate_form_token('unsubscribe'.$post_info['topic_id'].$cur_subscriber['id']))), $mail_message_full);
1595                      $mail_message_full = str_replace('<board_mailer>', sprintf($lang_common['Forum mailer'], $forum_config['o_board_title']), $mail_message_full);
1596  
1597                      $notification_emails[$cur_subscriber['language']][0] = $mail_subject;
1598                      $notification_emails[$cur_subscriber['language']][1] = $mail_message;
1599                      $notification_emails[$cur_subscriber['language']][2] = $mail_subject_full;
1600                      $notification_emails[$cur_subscriber['language']][3] = $mail_message_full;
1601  
1602                      $mail_subject = $mail_message = $mail_subject_full = $mail_message_full = null;
1603                  }
1604              }
1605  
1606              // We have to double check here because the templates could be missing
1607              if (isset($notification_emails[$cur_subscriber['language']]))
1608              {
1609                  // Make sure the e-mail address format is valid before sending
1610                  if (is_valid_email($cur_subscriber['email']))
1611                  {
1612                      if ($cur_subscriber['notify_with_post'] == '0')
1613                          forum_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][0], $notification_emails[$cur_subscriber['language']][1]);
1614                      else
1615                          forum_mail($cur_subscriber['email'], $notification_emails[$cur_subscriber['language']][2], $notification_emails[$cur_subscriber['language']][3]);
1616                  }
1617              }
1618          }
1619      }
1620  
1621      ($hook = get_hook('fn_send_subscriptions_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1622  }
1623  
1624  
1625  //
1626  // Make a string safe to use in a URL
1627  //
1628  function sef_friendly($str)
1629  {
1630      global $forum_user;
1631      static $lang_replace;
1632  
1633      if (!isset($lang_replace))
1634          require FORUM_ROOT.'lang/'.$forum_user['language'].'/url_replace.php';
1635  
1636      $return = ($hook = get_hook('fn_sef_friendly_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1637      if ($return != null)
1638          return $return;
1639  
1640      $str = strtr($str, $lang_url_replace);
1641      $str = strtolower(utf8_decode($str));
1642      $str = preg_replace(array('/[^a-z0-9\s]/', '/[\s]+/'), array('', '-'), $str);
1643  
1644      return $str != '-' ? forum_trim($str, '-') : '';
1645  }
1646  
1647  
1648  //
1649  // Replace censored words in $text
1650  //
1651  function censor_words($text)
1652  {
1653      global $forum_db;
1654      static $search_for, $replace_with;
1655  
1656      $return = ($hook = get_hook('fn_censor_words_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1657      if ($return != null)
1658          return $return;
1659  
1660      // If not already loaded in a previous call, load the cached censors
1661      if (!defined('FORUM_CENSORS_LOADED'))
1662      {
1663          if (file_exists(FORUM_CACHE_DIR.'cache_censors.php'))
1664              include FORUM_CACHE_DIR.'cache_censors.php';
1665  
1666          if (!defined('FORUM_CENSORS_LOADED'))
1667          {
1668              if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
1669                  require  FORUM_ROOT.'include/cache.php';
1670  
1671              generate_censors_cache();
1672              require FORUM_CACHE_DIR.'cache_censors.php';
1673          }
1674  
1675          $search_for = array();
1676          $replace_with = array();
1677  
1678          foreach ($forum_censors as $censor_key => $cur_word)
1679          {
1680              $search_for[$censor_key] = '/\b('.str_replace('\*', '\w*?', preg_quote($cur_word['search_for'], '/')).')\b/iu';
1681              $replace_with[$censor_key] = $cur_word['replace_with'];
1682  
1683              ($hook = get_hook('fn_censor_words_setup_regex')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1684          }
1685      }
1686  
1687      if (!empty($search_for))
1688          $text = utf8_substr(preg_replace($search_for, $replace_with, ' '.$text.' '), 1, -1);
1689  
1690      return $text;
1691  }
1692  
1693  
1694  //
1695  // Check if a username is occupied
1696  //
1697  function check_username_dupe($username, $exclude_id = null)
1698  {
1699      global $forum_db;
1700  
1701      $return = ($hook = get_hook('fn_check_username_dupe_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1702      if ($return != null)
1703          return $return;
1704  
1705      $query = array(
1706          'SELECT'    => 'u.username',
1707          'FROM'        => 'users AS u',
1708          'WHERE'        => '(UPPER(username)=UPPER(\''.$forum_db->escape($username).'\') OR UPPER(username)=UPPER(\''.$forum_db->escape(preg_replace('/[^\w]/u', '', $username)).'\')) AND id>1'
1709      );
1710  
1711      if ($exclude_id)
1712          $query['WHERE'] .= ' AND id!='.$exclude_id;
1713  
1714      ($hook = get_hook('fn_check_username_dupe_qr_check_username_dupe')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1715      $result = $forum_db->query_build($query) or error(__FILE__, __LINE__);
1716  
1717      return $forum_db->num_rows($result) ? $forum_db->result($result) : false;
1718  }
1719  
1720  
1721  //
1722  // Determines the correct title for $user
1723  // $user must contain the elements 'username', 'title', 'posts', 'g_id' and 'g_user_title'
1724  //
1725  function get_title($user)
1726  {
1727      global $forum_db, $forum_config, $forum_bans, $lang_common;
1728      static $ban_list, $forum_ranks;
1729  
1730      $return = ($hook = get_hook('fn_get_title_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1731      if ($return != null)
1732          return $return;
1733  
1734      // If not already built in a previous call, build an array of lowercase banned usernames
1735      if (empty($ban_list))
1736      {
1737          $ban_list = array();
1738  
1739          foreach ($forum_bans as $cur_ban)
1740              $ban_list[] = utf8_strtolower($cur_ban['username']);
1741      }
1742  
1743      // If not already loaded in a previous call, load the cached ranks
1744      if ($forum_config['o_ranks'] == '1' && !defined('FORUM_RANKS_LOADED'))
1745      {
1746          if (file_exists(FORUM_CACHE_DIR.'cache_ranks.php'))
1747              include FORUM_CACHE_DIR.'cache_ranks.php';
1748  
1749          if (!defined('FORUM_RANKS_LOADED'))
1750          {
1751              if (!defined('FORUM_CACHE_FUNCTIONS_LOADED'))
1752                  require  FORUM_ROOT.'include/cache.php';
1753  
1754              generate_ranks_cache();
1755              require FORUM_CACHE_DIR.'cache_ranks.php';
1756          }
1757      }
1758  
1759      // If the user has a custom title
1760      if ($user['title'] != '')
1761          $user_title = forum_htmlencode($forum_config['o_censoring'] == '1' ? censor_words($user['title']) : $user['title']);
1762      // If the user is banned
1763      else if (in_array(utf8_strtolower($user['username']), $ban_list))
1764          $user_title = $lang_common['Banned'];
1765      // If the user group has a default user title
1766      else if ($user['g_user_title'] != '')
1767          $user_title = forum_htmlencode($user['g_user_title']);
1768      // If the user is a guest
1769      else if ($user['g_id'] == FORUM_GUEST)
1770          $user_title = $lang_common['Guest'];
1771      else
1772      {
1773          // Are there any ranks?
1774          if ($forum_config['o_ranks'] == '1' && !empty($forum_ranks))
1775          {
1776              @reset($forum_ranks);
1777              while (list(, $cur_rank) = @each($forum_ranks))
1778              {
1779                  if (intval($user['num_posts']) >= $cur_rank['min_posts'])
1780                      $user_title = forum_htmlencode($cur_rank['rank']);
1781              }
1782          }
1783  
1784          // If the user didn't "reach" any rank (or if ranks are disabled), we assign the default
1785          if (!isset($user_title))
1786              $user_title = $lang_common['Member'];
1787      }
1788  
1789      ($hook = get_hook('fn_get_title_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1790  
1791      return $user_title;
1792  }
1793  
1794  
1795  //
1796  // Generate a string with page and item information for multipage headings
1797  //
1798  function generate_page_info($label, $first, $total)
1799  {
1800      global $forum_page, $lang_common;
1801  
1802      $return = ($hook = get_hook('fn_generate_page_info_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1803      if ($return != null)
1804          return $return;
1805  
1806      if ($forum_page['num_pages'] == 1)
1807      {
1808          $page_info = '';
1809          $item_info =  '<span class="item-info">'.sprintf($lang_common['Item info single'], $label, forum_number_format($total)).'</span>';
1810      }
1811      else
1812      {
1813          $page_info = '<span class="page-info">'.sprintf($lang_common['Page info'], forum_number_format($forum_page['page']), forum_number_format($forum_page['num_pages'])).$lang_common['Info separator'].'</span>';
1814          $item_info = '<span class="item-info">'.sprintf($lang_common['Item info plural'], $label, forum_number_format($first), forum_number_format($forum_page['finish_at']), forum_number_format($total)).'</span>';
1815      }
1816  
1817      ($hook = get_hook('fn_generate_page_info_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1818  
1819      return $page_info.$item_info;
1820  }
1821  
1822  
1823  //
1824  // Generate a string with numbered links (for multipage scripts)
1825  //
1826  function paginate($num_pages, $cur_page, $link, $separator, $args = null)
1827  {
1828      global $forum_url, $lang_common;
1829  
1830      $pages = array();
1831      $link_to_all = false;
1832  
1833      $return = ($hook = get_hook('fn_paginate_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1834      if ($return != null)
1835          return $return;
1836  
1837      // If $cur_page == -1, we link to all pages (used in viewforum.php)
1838      if ($cur_page == -1)
1839      {
1840          $cur_page = 1;
1841          $link_to_all = true;
1842      }
1843  
1844      if ($num_pages <= 1)
1845          $pages = array('<strong class="item1">1</strong>');
1846      else
1847      {
1848          // Add a previous page link
1849          if ($num_pages > 1 && $cur_page > 1)
1850              $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.forum_sublink($link, $forum_url['page'], ($cur_page - 1), $args).'">'.$lang_common['Previous'].'</a>';
1851  
1852          if ($cur_page > 3)
1853          {
1854              $pages[] = '<a'.(empty($pages) ? ' class="item1"' : '').' href="'.forum_sublink($link, $forum_url['page'], 1, $args).'">1</a>';
1855  
1856              if ($cur_page > 5)
1857                  $pages[] = '<span>'.$lang_common['Spacer'].'</span>';
1858          }
1859  
1860          // Don't ask me how the following works. It just does, OK? :-)
1861          for ($current = ($cur_page == 5) ? $cur_page - 3 : $cur_page - 2, $stop = ($cur_page + 4 == $num_pages) ? $cur_page + 4 : $cur_page + 3; $current < $stop; ++$current)
1862          {
1863              if ($current < 1 || $current > $num_pages)
1864                  continue;
1865              else if ($current != $cur_page || $link_to_all)
1866                  $pages[] = '<a'.(empty($pages) ? ' class="item1" ' : '').' href="'.forum_sublink($link, $forum_url['page'], $current, $args).'">'.forum_number_format($current).'</a>';
1867              else
1868                  $pages[] = '<strong'.(empty($pages) ? ' class="item1"' : '').'>'.forum_number_format($current).'</strong>';
1869          }
1870  
1871          if ($cur_page <= ($num_pages-3))
1872          {
1873              if ($cur_page != ($num_pages-3) && $cur_page != ($num_pages-4))
1874                  $pages[] = '<span>'.$lang_common['Spacer'].'</span>';
1875  
1876              $pages[] = '<a'.(empty($pages) ? ' class="item1" ' : '').' href="'.forum_sublink($link, $forum_url['page'], $num_pages, $args).'">'.forum_number_format($num_pages).'</a>';
1877          }
1878  
1879          // Add a next page link
1880          if ($num_pages > 1 && !$link_to_all && $cur_page < $num_pages)
1881              $pages[] = '<a'.(empty($pages) ? ' class="item1" ' : '').' href="'.forum_sublink($link, $forum_url['page'], ($cur_page + 1), $args).'">'.$lang_common['Next'].'</a>';
1882      }
1883  
1884      ($hook = get_hook('fn_paginate_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1885  
1886      return implode($separator, $pages);
1887  }
1888  
1889  
1890  //
1891  // Clean version string from trailing '.0's
1892  //
1893  function clean_version($version)
1894  {
1895      return preg_replace('/(\.0)+(?!\.)|(\.0+$)/', '$2', $version);
1896  }
1897  
1898  
1899  //
1900  // Display a message
1901  //
1902  function message($message, $link = '', $heading = '')
1903  {
1904      global $forum_db, $forum_url, $lang_common, $forum_config, $base_url, $forum_start, $tpl_main, $forum_user, $forum_page, $forum_updates;
1905  
1906      ($hook = get_hook('fn_message_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1907  
1908      if (!defined('FORUM_HEADER'))
1909      {
1910          if ($heading == '')
1911              $heading = $lang_common['Forum message'];
1912  
1913          // Setup breadcrumbs
1914          $forum_page['crumbs'] = array(
1915              array($forum_config['o_board_title'], forum_link($forum_url['index'])),
1916              $heading
1917          );
1918  
1919          ($hook = get_hook('fn_message_pre_header_load')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1920  
1921          define('FORUM_PAGE', 'message');
1922          define('FORUM_PAGE_TYPE', 'basic');
1923          require  FORUM_ROOT.'header.php';
1924  
1925          // START SUBST - <!-- forum_main -->
1926          ob_start();
1927  
1928          ($hook = get_hook('fn_message_output_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1929      }
1930  
1931  ?>
1932      <div class="main-content main-message">
1933          <p><?php echo $message ?><?php if ($link != '') echo ' <span>'.$link.'</span>' ?></p>
1934      </div>
1935  <?php
1936  
1937      ($hook = get_hook('fn_message_output_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1938  
1939      $tpl_temp = forum_trim(ob_get_contents());
1940      $tpl_main = str_replace('<!-- forum_main -->', "\t".$tpl_temp, $tpl_main);
1941      ob_end_clean();
1942      // END SUBST - <!-- forum_main -->
1943  
1944      require  FORUM_ROOT.'footer.php';
1945  }
1946  
1947  
1948  //
1949  // Display a form that the user can use to confirm that they want to undertake an action.
1950  // Used when the CSRF token from the request does not match the token stored in the database.
1951  //
1952  function csrf_confirm_form()
1953  {
1954      global $forum_db, $forum_url, $lang_common, $forum_config, $base_url, $forum_start, $tpl_main, $forum_user, $forum_page, $forum_updates;
1955  
1956      // If we've disabled the CSRF check for this page, we have nothing to do here.
1957      if (defined('FORUM_DISABLE_CSRF_CONFIRM'))
1958          return;
1959  
1960      // User pressed the cancel button
1961      if (isset($_POST['confirm_cancel']))
1962          redirect(forum_htmlencode($_POST['prev_url']), $lang_common['Cancel redirect']);
1963  
1964      //
1965      // A helper function for csrf_confirm_form. It takes a multi-dimensional array and returns it as a
1966      // single-dimensional array suitable for use in hidden fields.
1967      //
1968  	function _csrf_confirm_form($key, $values)
1969      {
1970          $fields = array();
1971  
1972          if (is_array($values))
1973          {
1974              foreach ($values as $cur_key => $cur_values)
1975                  $fields = array_merge($fields, _csrf_confirm_form($key.'['.$cur_key.']', $cur_values));
1976  
1977              return $fields;
1978          }
1979          else
1980              $fields[$key] = $values;
1981  
1982          return $fields;
1983      }
1984  
1985      $return = ($hook = get_hook('fn_csrf_confirm_form_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
1986      if ($return != null)
1987          return;
1988  
1989      // Setup breadcrumbs
1990      $forum_page['crumbs'] = array(
1991          array($forum_config['o_board_title'], forum_link($forum_url['index'])),
1992          $lang_common['Confirm action']
1993      );
1994  
1995      $forum_page['form_action'] = get_current_url();
1996  
1997      $forum_page['hidden_fields'] = array(
1998          'csrf_token'    => '<input type="hidden" name="csrf_token" value="'.generate_form_token($forum_page['form_action']).'" />',
1999          'prev_url'        => '<input type="hidden" name="prev_url" value="'.forum_htmlencode($forum_user['prev_url']).'" />'
2000      );
2001  
2002      foreach ($_POST as $submitted_key => $submitted_val)
2003      {
2004          if ($submitted_key != 'csrf_token' && $submitted_key != 'prev_url')
2005          {
2006              $hidden_fields = _csrf_confirm_form($submitted_key, $submitted_val);
2007              foreach ($hidden_fields as $field_key => $field_val)
2008                  $forum_page['hidden_fields'][$field_key] = '<input type="hidden" name="'.forum_htmlencode($field_key).'" value="'.forum_htmlencode($field_val).'" />';
2009          }
2010      }
2011  
2012      define('FORUM_PAGE', 'dialogue');
2013      require  FORUM_ROOT.'header.php';
2014  
2015      // START SUBST - <!-- forum_main -->
2016      ob_start();
2017  
2018      ($hook = get_hook('fn_csrf_confirm_form_pre_header_load')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2019  
2020  ?>
2021  <div id="brd-main" class="main">
2022  
2023      <h1><span><?php echo end($forum_page['crumbs']) ?></span></h1>
2024  
2025      <div class="main-head">
2026          <h2><span><?php echo $lang_common['Confirm action head'] ?></span></h2>
2027      </div>
2028      <div class="main-content main-frm">
2029          <div class="frm-info">
2030              <p><?php echo $lang_common['CSRF token mismatch'] ?></p>
2031          </div>
2032          <form class="frm-form" method="post" accept-charset="utf-8" action="<?php echo $forum_page['form_action'] ?>">
2033              <div class="hidden">
2034                  <?php echo implode("\n\t\t\t\t", $forum_page['hidden_fields'])."\n" ?>
2035              </div>
2036              <div class="frm-buttons">
2037                  <span class="submit"><input type="submit" value="<?php echo $lang_common['Confirm'] ?>" /></span>
2038                  <span class="cancel"><input type="submit" name="confirm_cancel" value="<?php echo $lang_common['Cancel'] ?>" /></span>
2039              </div>
2040          </form>
2041      </div>
2042  </div>
2043  <?php
2044  
2045      ($hook = get_hook('fn_csrf_confirm_form_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2046  
2047      $tpl_temp = forum_trim(ob_get_contents());
2048      $tpl_main = str_replace('<!-- forum_main -->', $tpl_temp, $tpl_main);
2049      ob_end_clean();
2050      // END SUBST - <!-- forum_main -->
2051  
2052      require  FORUM_ROOT.'footer.php';
2053  }
2054  
2055  
2056  //
2057  // Generate a hyperlink with parameters and anchor
2058  //
2059  function forum_link($link, $args = null)
2060  {
2061      global $forum_config, $base_url;
2062  
2063      $return = ($hook = get_hook('fn_forum_link_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2064      if ($return != null)
2065          return $return;
2066  
2067      $gen_link = $link;
2068      if ($args == null)
2069          $gen_link = $base_url.'/'.$link;
2070      else if (!is_array($args))
2071          $gen_link = $base_url.'/'.str_replace('$1', $args, $link);
2072      else
2073      {
2074          for ($i = 0; isset($args[$i]); ++$i)
2075              $gen_link = str_replace('$'.($i + 1), $args[$i], $gen_link);
2076          $gen_link = $base_url.'/'.$gen_link;
2077      }
2078  
2079      ($hook = get_hook('fn_forum_link_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2080  
2081      return $gen_link;
2082  }
2083  
2084  
2085  //
2086  // Generate a hyperlink with parameters and anchor and a subsection such as a subpage
2087  //
2088  function forum_sublink($link, $sublink, $subarg, $args = null)
2089  {
2090      global $forum_config, $forum_url, $base_url;
2091  
2092      $return = ($hook = get_hook('fn_forum_sublink_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2093      if ($return != null)
2094          return $return;
2095  
2096      $gen_link = $link;
2097      if (!is_array($args) && $args != null)
2098          $gen_link = str_replace('$1', $args, $link);
2099      else
2100      {
2101          for ($i = 0; isset($args[$i]); ++$i)
2102              $gen_link = str_replace('$'.($i + 1), $args[$i], $gen_link);
2103      }
2104  
2105      if (isset($forum_url['insertion_find']))
2106          $gen_link = $base_url.'/'.str_replace($forum_url['insertion_find'], str_replace('$1', str_replace('$1', $subarg, $sublink), $forum_url['insertion_replace']), $gen_link);
2107      else
2108          $gen_link = $base_url.'/'.$gen_link.str_replace('$1', $subarg, $sublink);
2109  
2110      ($hook = get_hook('fn_forum_sublink_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2111  
2112      return $gen_link;
2113  }
2114  
2115  
2116  //
2117  // Format a time string according to $date_format, $time_format, and timezones
2118  // $type: 0 = date/time, 1 = date, 2 = time
2119  //
2120  function format_time($timestamp, $type = 0, $date_format = null, $time_format = null, $no_text = false)
2121  {
2122      global $forum_config, $lang_common, $forum_user, $forum_time_formats, $forum_date_formats;
2123  
2124      $return = ($hook = get_hook('fn_format_time_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2125      if ($return != null)
2126          return $return;
2127  
2128      if ($timestamp == '')
2129          return ($no_text ? '' : $lang_common['Never']);
2130  
2131      if ($date_format == null)
2132          $date_format = $forum_date_formats[$forum_user['date_format']];
2133  
2134      if ($time_format == null)
2135          $time_format = $forum_time_formats[$forum_user['time_format']];
2136  
2137      $diff = ($forum_user['timezone'] + $forum_user['dst']) * 3600;
2138      $timestamp += $diff;
2139      $now = time();
2140  
2141      $formatted_time = '';
2142  
2143      if ($type == 0 || $type == 1)
2144      {
2145          $formatted_time = gmdate($date_format, $timestamp);
2146  
2147          if (!$no_text)
2148          {
2149              $base = gmdate('Y-m-d', $timestamp);
2150              $today = gmdate('Y-m-d', $now + $diff);
2151              $yesterday = gmdate('Y-m-d', $now + $diff - 86400);
2152  
2153              if ($base == $today)
2154                  $formatted_time = $lang_common['Today'];
2155              else if ($base == $yesterday)
2156                  $formatted_time = $lang_common['Yesterday'];
2157          }
2158      }
2159  
2160      if ($type == 0)
2161          $formatted_time .= ' ';
2162   
2163      if ($type == 0 || $type == 2)
2164          $formatted_time .= gmdate($time_format, $timestamp);
2165  
2166      return $formatted_time;
2167  }
2168  
2169  
2170  //
2171  // A wrapper for PHP's number_format function
2172  //
2173  function forum_number_format($number, $decimals = 0)
2174  {
2175      global $lang_common;
2176  
2177      $return = ($hook = get_hook('fn_forum_number_format_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2178      if ($return != null)
2179          return $return;
2180  
2181      return number_format($number, $decimals, $lang_common['lang_decimal_point'], $lang_common['lang_thousands_sep']);
2182  }
2183  
2184  
2185  //
2186  // Generate a random key of length $len
2187  //
2188  function random_key($len, $readable = false, $hash = false)
2189  {
2190      $key = '';
2191  
2192      $return = ($hook = get_hook('fn_random_key_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2193      if ($return != null)
2194          return $return;
2195  
2196      if ($hash)
2197          $key = substr(sha1(uniqid(rand(), true)), 0, $len);
2198      else if ($readable)
2199      {
2200          $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
2201  
2202          for ($i = 0; $i < $len; ++$i)
2203              $key .= substr($chars, (mt_rand() % strlen($chars)), 1);
2204      }
2205      else
2206      {
2207          for ($i = 0; $i < $len; ++$i)
2208              $key .= chr(mt_rand(33, 126));
2209      }
2210  
2211      ($hook = get_hook('fn_random_key_end')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2212  
2213      return $key;
2214  }
2215  
2216  
2217  //
2218  // Generates a valid CSRF token for use when submitting a form to $target_url
2219  // $target_url should be an absolute URL and it should be exactly the URL that the user is going to
2220  // Alternately, if the form token is going to be used in GET (which would mean the token is going to be
2221  // a part of the URL itself), $target_url may be a plain string containing information related to the URL.
2222  //
2223  function generate_form_token($target_url)
2224  {
2225      global $forum_user;
2226  
2227      $return = ($hook = get_hook('fn_generate_form_token_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2228      if ($return != null)
2229          return $return;
2230  
2231      return sha1(str_replace('&amp;', '&', $target_url).$forum_user['csrf_token']);
2232  }
2233  
2234  
2235  //
2236  // Generates a salted, SHA-1 hash of $str
2237  //
2238  function forum_hash($str, $salt)
2239  {
2240      $return = ($hook = get_hook('fn_forum_hash_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2241      if ($return != null)
2242          return $return;
2243  
2244      return sha1($salt.sha1($str));
2245  }
2246  
2247  
2248  //
2249  // Try to determine the correct remote IP-address
2250  //
2251  function get_remote_address()
2252  {
2253      $return = ($hook = get_hook('fn_get_remote_address_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2254      if ($return != null)
2255          return $return;
2256  
2257      return $_SERVER['REMOTE_ADDR'];
2258  }
2259  
2260  
2261  //
2262  // Try to determine the current URL
2263  //
2264  function get_current_url($max_length = 0)
2265  {
2266      global $base_url;
2267  
2268      $return = ($hook = get_hook('fn_get_current_url_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2269      if ($return != null)
2270          return $return;
2271  
2272      $protocol = (!isset($_SERVER['HTTPS']) || strtolower($_SERVER['HTTPS']) == 'off') ? 'http://' : 'https://';
2273      $port = (isset($_SERVER['SERVER_PORT']) && (($_SERVER['SERVER_PORT'] != '80' && $protocol == 'http://') || ($_SERVER['SERVER_PORT'] != '443' && $protocol == 'https://')) && strpos($_SERVER['HTTP_HOST'], ':') === false) ? ':'.$_SERVER['SERVER_PORT'] : '';
2274  
2275      $url = $protocol.$_SERVER['HTTP_HOST'].$port.$_SERVER['REQUEST_URI'];
2276  
2277      if (strlen($url) <= $max_length || $max_length == 0)
2278          return $url;
2279  
2280      // We can't find a short enough url
2281      return null;
2282  }
2283  
2284  
2285  //
2286  // Encodes the contents of $str so that they are safe to output on an (X)HTML page
2287  //
2288  function forum_htmlencode($str)
2289  {
2290      $return = ($hook = get_hook('fn_forum_htmlencode_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2291      if ($return != null)
2292          return $return;
2293  
2294      return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
2295  }
2296  
2297  
2298  //
2299  // Convert \r\n and \r to \n
2300  //
2301  function forum_linebreaks($str)
2302  {
2303      return str_replace(array("\r\n", "\r"), "\n", $str);
2304  }
2305  
2306  
2307  //
2308  // Trim whitespace including non-breaking space
2309  //
2310  function forum_trim($str, $charlist = " \t\n\r\x0b\xc2\xa0")
2311  {
2312      return utf8_trim($str, $charlist);
2313  }
2314  
2315  
2316  //
2317  // Inserts $element into $input at $offset
2318  // $offset can be either a numerical offset to insert at (eg: 0 inserts at the beginning of the array)
2319  // or a string, which is the key that the new element should be inserted before
2320  // $key is optional: it's used when inserting a new key/value pair into an associative array
2321  //
2322  function array_insert(&$input, $offset, $element, $key = null)
2323  {
2324      if ($key == null)
2325          $key = $offset;
2326  
2327      // Determine the proper offset if we're using a string
2328      if (!is_int($offset))
2329          $offset = array_search($offset, array_keys($input), true);
2330  
2331      // Out of bounds checks
2332      if ($offset > count($input))
2333          $offset = count($input);
2334      else if ($offset < 0)
2335          $offset = 0;
2336  
2337      $input = array_merge(array_slice($input, 0, $offset), array($key => $element), array_slice($input, $offset));
2338  }
2339  
2340  
2341  //
2342  // Attempts to fetch the provided URL using any available means
2343  //
2344  function get_remote_file($url, $timeout, $head_only = false)
2345  {
2346      $result = null;
2347      $parsed_url = parse_url($url);
2348      $allow_url_fopen = strtolower(@ini_get('allow_url_fopen'));
2349  
2350      // Quite unlikely that this will be allowed on a shared host, but it can't hurt
2351      if (function_exists('ini_set'))
2352          @ini_set('default_socket_timeout', $timeout);
2353  
2354      // If we have cURL, we might as well use it
2355      if (function_exists('curl_init'))
2356      {
2357          // Setup the transfer
2358          $ch = curl_init();
2359          curl_setopt($ch, CURLOPT_URL, $url);
2360          curl_setopt($ch, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0);
2361          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
2362          curl_setopt($ch, CURLOPT_HEADER, true);
2363          curl_setopt($ch, CURLOPT_NOBODY, $head_only);
2364          curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
2365          curl_setopt($ch, CURLOPT_USERAGENT, 'FluxBB');
2366  
2367          // Grab the page
2368          $content = @curl_exec($ch);
2369  
2370          // Ignore everything except a 200 response code
2371          if ($content !== false && curl_getinfo($ch, CURLINFO_HTTP_CODE) == '200')
2372          {
2373              if ($head_only)
2374                  $result['headers'] = explode("\r\n", forum_trim($content));
2375              else
2376              {
2377                  $content_start = strpos($content, "\r\n\r\n");
2378                  if ($content_start !== false)
2379                  {
2380                      $result['headers'] = explode("\r\n", substr($content, 0, $content_start));
2381                      $result['content'] = forum_trim(substr($content, $content_start));
2382                  }
2383              }
2384          }
2385  
2386          curl_close($ch);
2387      }
2388      // fsockopen() is the second best thing
2389      else if (function_exists('fsockopen'))
2390      {
2391          $remote = @fsockopen($parsed_url['host'], !empty($parsed_url['port']) ? intval($parsed_url['port']) : 80, $errno, $errstr, $timeout);
2392          if ($remote)
2393          {
2394              // Send a standard HTTP 1.0 request for the page
2395              $method = $head_only ? 'HEAD' : 'GET';
2396              fwrite($remote, ($head_only ? 'HEAD' : 'GET').' '.(!empty($parsed_url['path']) ? $parsed_url['path'] : '/').(!empty($parsed_url['query']) ? '?'.$parsed_url['query'] : '').' HTTP/1.0'."\r\n");
2397              fwrite($remote, 'Host: '.$parsed_url['host']."\r\n");
2398              fwrite($remote, 'User-Agent: FluxBB'."\r\n");
2399              fwrite($remote, 'Connection: Close'."\r\n\r\n");
2400  
2401              stream_set_timeout($remote, $timeout);
2402              $stream_meta = stream_get_meta_data($remote);
2403  
2404              // Fetch the response 1024 bytes at a time and watch out for a timeout
2405              $content = false;
2406              while (!feof($remote) && !$stream_meta['timed_out'])
2407              {
2408                  $content .= fgets($remote, 1024);
2409                  $stream_meta = stream_get_meta_data($remote);
2410              }
2411  
2412              fclose($remote);
2413  
2414              // Ignore everything except a 200 response code (we don't support redirects)
2415              if ($content !== false && preg_match('#^HTTP/1.[01] 200 OK#', $content))
2416              {
2417                  if ($head_only)
2418                      $result['headers'] = explode("\r\n", forum_trim($content));
2419                  else
2420                  {
2421                      $content_start = strpos($content, "\r\n\r\n");
2422                      if ($content_start !== false)
2423                      {
2424                          $result['headers'] = explode("\r\n", substr($content, 0, $content_start));
2425                          $result['content'] = forum_trim(substr($content, $content_start));
2426                      }
2427                  }
2428              }
2429          }
2430      }
2431      // Last case scenario, we use file_get_contents provided allow_url_fopen is enabled (any non 200 response results in a failure)
2432      else if (in_array($allow_url_fopen, array('on', 'true', '1')))
2433      {
2434          // PHP5's version of file_get_contents() supports stream options
2435          if (version_compare(PHP_VERSION, '5.0.0', '>='))
2436          {
2437              // Setup a stream context
2438              $stream_context = stream_context_create(
2439                  array(
2440                      'http' => array(
2441                          'method'        => $head_only ? 'HEAD' : 'GET',
2442                          'user_agent'    => 'FluxBB',
2443                          'max_redirects'    => 3,        // PHP >=5.1.0 only
2444                          'timeout'        => $timeout    // PHP >=5.2.1 only
2445                      )
2446                  )
2447              );
2448  
2449              $content = @file_get_contents($url, false, $stream_context);
2450          }
2451          else
2452              $content = @file_get_contents($url);
2453  
2454          // Did we get anything?
2455          if ($content !== false)
2456          {
2457              // Gotta love the fact that $http_response_header just appears in the global scope (*cough* hack! *cough*)
2458              $result['headers'] = $http_response_header;
2459              if (!$head_only)
2460                  $result['content'] = forum_trim($content);
2461          }
2462      }
2463  
2464      return $result;
2465  }
2466  
2467  
2468  //
2469  // Display a message when board is in maintenance mode
2470  //
2471  function maintenance_message()
2472  {
2473      global $forum_db, $forum_config, $lang_common, $forum_user, $base_url;
2474  
2475      $return = ($hook = get_hook('fn_maintenance_message_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2476      if ($return != null)
2477          return $return;
2478  
2479      // Deal with newlines, tabs and multiple spaces
2480      $pattern = array("\t\t", '  ', '  ');
2481      $replace = array('&#160; &#160; ', '&#160; ', ' &#160;');
2482      $message = str_replace($pattern, $replace, $forum_config['o_maintenance_message']);
2483  
2484      // Send the Content-type header in case the web server is setup to send something else
2485      header('Content-type: text/html; charset=utf-8');
2486  
2487      // Send a 503 HTTP response code to prevent search bots from indexing the maintenace message
2488      header('HTTP/1.1 503 Service Temporarily Unavailable');
2489  
2490      // Load the maintenance template
2491      if (file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/maintenance.tpl'))
2492          $tpl_path = FORUM_ROOT.'style/'.$forum_user['style'].'/maintenance.tpl';
2493      else
2494          $tpl_path = FORUM_ROOT.'include/template/maintenance.tpl';
2495  
2496      ($hook = get_hook('fn_maintenance_message_pre_template_loaded')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2497  
2498      $tpl_maint = forum_trim(file_get_contents($tpl_path));
2499  
2500      ($hook = get_hook('fn_maintenance_message_template_loaded')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2501  
2502      // START SUBST - <!-- forum_local -->
2503      $tpl_maint = str_replace('<!-- forum_local -->', 'xml:lang="'.$lang_common['lang_identifier'].'" lang="'.$lang_common['lang_identifier'].'" dir="'.$lang_common['lang_direction'].'"', $tpl_maint);
2504      // END SUBST - <!-- forum_local -->
2505  
2506  
2507      // START SUBST - <!-- forum_head -->
2508      ob_start();
2509  
2510  ?>
2511  <title><?php echo $lang_common['Maintenance'].' - '.forum_htmlencode($forum_config['o_board_title']) ?></title>
2512  <link rel="stylesheet" type="text/css" media="screen" href="<?php echo $base_url ?>/style/<?php echo $forum_user['style'] ?>/<?php echo $forum_user['style'].'.css' ?>" />
2513  <!--[if lte IE 6]><link rel="stylesheet" type="text/css" href="<?php echo $base_url ?>/style/<?php echo $forum_user['style'] ?>/<?php echo $forum_user['style'].'_fix.css' ?>" /><![endif]-->
2514  <!--[if IE 7]><link rel="stylesheet" type="text/css" href="<?php echo $base_url ?>/style/<?php echo $forum_user['style'] ?>/<?php echo $forum_user['style'].'_fix7.css' ?>" /><![endif]-->
2515  <?php
2516  
2517      $tpl_temp = forum_trim(ob_get_contents());
2518      $tpl_maint = str_replace('<!-- forum_head -->', $tpl_temp, $tpl_maint);
2519      ob_end_clean();
2520      // END SUBST - <!-- forum_head -->
2521  
2522  
2523      // START SUBST - <!-- forum_maint_main -->
2524      ob_start();
2525  
2526  ?>
2527  <div id="brd-main" class="main basic">
2528  
2529      <div class="main-head">
2530          <h1 class="hn"><span><?php echo $lang_common['Maintenance mode'] ?></span></h1>
2531      </div>
2532      <div class="main-content main-message">
2533          <div class="ct-box user-box">
2534              <?php echo $message."\n" ?>
2535          </div>
2536      </div>
2537  
2538  </div>
2539  <?php
2540  
2541      $tpl_temp = "\t".forum_trim(ob_get_contents());
2542      $tpl_maint = str_replace('<!-- forum_maint_main -->', $tpl_temp, $tpl_maint);
2543      ob_end_clean();
2544      // END SUBST - <!-- forum_maint_main -->
2545  
2546  
2547      // End the transaction
2548      $forum_db->end_transaction();
2549  
2550  
2551      // START SUBST - <!-- forum_include "*" -->
2552      while (preg_match('#<!-- ?forum_include "([^/\\\\]*?)" ?-->#', $tpl_maint, $cur_include))
2553      {
2554          if (!file_exists(FORUM_ROOT.'include/user/'.$cur_include[1]))
2555              error('Unable to process user include &lt;!-- forum_include "'.forum_htmlencode($cur_include[1]).'" --&gt; from template maintenance.tpl. There is no such file in folder /include/user/.');
2556  
2557          ob_start();
2558          include FORUM_ROOT.'include/user/'.$cur_include[1];
2559          $tpl_temp = ob_get_contents();
2560          $tpl_maint = str_replace($cur_include[0], $tpl_temp, $tpl_maint);
2561          ob_end_clean();
2562      }
2563      // END SUBST - <!-- forum_include "*" -->
2564  
2565  
2566      // Close the db connection (and free up any result data)
2567      $forum_db->close();
2568  
2569      exit($tpl_maint);
2570  }
2571  
2572  
2573  //
2574  // Display $message and redirect user to $destination_url
2575  //
2576  function redirect($destination_url, $message)
2577  {
2578      global $forum_db, $forum_config, $lang_common, $forum_user, $base_url;
2579  
2580      define('FORUM_PAGE', 'redirect');
2581  
2582      ($hook = get_hook('fn_redirect_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2583  
2584      // Prefix with base_url (unless it's there already)
2585      if (strpos($destination_url, 'http://') !== 0 && strpos($destination_url, 'https://') !== 0 && strpos($destination_url, '/') !== 0)
2586          $destination_url = $base_url.'/'.$destination_url;
2587  
2588      // Do a little spring cleaning
2589      $destination_url = preg_replace('/([\r\n])|(%0[ad])|(;[\s]*data[\s]*:)/i', '', $destination_url);
2590  
2591      // If the delay is 0 seconds, we might as well skip the redirect all together
2592      if ($forum_config['o_redirect_delay'] == '0')
2593          header('Location: '.str_replace('&amp;', '&', $destination_url));
2594  
2595      // Send no-cache headers
2596      header('Expires: Thu, 21 Jul 1977 07:30:00 GMT');    // When yours truly first set eyes on this world! :)
2597      header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
2598      header('Cache-Control: post-check=0, pre-check=0', false);
2599      header('Pragma: no-cache');        // For HTTP/1.0 compability
2600  
2601      // Send the Content-type header in case the web server is setup to send something else
2602      header('Content-type: text/html; charset=utf-8');
2603  
2604      // Load the redirect template
2605      if (file_exists(FORUM_ROOT.'style/'.$forum_user['style'].'/redirect.tpl'))
2606          $tpl_path = FORUM_ROOT.'style/'.$forum_user['style'].'/redirect.tpl';
2607      else
2608          $tpl_path = FORUM_ROOT.'include/template/redirect.tpl';
2609  
2610      ($hook = get_hook('fn_redirect_pre_template_loaded')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2611  
2612      $tpl_redir = forum_trim(file_get_contents($tpl_path));
2613  
2614      ($hook = get_hook('fn_redirect_template_loaded')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2615  
2616      // START SUBST - <!-- forum_local -->
2617      $tpl_redir = str_replace('<!-- forum_local -->', 'xml:lang="'.$lang_common['lang_identifier'].'" lang="'.$lang_common['lang_identifier'].'" dir="'.$lang_common['lang_direction'].'"', $tpl_redir);
2618      // END SUBST - <!-- forum_local -->
2619  
2620  
2621      // START SUBST - <!-- forum_head -->
2622  
2623      $forum_head['refresh'] = '<meta http-equiv="refresh" content="'.$forum_config['o_redirect_delay'].';URL='.str_replace(array('<', '>', '"'), array('&lt;', '&gt;', '&quot;'), $destination_url).'" />';
2624      $forum_head['title'] = '<title>'.$lang_common['Redirecting'].' - '.forum_htmlencode($forum_config['o_board_title']).'</title>';
2625  
2626      ob_start();
2627  
2628      // Include stylesheets
2629      require FORUM_ROOT.'style/'.$forum_user['style'].'/'.$forum_user['style'].'.php';
2630  
2631      $head_temp = forum_trim(ob_get_contents());
2632      $num_temp = 0;
2633      foreach (explode("\n", $head_temp) as $style_temp)
2634          $forum_head['style'.$num_temp++] = $style_temp;
2635  
2636      ob_end_clean();
2637  
2638      ($hook = get_hook('fn_redirect_head')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2639  
2640      $tpl_redir = str_replace('<!-- forum_head -->', implode("\n",$forum_head), $tpl_redir);
2641      unset($forum_head);
2642  
2643      // END SUBST - <!-- forum_head -->
2644  
2645  
2646      // START SUBST - <!-- forum_redir_main -->
2647      ob_start();
2648  
2649  ?>
2650  <div id="brd-main" class="main basic">
2651  
2652      <div class="main-head">
2653          <h1 class="hn"><span><?php echo $message ?></span></h1>
2654      </div>
2655  
2656      <div class="main-content main-message">
2657          <p><?php printf($lang_common['Forwarding info'], $forum_config['o_redirect_delay'], intval($forum_config['o_redirect_delay']) == 1 ? $lang_common['second'] : $lang_common['seconds']) ?><span> <a href="<?php echo $destination_url ?>"><?php echo $lang_common['Click redirect'] ?></a></span></p>
2658      </div>
2659  
2660  </div>
2661  <?php
2662  
2663      $tpl_temp = "\t".forum_trim(ob_get_contents());
2664      $tpl_redir = str_replace('<!-- forum_redir_main -->', $tpl_temp, $tpl_redir);
2665      ob_end_clean();
2666      // END SUBST - <!-- forum_redir_main -->
2667  
2668  
2669      // START SUBST - <!-- forum_debug -->
2670      if (defined('FORUM_SHOW_QUERIES'))
2671          $tpl_redir = str_replace('<!-- forum_debug -->', get_saved_queries(), $tpl_redir);
2672  
2673      // End the transaction
2674      $forum_db->end_transaction();
2675      // END SUBST - <!-- forum_debug -->
2676  
2677  
2678      // START SUBST - <!-- forum_include "*" -->
2679      while (preg_match('#<!-- ?forum_include "([^/\\\\]*?)" ?-->#', $tpl_redir, $cur_include))
2680      {
2681          if (!file_exists(FORUM_ROOT.'include/user/'.$cur_include[1]))
2682              error('Unable to process user include &lt;!-- forum_include "'.forum_htmlencode($cur_include[1]).'" --&gt; from template redirect.tpl. There is no such file in folder /include/user/.');
2683  
2684          ob_start();
2685          include FORUM_ROOT.'include/user/'.$cur_include[1];
2686          $tpl_temp = ob_get_contents();
2687          $tpl_redir = str_replace($cur_include[0], $tpl_temp, $tpl_redir);
2688          ob_end_clean();
2689      }
2690      // END SUBST - <!-- forum_include "*" -->
2691  
2692  
2693      // Close the db connection (and free up any result data)
2694      $forum_db->close();
2695  
2696      exit($tpl_redir);
2697  }
2698  
2699  
2700  //
2701  // Display a simple error message
2702  //
2703  function error()
2704  {
2705      global $forum_config;
2706  
2707      /*
2708          Parse input parameters. Possible function signatures:
2709          error('Error message.');
2710          error(__FILE__, __LINE__);
2711          error('Error message.', __FILE__, __LINE__);
2712      */
2713      $num_args = func_num_args();
2714      if ($num_args == 3)
2715      {
2716          $message = func_get_arg(0);
2717          $file = func_get_arg(1);
2718          $line = func_get_arg(2);
2719      }
2720      else if ($num_args == 2)
2721      {
2722          $file = func_get_arg(0);
2723          $line = func_get_arg(1);
2724      }
2725      else if ($num_args == 1)
2726          $message = func_get_arg(0);
2727  
2728      // Set a default title if the script failed before $forum_config could be populated
2729      if (empty($forum_config))
2730          $forum_config['o_board_title'] = 'FluxBB';
2731  
2732      // Empty all output buffers and stop buffering
2733      while (@ob_end_clean());
2734  
2735      // "Restart" output buffering if we are using ob_gzhandler (since the gzip header is already sent)
2736      if (!empty($forum_config['o_gzip']) && extension_loaded('zlib') && (strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== false || strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'deflate') !== false))
2737          ob_start('ob_gzhandler');
2738  
2739  ?>
2740  <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2741  
2742  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
2743  <head>
2744  <title>Error - <?php echo forum_htmlencode($forum_config['o_board_title']) ?></title>
2745  </head>
2746  <body style="margin: 40px; font: 85%/130% verdana, arial, sans-serif; color: #333;">
2747  
2748  <h1>An error was encountered</h1>
2749  <hr />
2750  <?php
2751  
2752      if (isset($message))
2753          echo '<p>'.$message.'</p>'."\n";
2754  
2755      if ($num_args > 1)
2756      {
2757          if (defined('FORUM_DEBUG'))
2758          {
2759              if (isset($file) && isset($line))
2760                  echo '<p><em>The error occurred on line '.$line.' in '.$file.'</em></p>'."\n";
2761  
2762              $db_error = isset($GLOBALS['forum_db']) ? $GLOBALS['forum_db']->error() : array();
2763              if (!empty($db_error['error_msg']))
2764              {
2765                  echo '<p><strong>Database reported:</strong> '.forum_htmlencode($db_error['error_msg']).(($db_error['error_no']) ? ' (Errno: '.$db_error['error_no'].')' : '').'.</p>'."\n";
2766  
2767                  if ($db_error['error_sql'] != '')
2768                      echo '<p><strong>Failed query:</strong> <code>'.forum_htmlencode($db_error['error_sql']).'</code></p>'."\n";
2769              }
2770          }
2771          else
2772              echo '<p><strong>Note:</strong> For detailed error information (necessary for troubleshooting), enable "DEBUG mode". To enable "DEBUG mode", open up the file config.php in a text editor, add a line that looks like "define(\'FORUM_DEBUG\', 1);" (without the quotation marks), and re-upload the file. Once you\'ve solved the problem, it is recommended that "DEBUG mode" be turned off again (just remove the line from the file and re-upload it).</p>'."\n";
2773      }
2774  
2775  ?>
2776  
2777  </body>
2778  </html>
2779  <?php
2780  
2781      // If a database connection was established (before this error) we close it
2782      if (isset($GLOBALS['forum_db']))
2783          $GLOBALS['forum_db']->close();
2784  
2785      exit;
2786  }
2787  
2788  
2789  //
2790  // Unset any variables instantiated as a result of register_globals being enabled
2791  //
2792  function forum_unregister_globals()
2793  {
2794      $register_globals = @ini_get('register_globals');
2795      if ($register_globals === "" || $register_globals === "0" || strtolower($register_globals) === "off")
2796          return;
2797  
2798      // Prevent script.php?GLOBALS[foo]=bar
2799      if (isset($_REQUEST['GLOBALS']) || isset($_FILES['GLOBALS']))
2800          exit('I\'ll have a steak sandwich and... a steak sandwich.');
2801  
2802      // Variables that shouldn't be unset
2803      $no_unset = array('GLOBALS', '_GET', '_POST', '_COOKIE', '_REQUEST', '_SERVER', '_ENV', '_FILES');
2804  
2805      // Remove elements in $GLOBALS that are present in any of the superglobals
2806      $input = array_merge($_GET, $_POST, $_COOKIE, $_SERVER, $_ENV, $_FILES, isset($_SESSION) && is_array($_SESSION) ? $_SESSION : array());
2807      foreach ($input as $k => $v)
2808      {
2809          if (!in_array($k, $no_unset) && isset($GLOBALS[$k]))
2810          {
2811              unset($GLOBALS[$k]);
2812              unset($GLOBALS[$k]);    // Double unset to circumvent the zend_hash_del_key_or_index hole in PHP <4.4.3 and <5.1.4
2813          }
2814      }
2815  }
2816  
2817  
2818  //
2819  // Removes any "bad" characters (characters which mess with the display of a page, are invisible, etc) from user input
2820  //
2821  function forum_remove_bad_characters()
2822  {
2823      global $bad_utf8_chars;
2824  
2825      $bad_utf8_chars = array("\0", "\xc2\xad", "\xcc\xb7", "\xcc\xb8", "\xe1\x85\x9F", "\xe1\x85\xA0", "\xe2\x80\x80", "\xe2\x80\x81", "\xe2\x80\x82", "\xe2\x80\x83", "\xe2\x80\x84", "\xe2\x80\x85", "\xe2\x80\x86", "\xe2\x80\x87", "\xe2\x80\x88", "\xe2\x80\x89", "\xe2\x80\x8a", "\xe2\x80\x8b", "\xe2\x80\x8e", "\xe2\x80\x8f", "\xe2\x80\xaa", "\xe2\x80\xab", "\xe2\x80\xac", "\xe2\x80\xad", "\xe2\x80\xae", "\xe2\x80\xaf", "\xe2\x81\x9f", "\xe3\x80\x80", "\xe3\x85\xa4", "\xef\xbb\xbf", "\xef\xbe\xa0", "\xef\xbf\xb9", "\xef\xbf\xba", "\xef\xbf\xbb");
2826  
2827      ($hook = get_hook('fn_remove_bad_characters_start')) ? (defined('FORUM_USE_INCLUDE') ? include $hook : eval($hook)) : null;
2828  
2829  	function _forum_remove_bad_characters($array)
2830      {
2831          global $bad_utf8_chars;
2832          return is_array($array) ? array_map('_forum_remove_bad_characters', $array) : str_replace($bad_utf8_chars, '', $array);
2833      }
2834  
2835      $_GET = _forum_remove_bad_characters($_GET);
2836      $_POST = _forum_remove_bad_characters($_POST);
2837      $_COOKIE = _forum_remove_bad_characters($_COOKIE);
2838      $_REQUEST = _forum_remove_bad_characters($_REQUEST);
2839  }
2840  
2841  
2842  // DEBUG FUNCTIONS BELOW
2843  
2844  //
2845  // Display executed queries (if enabled)
2846  //
2847  function get_saved_queries()
2848  {
2849      global $forum_db, $lang_common;
2850  
2851      // Get the queries so that we can print them out
2852      $saved_queries = $forum_db->get_saved_queries();
2853  
2854      $output = '
2855  <div id="brd-debug" class="main">
2856  
2857      <div class="main-head">
2858          <h2><span>'.$lang_common['Debug table'].'</span></h2>
2859      </div>
2860  
2861      <div class="main-content debug">
2862          <table cellspacing="0" summary="Database query performance information">
2863              <thead>
2864                  <tr>
2865                      <th class="tcl" scope="col">Time (s)</th>
2866                      <th class="tcr" scope="col">Query</th>
2867                  </tr>
2868              </thead>
2869              <tbody>
2870  ';
2871  
2872      $query_time_total = 0.0;
2873      while (list(, $cur_query) = @each($saved_queries))
2874      {
2875          $query_time_total += $cur_query[1];
2876  
2877          $output .= '
2878                  <tr>
2879                      <td class="tcl">'.(($cur_query[1] != 0) ? forum_number_format($cur_query[1], 5) : '&#160;').'</td>
2880                      <td class="tcr">'.forum_htmlencode($cur_query[0]).'</td>
2881                  </tr>
2882  ';
2883  
2884      }
2885  
2886      $output .= '
2887                  <tr class="totals">
2888                      <td class="tcl"><em>'.forum_number_format($query_time_total, 5).'</em></td>
2889                      <td class="tcr"><em>Total query time</em></td>
2890                  </tr>
2891              </tbody>
2892          </table>
2893      </div>
2894  </div>
2895  ';
2896  
2897      return $output;
2898  }
2899  
2900  
2901  //
2902  // Extract part of a template file
2903  //
2904  function extract_part($whole, $start, $end)
2905  {
2906     $start_pos = stripos($whole, $start) + strlen($start);
2907  
2908     $end_pos = stripos($whole, $end, $start_pos + 1);
2909  
2910     return  substr($whole, $start_pos, $end_pos - $start_pos);
2911  }
2912  
2913  
2914  //
2915  // Dump contents of variable(s)
2916  //
2917  function dump()
2918  {
2919      echo '<pre>';
2920  
2921      $num_args = func_num_args();
2922  
2923      for ($i = 0; $i < $num_args; ++$i)
2924      {
2925          print_r(func_get_arg($i));
2926          echo "\n\n";
2927      }
2928  
2929      echo '</pre>';
2930      exit;
2931  }


[ Powered by PHPXref - Served by Debian GNU/Linux ]