4 * Drupal vB CRUD functions.
8 * Set the necessary cookies for the user to be logged into the forum.
10 * Frontend cookie names:
11 * - lastvisit, lastactivity, sessionhash
12 * Backend cookie names:
13 * - cpsession, userid, password
15 * However, in all cases the cookiedomain is NOT prefixed with a dot unless
16 * cookie domain has not been manually altered to either a suggested value or
17 * custom value in vB's settings.
19 function drupalvb_set_login_cookies($userid) {
20 // Load required vB user data.
21 $vbuser = drupalvb_db_query("SELECT userid, password, salt FROM {user} WHERE userid = :userid", array(":userid" => $userid))->fetchAssoc();
26 $vb_options = drupalvb_get_options();
27 $cookie_prefix = drupalvb_get_cookieprefix();
28 $cookie_path = $vb_options['cookiepath'];
30 $expire = $now + (@
ini_get('session.cookie_lifetime') ?
ini_get('session.cookie_lifetime') : 60 * 60 * 24 * 365);
32 $vb_cookie_domain = (!empty($vb_options['cookiedomain']) ?
$vb_options['cookiedomain'] : $GLOBALS['cookie_domain']);
33 // Per RFC 2109, cookie domains must contain at least one dot other than the
34 // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
36 if (!(count(explode('.', $vb_cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $vb_cookie_domain)))) {
37 $vb_cookie_domain = '';
40 // Clear out old session (if available).
41 if (!empty($_COOKIE[$cookie_prefix .
'sessionhash'])) {
42 drupalvb_db_query("DELETE FROM {session} WHERE sessionhash = :hash", array(":hash" => $_COOKIE[$cookie_prefix .
'sessionhash']));
45 // Setup user session.
46 $ip = implode('.', array_slice(explode('.', drupalvb_get_ip()), 0, 4 - $vb_options['ipcheck']));
47 $idhash = md5($_SERVER['HTTP_USER_AGENT'] .
$ip);
48 $sessionhash = md5($now .
request_uri() .
$idhash .
$_SERVER['REMOTE_ADDR'] .
user_password(6));
49 $browserstring = substr(trim($_SERVER['HTTP_USER_AGENT']), 0, 100);
51 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));
54 setcookie($cookie_prefix .
'sessionhash', $sessionhash, $expire, $cookie_path, $vb_cookie_domain);
55 setcookie($cookie_prefix .
'lastvisit', $now, $expire, $cookie_path, $vb_cookie_domain);
56 setcookie($cookie_prefix .
'lastactivity', $now, $expire, $cookie_path, $vb_cookie_domain);
57 setcookie($cookie_prefix .
'userid', $vbuser['userid'], $expire, $cookie_path, $vb_cookie_domain);
58 setcookie($cookie_prefix .
'password', md5($vbuser['password'] .
variable_get('drupalvb_license', '')), $expire, $cookie_path, $vb_cookie_domain);
63 * Clear all vB cookies for the current user.
65 * @see drupalvb_logout(), drupalvb_user_logout()
67 function drupalvb_clear_cookies($userid = NULL
) {
68 $vb_options = drupalvb_get_options();
70 $cookie_prefix = drupalvb_get_cookieprefix();
71 $cookie_path = $vb_options['cookiepath'];
72 $expire = time() - 86400;
74 $vb_cookie_domain = (!empty($vb_options['cookiedomain']) ?
$vb_options['cookiedomain'] : $GLOBALS['cookie_domain']);
75 // Per RFC 2109, cookie domains must contain at least one dot other than the
76 // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
78 if (!(count(explode('.', $vb_cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $vb_cookie_domain)))) {
79 $vb_cookie_domain = '';
82 // @todo Without a vB user id, we cannot delete the session, so vBulletin
83 // will automatically authenticate again. We badly need a solution here,
84 // since this is the cause for broken session handling. Proposal: Use
85 // drupalvb_get_ip() to count the # of sessions; if there exactly one,
87 // @ttkaminski's suggestion - validate the sessionhash and userid from the
88 // cookies against the vBulletin session table. If it matches, then kill
91 if (!empty($userid)) {
92 drupalvb_db_query("DELETE FROM {session} WHERE userid = :userid", array(':userid' => $userid));
93 drupalvb_db_query("UPDATE {user} SET lastvisit = :time WHERE userid = :userid", array(":time" => time(), ":userid" => $userid));
96 setcookie($cookie_prefix .
'sessionhash', '', $expire, $cookie_path, $vb_cookie_domain);
97 setcookie($cookie_prefix .
'lastvisit', '', $expire, $cookie_path, $vb_cookie_domain);
98 setcookie($cookie_prefix .
'lastactivity', '', $expire, $cookie_path, $vb_cookie_domain);
99 setcookie($cookie_prefix .
'userid', '', $expire, $cookie_path, $vb_cookie_domain);
100 setcookie($cookie_prefix .
'password', '', $expire, $cookie_path, $vb_cookie_domain);
104 * Determines the IP address of current user.
106 * @todo Duplicate of ip_address() in D6+ ?
108 function drupalvb_get_ip() {
109 $ip = $_SERVER['REMOTE_ADDR'];
111 if (isset($_SERVER['HTTP_CLIENT_IP'])) {
112 $ip = $_SERVER['HTTP_CLIENT_IP'];
114 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)) {
115 // Make sure we don't pick up an internal IP defined by RFC1918.
116 foreach ($matches[0] as
$match) {
117 if (!preg_match("#^(10|172\.16|192\.168)\.#", $match)) {
123 elseif (isset($_SERVER['HTTP_FROM'])) {
124 $ip = $_SERVER['HTTP_FROM'];
130 * Create a user in vBulletin.
132 * @param object $account
133 * A Drupal user account.
135 * Form values provided by hook_user().
137 function drupalvb_create_user($account, $edit) {
138 // Ensure we are not duplicating a user.
139 $userid = drupalvb_db_select("user","u")
140 ->fields("u", array("userid") )
141 ->condition("username", drupalvb_htmlspecialchars($edit['name']) )
147 for ($i = 0; $i < 3; $i++) {
148 $salt .
= chr(rand(32, 126));
150 // Note: Password is already hashed during user export.
151 if (isset($edit['md5pass'])) {
152 $passhash = md5($edit['md5pass'] .
$salt);
155 $passhash = md5(md5($edit['pass']) .
$salt);
158 $time = $account->created
;
159 $passdate = date('Y-m-d', $time);
162 // Attempt to grab the user title from the database.
163 $result = drupalvb_db_query("SELECT title FROM {usertitle} WHERE minposts = 0");
164 if ($resarray = $result->fetchAssoc()) {
165 $usertitle = $resarray['title'];
168 $usertitle = 'Junior Member';
171 // Divide timezone by 3600, since vBulletin stores hours.
172 $timezone = variable_get('date_default_timezone', 0);
173 $timezone = ($timezone != 0 ?
$timezone / 3600 : 0);
175 // Default new user options: I got these by setting up a new user how I
176 // wanted and looking in the database to see what options were set for him.
177 $options = variable_get('drupalvb_default_options', '3415');
179 // Default usergroup id.
180 $usergroupid = variable_get('drupalvb_default_usergroup', '2');
181 $lid = drupalvb_get_languageid();
185 // Insert user to vBulletin
186 $userid = drupalvb_db_insert("user")
188 "username" => drupalvb_htmlspecialchars($edit['name']),
189 "usergroupid" => $usergroupid,
190 "password" => $passhash,
191 "passworddate" => $passdate,
192 "usertitle" => $usertitle,
193 "email" => $account->mail,
196 "timezoneoffset" => $timezone,
197 "joindate" => $joindate,
198 "lastvisit" => time(),
199 "lastactivity" => time(),
200 "options" => $options
203 $rr = drupalvb_db_query("SELECT * FROM {userfield} WHERE userid=1");
204 $fields = $rr->fetchAssoc();
205 foreach($fields as
$f => $v) $fields[$f] = '';
206 $fields['userid'] = $userid;
207 drupalvb_db_insert("userfield")->fields($fields)->execute();
209 $rr = drupalvb_db_query("SELECT * FROM {usertextfield} WHERE userid=1");
210 $fields = $rr->fetchAssoc();
211 foreach($fields as
$f => $v) $fields[$f] = '';
212 $fields['userid'] = $userid;
213 drupalvb_db_insert("usertextfield")->fields($fields)->execute();
216 // Insert new user into mapping table.
217 drupalvb_set_mapping($account->uid
, $userid);
219 // Return userid of newly created account.
224 * Update a user in vBulletin.
226 function drupalvb_update_user($account, $edit) {
227 $fields = $values = array();
229 foreach ($edit as
$field => $value) {
235 $fields[] = "username = :name";
236 $values[':name'] = drupalvb_htmlspecialchars($value);
240 $fields[] = "password = :password";
241 $values[':password'] = md5(md5($value) .
$edit['salt']);
242 $fields[] = "salt = :salt";
243 $values[':salt'] = $edit['salt'];
244 $fields[] = "passworddate = :date";
245 $values[':date'] = date('Y-m-d', time());
249 $fields[] = "email = :email";
250 $values[':email'] = $value;
254 $fields[] = "languageid = :lid";
259 $fields[] = 'lastactivity = :activity';
260 $values[':activity'] = time();
262 // Ensure this user exists in the mapping table.
263 // When integrating an existing installation, the mapping may not yet exist.
264 if (isset($edit['userid'])) {
265 $userid = $edit['userid'];
268 $userid = drupalvb_db_query("SELECT userid FROM {user} WHERE username = :name", array(':name' => drupalvb_htmlspecialchars($account->name
)))->fetchField();
270 drupalvb_set_mapping($account->uid
, $userid);
272 $values[':userid'] = $userid;
273 drupalvb_db_query("UPDATE {user} SET " .
implode(', ', $fields) .
" WHERE userid=:userid", $values);
277 * Ensure that a mapping between two existing user accounts exists.
282 * A vBulletin user id.
284 function drupalvb_set_mapping($uid, $userid) {
285 db_query("INSERT IGNORE INTO {drupalvb_users} (uid, userid) VALUES (:uid, :userid)", array(':uid' => $uid, ':userid' => $userid));
289 * Export all drupal users to vBulletin.
291 function drupalvb_export_drupal_users() {
292 module_load_include('inc', 'drupalvb');
294 $result = db_query("SELECT * FROM {users} WHERE uid>0 ORDER BY uid");
295 foreach ($result as
$user) {
296 // Let create/update functions know that passwords are hashed already.
297 $user->md5pass
= $user->pass
;
298 if (!drupalvb_create_user($user, (array)$user)) {
299 // Username already exists, update email and password only.
300 // Case insensitive username is required to detect collisions.
301 $vbuser = drupalvb_db_query("SELECT salt,userid FROM {user} WHERE LOWER(username) = LOWER(:name)", array(':name' => drupalvb_htmlspecialchars($user->name
)))->fetchAssoc();
302 drupalvb_update_user($user, array_merge((array)$user, $vbuser));
308 * Get vBulletin configuration options.
310 function drupalvb_get_options() {
311 static
$options = array();
313 if (empty($options)) {
314 $result = drupalvb_db_query("SELECT varname, value FROM {setting}");
315 foreach ($result as
$var) {
316 $options[$var->varname
] = $var->value
;
323 * Get vBulletin configuration.
326 * An associative array containing the vBulletin configuration, plus
328 * - version: The vBulletin version number string, as contained in the first
329 * PHP comment lines of config.php.
331 function drupalvb_get_config() {
334 if (!isset($config)) {
336 $config['version'] = NULL
;
338 // @todo Find & include vB's config automatically?
339 // $files = file_scan_directory('.', '^config.php$', $nomask = array('.', '..', 'CVS', '.svn'));
340 $config_file = './' .
conf_path() .
'/config.php';
341 if (!file_exists($config_file)) {
342 $config_file = drupal_get_path('module', 'drupalvb') .
'/config.php';
344 if (file_exists($config_file)) {
345 require_once
$config_file;
347 // Additionally parse the vBulletin version out of the php file header, as
348 // some integration functionality needs to account for API changes in
349 // later vBulletin versions.
350 $file = fopen($config_file, 'r');
352 while ($max_lines && $line = fgets($file, 30)) {
353 if (preg_match('@vBulletin\s+([0-9a-zA-Z\.-]+)@', $line, $version)) {
354 $config['version'] = $version[1];
367 function drupalvb_get_roles() {
368 $result = drupalvb_db_query("SELECT usergroupid, title FROM {usergroup}");
371 foreach ($result as
$data) {
372 $roles[$data->usergroupid
] = $data->title
;
375 $roles[] = t('No user roles could be found.');
381 * Get vB language id by given ISO language code.
383 function drupalvb_get_languageid($language = NULL
) {
384 //static $vblanguages
386 if (!isset($vblanguages)) {
387 $vblanguages = array();
388 $result = drupalvb_db_query("SELECT languageid, title, languagecode FROM {language}");
389 foreach ($result as
$lang) {
390 $vblanguages[$lang->languagecode
] = $lang->languageid
;
393 $options = drupalvb_get_options();
395 //return (!empty($language) && isset($vblanguages[$language]) ? $vblanguages[$language] : $vblanguages[$options['languageid']]);
399 * Get counts of guests and members currently online.
401 function drupalvb_get_users_online() {
402 $vb_options = drupalvb_get_options();
404 $datecut = time() - $vb_options['cookietimeout'];
406 $numberregistered = 0;
409 $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));
411 $userinfos = array();
413 while ($loggedin = $result->fetchAssoc()) {
414 $userid = $loggedin['userid'];
418 elseif (empty($userinfos[$userid]) || ($userinfos[$userid]['lastactivity'] < $loggedin['lastactivity'])) {
419 $userinfos[$userid] = $loggedin;
422 foreach ($userinfos as
$userid => $loggedin) {
425 return array('guests' => $numberguest, 'members' => $numberregistered);
429 * Get counts of new or recent posts for the current user.
431 function drupalvb_get_recent_posts($scope = 'last') {
434 // Queries the vB user database to find a matching set of user data.
435 $result = drupalvb_db_query("SELECT userid, username, lastvisit FROM {user} WHERE username = :name", array(':name' => drupalvb_htmlspecialchars($user->name
)));
437 // Make sure a user is logged in to get their last visit and appropriate post
439 if ($vb_user = $result->fetchAssoc()) {
440 if ($scope == 'last') {
441 $datecut = $vb_user['lastvisit'];
443 elseif ($scope == 'daily') {
444 $datecut = time() - 86400;
446 $posts = drupalvb_db_query("SELECT COUNT(postid) FROM {post} WHERE dateline > :datecut", array(':datecut' => $datecut))->fetchField();
454 function drupalvb_htmlspecialchars($text) {
455 $text = preg_replace('/&(?!#[0-9]+|shy;)/si', '&', $text);
456 return str_replace(array('<', '>', '"'), array('<', '>', '"'), $text);
460 * Get vB cookie prefix.
462 function drupalvb_get_cookieprefix() {
463 $vb_config = drupalvb_get_config();
464 $cookie_prefix = (isset($vb_config['Misc']['cookieprefix']) ?
$vb_config['Misc']['cookieprefix'] : 'bb');
466 // Version 4 began using an underscore following the prefix.
467 if (version_compare($vb_config['version'], 4, '>=')) {
468 $cookie_prefix .
= '_';
471 return $cookie_prefix;