From 9e9e530465dc65bdf6935ffddb0d1a7a99983564 Mon Sep 17 00:00:00 2001
From: Chris
}iso;
-
- $content =~ s/</g;
- $content =~ s/>/>/g;
-
- return "Unexpected response to registration:
$errmsg
Unable to add user.
$content
" - } - - # Okay, registration is completed, now we need to find out which user id the new user has - my $user = $self -> get_user($args -> {"username"}); - return "Unable to determine the user id for ".$args -> {"username"}.", unable to complete registration." - if(!$user); - - return $user; -} - - -## @method $ get_user($username) -# Search for a user with the specified username in the database. This will attempt -# to obtain a user record for a user with the specified username in the phpBB3 -# database, and return a reference to a hash containing the data if successful. -# -# @param username The name of the user to locate. -# @return A reference to the user's data, or undef if the user could not be located -# or an error occurred. -sub get_user { - my $self = shift; - my $username = shift; - - my $userh = $self -> {"dbh"} -> prepare("SELECT * FROM ".$self -> {"prefix"}."users - WHERE username_clean LIKE ?"); - $userh -> execute(lc($username)) - or die "phpBB3::get_user(): Unable to execute user lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - return $userh -> fetchrow_hashref(); -} - - -## @method $ get_user_byid($userid, $onlyreal) -# Search for a user with the specified id in the database. This will attempt -# to obtain a user record for a user with the specified id in the phpBB3 -# database, and return a reference to a hash containing the data if successful. -# -# @param userid The id of the user to locate. -# @param onlyreal If set, the userid must correspond to a 'real' user: if the id -# is a bot or inactive account, this returns undef. -# @return A reference to the user's data, or undef if the user could not be located -# or an error occurred. -sub get_user_byid { - my $self = shift; - my $userid = shift; - my $onlyreal = shift; - - my $userh = $self -> {"dbh"} -> prepare("SELECT * FROM ".$self -> {"prefix"}."users - WHERE user_id = ?". - ($onlyreal ? " AND user_type IN (0,3)" : "")); - $userh -> execute($userid) - or die "phpBB3::get_user_byid(): Unable to execute user lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - return $userh -> fetchrow_hashref(); -} - - - -## @method $ get_group($groupname) -# Search for a group with the specified name in the database. This will attempt -# to obtain a group record for a group with the specified group name in the phpBB3 -# database, and return a reference to a hash containing the data if successful. -# -# @param groupname The name of the group to locate. -# @return A reference to the group's data, or undef if the group could not be located -# or an error occurred. -sub get_group { - my $self = shift; - my $groupname = shift; - - my $grouph = $self -> {"dbh"} -> prepare("SELECT * FROM ".$self -> {"prefix"}."groups - WHERE group_name LIKE ?"); - $grouph -> execute($groupname) - or die "phpBB3::get_group(): Unable to execute group lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - return $grouph -> fetchrow_hashref(); -} - - -## @method $ user_in_group(%args) -# Determine whether a user is a member of a group. This will check whether a -# user is listed as being a member of a group, and return true if they are. The -# arguments that can be specified for this are: -# username - the name of the user to search for. -# user_id - the id of the user to search for. -# group - the name of the group to check in. -# group_id - the id of the group to check in. -# If user_id is specified, username is ignored if it is also provided. If user_id -# is not provided, username must be (ie: you must specify at least one of username -# or user_id, and user_id takes precedence) Similarly, you must specify at least -# one of group or group_id, and group_id takes precedence over group. -# -# @param args A hash of arguments. -# @return true if the user is present in the group, false if not or if an error -# occured while attempting to check. -sub user_in_group { - my $self = shift; - my %args = @_; - - set_error(""); - - # Check that we have one of username or user_id - return set_error("No username or user_id provided") - if(!$args{'username'} && !$args{'user_id'}); - - # similarly for the group and group_id - return set_error("No group or group_id provided") - if(!$args{'group'} && !$args{'group_id'}); - - # If we don't have a user_id, we need to look it up - if(!$args{"user_id"}) { - my $user = $self -> get_user($args{'username'}) - or return set_error("Unable to find user $args{'username'}"); - $args{"user_id"} = $user -> {"user_id"}; - } - - # If we don't have a group_id, we need to look it up - if(!$args{"group_id"}) { - my $group = $self -> get_group($args{'group'}) - or return set_error("Unable to find user $args{'group'}"); - $args{"group_id"} = $group -> {"group_id"}; - } - - - # Now we should have a user id and group id, so we can go look in the user_group table - my $ugh = $self -> {"dbh"} -> prepare("SELECT * FROM ".$self -> {"prefix"}."user_group - WHERE group_id = ? AND user_id = ?"); - $ugh -> execute($args{"group_id"}, $args{"user_id"}) - or die "phpBB3::user_in_group(): Unable to execute user_group lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - # Do we have one or more rows? - my $ugr = $ugh -> fetchrow_arrayref(); - - return defined($ugr); -} - - -## @method $ valid_user($username, $password) -# Attempt to confirm whether the provided user credentials are valid. This will check -# whether the specified username corresponds to a valid user, and if it does it will -# check that the hash of the provided password matches. If the password matches, this -# returns a reference to a hash containing the user's entry in the users table. -# -# @param username The username of the user to check. -# @param password The password to check against this user. -# @return A reference to a hash containing the user's data, or undef if an error -# occured, the user could not be found, or the password was invalid. -sub valid_user { - my $self = shift; - my $username = shift; - my $password = shift; - - # first get hold of the user - my $user = $self -> get_user($username) - or return set_error("Unable to locate user $username in the forum database."); - - # We have a user, do the passwords match? If so, return the user's hash - return $user if(_check_hash($password, $user -> {"user_password"})); - - return set_error("The specified password is not valid."); -} - - -## @method $ get_profile_url($userid) -# Given a userid, produce a full URL that can be used to view the user's profile. -# -# @param userid The ID of the user whose profile URL should be generated. -# @return A string containing the URL of the user's profile. -sub get_profile_url { - my $self = shift; - my $userid = shift; - - return path_join($self -> {"url"}, "memberlist.php?mode=viewprofile&u=$userid"); -} - - -## @method $ email_in_use($email) -# Determine whether the specified email address is already in use within the -# system. -# -# @param email The email address to check. -# @return true if the email address already exists within the database, false -# if it does not. -sub email_in_use { - my $self = shift; - my $email = shift; - - my $emailh = $self -> {'dbh'} -> prepare("SELECT user_id FROM ".$self -> {"prefix"}."users - WHERE user_email LIKE ?"); - $emailh -> execute($email) - or die "phpBB3::email_in_use(): Unable to execute email lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - return $emailh -> fetchrow_hashref(); -} - - -## @method $ is_valid_password($plaintext) -# This determines whether the specified password is valid, based on the current -# phpBB3 settings. If this returns true, the password should have passed all the -# requirements currently set in phpBB3, otherwise it is either the wrong length -# or it does not contain the correct characters. -# -# @param plaintext The plain text password to verify. -# @return true if the password passes the suitability checks, false otherwise. -sub is_valid_password { - my $self = shift; - my $plaintext = shift; - - # Length checks first, the password must be within min_pass_chars to max_pass_chars - return 0 if(length($plaintext) < $self -> get_config('min_pass_chars') || - length($plaintext) > $self -> get_config('max_pass_chars')); - - # Now we need to check for character content based on pass_complex - my $passmode = $self -> get_config('pass_complex'); - - # _ANY is automatically true now.. - if($passmode eq "PASS_TYPE_ANY") { - return 1; - - # _CASE requires mixed case - } elsif($passmode eq "PASS_TYPE_CASE" && $plaintext =~ /[a-z]/ && $plaintext =~ /[A-Z]/) { - return 1; - - # _ALPHA requires letters and numbers - } elsif($passmode eq "PASS_TYPE_ALPHA" && $plaintext =~ /[a-zA-Z]/ && $plaintext =~ /[0-9]/) { - return 1; - - # _SYMBOL is as _ALPHA plus symbols - } elsif($passmode eq "PASS_TYPE_SYMBOL" && $plaintext =~ /[a-zA-Z]/ && $plaintext =~ /[0-9]/ && $plaintext =~ /[^a-zA-Z0-9]/) { - return 1; - } - - return 0; -} - - -# ============================================================================== -# Session handling - -## @method @ get_session(void) -# Attempt to obtain the userid and username of the current session user. This -# attempts to determine whether the session in the user's cookies is a valid -# phpBB3 session, and if it is it returns a reference to a hash containing user -# and session data. This will update the timestamp on the session, if needed. -# -# @return A reference to a hash containing user and session data if the session -# is valid, undef otherwise. -# -# @todo This does not currently support forwarded_for checks, referer checks, -# or load limiting. It also does not support 'alternative' auth methods: -# only database auth is supported. -sub get_session { - my $self = shift; - - # First grab the name of the cookie, and fall over if it isn't available for some reason - my $cookiebase = $self -> get_config("cookie_name") - or return set_error("Unable to determine phpBB3 cookie name"); - - # First, try to obtain a session id - start by looking at the cookies - my $sessid = $self -> {"cgi"} -> cookie($cookiebase."_sid"); - my $sessuser = $self -> {"cgi"} -> cookie($cookiebase."_u"); # Which users does this session claim to be? - my $autokey = $self -> {"cgi"} -> cookie($cookiebase."_k"); # Do we have an autologin key for the user? - - # If we don't have a session id now, try to pull it from the query string - $sessid = $self -> {"cgi"} -> param("sid") if(!$sessid); - - # If we still don't have a session id, the user hasn't logged into phpBB3. Give up on them - return set_error("Unable to obtain a session id for user.") if(!$sessid); - - # Obtain the session and user record from the database - my $sessh = $self -> {"dbh"} -> prepare("SELECT u.*,s.* - FROM ".$self -> {"prefix"}."users AS u, ".$self -> {"prefix"}."sessions AS s - WHERE s.session_id = ? AND u.user_id = s.session_user_id"); - $sessh -> execute($sessid) - or die "phpBB3::get_session(): Unable to obtain session and user data from database.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - my $sessdata = $sessh -> fetchrow_hashref(); - - # if we have a session, we need to validate it - if($sessdata) { - # If we have anonymous disabled, at this is the anon user, exit immediately - return set_error("Anonymous user session rejected.") - if(!$self -> {"allowanon"} && $sessdata -> {"user_id"} == $ANONYMOUS); - - # Do some basic checks on the IP and useragent if they are enabled. - my ($valid_ip, $valid_ua) = (1, 1); - - # if ip address checking is needed, do it - my $ipfrags = $self -> get_config("ip_check"); - if($ipfrags) { - my @sess_parts = split('.', $sessdata -> {"session_ip"}); - my @user_parts = split('.', $ENV{"REMOTE_ADDR"}); - my $session_ip = join(".",splice(@sess_parts, 0, $ipfrags)); - my $user_ip = join(".",splice(@user_parts, 0, $ipfrags)); - - $valid_ip = $session_ip eq $user_ip; - } - - # Check that the browsers match if needed - if($self -> get_config("browser_check")) { - $valid_ua = substr(lc($sessdata -> {"session_browser"}), 0, 150) eq - substr(lc($self -> {"cgi"} -> user_agent()), 0, 150); - } - - # If the ip and browser checks are okay, continue with the validation - # TODO: add referer and forwarded_for checks here? - if($valid_ip && $valid_ua) { - my $expired = 0; - - # If the session is not an autologin, check whether it has timed out - if(!$sessdata -> {"session_autologin"}) { - $expired = $sessdata -> {"session_time"} < (time() - ($self -> get_config("session_length") + 60)); - - # If the session claims to be autologin, but the server doesn't support it, expire the session - } elsif(!$self -> get_config("allow_autologin")) { - $expired = 1; - - # Otherwise, if check whether a maximum autologin time limit has been set, and that the session is within it - } else { - my $max_autologin = $self -> get_config("max_autologin_time"); - $expired = ($max_autologin && $sessdata -> {"session_time"} < (time() - ($max_autologin + 60))); - } - - # If the session has not expired, we want to touch it - if(!$expired) { - if(time() - $sessdata -> {"session_time"} > 60) { - my $touch = $self -> {"dbh"} -> prepare("UPDATE ".$self -> {"prefix"}."sessions - SET session_time = ? - WHERE session_id = ?"); - $touch -> execute(time(), $sessdata -> {"session_id"}) - or die "phpBB3::get_session(): Unable to update session timestamp for ".$sessdata -> {"session_id"}."\nError was: ".$self -> {"dbh"} -> errstr."\n"; - } - return $sessdata; - } else { # if(!$expired) { - set_error("The phpBB3 session has expired"); - } - } else { # if($valid_ip && $valid_ua) { - set_error("phpBB3 session validation has failed"); - } - } else { # if($sessdata) { - set_error("Invalid session ID provided"); - } - - # If we get here, we did not have a valid session, or it has expired. Fall over - return undef; -} - - -# ============================================================================== -# Forum listing and extraction - -## @method $ get_forum($forumid) -# Obtain the forum row that corresponds to the provided forumid. This will obtain -# reference to a hash containing the data for the forum given by the provided -# forumid, or undef if it can not be located in the database. -# -# @note This function does no permissions checking whatsoever. It is up -# to the caller to determine whether or not the forum should be visible. -# If you expose private forums with this function, you have nobody to -# blame but yourself. You have been warned. -# -# @param forumid The id of the forum to obtain data on. -# @return A reference to a hash containing the forum data, or undef if the -# forum does not exist in the database. -sub get_forum { - my $self = shift; - my $forumid = shift; - - my $forumh = $self -> {"dbh"} -> prepare("SELECT * FROM ".$self -> {"prefix"}."forums - WHERE forum_id = ?"); - $forumh -> execute($forumid) - or die "phpBB3::get_forum(): Unable to perform forum lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - # Just return the hashref as-is... - return $forumh -> fetchrow_hashref(); -} - - -# ============================================================================== -# Topic listing and extraction - -## @method $ get_topic_ids($forum, $count, $offset, $sort_by_last) -# Given a forum id, a topic count, and an offset, obtain a list of topic IDs -# for topics in the forum. This follows the same treatement of posts as the forum -# view in phpBB3: announcements always appear at the start of the list, regardles -# of the offset. The remaining posts are sorted so that sticky topics will -# appear before normal topics, but are otherwise treated normally. -# -# @note This function does no permissions checking whatsoever. It is up -# to the caller to determine whether or not the forum should be visible. -# If you expose private forums with this function, you have nobody to -# blame but yourself. You have been warned. -# -# @param forum The ID of the forum to obtain a topic list for. -# @param count The number of topics ids to return, if not specified defaults to 10. -# if set to 0, all post ids are returned. -# @param offset The number of posts to skip, if not specified defaults to 0. This is -# ignored if count is set to 0. -# @param sort_by_last If true, posts are sorted by the last reply time rather -# than the default creation time order (note that this must -# be true to generate the same listing phpBB3 shows) -# @return A reference to an array of topic ids, or undef if no topics are available -# or an error ocurred. -sub get_topic_ids { - my $self = shift; - my $forum = shift; - my $count = shift; - my $offset = shift || 0; - my $slast = shift; - - $count = 10 if(!defined($count)); - my $fetchall = ($count == 0); # record whether we need to fetch all entries - - # $count will be used directly in queries, so make damned sure it's just numbers - return set_error("Count contains non-digit characters. Possible SQL insertion attack detected!") - if($count =~ /\D/); - - # And the same for the offset - return set_error("Offset contains non-digit characters. Possible SQL insertion attack detected!") - if($offset =~ /\D/); - - # Check that the forum is valid - return set_error("Unable to locate a forum with the specified forumid.") - if(!$self -> get_forum($forum)); - - # Work out the order fragment - my $order = "ORDER BY topic_type DESC, ".($slast ? "topic_last_post_time" : "topic_time")." DESC"; - - # First pull out a list of announcements. We can't do this at the same time - # as pulling out stickies and normal threads, as the offset would screw up - # always having announcements at the front. - my $announceh = $self -> {"dbh"} -> prepare("SELECT topic_id FROM ".$self -> {"prefix"}."topics - WHERE forum_id = ? AND topic_type = 2 - $order - ".($fetchall ? "" : "LIMIT $count")); - $announceh -> execute($forum) - or die "phpBB3::get_topic_ids(): Unable to obtain announcement topic list.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - my @topics; # This is where the topic list will be stored - - # process announcement topic rows until we've processed all the annoucements - while(my $announcer = $announceh -> fetchrow_arrayref()) { - push(@topics, $announcer -> [0]); - --$count if(!$fetchall); # LIMIT $count in the query should prevent this from going negative - } - - # Do we have any space left in the return array? - if($count || $fetchall) { - # Now we want to pull out the stickies and normal topics. topic_type must be < 2 to exclude - # annoucements. - my $topich = $self -> {"dbh"} -> prepare("SELECT topic_id FROM ".$self -> {"prefix"}."topics - WHERE forum_id = ? AND topic_type < 2 - $order - ".($fetchall ? "" : "LIMIT $offset, $count")); - $topich -> execute($forum) - or die "phpBB3::get_topic_ids(): Unable to obtain normal/sticky topic list.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - # Store the topic ids we have from the database... - while(my $topicr = $topich -> fetchrow_arrayref()) { - push(@topics, $topicr -> [0]); - } - - } # if($count) { - - # And we're done. Return a reference to the topics array if it has any contents, - # undef if it does not. - return scalar(@topics) ? \@topics : set_error(""); -} - - -## @method $ get_topic($topicid) -# Obtain the data for the topic identified by the specified topicid. This will attempt -# to locate a topic entry with the specified topicid and return a reference to a hash -# containing the topic information. -# -# @param topicid The id of the topic to look up. -# @return A reference to a hash containing the topic data, undef if the topic could -# not be located in the database. -sub get_topic { - my $self = shift; - my $topicid = shift; - - my $topich = $self -> {"dbh"} -> prepare("SELECT * FROM ".$self -> {"prefix"}."topics - WHERE topic_id = ?"); - $topich -> execute($topicid) - or die "phpBB3::get_topic(): Unable to execute topic lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - return $topich -> fetchrow_hashref(); -} - - -## @method $ get_topic_firstpost($topicid, $bbc_to_html) -# Create a reference to a hash containing the text of the first post in the specified -# topic, the number of replies, the poster's details and various other useful pieces -# of information. -# -# @param topicid The topic id to obtain the first post for -# @param bbc_to_html If true, any bbcode in the post will be converted to html for you. -# @return A reference to a hash containing the first post data and other useful info, -# or undef if a problem was encountered while generating the hash. -sub get_topic_firstpost { - my $self = shift; - my $topicid = shift; - my $bbc_to_html = shift; - - # First we need the topic header - my $topic = $self -> get_topic($topicid) - or return set_error("Unable to locate topic $topicid in the database"); - - # Now we can obtain the first post and user details - my $posth = $self -> {"dbh"} -> prepare("SELECT p.*, u.* - FROM ".$self -> {"prefix"}."posts AS p, ".$self -> {"prefix"}."users AS u - WHERE p.post_id = ? AND u.user_id = p.poster_id"); - $posth -> execute($topic -> {"topic_first_post_id"}) - or die "phpBB3::get_topic_firstpost(): Unable to execute post lookup query.\nError was: ".$self -> {"dbh"} -> errstr."\n"; - - # Did we get a post? - my $post = $posth -> fetchrow_hashref(); - if($post) { - # okay, we can start to build the post data hash now - my $pdata = { "forum_id" => $topic -> {"forum_id"}, - "topic_id" => $topic -> {"topic_id"}, - "post_id" => $topic -> {"post_id"}, - "post_time" => $topic -> {"topic_time"}, - "post_replies" => $topic -> {"topic_replies"}, - "post_subject" => $topic -> {"topic_title"}, - "post_body" => "
".$post -> {"post_text"}."
", - "post_uid" => $post -> {"bbcode_uid"}, - "poster_username" => $post -> {"username"}, - "poster_userid" => $post -> {"user_id"}}; - - # If the user has an avatar, we want to record it. Note that this expects phpBB3 - # to have enforced any restrictions on avatar types. - if($post -> {"user_avatar_type"}) { - # width and height should be there regardless of type - $pdata -> {"avatar_width"} = $post -> {"user_avatar_width"}; - $pdata -> {"avatar_height"} = $post -> {"user_avatar_height"}; - - # type 1 is uploaded - if($post -> {"user_avatar_type"} == 1) { - $pdata -> {"avatar_url"} = $self -> get_config("server_protocol"). - $self -> get_config("server_name"). - path_join($self -> get_config("script_path"). - $self -> get_config("avatar_path"). - $post -> {"user_avatar"}); - # type 2 avatars are remote linked, so the url should be usable as-is - } elsif($post -> {"user_avatar_type"} == 2) { - $pdata -> {"avatar_url"} = $post -> {"user_avatar"}; - - # type 3 avatars are gallery avatars - } elsif($post -> {"user_avatar_type"} == 3) { - $pdata -> {"avatar_url"} = $self -> get_config("server_protocol"). - $self -> get_config("server_name"). - path_join($self -> get_config("script_path"). - $self -> get_config("avatar_gallery_path"). - $post -> {"user_avatar"}); - } - } - - # Fix up bbcode if we need to - $self -> {"bbcode"} -> convert(\$pdata -> {"post_body"}, $pdata -> {"post_uid"}) - if($bbc_to_html && $self -> {"bbcode"}); - - # And done... - return $pdata; - } # if($post) { - - return set_error("Unable to get post data for the first post in topic $topicid. This should not happen."); -} - - -## @method $ get_topic_url($forumid, $topicid) -# Given a topicid, produce a full URL that can be used to view the topic thread. -# -# @param forumid The forum the topic is inside. -# @param topicid The ID of the topic to obtain the URL for. -# @return A string containing the URL of the topic thread. -sub get_topic_url { - my $self = shift; - my $forumid = shift; - my $topicid = shift; - - return path_join($self -> {"url"}, "viewtopic.php?f=$forumid&t=$topicid"); -} - - -## @method $ get_posting_url($forumid, $topicid) -# Given a topicid, produce a full URL that can be used to post to the topic thread. -# -# @param forumid The forum the topic is inside. -# @param topicid The ID of the topic to obtain the URL for. -# @return A string containing the URL of the topic thread. -sub get_posting_url { - my $self = shift; - my $forumid = shift; - my $topicid = shift; - - return path_join($self -> {"url"}, "posting.php?mode=reply&f=$forumid&t=$topicid"); -} - - -# ============================================================================== -# Theoretically internal stuff - -## @method $ get_smilie_url(void) -# Obtain the URL of the directory containing smilies used by the forum. -# -# @return The URL of the smilies directory, or undef if a problem occured -sub get_smilie_url { - my $self = shift; - - my $path = $self -> get_config("smilies_path") - or return set_error("Unable to obtain smilie_path from the database"); - - return path_join($self -> {"url"}, $path); -} - - -## @method $ get_config($name, $default) -# Obtain the value for the specified phpBB3 configuration variable. This will -# return the value for the specified configuration variable if it is found. If -# it is not found, but default is specified, the default is returned, otherwise -# this returns undef. -# -# @param name The name of the variable to obtain the value for -# @param default An optional default value to return if the named variable can not be found -# @return The value for the named variable, or the default or undef if the -# variable is not present. -sub get_config { - my $self = shift; - my $name = shift; - my $default = shift; - - my $configh = $self -> {"dbh"} -> prepare("SELECT config_value FROM ".$self -> {"prefix"}."config WHERE config_name LIKE ?"); - $configh -> execute($name) - or die "phpBB3::get_config(): Unable to query database for $name.\nError was:".$self -> {"dbh"} -> errstr."\n"; - - my $configr = $configh -> fetchrow_arrayref(); - - # If we have a row, and a defined value, return it - return $configr -> [0] - if($configr && defined($configr -> [0])); - - # Otherwise, return the default or undef - return $default; -} - - -## @method $ unique_id($extra) -# Generate a unique ID that can be used with phpBB3 tables. -# -# @param extra Optional extra string to append to the seed. -# @return a unique ID compatible with phpBB3 -sub unique_id { - my $self = shift; - my $extra = shift || ""; - - my @bits = gettimeofday(); - my $seed = $self -> get_config("rand_seed").sprintf("%0.8f %d", $bits[1]/1000000, $bits[0]).$extra; - $seed = md5_hex($seed); - - return substr($seed, 4, 16); -} - - -## @method $ phpdate_to_strftime($format) -# Convert a php dateformat into something that can be passed to strftime. This goes through -# the provided format string and attempts to convert the format markers from the form used -# by the php date() function into something that can be passed to strftime to get the same -# result. Note that the following php date() format options are not supported and will be -# replaced with the empty string: S, t, L, B, u, I, O, and T. The following options are -# partially supported but the resulting strings are not identical: n (will generate the -# same output as m), g (hour has a leading space instead of zero), and G (hour has a -# leading space instead of zero) -# -# @param format The php date() format string to convert. -# @return The converted format ready to pass to strftime(). -sub phpdate_to_strftime { - my $self = shift; - my $format = shift; - - # Yeah, this is really horrible, but applying the hash using a regexp would - # get really nasty, really quick. - my @chars = split //, $format; - my $result = ""; - - # Go through each character, converting it to an strftime format character - # if there is a conversions specified in the map table - foreach my $char (@chars) { - # Do we have a conversion for tis character? - my $conv = $fmt_map{$char}; - # Append the character or the conversion, if we have one. - $result .= $conv ? $conv: $char; - } - - return $result; -} - - -## @fn $ set_error($error) -# Set the error string to the specified value. This updates the class error -# string and returns undef. -# -# @param error The message to set in the error string -# @return undef, always. -sub set_error { - $errstr = shift; - - return undef; -} - - -# ============================================================================== -# Seriously internal stuff - -# The following functions have been ported wholesale from the phpBB3 'includes/functions.php' -# The port has been done with minimal regard for perlification, and it almost certainly -# could be implemented in a far more efficient and perl-friendly fashion. -# -# Beware, voodoo programming follows. - -## @fn $ _hash_encode64($input, $count, $itoa64) -# Convert a number into an encoded string form. -# -# @param input The number to encode. -# @param count The number of characters in the number to convert. -# @param itoa64 The string containing the encoding key. -# @return The encoded string. -sub _hash_encode64 { - my ($input, $count, $itoa64) = @_; - - my $output = ''; - my $i = 0; - - while($i < $count) { - my $value = ord(substr($input, $i++, 1)); - $output .= substr($itoa64, $value & 0x3F, 1); - - $value |= ord(substr($input, $i, 1)) << 8 if($i < $count); - - $output .= substr($itoa64, ($value >> 6) & 0x3F, 1); - last if($i++ >= $count); - - $value |= ord(substr($input, $i, 1)) << 16 if($i < $count); - - $output .= substr($itoa64, ($value >> 12) & 0x3F, 1); - last if($i++ >= $count); - - $output .= substr($itoa64, ($value >> 18) & 0x3F, 1); - } - - return $output; -} - - -## @fn _hash_crypt_private($password, $setting, $itoa64) -# Hash a password within the specified setting. This is mostly voodoo pulled -# straight from phpBB3. Have fun with it. -# -# @param password The plain-text password to hash. -# @param setting The setting in which the password should be hashed (should be another hash) -# @param itoa64 The string containing the encoding key -# @return A string containing the hashed password. -sub _hash_crypt_private { - my ($password, $setting, $itoa64) = @_; - - my $output = '*'; - - return $output if(substr($setting, 0, 3) ne '$H$'); - - my $count_log2 = index($itoa64, substr($setting, 3, 1)); - return $output if($count_log2 < 7 || $count_log2 > 30); - - my $count = 1 << $count_log2; - my $salt = substr($setting, 4, 8); - - return $output if(length($salt) != 8); - - my $hash = md5($salt.$password); - do { - $hash = md5($hash.$password); - } while(--$count); - - $output = substr($setting, 0, 12); - $output .= _hash_encode64($hash, 16, $itoa64); - - return $output; -} - - -## @fn $ _check_hash($password, $hash) -# Determine whether the specified password hashes to the same string as the provided hash. -# This checks whether the plain-text password, and the previously generated hash, are -# actually representing the same string by hashing the plain-text password and comparing -# it to the specified hash. This function can handle phpBB3 (salted md5 hash) and phpBB2 -# (straight md5 hash) hashes and chooses the appropriate algorithm based on the length of -# the hash string: if it is 34 characters, it is assumed to be a phpBB3 hash, otherwise it -# is assumed to be a hex encoded 32 character string. -# -# @param password The plain-text password to hash. -# @param hash The hash to compared the newly hashed password against -# @return true if the password and hash represent the same string, false if the password -# hashes to a different string. -sub _check_hash { - my ($password, $hash) = @_; - - # lifted straight from phpBB3, if that changes, this must be changed! - my $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'; - - return (_hash_crypt_private($password, $hash, $itoa64) eq $hash) - if (length($hash) == 34); - - return md5_hex($password) eq $hash; -} - -1;