Logging into SMF Progamatically (Was: Functionality requests for SSI...)

Started by ExcalibursZone, August 21, 2007, 09:52:32 PM

Previous topic - Next topic

ExcalibursZone

I would like to see SSI.php be made into an actual API/SDK to attach websites to SMF. As it stands now, all you really get are forms or other pre-generated html.

I'd like to see:
ssi_login($user, $pass): Log into SMF, Create the SMF cookies, Update the user as online, etc.
ssi_logoff($user): Log out of SMF, Delete the SMF cookies, Update the user as not online, etc.

Those are the two MOST useful functions I can think of right now. It's driving me batty having to figure out how to do it properly when integrating into other programs (like WordPress and Gallery).

Basically, an API is a low-level way to access the functionality that an application has to offer. SMF does not do this at all, yet anyway. While the functions that are there were pretty nice in setting up a few of my site's pages that I wrote from scratch, it does little to help in the way of logging on.

And no, smf_api doesn't do this either. It's a huge pain in the butt to get even that to work properly.


Edit: Split this discussion into its own topic. - Motoko

青山 素子

As a note, look closer. You can get the data back in an array from the functions, letting you manipulate things a bit more.
Motoko-chan
Director, Simple Machines

Note: Unless otherwise stated, my posts are not representative of any official position or opinion of Simple Machines.


B Patterson

Also note that you can just use ssi login form and set the $_SESSION['login_url'] and $_SESSION['logout_url'] respectively to change where they end up after login or log out.

For example, if you go to the RoundCube Forum you'll see a login form.  If you have an account, and login there, you'll be taken back there, marked as online and will show up in the box on the right-hand side.  You don't need ssi_login or ssi_logout, you just need to know how to set things up properly.  An API would help (as it took me about an hour to find those $_SESSION items) but it's not impossible to do what you want without it.  It's just going to take time.

ExcalibursZone

Nope, you've gotten it wrong. I am asking for PROGMATIC means to log in and log off without showing a form at all. SSI is not an API or SDK for SMF, it's an integration tool, yes, but not a way to use a custom login/logoff form to log out of SMF and whatever else you want.

So, say I create a site that has it's own members table and I deicde that I want to use the SMF member table instead. Sure, I could use SSI to replace my site's login/logoff forms, but I don't want to do that. I want to call up my own custom page that does the logging out where I log out of SMF by using a simple function smf_logout() (or login with smf_login()). I do not want to show forms, I want the SMF API to set database information and add/remove cookies as necessary without showing a single drop of html from anything but my custom stuff.

It's not a difficult request and saying to use SSI.php or smf_api will not give what I'm asking for. They're good if you design your site to use them, but if you convert a site that you don't want to use specialized forms, they are worthless. Unless there's some magic way to use SSI to NOT show a login form and still log me into SMF properly...

B Patterson

Okay.  I thought you were just trying to log in but from a different page and you wanted to go back to that page.  I agree, that could be helpful in certain situations.

青山 素子

If you want that kind of functionality, you'll probably want to look at the bridges we have for download. There are lots of integration hooks in SMF, you just need to use them.

Also check out: A guide to the SMF integration hooks
Motoko-chan
Director, Simple Machines

Note: Unless otherwise stated, my posts are not representative of any official position or opinion of Simple Machines.


ExcalibursZone

#6
Well, if you're talking about the CMS bridges? Um, no. Again, I have my own custom stuff and I don't want to tie in yet *another* layer into it. I want a simple login() and logoff() function to SMF. IPB has it in their SDK and while SSI is damn nice when you actually build your site to use it, you simply cannot use low-level access stuff to log into SMF at the same time.

Example: I attempted to connect WordPress to SMF. At the time I decided to use both of the user tables because I wanted to make sure I could log in to both from the same login page (WordPress's login page for this example). As you can tell, I didn't want to use SSI to show an SMF login and I don't want a CMS mucking up my system and replacing my WP login. I want to log in from WP's login page and have the forums log in as well. The user exists in both user tables with the same password. WOO! Cool. SSI? Useless. Sure I can use it to check the SMF password but that doesn't do a thing. So, I get ahold of the smf_api.php file. Now, it's really strange using that because there isn't a login() funciton you have to do smf_setLoginCookie(), smf_authenticate(), smf_loadSession(), etc. etc. etc. just to log in. Now, yeah, it's obvious that you have to do all that to log in. And it worked... once and after I had to fix smf_api because of a glitch that happens with WP: It goes through the plug-in code twice for the wp_login function.

Now, had I had a function: smf_login() that was written by the people who wrote SMF that took care of creating all the cookie information, setting up all the database entries, taking care of the session SMF uses, etc. Then things would be damned easy for me to log in at the same time. With smf_api, for some reason, if the login code is repeated, the user is logged out of SMF... *ponder* strange behavior.

Now, I *may* look at these bridges to see what was done to get SMF to login at the same time as whatever else uses the content management systems. But my question then would be: Were the bridges written around SSI so that the smf_login() function was used to provide the login form then a custom redirect sends you to a special page that logs you into the CMS or whatever else the bridge takes care of? If so, you have the wrong Idea about what I'm looking for.

Also, thanks for the link to the SMF integration. But I'm not trying to integrate something into SMF to have the forums log me into something or whatnot. In this application I could care less what the forums do. I want to log into the forums from an EXTERNAL location without showing a bit of html tied to SSI or anything else. I just want to include SSI.php and be able to login/logout without having to show a single form. Though, the integration hooks would be great if I were writing something for SMF, it does nothing for integrating login from someplace else.

青山 素子

No no, the bridges take care of login in both directions. There are hooks for doing the login without showing SMF, you just have to enable the hook features. The bridges are a great demonstration of the functionality. You can do full integration if you want, but you don't need to.

That integration hook guide goes over the actual hooks the bridges use.
Motoko-chan
Director, Simple Machines

Note: Unless otherwise stated, my posts are not representative of any official position or opinion of Simple Machines.


ExcalibursZone

KO, I've downloaded one of the bridges (mambo I think) and will take a look at how they do it.

Issue is: I'm not running any of those bridges and it still does not address the request I made for two simple ways for us to log into and out of SMF without having to write all the stuff ourselves. Yeah, I looked at the mambo login stuff and yeah, it doesn't show anything of SMF while logging in, but we'd have to sit down and code all the database access and such ourselves or rip off the stuff from the bridge code.

I think this is unacceptable if the makers of SMF made it *EASY* to log in and out of the forums in the same way that the bridges do, but with two simple functions: login and logout. Of course, that doesn't mean you have to provide ways to check other user databases, just to provide the necessary stuff to log a user into and out of SMF progmatically. It would make all that code in the bridges unneeded and allow those who write bridges to easily include forums in their stuff.

Ensiferous

I ended up modifying smf_api to what I actually needed.

This code isn't exactly fit to be spread as it's highly customized to my site, like I don't let SMF create the database connection I merely supply it my connection identifier. It's a highly stripped down version, but it sounds lik you could probably figure out how to use it.

<?php
// If $maintenance is set to 2, don't connect to the database at all.
if ($maintenance != 2)
{
$smf_connection true;
$request smf_query("
SELECT variable, value
FROM 
{$db_prefix}settings"__FILE____LINE__);
$smf_settings = array();
while ( $row = @mysql_fetch_row($request) )

$smf_settings[$row[0]] = $row[1];
mysql_free_result($request);
}

// Load stuff from the Settings.php file into $smf_settings.
$smf_settings['cookiename'] = $cookiename;
$smf_settings['language'] = $language;
$smf_settings['forum_name'] = $mbname;
$smf_settings['forum_url'] = $boardurl;
$smf_settings['webmaster_email'] = $webmaster_email;
$smf_settings['db_prefix'] = $db_prefix;

$smf_user_info = array();

// Actually set the login cookie...
function smf_setLoginCookie($cookie_length$id$password ''$encrypted false)
{
// This should come from Settings.php, hopefully.
global $smf_connection$smf_settings;

// The $id is not numeric; it's probably a username.
if (!is_integer($id))
{
if (!$smf_connection)
return false;

// Save for later use.
$username $id;

$result smf_query("
SELECT ID_MEMBER
FROM 
$smf_settings[db_prefix]members
WHERE memberName = '
$username'
LIMIT 1"
__FILE____LINE__);
if (!is_resource($result))
{
return false;
}
list ($id) = mysql_fetch_row($result);
mysql_free_result($result);

// It wasn't found, after all?
if (empty($id))
{
$id = (int) $username;
unset($username);
}
}

// Oh well, I guess it just was not to be...
if (empty($id))
return false;

// The password isn't encrypted, do so.
if (!$encrypted)
{
if (!$smf_connection)
return false;

$result smf_query("
SELECT memberName, passwordSalt
FROM 
$smf_settings[db_prefix]members
WHERE ID_MEMBER = '" 
. (int) $id "'
LIMIT 1"
__FILE____LINE__);
list ($username$salt) = mysql_fetch_row($result);
mysql_free_result($result);

if (empty($username))
return false;

$password sha1(sha1(strtolower($username) . $password) . $salt);
}

function smf_cookie_url($local$global)
{
global $smf_settings;

// Use PHP to parse the URL, hopefully it does its job.
$parsed_url parse_url($smf_settings['forum_url']);

if (isset($parsed_url['port']))
$parsed_url['host'] .= ':' $parsed_url['port'];

// Set the cookie to the forum's path only?
if (empty($parsed_url['path']) || !$local)
$parsed_url['path'] = '';

// This is probably very likely for apis and such, no?
if ($global)
{
// Try to figure out where to set the cookie; this can be confused, though.
if (preg_match('~(?:[^\.]+\.)?(.+)\z~i'$parsed_url['host'], $parts) == 1)
$parsed_url['host'] = '.' $parts[1];
}
// If both options are off, just use no host and /.
elseif (!$local)
$parsed_url['host'] = '';

return $parsed_url;
}

// The cookie may already exist, and have been set with different options.
$cookie_state = (empty($smf_settings['localCookies']) ? 1) | (empty($smf_settings['globalCookies']) ? 2);
if (isset($_COOKIE[$smf_settings['cookiename']]))
{
$array = @unserialize($_COOKIE[$smf_settings['cookiename']]);

if (isset($array[3]) && $array[3] != $cookie_state)
{
$parsed_url smf_cookie_url($array[3] & 0$array[3] & 0);
setcookie($smf_settings['cookiename'], serialize(array(0''0)), time() - 3600$parsed_url['path'] . '/'$parsed_url['host'], 0);
}
}

// Get the data and path to set it on.
$data serialize(empty($id) ? array(0''0) : array($id$passwordtime() + $cookie_length));
$parsed_url smf_cookie_url(!empty($smf_settings['localCookies']), !empty($smf_settings['globalCookies']));

// Set the cookie, $_COOKIE, and session variable.
if ( !setcookie($smf_settings['cookiename'], $datatime() + $cookie_length$parsed_url['path'] . '/'$parsed_url['host'], falsetrue) )
{
return false;
}

$_COOKIE[$smf_settings['cookiename']] = $data;
$_SESSION['login_' $smf_settings['cookiename']] = $data;

return true;
}

function 
smf_authenticateUser()
{
global $smf_connection$smf_settings$smf_user_info;

// No connection, no authentication!
if (!$smf_connection)
return false;

// Check first the cookie, then the session.
if (isset($_COOKIE[$smf_settings['cookiename']]))
{
$_COOKIE[$smf_settings['cookiename']] = stripslashes($_COOKIE[$smf_settings['cookiename']]);

// Fix a security hole in PHP 4.3.9 and below...
if (preg_match('~^a:[34]:\{i:0;(i:\d{1,6}|s:[1-8]:"\d{1,8}");i:1;s:(0|40):"([a-fA-F0-9]{40})?";i:2;[id]:\d{1,14};(i:3;i:\d;)?\}$~'$_COOKIE[$smf_settings['cookiename']]) == 1)
{
list ($ID_MEMBER$password) = @unserialize($_COOKIE[$smf_settings['cookiename']]);
$ID_MEMBER = !empty($ID_MEMBER) ? (int) $ID_MEMBER 0;
}
else
$ID_MEMBER 0;
}
elseif (isset($_SESSION['login_' $smf_settings['cookiename']]))
{
list ($ID_MEMBER$password$login_span) = @unserialize(stripslashes($_SESSION['login_' $smf_settings['cookiename']]));
$ID_MEMBER = !empty($ID_MEMBER) && $login_span time() ? (int) $ID_MEMBER 0;
}
else
$ID_MEMBER 0;

// Don't even bother if they have no authentication data.
if (!empty($ID_MEMBER))
{
$request smf_query("
SELECT *
FROM 
$smf_settings[db_prefix]members
WHERE ID_MEMBER = 
$ID_MEMBER
LIMIT 1"
__FILE____LINE__);
// Did we find 'im?  If not, junk it.
if (mysql_num_rows($request) != 0)
{
// The base settings array.
$smf_user_info += mysql_fetch_assoc($request);

if (strlen($password) == 40)
$check sha1($smf_user_info['passwd'] . $smf_user_info['passwordSalt']) == $password;
else
$check false;

// Wrong password or not activated - either way, you're going nowhere.
$ID_MEMBER $check && ($smf_user_info['is_activated'] == || $smf_user_info['is_activated'] == 11) ? $smf_user_info['ID_MEMBER'] : 0;
}
else
$ID_MEMBER 0;
mysql_free_result($request);
}

if (empty($ID_MEMBER))
$smf_user_info = array('groups' => array(-1));
else
{
if (empty($smf_user_info['additionalGroups']))
$smf_user_info['groups'] = array($smf_user_info['ID_GROUP'], $smf_user_info['ID_POST_GROUP']);
else
$smf_user_info['groups'] = array_merge(
array($smf_user_info['ID_GROUP'], $smf_user_info['ID_POST_GROUP']),
explode(','$smf_user_info['additionalGroups'])
);
}

// A few things to make life easier...
$smf_user_info['id'] = &$smf_user_info['ID_MEMBER'];
$smf_user_info['username'] = &$smf_user_info['memberName'];
$smf_user_info['name'] = &$smf_user_info['realName'];
$smf_user_info['email'] = &$smf_user_info['emailAddress'];
$smf_user_info['messages'] = &$smf_user_info['instantMessages'];
$smf_user_info['unread_messages'] = &$smf_user_info['unreadMessages'];
$smf_user_info['is_guest'] = $ID_MEMBER == 0;
$smf_user_info['is_admin'] = in_array(1$smf_user_info['groups']);

// Set last login date.
smf_query('UPDATE `forum_members` SET `lastLogin` = ' time() . ' WHERE `ID_MEMBER` = ' $smf_user_info['id'], __FILE____LINE__);

return !$smf_user_info['is_guest'];
}

function 
smf_registerMember($username$email$password$password2)
{
global $sql;

if ( !empty($_SESSION['just_registered']) )
return array('You already registered an account! Check your email.');

/* USERNAME VALIDATION -> */
// No name?!  How can you register with no name?
if (empty($username))
return array('Empty username not allowed.');

// Spaces and other odd characters are evil...
$username preg_replace('~[\t\n\r\x0B\0\x{A0}' pack('C*'0xC20xA0) . '\xA0' ']+~'' '$username);

// Don't use too long a name.
if (strlen($username) > 25)
return array('Username too long, please keep it below 25 characters');

// Can't use that username.
if (preg_match('~[<>&"\'=\\\]~'$username) != || $username == '_' || $username == '|' || strpos($username'[code') !== false || strpos($username'[/code') !== false)
return array('Username contains invalid characters.');

if (stristr($username'guest') !== false)
return array ('Username cannot contain text "guest"');

// Get rid of any SQL parts of the reserved name...
$check_name strtr($username, array('_' => '\\_''%' => '\\%') );

// Make sure they don't want someone else's name.
$request smf_query("
SELECT ID_MEMBER
FROM forum_members
WHERE (realName LIKE '
$check_name' OR memberName LIKE '$check_name')
LIMIT 1"
__FILE____LINE__);
if (mysql_num_rows($request) > 0)
{
mysql_free_result($request);
return array('Restricted username');
}

// Does name case insensitive match a member group name?
$request smf_query("
SELECT ID_GROUP
FROM forum_membergroups
WHERE groupName LIKE '
$check_name'
LIMIT 1"
__FILE____LINE__);
if (mysql_num_rows($request) > 0)
{
mysql_free_result($request);
return array('Restricted username');;
}

/* <- END USERNAME VALIDATION */

/* EMAIL VALIDATION -> */

//if (empty($email) || preg_match('~^)[0-9A-Za-z=_+\-/][0-9A-Za-z=_\'+\-/\.]*@[\w\-]+(\.[\w\-]+)*(\.[\w]{2,6})$~', stripslashes($email)) === 0 || strlen(stripslashes($email)) > 255)
// return array('Please enter a valid email address'); 

// Check if the email address is in use.
$request smf_query("
SELECT ID_MEMBER
FROM forum_members
WHERE emailAddress = '
$email'
OR emailAddress = '
$username'
LIMIT 1"
__FILE____LINE__);

if (mysql_num_rows($request) != 0)
return array('Email is already in use.');

mysql_free_result($request);

/* <- END EMAIL VALIDATION */

/* PASSWORD VALIDATION -> */
// That's kind of easy to guess...
if ($password == '')
return array('Empty password not allowed.');

if ($password == $username)
return array('Your password cannot be the same as your username.');

if (strlen($password) <= 5)
return array('Your password must be at least 6 characters.');

if ($password !== $password2)
return array('Passwords did not match.');

/* <- END PASSWORD VALIDATION */

$validation_code substr(preg_replace('/\W/'''md5(mt_rand())), 010);
$ip = empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['REMOTE_ADDR'] : $_SERVER['HTTP_X_FORWARDED_FOR'];
/* PREPARE TO INSERT DATA -> */
// Some of these might be overwritten. (the lower ones that are in the arrays below.)
$regOptions['register_vars'] = array(
'memberName' => "'$username'",
'emailAddress' => "'$email'",
'passwd' => '\'' sha1(strtolower($username) . $password) . '\'',
'passwordSalt' => '\'' substr(md5(rand()), 04) . '\'',
'posts' => 0,
'dateRegistered' => time(),
'memberIP' => "'$ip'",
'memberIP2' => "'$_SERVER[REMOTE_ADDR]'",
'validation_code' => "'$validation_code'",
'realName' => "'$username'",
'personalText' => "''",
'is_activated' => "'0'",
'pm_email_notify' => 1,
'ID_THEME' => 0,
'ID_POST_GROUP' => 4,
'lngfile' => "''",
'buddy_list' => "''",
'pm_ignore_list' => "''",
'messageLabels' => "''",
'personalText' => "''",
'websiteTitle' => "''",
'websiteUrl' => "''",
'location' => "''",
'ICQ' => "''",
'AIM' => "''",
'YIM' => "''",
'MSN' => "''",
'timeFormat' => "''",
'signature' => "''",
'avatar' => "''",
'usertitle' => "''",
'secretQuestion' => "''",
'secretAnswer' => "''",
'additionalGroups' => "''",
'smileySet' => "''",
);

/* <- END PREPARE TO INSERT DATA */

// Register them into the database.
smf_query("
INSERT INTO forum_members
(" 
implode(', 'array_keys($regOptions['register_vars'])) . ")
VALUES (" 
implode(', '$regOptions['register_vars']) . ')'__FILE____LINE__);
$memberID mysql_insert_id($sql->link);

smf_query("
UPDATE forum_settings
SET value = value + 1
WHERE variable = 'totalMembers'
LIMIT 1"
__FILE____LINE__);

smf_query("
REPLACE INTO forum_settings
(variable, value)
VALUES ('latestMember', 
$memberID),
('latestRealName', '
$username')"__FILE____LINE__);

smf_query("
UPDATE forum_log_activity
SET registers = registers + 1
WHERE date = NOW()
LIMIT 1"
__FILE____LINE__);

if (mysql_affected_rows($sql->link) == 0)
{
smf_query("
INSERT IGNORE INTO forum_log_activity
(date, registers)
VALUES (NOW(), 0)"
__FILE____LINE__);
}

return $memberID;
}

// Log the current user online.
function smf_logOnline($action null)
{
global $smf_settings$smf_connection$smf_user_info;

if (!$smf_connection)
return false;

// Determine number of seconds required.
$lastActive $smf_settings['lastActive'] * 60;

// Don't mark them as online more than every so often.
if (empty($_SESSION['log_time']) || $_SESSION['log_time'] < (time() - 8))
$_SESSION['log_time'] = time();
else
return;

$serialized $_GET;
$serialized['USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Unknown';
unset($serialized['sesc']);
if ($action !== null)
$serialized['action'] = $action;

$serialized addslashes(serialize($serialized));

// Guests use 0, members use ID_MEMBER.
if ($smf_user_info['is_guest'])
{
smf_query("
DELETE FROM 
$smf_settings[db_prefix]log_online
WHERE logTime < NOW() - INTERVAL 
$lastActive SECOND OR session = 'ip$_SERVER[REMOTE_ADDR]'"__FILE____LINE__);
smf_query("
INSERT IGNORE INTO 
$smf_settings[db_prefix]log_online
(session, ID_MEMBER, ip, url)
VALUES ('ip
$_SERVER[REMOTE_ADDR]', 0, IFNULL(INET_ATON('$_SERVER[REMOTE_ADDR]'), 0), '$serialized')"__FILE____LINE__);
}
else
{
smf_query("
DELETE FROM 
$smf_settings[db_prefix]log_online
WHERE logTime < NOW() - INTERVAL 
$lastActive SECOND OR ID_MEMBER = $smf_user_info[id] OR session = '" . @session_id() . "'"__FILE____LINE__);
smf_query("
INSERT IGNORE INTO 
$smf_settings[db_prefix]log_online
(session, ID_MEMBER, ip, url)
VALUES ('" 
. @session_id() . "', $smf_user_info[id], IFNULL(INET_ATON('$_SERVER[REMOTE_ADDR]'), 0), '$serialized')"__FILE____LINE__);
}
}

function 
smf_isOnline($user)
{
global $smf_settings$smf_connection;

if (!$smf_connection)
return false;

$result smf_query("
SELECT lo.ID_MEMBER
FROM 
$smf_settings[db_prefix]log_online AS lo" . (!is_integer($user) ? "
LEFT JOIN 
$smf_settings[db_prefix]members AS mem ON (mem.ID_MEMBER = lo.ID_MEMBER)" '') . "
WHERE lo.ID_MEMBER = " 
. (int) $user . (!is_integer($user) ? " OR mem.memberName = '$user'" '') . "
LIMIT 1"
__FILE____LINE__);
$return mysql_num_rows($result) != 0;
mysql_free_result($result);

return $return;
}

// Log an error, if the option is on.
function smf_logError($error_message$file null$line null)
{
global $smf_settings$smf_connection;

// Check if error logging is actually on and we're connected...
if (empty($smf_settings['enableErrorLogging']) || !$smf_connection)
return $error_message;

// Basically, htmlspecialchars it minus &. (for entities!)
$error_message strtr($error_message, array('<' => '<''>' => '>''"' => '"'));
$error_message strtr($error_message, array('<br />' => '<br />''<b>' => '<b>''</b>' => '</b>'"\n" => '<br />'));

// Add a file and line to the error message?
if ($file != null)
$error_message .= '<br />' $file;
if ($line != null)
$error_message .= '<br />' $line;

// Just in case there's no ID_MEMBER or IP set yet.
if (empty($smf_user_info['id']))
$smf_user_info['id'] = 0;

// Insert the error into the database.
smf_query("
INSERT INTO 
$smf_settings[db_prefix]log_errors
(ID_MEMBER, logTime, ip, url, message, session)
VALUES (
$smf_user_info[id], " time() . ", '$_SERVER[REMOTE_ADDR]', '" . (empty($_SERVER['QUERY_STRING']) ? '' addslashes(htmlspecialchars('?' $_SERVER['QUERY_STRING']))) . "', '" addslashes($error_message) . "', '" . @session_id() . "')"__FILE____LINE__);

// Return the message to make things simpler.
return $error_message;
}

// Format a time to make it look purdy.
function smf_formatTime($logTime)
{
global $smf_user_info$smf_settings;

// Offset the time - but we can't have a negative date!
$time max($logTime + (@$smf_user_info['timeOffset'] + $smf_settings['time_offset']) * 36000);

// Format some in caps, and then any other characters..
return strftime(strtr(!empty($smf_user_info['timeFormat']) ? $smf_user_info['timeFormat'] : $smf_settings['time_format'], array('%a' => ucwords(strftime('%a'$time)), '%A' => ucwords(strftime('%A'$time)), '%b' => ucwords(strftime('%b'$time)), '%B' => ucwords(strftime('%B'$time)))), $time);
}

// Do a query, and if it fails log an error in the SMF error log.
function smf_query($string$file$line)
{
global $smf_settings$sql$basedir;

if (!$sql)
{
global $db_host$db_name$db_user$db_passwd;

$sql = new sql($db_host$db_name$db_user$db_passwd);
}

$ret = @mysql_query($string$sql->link);
return $ret;
}

// Mother, may I?
// Get a list of groups that have a given permission (on a given board).
function smf_allowedTo($permission$board_id null)
{
global $smf_settings$smf_user_info;

// Admins are allowed to do anything.
$membergroups = array(
'allowed' => array(1),
'denied' => array(),
);

// Assume we're dealing with regular permissions (like profile_view_own).
if ($board_id === null)
{
$request smf_query("
SELECT ID_GROUP, addDeny
FROM 
$smf_settings[db_prefix]permissions
WHERE permission = '
$permission'"__FILE____LINE__);
while ($row mysql_fetch_assoc($request))
$membergroups[$row['addDeny'] === '1' 'allowed' 'denied'][] = $row['ID_GROUP'];
mysql_free_result($request);
}

// Denied is never allowed.
$membergroups['allowed'] = array_diff($membergroups['allowed'], $membergroups['denied']);

foreach($smf_user_info['groups'] as $groups)
{
if ( in_array($groups$membergroups['allowed']) )
{
return true;
}
}

return false;
}

function 
smf_loadThemeData($ID_THEME 0)
{
global $smf_settings$smf_user_info$smf_connection;

if (!$smf_connection)
return null;

// The theme was specified by parameter.
if (!empty($ID_THEME))
$theme = (int) $ID_THEME;
// The theme was specified by REQUEST.
elseif (!empty($_REQUEST['theme']))
{
$theme = (int) $_REQUEST['theme'];
$_SESSION['ID_THEME'] = $theme;
}
// The theme was specified by REQUEST... previously.
elseif (!empty($_SESSION['ID_THEME']))
$theme = (int) $_SESSION['ID_THEME'];
// The theme is just the user's choice. (might use ?board=1;theme=0 to force board theme.)
elseif (!empty($smf_user_info['theme']) && !isset($_REQUEST['theme']))
$theme $smf_user_info['theme'];
// The theme is the forum's default.
else
$theme $smf_settings['theme_guests'];

// Verify the ID_THEME... no foul play.
if (empty($smf_settings['theme_default']) && $theme == && $ID_THEME != 1)
$theme $smf_settings['theme_guests'];
elseif (!empty($smf_settings['knownThemes']) && !empty($smf_settings['theme_allow']))
{
$themes explode(','$smf_settings['knownThemes']);
if (!in_array($theme$themes))
$theme $smf_settings['theme_guests'];
else
$theme = (int) $theme;
}
else
$theme = (int) $theme;

$member = empty($smf_user_info['id']) ? -$smf_user_info['id'];

// Load variables from the current or default theme, global or this user's.
$result smf_query("
SELECT variable, value, ID_MEMBER, ID_THEME
FROM 
$smf_settings[db_prefix]themes
WHERE ID_MEMBER IN (-1, 0, 
$member)
AND ID_THEME" 
. ($theme == ' = 1' " IN ($theme, 1)"), __FILE____LINE__);
// Pick between $smf_settings['theme'] and $smf_user_info['theme'] depending on whose data it is.
$themeData = array(=> array(), $member => array());
while ($row mysql_fetch_assoc($result))
{
// If this is the themedir of the default theme, store it.
if (in_array($row['variable'], array('theme_dir''theme_url''images_url')) && $row['ID_THEME'] == '1' && empty($row['ID_MEMBER']))
$themeData[0]['default_' $row['variable']] = $row['value'];

// If this isn't set yet, is a theme option, or is not the default theme..
if (!isset($themeData[$row['ID_MEMBER']][$row['variable']]) || $row['ID_THEME'] != '1')
$themeData[$row['ID_MEMBER']][$row['variable']] = substr($row['variable'], 05) == 'show_' $row['value'] == '1' $row['value'];
}
mysql_free_result($result);

$smf_settings['theme'] = $themeData[0];
$smf_user_info['theme'] = $themeData[$member];

if (!empty($themeData[-1]))
foreach ($themeData[-1] as $k => $v)
{
if (!isset($smf_user_info['theme'][$k]))
$smf_user_info['theme'][$k] = $v;
}

$smf_settings['theme']['theme_id'] = $theme;

$smf_settings['theme']['actual_theme_url'] = $smf_settings['theme']['theme_url'];
$smf_settings['theme']['actual_images_url'] = $smf_settings['theme']['images_url'];
$smf_settings['theme']['actual_theme_dir'] = $smf_settings['theme']['theme_dir'];
}

// Get latest 5 threads, nice and simple, unlike the SSI bloat.
function smf_getThreads($exclude false)
{
$stmt '
SELECT 
`m`.`subject`, 
`m`.`ID_TOPIC` 
FROM 
`forum_messages` AS m, 
`forum_topics` AS t 
WHERE
`t`.`ID_LAST_MSG` = `m`.`ID_MSG` AND
`t`.`locked` = 0 '
;

if ( is_array($exclude) )
{
$stmt .= 'AND `t`.`ID_BOARD` NOT IN (' implode(', '$exclude) . ')';
}

$stmt .= '
ORDER BY 
`t`.`ID_LAST_MSG` DESC
LIMIT 
5'
;

$result smf_query($stmt__FILE____LINE__);
$topics = array();

$x 0;
while ( $row mysql_fetch_assoc($result) )
{
$topics[$x]            = $row;
$topics[$x]['subject'] = str_replace('Re: '''$row['subject']);
$x++;
}

return $topics;
}

// Attempt to start the session, unless it already has been.
function smf_loadSession()
{
global $HTTP_SESSION_VARS$sql$smf_settings$smf_user_info;

// Attempt to change a few PHP settings.
@ini_set('session.use_cookies'true);
@ini_set('session.use_only_cookies'false);
@ini_set('arg_separator.output''&amp;');

// If it's already been started... probably best to skip this.
if ((@ini_get('session.auto_start') == && !empty($smf_settings['databaseSession_enable'])) || session_id() == '')
{
// Attempt to end the already-started session.
if (@ini_get('session.auto_start') == 1)
@session_write_close();

// This is here to stop people from using bad junky PHPSESSIDs.
if (isset($_REQUEST[session_name()]) && preg_match('~^[A-Za-z0-9]{32}$~'$_REQUEST[session_name()]) == && !isset($_COOKIE[session_name()]))
$_COOKIE[session_name()] = md5(md5('smf_sess_' time()) . rand());

// Use database sessions?
if (!empty($smf_settings['databaseSession_enable']) && $sql->link)
session_set_save_handler('smf_sessionOpen''smf_sessionClose''smf_sessionRead''smf_sessionWrite''smf_sessionDestroy''smf_sessionGC');
elseif (@ini_get('session.gc_maxlifetime') <= 1440 && !empty($smf_settings['databaseSession_lifetime']))
@ini_set('session.gc_maxlifetime'max($smf_settings['databaseSession_lifetime'], 60));

session_start();
}

// While PHP 4.1.x should use $_SESSION, it seems to need this to do it right.
if (@version_compare(PHP_VERSION'4.2.0') == -1)
$HTTP_SESSION_VARS['smf_php_412_bugfix'] = true;

// Set the randomly generated code.
if (!isset($_SESSION['rand_code']))
$_SESSION['rand_code'] = md5(session_id() . rand());
$smf_user_info['session_id'] = &$_SESSION['rand_code'];

if (!isset($_SESSION['USER_AGENT']))
$_SESSION['USER_AGENT'] = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'Unknown';
}

function 
smf_sessionOpen($save_path$session_name)
{
return true;
}

function 
smf_sessionClose()
{
return true;
}

function 
smf_sessionRead($session_id)
{
global $smf_settings;

if (preg_match('~^[A-Za-z0-9]{16,32}$~'$session_id) == 0)
return false;

// Look for it in the database.
$result smf_query("
SELECT data
FROM 
$smf_settings[db_prefix]sessions
WHERE session_id = '" 
addslashes($session_id) . "'
LIMIT 1"
__FILE____LINE__);
list ($sess_data) = mysql_fetch_row($result);
mysql_free_result($result);

return $sess_data;
}

function 
smf_sessionWrite($session_id$data)
{
global $smf_settings$sql;

if (preg_match('~^[A-Za-z0-9]{16,32}$~'$session_id) == 0)
return false;

// First try to update an existing row...
$result smf_query("
UPDATE 
$smf_settings[db_prefix]sessions
SET data = '" 
addslashes($data) . "', last_update = " time() . "
WHERE session_id = '" 
addslashes($session_id) . "'
LIMIT 1"
__FILE____LINE__);

// If that didn't work, try inserting a new one.
if ($result)
{
$result smf_query("
INSERT IGNORE INTO 
$smf_settings[db_prefix]sessions
(session_id, data, last_update)
VALUES ('" 
addslashes($session_id) . "', '" addslashes($data) . "', " time() . ")"__FILE____LINE__);
}

return $result;
}

function 
smf_sessionDestroy($session_id)
{
global $smf_settings;

if (preg_match('~^[A-Za-z0-9]{16,32}$~'$session_id) == 0)
return false;

// Just delete the row...
return smf_query("
DELETE FROM 
$smf_settings[db_prefix]sessions
WHERE session_id = '" 
addslashes($session_id) . "'
LIMIT 1"
__FILE____LINE__);
}

function 
smf_sessionGC($max_lifetime)
{
global $smf_settings;

// Just set to the default or lower?  Ignore it for a higher value. (hopefully)
if ($max_lifetime <= 1440 && !empty($smf_settings['databaseSession_lifetime']))
$max_lifetime max($smf_settings['databaseSession_lifetime'], 60);

// Clean up ;).
return smf_query("
DELETE FROM 
$smf_settings[db_prefix]sessions
WHERE last_update < " 
. (time() - $max_lifetime), __FILE____LINE__);
}
?>


Then on my login page I have:
<?php
if ($smf_user_info['is_guest'] && isset($_POST['user'], $_POST['passwd']) )
{
if ( smf_setLoginCookie(strtotime('+5 year'), mysql_real_escape_string($_POST['user']), mysql_real_escape_string($_POST['passwd']), false) )
{
if ( isset($_SERVER['HTTP_REFERER']) )
{
header('location: ' $_SERVER['HTTP_REFERER']);
}
else
{
header('location: ' $basedir 'index.php');
}
}
else
{
$tpl->feedback[] = 'Didn\'t work mate, try again.';
}
}
?>


and to actually check if a user is logged in:
<?php
// do a print_r($smf_user_info) to see the glory.
include 'smf_api.php';
smf_loadSession();
smf_authenticateUser();
?>
My Latest Blog Post: Debugging Nginx Errors

ExcalibursZone

Hey, thanks for the options, I'll look into it when I get back from school.

Advertisement: