Skip to content
drupalvb.inc.php 16.1 KiB
Newer Older
 * @file
 * Drupal vB CRUD functions.
 * Set the necessary cookies for the user to be logged into the forum.
 *
 * Frontend cookie names:
 * - lastvisit, lastactivity, sessionhash
 * Backend cookie names:
 * - cpsession, userid, password
 *
 * However, in all cases the cookiedomain is NOT prefixed with a dot unless
 * cookie domain has not been manually altered to either a suggested value or
 * custom value in vB's settings.
function drupalvb_set_login_cookies($userid) {
  // Load required vB user data.
  $vbuser = drupalvb_db_query("SELECT userid, password, salt FROM {user} WHERE userid = :userid", array(":userid" => $userid))->fetchAssoc();
  $cookie_prefix = drupalvb_get_cookieprefix();
  $cookie_path = $vb_options['cookiepath'];
  $now = time();
  $expire = $now + (@ini_get('session.cookie_lifetime') ? ini_get('session.cookie_lifetime') : 60 * 60 * 24 * 365);
  $vb_cookie_domain = (!empty($vb_options['cookiedomain']) ? $vb_options['cookiedomain'] : $GLOBALS['cookie_domain']);
  // Per RFC 2109, cookie domains must contain at least one dot other than the
  // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
  // @see conf_init()
  if (!(count(explode('.', $vb_cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $vb_cookie_domain)))) {
    $vb_cookie_domain = '';
  }

  // Clear out old session (if available).
  if (!empty($_COOKIE[$cookie_prefix .'sessionhash'])) {
    drupalvb_db_query("DELETE FROM {session} WHERE sessionhash = :hash", array(":hash" => $_COOKIE[$cookie_prefix .'sessionhash']));
  $ip = implode('.', array_slice(explode('.', drupalvb_get_ip()), 0, 4 - $vb_options['ipcheck']));
  $idhash = md5($_SERVER['HTTP_USER_AGENT'] . $ip);
  $sessionhash = md5($now . request_uri() . $idhash . $_SERVER['REMOTE_ADDR'] . user_password(6));
  $browserstring = substr(trim($_SERVER['HTTP_USER_AGENT']), 0, 100);
  drupalvb_db_query("REPLACE INTO {session} (sessionhash, userid, host, idhash, lastactivity, location, useragent, loggedin) VALUES (:hash, :userid, :host, :idhash, :lastactivity, :location, :useragent, :loggedin)", array(":hash" => $sessionhash, ":userid" => $vbuser['userid'], ":host" => substr($_SERVER['REMOTE_ADDR'], 0, 15), ":idhash" => $idhash, ":lastactivity" => $now, ":location" => '/forum/', ":useragent" => $browserstring, ":loggedin" => 2));
  setcookie($cookie_prefix .'sessionhash', $sessionhash, $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'lastvisit', $now, $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'lastactivity', $now, $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'userid', $vbuser['userid'], $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'password', md5($vbuser['password'] . variable_get('drupalvb_license', '')), $expire, $cookie_path, $vb_cookie_domain);
 * Clear all vB cookies for the current user.
 *
 * @see drupalvb_logout(), drupalvb_user_logout()
function drupalvb_clear_cookies($userid = NULL) {
  $cookie_prefix = drupalvb_get_cookieprefix();
  $cookie_path = $vb_options['cookiepath'];

  $vb_cookie_domain = (!empty($vb_options['cookiedomain']) ? $vb_options['cookiedomain'] : $GLOBALS['cookie_domain']);
  // Per RFC 2109, cookie domains must contain at least one dot other than the
  // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
  // @see conf_init()
  if (!(count(explode('.', $vb_cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $vb_cookie_domain)))) {
    $vb_cookie_domain = '';
  }

  // @todo Without a vB user id, we cannot delete the session, so vBulletin
  //   will automatically authenticate again. We badly need a solution here,
  //   since this is the cause for broken session handling. Proposal: Use
  //   drupalvb_get_ip() to count the # of sessions; if there exactly one,
  //   kill it.
  // @ttkaminski's suggestion - validate the sessionhash and userid from the
  //   cookies against the vBulletin session table.  If it matches, then kill
  //   the session.

    drupalvb_db_query("DELETE FROM {session} WHERE userid = :userid", array(':userid' => $userid));
    drupalvb_db_query("UPDATE {user} SET lastvisit = :time WHERE userid = :userid", array(":time" => time(), ":userid" => $userid));
  setcookie($cookie_prefix .'sessionhash', '', $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'lastvisit', '', $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'lastactivity', '', $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'userid', '', $expire, $cookie_path, $vb_cookie_domain);
  setcookie($cookie_prefix .'password', '', $expire, $cookie_path, $vb_cookie_domain);
 * Determines the IP address of current user.
function drupalvb_get_ip() {
  if (isset($_SERVER['HTTP_CLIENT_IP'])) {
  elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
    // Make sure we don't pick up an internal IP defined by RFC1918.
    foreach ($matches[0] as $match) {
      if (!preg_match("#^(10|172\.16|192\.168)\.#", $match)) {
        $ip = $match;
  elseif (isset($_SERVER['HTTP_FROM'])) {
 * Create a user in vBulletin.
 *
 * @param object $account
 *   A Drupal user account.
 * @param array $edit
 *   Form values provided by hook_user().
function drupalvb_create_user($account, $edit) {
  // Ensure we are not duplicating a user.
  $userid = drupalvb_db_select("user","u")
          ->fields("u", array("userid") )
          ->condition("username", drupalvb_htmlspecialchars($edit['name']) )
          ->execute()
          ->fetchColumn(0);

  if(!$userid) {
    $salt = '';
    for ($i = 0; $i < 3; $i++) {
      $salt .= chr(rand(32, 126));
    }
    // Note: Password is already hashed during user export.
    if (isset($edit['md5pass'])) {
      $passhash = md5($edit['md5pass'] . $salt);
    }
    else {
      $passhash = md5(md5($edit['pass']) . $salt);
    }
  
    $time = $account->created;
    $passdate = date('Y-m-d', $time);
    $joindate = $time;
  
    // Attempt to grab the user title from the database.
    $result = drupalvb_db_query("SELECT title FROM {usertitle} WHERE minposts = 0");
    if ($resarray = $result->fetchAssoc()) {
      $usertitle = $resarray['title'];
    }
    else {
      $usertitle = 'Junior Member';
    }
  
    // Divide timezone by 3600, since vBulletin stores hours.
    $timezone = variable_get('date_default_timezone', 0);
    $timezone = ($timezone != 0 ? $timezone / 3600 : 0);
  
    // Default new user options: I got these by setting up a new user how I
    // wanted and looking in the database to see what options were set for him.
    $options = variable_get('drupalvb_default_options', '3415');
  
    // Default usergroup id.
    $usergroupid = variable_get('drupalvb_default_usergroup', '2');
    $lid = drupalvb_get_languageid();
    if (!isset($lid)) {
      $lid = 1;
    }
    // Insert user to vBulletin
    $userid = drupalvb_db_insert("user")
    ->fields( array(
      "username" => drupalvb_htmlspecialchars($edit['name']),
      "usergroupid" => $usergroupid,
      "password" => $passhash,
      "passworddate" => $passdate,
      "usertitle" => $usertitle,
      "email" => $account->mail,
      "salt" => $salt,
      "languageid" => 1,
      "timezoneoffset" => $timezone,
      "joindate" => $joindate,
      "lastvisit" => time(),
      "lastactivity" => time(),
      "options" => $options
    ))->execute();
  
    $rr = drupalvb_db_query("SELECT * FROM {userfield} WHERE userid=1");
    $fields = $rr->fetchAssoc();
    foreach($fields as $f => $v) $fields[$f] = '';
    $fields['userid'] = $userid;
    drupalvb_db_insert("userfield")->fields($fields)->execute();
    
    $rr = drupalvb_db_query("SELECT * FROM {usertextfield} WHERE userid=1");
    $fields = $rr->fetchAssoc();
    foreach($fields as $f => $v) $fields[$f] = '';
    $fields['userid'] = $userid;    
    drupalvb_db_insert("usertextfield")->fields($fields)->execute();
  drupalvb_set_mapping($account->uid, $userid);
  // Return userid of newly created account.
  return $userid;
function drupalvb_update_user($account, $edit) { 
  foreach ($edit as $field => $value) {
    if (empty($value)) {
      continue;
    }
    switch ($field) {
      case 'name':
        $fields[] = "username = :name";
        $values[':name'] = drupalvb_htmlspecialchars($value);
        $fields[] = "password = :password";
        $values[':password'] = md5(md5($value) . $edit['salt']);
        $fields[] = "salt = :salt";
        $values[':salt'] = $edit['salt'];
        $fields[] = "passworddate = :date";
        $values[':date'] = date('Y-m-d', time());
        $fields[] = "email = :email";
        $values[':email'] = $value;
        $fields[] = "languageid = :lid";
        $values[':lid'] = 1;
  $fields[] = 'lastactivity = :activity';
  $values[':activity'] = time();
  // Ensure this user exists in the mapping table.
  // When integrating an existing installation, the mapping may not yet exist.
  if (isset($edit['userid'])) {
    $userid = $edit['userid'];
  }
  else {
    $userid = drupalvb_db_query("SELECT userid FROM {user} WHERE username = :name", array(':name' => drupalvb_htmlspecialchars($account->name)))->fetchField();
  }
  drupalvb_set_mapping($account->uid, $userid);

  $values[':userid'] = $userid;    
  drupalvb_db_query("UPDATE {user} SET " . implode(', ', $fields) . " WHERE userid=:userid", $values);
}

/**
 * Ensure that a mapping between two existing user accounts exists.
 *
 * @param $uid
 *   A Drupal user id.
 * @param $userid
 *   A vBulletin user id.
 */
function drupalvb_set_mapping($uid, $userid) {
  db_query("INSERT IGNORE INTO {drupalvb_users} (uid, userid) VALUES (:uid, :userid)", array(':uid' => $uid, ':userid' => $userid));
/**
 * Export all drupal users to vBulletin.
 */
function drupalvb_export_drupal_users() {
  module_load_include('inc', 'drupalvb');
  $result = db_query("SELECT * FROM {users} WHERE uid>0 ORDER BY uid");
  foreach ($result as $user) {
    // Let create/update functions know that passwords are hashed already.
    $user->md5pass = $user->pass;
    if (!drupalvb_create_user($user, (array)$user)) {
      // Username already exists, update email and password only.
      // Case insensitive username is required to detect collisions.
      $vbuser = drupalvb_db_query("SELECT salt,userid FROM {user} WHERE LOWER(username) = LOWER(:name)", array(':name' => drupalvb_htmlspecialchars($user->name)))->fetchAssoc();
      drupalvb_update_user($user, array_merge((array)$user, $vbuser));
    }
 * Get vBulletin configuration options.
function drupalvb_get_options() {
  static $options = array();
  if (empty($options)) {
    $result = drupalvb_db_query("SELECT varname, value FROM {setting}");
    foreach ($result as $var) {
      $options[$var->varname] = $var->value;
    }
  }
  return $options;
}

/**
 * Get vBulletin configuration.
 *
 * @return array
 *   An associative array containing the vBulletin configuration, plus
 *   additional key:
 *   - version: The vBulletin version number string, as contained in the first
 *     PHP comment lines of config.php.
 */
function drupalvb_get_config() {
  static $config;

  if (!isset($config)) {
    $config = array();
    $config['version'] = NULL;
    // @todo Find & include vB's config automatically?
    // $files = file_scan_directory('.', '^config.php$', $nomask = array('.', '..', 'CVS', '.svn'));
    $config_file = './' . conf_path() . '/config.php';
    if (!file_exists($config_file)) {
      $config_file = drupal_get_path('module', 'drupalvb') . '/config.php';
    }
    if (file_exists($config_file)) {
      require_once $config_file;

      // Additionally parse the vBulletin version out of the php file header, as
      // some integration functionality needs to account for API changes in
      // later vBulletin versions.
      $file = fopen($config_file, 'r');
      $max_lines = 10;
      while ($max_lines && $line = fgets($file, 30)) {
        if (preg_match('@vBulletin\s+([0-9a-zA-Z\.-]+)@', $line, $version)) {
          $config['version'] = $version[1];
        }
        $max_lines--;
      }
      fclose($file);
    }
  }
  return $config;
}

/**
 * Get vB user roles.
 */
function drupalvb_get_roles() {
  $result = drupalvb_db_query("SELECT usergroupid, title FROM {usergroup}");

  $roles = array();
  foreach ($result as $data) {
    $roles[$data->usergroupid] = $data->title;
  }
  if (!$roles) {
    $roles[] = t('No user roles could be found.');
  }
  return $roles;
}

/**
 * Get vB language id by given ISO language code.
 */
function drupalvb_get_languageid($language = NULL) {
  //static $vblanguages

  if (!isset($vblanguages)) {
    $vblanguages = array();
    $result = drupalvb_db_query("SELECT languageid, title, languagecode FROM {language}");
    foreach ($result as $lang) {
      $vblanguages[$lang->languagecode] = $lang->languageid;
  return 1;
  //return (!empty($language) && isset($vblanguages[$language]) ? $vblanguages[$language] : $vblanguages[$options['languageid']]);
/**
 * Get counts of guests and members currently online.
 */
function drupalvb_get_users_online() {

  $datecut          = time() - $vb_options['cookietimeout'];
Daniel Kudwien's avatar
Daniel Kudwien committed
  $numbervisible    = 0;
Daniel Kudwien's avatar
Daniel Kudwien committed
  $numberguest      = 0;
  $result = drupalvb_db_query("SELECT user.username, user.usergroupid, session.userid, session.lastactivity FROM {session} AS session LEFT JOIN {user} AS user ON (user.userid = session.userid) WHERE session.lastactivity > :datecut", array(':datecut' => $datecut));
  while ($loggedin = $result->fetchAssoc()) {
Daniel Kudwien's avatar
Daniel Kudwien committed
    if (!$userid) {
Daniel Kudwien's avatar
Daniel Kudwien committed
    }
    elseif (empty($userinfos[$userid]) || ($userinfos[$userid]['lastactivity'] < $loggedin['lastactivity'])) {
Daniel Kudwien's avatar
Daniel Kudwien committed
      $userinfos[$userid] = $loggedin;
    }
Daniel Kudwien's avatar
Daniel Kudwien committed
  foreach ($userinfos as $userid => $loggedin) {
Daniel Kudwien's avatar
Daniel Kudwien committed
  }
  return array('guests' => $numberguest, 'members' => $numberregistered);
}

 * Get counts of new or recent posts for the current user.
function drupalvb_get_recent_posts($scope = 'last') {
Daniel Kudwien's avatar
Daniel Kudwien committed

  // Queries the vB user database to find a matching set of user data.
  $result = drupalvb_db_query("SELECT userid, username, lastvisit FROM {user} WHERE username = :name", array(':name' => drupalvb_htmlspecialchars($user->name)));
Daniel Kudwien's avatar
Daniel Kudwien committed
  // Make sure a user is logged in to get their last visit and appropriate post
  // count.
  if ($vb_user = $result->fetchAssoc()) {
Daniel Kudwien's avatar
Daniel Kudwien committed
    if ($scope == 'last') {
      $datecut = $vb_user['lastvisit'];
Daniel Kudwien's avatar
Daniel Kudwien committed
    }
    elseif ($scope == 'daily') {
Daniel Kudwien's avatar
Daniel Kudwien committed
    }
    $posts = drupalvb_db_query("SELECT COUNT(postid) FROM {post} WHERE dateline > :datecut", array(':datecut' => $datecut))->fetchField();
Daniel Kudwien's avatar
Daniel Kudwien committed
  return $posts;
function drupalvb_htmlspecialchars($text) {
  $text = preg_replace('/&(?!#[0-9]+|shy;)/si', '&amp;', $text);
  return str_replace(array('<', '>', '"'), array('&lt;', '&gt;', '&quot;'), $text);
}

/**
 * Get vB cookie prefix.
 */
function drupalvb_get_cookieprefix() {
  $cookie_prefix = (isset($vb_config['Misc']['cookieprefix']) ? $vb_config['Misc']['cookieprefix'] : 'bb');

  // Version 4 began using an underscore following the prefix.
  if (version_compare($vb_config['version'], 4, '>=')) {
    $cookie_prefix .= '_';
  }

  return $cookie_prefix;
}