Requirements: You will need a PC or
Macintosh computer with sound capabilities,
internet connection and Internet Explorer 4
or later. More details here...
Registration Form
The asterisk * denotes a required fields.
The Foundation respects your privacy and will not reveal, disclose,
sell, distribute, share or otherwise pass on to any third parties
any personal information that you may have provided unless permitted
by law or we have your express written consent to do so.
Please review our
Privacy Statement.
Register for the webcast.
You need to Create a New Account. When you login this will allow you to access the webcast
console and where you may submit questions.
You can make up your own User Name. Choose something you will easily remember and that is
four or more letters and/or numbers.
You can also make up your own Password. Choose something you will easily remember and that
is seven or more letters and/or numbers.
#!/usr/bin/perl
#
# userbase.cgi
#
######################################################################
#
# DO NOT EDIT THIS FILE unless absolutely necessary; in most cases
# you should be editing userbase_prefs.cgi instead.
#
######################################################################
#
# This program is the copyrighted work of Encodable Industries.
# Redistribution is prohibited, and copying is only permitted for
# backup purposes. You are free to modify the program for your
# own use, but you may not distribute any modified copies of it.
#
# Use of this program requires a one-time license fee. You can
# obtain a license here:
#
# http://encodable.com/userbase/
#
# This software comes with no warranty. The author and many other
# people have found it to be useful, and it is our hope that you
# find it useful as well, but it comes with no guarantees. Under
# no circumstances shall Encodable Industries be held liable in
# any situation arising from your use of this program. We are
# generally happy to provide support to all our users, but we can
# make no guarantee of support.
#
# For more information about this program, as well as for help
# and support, please visit the following pages:
#
# Homepage: http://encodable.com/userbase/
# Contact: http://encodable.com/contact/
my $version = "2.02";
$ENV{PATH} = '/bin:/usr/bin';
delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
($ENV{DOCUMENT_ROOT}) = ($ENV{DOCUMENT_ROOT} =~ /(.*)/); # untaint.
use lib './perlmodules';
my (%PREF,%TEXT) = ();
use CGI::Carp 'fatalsToBrowser'; # uncomment to show errors in browser.
my $debuglog = undef; #open($debuglog, ">>ubdata/debuglog.ubtemp.log") or die_nice("couldn't open debuglog: $!\n"); flock $debuglog, 2; print $debuglog "\n\n";
use strict;
use Digest::MD5 'md5_hex'; # always required for backwards compatibility.
unless($PREF{use_md5_for_hashes} =~ /yes/i) { eval { require Digest::SHA1; }; die_nice($@) if $@; import Digest::SHA1 'sha1_hex'; }
use DBI;
sub printd;
sub die_nice;
my $qs = '';
sub create_new_session_id
{
my $username = shift;
my $password = shift;
my $id = offsettime() . $$ . $ENV{REMOTE_ADDR} . $ENV{HTTP_USER_AGENT} . $username . $password;
#$id =~ s/[^\d]//g;
#$id = substr($id,0,85);
$id = enc_hash($id);
return $id;
}
sub check_for_multiple_logins($)
{
my ($userid) = @_;
die_unless_numeric($userid,'userid');
my $old_login_time = enc_sql_select("SELECT `loggedin` FROM `$PREF{user_table_name}` WHERE `id` = $userid;");
if($old_login_time =~ /[1-9]/ && !login_session_expired($old_login_time))
{
if($PREF{prevent_multiple_simultaneous_logons_per_username} =~ /yes/i)
{
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?phase=emultlogin";
enc_redirect($go);
}
else
{
my $success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `numusers`=IFNULL(`numusers`,0)+1 WHERE `id` = '$userid';");
die_nice("Error: check_for_multiple_logins('$userid'): SQL returned '$success' instead of '1' while incrementing numusers column.") unless $success == 1;
my $existing_session_id = enc_sql_select("SELECT `mrsession` FROM `$PREF{user_table_name}` WHERE `id` = '$userid';");
return $existing_session_id;
}
}
}
sub log_user_into_db
{
my ($userid, $my_session_id, $logintime, $restrict_ip) = @_;
die_unless_numeric($userid,'userid');
die_unless_numeric($logintime,'logintime');
check_sessionid_for_sql_safeness($my_session_id);
my $success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `loggedin` = $logintime, `mrsession` = '$my_session_id' WHERE `id` = '$userid';");
die_nice("Error: log_user_into_db('$userid', '$my_session_id', '$logintime', '$restrict_ip'): SQL returned '$success' instead of '1' while logging user in.") unless $success == 1;
if($restrict_ip)
{
my $ip = $ENV{REMOTE_ADDR};
check_ip_for_sql_safeness($ip);
unless(enc_sql_select("SELECT `ip` FROM `$PREF{user_table_name}` WHERE `id` = '$userid'") eq $ip)
{
my $success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `ip` = '$ip' WHERE `id` = '$userid';");
die_nice("Error: log_user_into_db('$userid', '$my_session_id', '$logintime', '$restrict_ip'): SQL returned '$success' instead of '1' while setting user IP.") unless $success == 1;
}
}
else
{
unless(enc_sql_select("SELECT `ip` FROM `$PREF{user_table_name}` WHERE `id` = '$userid'") eq '')
{
my $success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `ip` = '' WHERE `id` = '$userid';");
die_nice("Error: log_user_into_db('$userid', '$my_session_id', '$logintime', '$restrict_ip'): SQL returned '$success' instead of '1' while clearing user IP.") unless $success == 1;
}
}
}
sub log_user_out_of_db
{
my ($username, $my_session_id) = @_;
check_username_for_sql_safeness($username);
check_sessionid_for_sql_safeness($my_session_id) unless $my_session_id eq 'force';
# It's possible (and probably not particularly uncommon) that a user logs in at one location, then leaves
# that location and his session goes idle, and then he logs in at another location with the same account. In
# that case, a call to log_user_out_of_db() from the first location should not actually do the db logout,
# because the session does not belong to him anymore. But note that this is not an error condition, so we
# should just silently return.
#
my $session_id_in_db = enc_sql_select("SELECT `mrsession` FROM `$PREF{user_table_name}` WHERE LOWER(`username`) = LOWER('$username');");
if($my_session_id == $session_id_in_db || $my_session_id eq 'force')
{
my $success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `loggedin` = 0 WHERE `username` = '$username';");
die_nice("Error: log_user_out_of_db('$username', '$my_session_id'): SQL returned '$success' instead of '1' while setting loggedin to zero.") unless $success == 1;
$success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `mrsession` = '' WHERE `username` = '$username';");
die_nice("Error: log_user_out_of_db('$username', '$my_session_id'): SQL returned '$success' instead of '1' while setting mrsession to null.") unless $success == 1;
my $numusers = enc_sql_select("SELECT `numusers` FROM `$PREF{user_table_name}` WHERE `username` = '$username';");
if($numusers) # this check only required because of crappy old MySQL versions that fail to ever set numusers properly in the DB (??).
{
$success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `numusers` = 0 WHERE `username` = '$username';");
die_nice("Error: log_user_out_of_db('$username', '$my_session_id'): SQL returned '$success' instead of '1' while setting numusers to zero.") unless $success == 1;
}
}
}
sub determine_default_login_destination
{
my $ref = shift;
my $go = ();
if($qs =~ /(?:^|&)whence=(.+)/)
{
# don't URL-decode here because this is getting passed right
# to the Location: header, which requires URL encoding.
$go = $1;
if($qs =~ /(?:^|&)encanchor=([^&]+)(?:&|$)/)
{
$go .= '#' . $1;
}
}
else
{
$go = $ref ? $ref : "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
my $us1 = $PREF{login_url};
my $us2 = $ENV{SCRIPT_NAME};
if($go =~ /($us1|$us2)\?.+/)
{
# If the page we were on before was a login page with some
# query-string, then just return to the login frontpage.
$go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
}
elsif($go !~ m!^https?://(www\.)?$ENV{HTTP_HOST}!)
{
# If the page we were on before was on an external site, then
# obviously we don't want to redirect there, since we won't be
# logged in there. Again, just return to the login frontpage.
$go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
}
}
return $go;
}
sub prompt_for_login()
{
printd "prompt_for_login()";
# don't urldecode here, because it's just going right back onto the URL.
my $whence = $qs =~ /(?:^|&)whence=(.+)/ ? "&whence=$1" : undef;
if($qs =~ /(?:^|&)encanchor=([^&]+)(?:&|$)/)
{
$whence = "&encanchor=$1" . $whence;
}
my $template = $qs =~ /format=mini/ ? $PREF{login_form_template__mini} : $PREF{login_form_template};
$template =~ s/%%whence%%/$whence/g;
$template = interpolate_userbase_variables($template);
print $template;
}
sub user_has_addmember_rights
{
return user_is_allowed_to($PREF{logged_in_userid}, 'create_new_accounts') || logged_in_user_is_subgroup_manager();
}
sub logged_in_user_is_part_of_a_subgroup()
{
#foreach my $group (enc_sql_select("SELECT "))
#{
# if($group =~ /.+$PREF{subgroup_groupname_suffix}$/i)
# {
# return enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table_name}` WHERE LOWER(`group`) = LOWER('$group') AND `members` REGEXP '(^|,)$PREF{logged_in_userid}(,|\$)'");
# }
#}
my $subgroup_groupname_suffix = $PREF{subgroup_groupname_suffix};
sql_untaint($subgroup_groupname_suffix);
exit_with_error("logged_in_user_is_part_of_a_subgroup() not SQL safe: \$subgroup_groupname_suffix ('$subgroup_groupname_suffix').") if not_sqlsafe($subgroup_groupname_suffix);
die_unless_numeric($PREF{logged_in_userid}, '$PREF{logged_in_userid}');
return enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table_name}` WHERE `group` REGEXP '.+$subgroup_groupname_suffix\$' AND `members` REGEXP '(^|,)$PREF{logged_in_userid}(,|\$)'");
}
sub logged_in_user_is_subgroup_manager
{
foreach my $group (split(/\s*,\s*/, $PREF{groups_limited_to_subgroup_user_mgmt}))
{
return 1 if user_is_member_of_group($PREF{logged_in_userid}, $group);
}
return 0;
}
sub logged_in_subgroup_manager_owns_this_user($)
{
my $userid_to_check = shift;
return user_is_member_of_group($userid_to_check, "$PREF{logged_in_username}$PREF{subgroup_groupname_suffix}");
}
sub user_has_addadmin_rights
{
return $PREF{admin_is_logged_in};
}
sub user_has_groupmod_rights
{
return $PREF{admin_is_logged_in};
}
sub user_is_allowed_to
{
my $userid_performing_action = shift;
my $action = shift;
my $user_affected_by_action = shift;
my $userid_affected_by_action = get_user_id($user_affected_by_action);
return 1 if is_admin($userid_performing_action);
if($action eq 'edit_user_info')
{
return 1 if (logged_in_user_is_subgroup_manager() && logged_in_subgroup_manager_owns_this_user($userid_affected_by_action));
}
my $allowed = 0;
foreach my $group (split(/[,\s]+/, $PREF{"groups_allowed_to_$action"}))
{
if($group =~ /^self$/i)
{
$allowed = 1 if ($PREF{member_is_logged_in} && $userid_performing_action == $userid_affected_by_action);
}
else
{
$allowed = 1 if user_is_member_of_group($userid_performing_action, $group);
}
return $allowed if $allowed;
}
return 0;
}
sub print_user_form
{
my $mode = shift;
my %vars = ();
if($mode eq 'added_by_admin')
{
$PREF{on_page} = 'adminadduser';
##my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
##if(!user_has_addmember_rights()) { enc_redirect("$go?phase=eneedlogin"); }
#if(!user_has_addmember_rights()) { exit_with_error($TEXT{Access_denied_}); }
if(!user_has_addmember_rights()) { exit_with_error($TEXT{Access_denied_}); }
$vars{title} = $TEXT{Add_New_User};
$vars{pw} = $TEXT{Password_};
$vars{pw2} = $TEXT{Password_again_};
$vars{button_label} = $TEXT{Add_User};
$vars{target} = 'action=commitadduser';
$vars{forcepwchng} = 1 if $PREF{enable_forced_password_change} =~ /yes/i;
$vars{username_required} = 'required';
$vars{password_required} = 'required';
}
elsif($mode eq 'user_signup')
{
$PREF{on_page} = 'usersignup';
#my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
#if(!user_has_addmember_rights()) { enc_redirect("$go?phase=eneedlogin"); }
unless($PREF{visitors_can_sign_up_for_their_own_accounts} =~ /yes/i) { exit_with_error($TEXT{This_feature_is_disabled_}); }
$vars{title} = $TEXT{Create_New_Account};
$vars{pw} = $TEXT{Password_};
$vars{pw2} = $TEXT{Password_again_};
$vars{button_label} = $TEXT{Sign_Up};
$vars{target} = 'action=commitadduser';
$vars{hidden_signup_input} = qq``;
$vars{forcepwchng} = 0;
$vars{username_required} = 'required';
$vars{password_required} = 'required';
}
else # edit the user info instead.
{
$PREF{on_page} = 'edituser';
$vars{user_id} = shift;
die_unless_numeric($vars{user_id}, 'userid (from print_user_form())');
$vars{username} = get_user_name($vars{user_id});
#my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
#if(!user_is_allowed_to($PREF{logged_in_userid}, 'edit_user_info', $vars{username})) { enc_redirect("$go?phase=eneedlogin"); }
if(!user_is_allowed_to($PREF{logged_in_userid}, 'edit_user_info', $vars{username})) { exit_with_error($TEXT{Access_denied_}); }
$vars{title} = $TEXT{Edit_Account_Information};
$vars{pw} = $TEXT{New_Password_};
$vars{pw2} = $TEXT{New_Password_again_};
$vars{button_label} = $TEXT{Modify_User};
$vars{target} = "action=commitedituser";
$vars{force_pw_chng_message} = $PREF{force_pw_chng_message} if force_pw_change($PREF{logged_in_userid});
$vars{username_readonly} = $PREF{usernames_are_immutable_once_created} =~ /no/i && user_is_allowed_to($PREF{logged_in_userid}, 'change_usernames', $vars{username}) ? undef : qq`readonly="readonly"`;
$vars{realname} = get_real_name($vars{user_id});
$vars{email} = get_email_address($vars{user_id});
$vars{account_locked} = enc_sql_select("SELECT `acct_locked` FROM `$PREF{user_table_name}` WHERE `id` = '$vars{user_id}';");
$vars{account_disabled} = enc_sql_select("SELECT `acct_disabled` FROM `$PREF{user_table_name}` WHERE `id` = '$vars{user_id}';");
$vars{forcepwchng} = enc_sql_select("SELECT `forcepwchng` FROM `$PREF{user_table_name}` WHERE `id` = '$vars{user_id}';") if $PREF{enable_forced_password_change} =~ /yes/i;
$vars{username_required} = 'required';
}
if($qs =~ /redo=true/)
{
while($qs =~ /(?:^|&)redo_(\w+)=([^&]*)/g)
{
my ($field,$value) = ($1,$2);
enc_urldecode($value);
$vars{$field} = $value if $value;
}
}
$vars{form_title} = $PREF{form_title_template};
$vars{form_title} =~ s/%%title%%/$vars{title}/g;
$vars{emailformat_for_username} = $PREF{usernames_must_be_email_addresses} =~ /yes/i ? 'emailformat' : '';
start_html_output("$vars{title}");
if($qs =~ /(?:^|&)phase=(\w+?)(?:&|$)/) { my $phase = $1; print qq`
` . get_message($phase) . qq`
\n`; }
my $i = 0;
print qq`
$vars{force_pw_chng_message}
`
. qq`\n`;
finish_html_output();
}
sub logged_in_user_must_enter_current_password_to_change_password_for_user($$)
{
my $username_of_target_user = shift;
my $userid_of_target_user = shift;
if(this_user_is_the_logged_in_admin($username_of_target_user))
{
# an admin is trying to change his own password, so
# we DO require the current password.
return 1;
}
elsif(logged_in_user_is_subgroup_manager() && logged_in_subgroup_manager_owns_this_user($userid_of_target_user))
{
# a subgroup manager is trying to change the password of one of his
# own users; he's effectively an admin for this purpose, so we DON'T
# require the current password.
return 0;
}
elsif($PREF{admin_is_logged_in})
{
# in general, an admin does NOT need the current password to change
# a password.
return 0;
}
else
{
# in general, a non-admin DOES need the current password to change
# a password.
return 1;
}
}
sub print_group_form
{
my $mode = shift;
my %vars = ();
if($mode eq 'add')
{
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
if(!user_is_allowed_to($PREF{logged_in_userid}, 'create_new_groups')) { enc_redirect("$go?phase=eneedlogin"); }
$vars{title} = 'Add New Group';
$vars{button_label} = 'Add Group';
$vars{target} = 'action=commitaddgroup';
}
else # edit the group info instead.
{
$vars{group_id} = shift;
$vars{group} = get_group_name($vars{group_id});
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
if(!user_is_allowed_to($PREF{logged_in_userid}, 'edit_group_info')) { enc_redirect("$go?phase=eneedlogin"); }
$vars{title} = 'Edit Group';
$vars{button_label} = 'Modify Group';
$vars{target} = "action=commiteditgroup";
$vars{groupname_readonly} = $PREF{groupnames_are_immutable_once_created} =~ /no/i && user_is_allowed_to($PREF{logged_in_userid}, 'change_groupnames') ? undef : qq`readonly="readonly"`;
$vars{groupdesc} = get_group_desc($vars{group_id});
}
start_html_output("$vars{title}");
my $i = 0;
print qq`
`
. qq`\n`;
finish_html_output();
}
sub process_new_account()
{
use CGI ':param';
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
my $user = my $user_redo = param('username'); enc_urlencode($user_redo);
my $realname = my $realname_redo = param('realname'); enc_urlencode($realname_redo);
my $email = my $email_redo = param('email'); enc_urlencode($email_redo);
my $pass = param('pw1'); # don't redo/refill the password because we don't want to pass that on the URL.
my $salt = create_random_salt($PREF{salt_length});
my $crypted_pass = salt_and_crypt_password($pass,$salt);
my $signup = param('user_signup') =~ /yes/i;
$PREF{prev_page} = $signup ? 'newaccount' : 'adduser';
my $redo = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?action=$PREF{prev_page}&redo=true";
$redo .= "&redo_username=$user_redo&redo_realname=$realname_redo&redo_email=$email_redo";
if(!user_has_addmember_rights() && !$signup) { enc_redirect("$redo&phase=eneedlogin"); }
elsif($PREF{visitors_can_sign_up_for_their_own_accounts} !~ /yes/i && $signup) { enc_redirect("$redo&phase=enosignup"); }
elsif(!realname_is_valid($realname) && $realname) { enc_redirect("$redo&phase=einvldr"); }
elsif(!emailaddr_is_valid($email) && $email) { enc_redirect("$redo&phase=einvlde"); }
elsif(!emailaddr_is_valid($user) && $PREF{usernames_must_be_email_addresses} =~ /yes/i) { enc_redirect("$redo&phase=einvlde"); }
elsif(!password_is_valid($pass)) { enc_redirect("$redo&phase=einvldp"); }
elsif(!hashedpw_is_valid($crypted_pass)) { enc_redirect("$redo&phase=einvldh"); }
elsif(!username_is_valid($user)) { enc_redirect("$redo&phase=ebadname"); }
elsif( username_is_taken($user)) { enc_redirect("$redo&phase=edupuser"); }
elsif( $email && email_address_is_taken($email)) { enc_redirect("$redo&phase=edupemail"); }
elsif( param('group-admin') =~ /on/i && !user_has_addadmin_rights()) { enc_redirect("$redo&phase=einsuff"); }
elsif( param('pw1') ne param('pw2')) { enc_redirect("$redo&phase=epwmismatch"); }
my $customfields_sqlsafe = get_sqlsafe_custom_field_values();
my $pending = 0;
if($signup && $PREF{require_email_verification_for_new_signups} =~ /yes/i) { $pending = 1; }
elsif($signup && $PREF{require_admin_approval_for_new_signups} =~ /yes/i) { $pending = 2; }
my $token = '';
if($pending == 1 || $pending == 2)
{
$token = enc_hash($email . $realname . $user . $pass . $salt . offsettime() . $$ . $ENV{REMOTE_ADDR} . $ENV{HTTP_USER_AGENT});
$token =~ s/[^\w]/X/g;
}
my $new_user_id = add_new_user($user, $crypted_pass, $salt, $realname, $email, $pending, $token);
my $query = new CGI;
my %params = $query->Vars;
if(user_has_groupmod_rights())
{
foreach my $param (sort keys %params)
{
if($param =~ /^group-(.+)$/)
{
my $group = $1;
next if ($group =~ /^$PREF{admin_group_name}$/i && !user_has_addadmin_rights());
next if ($group =~ /^($PREF{public_group_name}|$PREF{member_group_name})$/i); # every account is automatically a member of these groups.
add_user_to_group($user, $group) if $params{$param} =~ /on/i;
}
}
}
# When a subgroup manager creates an account, it's automatically
# put into a special group based on the manager's username.
#
if(logged_in_user_is_subgroup_manager())
{
my $groupname = "$PREF{logged_in_username}$PREF{subgroup_groupname_suffix}";
add_new_group($groupname, "Group managed by $PREF{logged_in_username}") unless group_exists($groupname);
add_user_to_group($user, $groupname);
}
my $table = $pending ? $PREF{pending_account_table} : $PREF{user_table_name};
foreach my $customfield (keys %$customfields_sqlsafe)
{
die_unless_numeric($new_user_id, "userid");
my $value = $$customfields_sqlsafe{$customfield};
unless($value eq enc_sql_select("SELECT `$customfield` FROM `$table` WHERE `id` = $new_user_id"))
{
my $statement = "UPDATE `$table` SET `$customfield` = '$value' WHERE `id` = $new_user_id";
my $success = enc_sql_update($statement);
die_nice("Error: process_new_account(): SQL returned '$success' instead of '1' while updating custom field '$customfield' to value '$value'. SQL was: [[$statement]]") unless $success == 1;
}
}
if(param('forcepwchng') =~ /on/i)
{
my $statement = "UPDATE `$table` SET `forcepwchng` = 1 WHERE `id` = $new_user_id;";
my $success = enc_sql_update($statement);
die_nice("Error: process_new_account(id='$new_user_id'): SQL returned '$success' instead of '1' while enabling forcepwchng. SQL was: [[$statement]]") unless $success == 1;
}
if($pending == 1)
{
enc_urlencode($user);
$PREF{verification_email_template} =~ s/%%link%%/$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?action=verify&u=$user&t=$token/g;
my $user_email = $PREF{usernames_must_be_email_addresses} =~ /yes/i ? $user : $email;
send_email( $user_email,
"$PREF{webmaster_name} <$PREF{login_script_email_address}>",
$PREF{verification_email_subject},
$PREF{verification_email_template},
$PREF{verification_email_format},
'die_on_email_error'
);
enc_redirect("$go?phase=sactvrf&one=$user");
}
elsif($pending == 2)
{
send_email_requesting_admin_approval_of_new_acct($user,$token);
enc_urlencode($user);
enc_redirect("$go?phase=sactapp&one=$user");
}
else
{
create_filechucker_userdir($user);
enc_urlencode($user);
notify_admin_of_new_signup($new_user_id) if $signup;
send_welcome_email($new_user_id,$user,$pass,$email,$realname);
enc_redirect("$go?phase=snewadd&one=$user");
}
}
sub process_new_group()
{
use CGI ':param';
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
my $group = param('group');
my $groupdesc = param('groupdesc');
if(!user_is_allowed_to($PREF{logged_in_userid}, 'create_new_groups')) { enc_redirect("$go?phase=eneedlogin"); }
elsif(!groupname_is_valid($group)) { enc_redirect("$go?phase=einvldgn"); }
elsif(!groupdesc_is_valid($groupdesc) && $groupdesc) { enc_redirect("$go?phase=einvldgd"); }
elsif(group_exists($group)) { enc_redirect("$go?phase=egrpexist"); }
elsif($group =~ /^(self)$/i) { enc_redirect("$go?phase=egrprsvd"); }
add_new_group($group, $groupdesc);
enc_redirect("$go?phase=snewgrp&one=$group");
}
sub get_sqlsafe_custom_field_values()
{
my %customfields_sqlsafe = ();
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{custom_field_table}`"))
{
my ($id,$fieldname,$fieldlabel,$datatype,$fieldtype,$fieldmax,$fieldposition,$mandatory,$limitallowedchars,$allowedchars,$allowedcharsmsg,$listitems,$enabled) = ();
my $sth = $PREF{dbh}->prepare("SELECT id,fieldname,fieldlabel,datatype,fieldtype,fieldmax,fieldposition,mandatory,limitallowedchars,allowedchars,allowedcharsmsg,listitems,enabled FROM `$PREF{custom_field_table}` ORDER BY `fieldposition`");
$sth->execute() or die_nice("$PREF{internal_appname}: Error: process_new_account(): $DBI::errstr\n");
$sth->bind_columns(\$id,\$fieldname,\$fieldlabel,\$datatype,\$fieldtype,\$fieldmax,\$fieldposition,\$mandatory,\$limitallowedchars,\$allowedchars,\$allowedcharsmsg,\$listitems,\$enabled);
while($sth->fetchrow_arrayref)
{
next unless ($enabled && db_column_exists($fieldname, $PREF{user_table_name}));
sql_un_untaint($allowedchars);
my $value = param($fieldname);
$value =~ s/\r\n/\n/g; # fix browser newlines.
#
# Do custom-field sanity checking:
#
if($datatype eq 'int' && $value =~ /[^\d-]/) { enc_urlencode($value); enc_redirect("$go?phase=enotint&one=$fieldname&two=$value"); }
if($datatype eq 'uint' && $value =~ /[^\d]/) { enc_urlencode($value); enc_redirect("$go?phase=enotuint&one=$fieldname&two=$value"); }
if($datatype eq 'float' && $value =~ /[^\d\.-]/) { enc_urlencode($value); enc_redirect("$go?phase=enotfloat&one=$fieldname&two=$value"); }
if($datatype eq 'ufloat' && $value =~ /[^\d\.]/) { enc_urlencode($value); enc_redirect("$go?phase=enotufloat&one=$fieldname&two=$value"); }
if(($datatype eq 'bool' || $fieldtype eq 'checkbox') && !($value =~ /^on$/i || !$value)) { enc_urlencode($value); enc_redirect("$go?phase=enotbool&one=$fieldname&two=$value"); }
if(($datatype eq 'bool' || $fieldtype eq 'checkbox') && ($value =~ /^on$/i)) { $value = 1; } # convert checkbox string value to a bool.
if($fieldtype =~ /^(dropdown|radio)$/)
{
my %allowable_values = map { $_ => 1 } split(/\n/, $listitems);
unless($allowable_values{$value}) { enc_urlencode($value); enc_redirect("$go?phase=ebadval&one=$fieldname&two=$value"); }
}
if($fieldmax =~ /^\d+$/ && $fieldmax > 0 && length($value) > $fieldmax) { enc_redirect("$go?phase=emaxlnth&one=$fieldname&two=$fieldmax&three=" . length($value)); }
if($mandatory && !$value) { enc_redirect("$go?phase=emandatory&one=$fieldname"); }
if($limitallowedchars)
{
# Escape any dashes or closing brackets, as per perlre:
#
# If you want either "-" or "]" itself to be a member of a class,
# put it at the start of the list (possibly after a "^"), or escape
# it with a backslash.
#
$allowedchars =~ s/\]/\\]/g;
$allowedchars =~ s/-/\\-/g;
if($value =~ /[^$allowedchars]/) { enc_redirect("$go?phase=ebadchar&one=$id"); }
}
#
# Do SQL sanity checking:
#
sql_untaint($value);
if(not_sqlsafe($value)) { enc_redirect("$go?phase=esqlsafe&one=$fieldname"); }
#
# If we got this far, the value is valid.
#
$customfields_sqlsafe{$fieldname} = $value;
}
}
return \%customfields_sqlsafe;
}
sub get_custom_field_names
{
my $which_user_table = shift;
my $include_disabled_fields = shift;
my (@custom_fields, %custom_fields) = ();
if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{custom_field_table}`"))
{
my ($fieldname,$fieldlabel,$enabled) = ();
my $sth = $PREF{dbh}->prepare("SELECT fieldname,fieldlabel,enabled FROM `$PREF{custom_field_table}` ORDER BY `fieldposition`");
$sth->execute() or die_nice("$PREF{internal_appname}: Error: get_custom_field_names(): $DBI::errstr\n");
$sth->bind_columns(\$fieldname,\$fieldlabel,\$enabled);
while($sth->fetchrow_arrayref)
{
next unless db_column_exists($fieldname, $which_user_table);
next if (!$enabled && !$include_disabled_fields);
$custom_fields{$fieldname} = 1;
push @custom_fields, $fieldname;
}
}
return wantarray ? @custom_fields : \%custom_fields;
}
sub this_user_is_the_logged_in_admin($)
{
my $username = shift;
return $PREF{admin_is_logged_in} && lc($PREF{logged_in_username}) eq lc($username);
}
sub do_email_verification($$)
{
my $username = shift;
my $token = shift;
enc_urldecode($username);
check_username_for_sql_safeness($username);
die_nice("Invalid token '$token'.") unless $token =~ /^\w+$/;
if($PREF{require_admin_approval_for_new_signups} =~ /yes/i)
{
enc_sql_update("UPDATE `$PREF{pending_account_table}` SET `status` = 3 WHERE `username` = '$username' AND `token` = '$token' AND `status` = 1") == 1 or die_nice("$PREF{internal_appname}: Error: do_email_verification('$username', '$token'): SQL returned something other than 1 while trying to set status to 3.");
start_html_output("Email Address Verified");
print $PREF{email_verified_pending_template};
send_email_requesting_admin_approval_of_new_acct($username,$token);
finish_html_output();
}
else
{
my ($password,$salt,$name,$email,$cdate) = enc_sql_select("SELECT `password`,`salt`,`name`,`email`,`cdate` FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND `status` = 1");
die_nice("Error: the username '$username' is taken; you must create a new account.") if username_is_taken($username);
die_nice("Error: the email address '$email' is taken; you must create a new account.") if email_address_is_taken($email);
add_new_user($username, $password, $salt, $name, $email);
my $new_user_id = enc_sql_select("SELECT `id` FROM `$PREF{user_table_name}` WHERE `username` = '$username' AND `password` = '$password' AND `salt` = '$salt' AND `email` = '$email'");
die_unless_numeric($new_user_id, 'User ID');
my @custom_fields = get_custom_field_names($PREF{pending_account_table});
foreach my $customfield (@custom_fields)
{
if(db_column_exists($customfield,$PREF{pending_account_table}) && db_column_exists($customfield,$PREF{user_table_name}))
{
my $value = enc_sql_select("SELECT `$customfield` FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND `status` = 1");
unless($value eq enc_sql_select("SELECT `$customfield` FROM `$PREF{user_table_name}` WHERE `id` = $new_user_id"))
{
my $statement = "UPDATE `$PREF{user_table_name}` SET `$customfield` = '$value' WHERE `id` = $new_user_id";
my $success = enc_sql_update($statement);
die_nice("Error: do_email_verification(): SQL returned '$success' instead of '1' while setting custom field '$customfield' to value '$value' in user table (from value in pending table). SQL was: [[$statement]]") unless $success == 1;
}
}
}
my $sth = $PREF{dbh}->prepare("DELETE FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND `status` = 1");
$sth->execute == 1 or die_nice("$PREF{internal_appname}: do_email_verification('$username', '$token'): SQL returned something other than 1 while deleting user info from 'pending' table.");
create_filechucker_userdir($username);
start_html_output("Email Address Verified");
$PREF{email_verified_active_template} =~ s/%%login_url%%/$PREF{login_url}/g;
print $PREF{email_verified_active_template};
notify_admin_of_new_signup($new_user_id);
finish_html_output();
}
}
sub send_email_requesting_admin_approval_of_new_acct($$)
{
my $username = shift;
my $token = shift;
my $username_urlencoded = $username;
enc_urlencode($username_urlencoded);
$PREF{admin_approval_email_subject} =~ s/%%username%%/$username/g;
$PREF{admin_approval_email_template} =~ s/%%username%%/$username/g;
$PREF{admin_approval_email_template} =~ s/%%approval_url%%/$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?action=approve_or_del&u=$username_urlencoded&t=$token/g;
send_email( $PREF{webmaster_email_address},
"$PREF{webmaster_name} <$PREF{login_script_email_address}>",
$PREF{admin_approval_email_subject},
$PREF{admin_approval_email_template},
undef,
'die_on_email_error'
);
}
sub approve_or_delete_pending_account($$)
{
exit_with_error($TEXT{Access_denied_}) unless user_has_addmember_rights();
my $username = shift;
my $token = shift;
enc_urldecode($username);
check_username_for_sql_safeness($username);
die_nice("Invalid token '$token'.") unless $token =~ /^\w+$/;
my ($password,$salt,$name,$email,$cdate) = enc_sql_select("SELECT `password`,`salt`,`name`,`email`,`cdate` FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND (`status` = 2 || `status` = 3)");
my $pending_status = enc_sql_select("SELECT `status` FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND (`status` = 2 || `status` = 3)");
my $username_urlencoded = $username;
enc_urlencode($username_urlencoded);
start_html_output("Approve or Delete Pending Account");
print qq``
. qq`\n
`
. qq`\n`
. qq`\n`;
finish_html_output();
}
sub approve_or_delete_pending_account_stage2($$$)
{
exit_with_error($TEXT{Access_denied_}) unless user_has_addmember_rights();
my $username = shift;
my $token = shift;
my $decision = shift;
enc_urldecode($username);
check_username_for_sql_safeness($username);
die_nice("Invalid token '$token'.") unless $token =~ /^\w+$/;
if($decision eq 'approve')
{
my ($password,$salt,$name,$email,$cdate) = enc_sql_select("SELECT `password`,`salt`,`name`,`email`,`cdate` FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND (`status` = 2 || `status` = 3)");
die_nice("Error: the username '$username' is taken; you must create a new account.") if username_is_taken($username);
die_nice("Error: the email address '$email' is taken; you must create a new account.") if email_address_is_taken($email);
my $new_user_id = add_new_user($username, $password, $salt, $name, $email);
my @custom_fields = get_custom_field_names($PREF{pending_account_table});
foreach my $customfield (@custom_fields)
{
if(db_column_exists($customfield,$PREF{pending_account_table}) && db_column_exists($customfield,$PREF{user_table_name}))
{
my $value = enc_sql_select("SELECT `$customfield` FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND (`status` = 2 || `status` = 3)");
unless($value eq enc_sql_select("SELECT `$customfield` FROM `$PREF{user_table_name}` WHERE `id` = $new_user_id"))
{
my $statement = "UPDATE `$PREF{user_table_name}` SET `$customfield` = '$value' WHERE `id` = $new_user_id";
my $success = enc_sql_update($statement);
die_nice("Error: approve_or_delete_pending_account_stage2(): SQL returned '$success' instead of '1' while setting custom field '$customfield' to value '$value' in user table (from value in pending table). SQL was: [[$statement]]") unless $success == 1;
}
}
}
create_filechucker_userdir($username);
$PREF{account_activated_email_subject} =~ s/%%username%%/$username/g;
$PREF{account_activated_email_template} =~ s/%%username%%/$username/g;
my $user_email = $PREF{usernames_must_be_email_addresses} =~ /yes/i ? $username : $email;
send_email( $user_email,
"$PREF{webmaster_name} <$PREF{login_script_email_address}>",
$PREF{account_activated_email_subject},
$PREF{account_activated_email_template},
undef,
'die_on_email_error'
);
notify_admin_of_new_signup($new_user_id);
}
my $sth = $PREF{dbh}->prepare("DELETE FROM `$PREF{pending_account_table}` WHERE `username` = '$username' AND `token` = '$token' AND (`status` = 2 || `status` = 3)");
$sth->execute == 1 or die_nice("$PREF{internal_appname}: do_email_verification('$username', '$token'): SQL returned something other than 1 while deleting user info from 'pending' table.");
if($decision eq 'approve')
{
start_html_output("Pending Account Approved and Activated");
print qq`
The '$username' account has been approved and is now active.
`;
}
}
sub get_login_status_string
{
if($PREF{member_is_logged_in})
{
my $status = $PREF{login_status_string_template};
die_unless_numeric($PREF{logged_in_userid}, 'logged_in_userid');
my $numusers = enc_sql_select("SELECT `numusers` FROM `$PREF{user_table_name}` WHERE `id` = '$PREF{logged_in_userid}';");
my $usertype = $PREF{admin_is_logged_in} ? 'Admin' : 'Member';
my $extra_info = $numusers > 1 ? ' (multiple locations)' : '';
$status =~ s/%%usertype%%/$usertype/g;
$status =~ s/%%username%%/$PREF{logged_in_username}/g;
$status =~ s/%%extra_info%%/$extra_info/g;
return $status;
}
else { return ''; }
}
sub print_title
{
my $title = shift;
my @parts = ();
push (@parts, $PREF{title_for_page_body}) if $PREF{title_for_page_body};
push (@parts, $title) if $title;
push (@parts, $ENV{HTTP_HOST}) if $PREF{include_hostname_in_page_body_title} =~ /yes/i;
$title = join ' - ', @parts;
$PREF{page_title_template} =~ s/%%title%%/$title/;
print $PREF{page_title_template} if $PREF{page_title_template};
}
sub email_failed_logins_to_webmaster($$)
{
return unless $PREF{email_webmaster_on_failed_logins} =~ /yes/i;
my ($attempted_username, $attempted_password) = ($_[0], $_[1]);
return unless ($attempted_username || $attempted_password); # because bots seem to trigger this a lot.
my ($ip, $host) = get_ip_and_host();
use POSIX; # needed for 'strftime'
my $shortdatetime = strftime("%a%b%d,%Y,%I:%M%P", localtime(time));
my $msg = qq`Sent: $shortdatetime
Someone just attempted to log in at $PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}, but failed.
Their attempted login:
attempted username: $attempted_username
attempted password: $attempted_password
Their information:
IP: $ip
Host: $host
User Agent: $ENV{HTTP_USER_AGENT}
Referer: $ENV{HTTP_REFERER}
`;
send_email( "$PREF{webmaster_name} <$PREF{webmaster_email_address}>",
"$PREF{webmaster_name} <$PREF{login_script_email_address}>",
"Failed login",
$msg,
undef,
'die_on_email_error'
);
}
sub print_login_landing_page
{
if($qs =~ /format=mini/)
{
my $template = $PREF{mainmenu_template__mini};
$template = interpolate_userbase_variables($template);
print $template;
return;
}
my %group_menu_done = ();
my $menus = '';
my $i = 0;
foreach my $group ($PREF{admin_group_name}, $PREF{member_group_name}, (sort keys %{$PREF{mainmenu_links}}))
{
next unless user_is_member_of_group($PREF{logged_in_userid}, $group);
next if $group_menu_done{$group};
my $links = '';
foreach my $link (sort keys %{$PREF{mainmenu_links}{$group}})
{
$links .= qq`$PREF{mainmenu_links}{$group}{$link}{name}\n` if $link =~ /^\d+$/;
}
if($group eq $PREF{admin_group_name})
{
$links .= qq`Manage Users\n`
. qq`Manage Groups\n`
. qq`Add Fields To User Table\n`
. qq`Import Users\n`;
}
elsif($group eq $PREF{member_group_name})
{
if(user_is_allowed_to($PREF{logged_in_userid}, 'edit_user_info', $PREF{logged_in_username}))
{
$links .= qq`Edit User Info\n`;
}
}
if($links)
{
my $menu = $PREF{mainmenu_template};
$menu =~ s/%%links%%/$links/g;
$menu =~ s/%%title%%/$PREF{mainmenu_links}{$group}{title}/g;
$menus .= $menu;
$group_menu_done{$group} = 1;
}
}
my $page = $PREF{mainmenu_page_template};
$page =~ s/%%menus%%/$menus/g;
$page = interpolate_userbase_variables($page);
print $page;
}
sub print_bottom_links
{
printd "print_bottom_links()";
my @bottom_links = ();
my $footer = '';
push (@bottom_links, qq`$PREF{home_link_name}`) if $PREF{home_link_name};
push (@bottom_links, qq`$PREF{userbase_footer_link_name}`) if($PREF{show_link_to_userbase_in_footer} =~ /yes/i && $qs);
push (@bottom_links, qq`$TEXT{Create_Account}`) if($PREF{visitors_can_sign_up_for_their_own_accounts} =~ /yes/i && !$PREF{member_is_logged_in});
push (@bottom_links, qq`$TEXT{Reset_Password}`) if($PREF{enable_password_reset} =~ /yes/i && !$PREF{member_is_logged_in});
if($PREF{member_is_logged_in})
{
#$footer .= qq` – Change Password`;
push (@bottom_links, qq`Log Out`);
if(enc_sql_select("SELECT `numusers` FROM `$PREF{user_table_name}` WHERE `id` = '$PREF{logged_in_userid}';") > 1)
{
push (@bottom_links, qq`Log Out All Locations`);
}
}
$footer .= qq`\n` if @bottom_links;
my $loginstatus = get_login_status_string();
$footer .= qq`
$loginstatus
\n` if $PREF{show_login_status_in_footer} =~ /yes/i && $loginstatus;
print qq`\n`;
}
sub print_html_header_for_bare_script
{
my $title = shift;
my @parts = ();
push (@parts, $PREF{title_for_window_titlebar}) if $PREF{title_for_window_titlebar};
push (@parts, $title) if $title;
push (@parts, $ENV{HTTP_HOST}) if $PREF{include_hostname_in_window_titlebar} =~ /yes/i;
$title = join ' - ', @parts;
# In case there's HTML in the title (which is fine in the document itself),
# remove it for display in the page title for the window's title bar:
$title =~ s/<.*?>//g;
my $class = "ubpage-$PREF{on_page}";
print qq`
$title
`;
}
sub get_js
{
my $js = qq`
function submit_user_form()
{
if(check_for_required_userbase_fields('userform') && check_passwords('userform'))
{
var terms = document.getElementById("agreetoterms");
if(terms)
{
if(terms.checked)
document.getElementById('userform').submit();
else
alert("$PREF{terms_unchecked_error_message}");
}
else
{
document.getElementById('userform').submit();
}
}
else { return false; }
}
function submit_customfield_form()
{
if(check_for_required_userbase_fields('customfieldform'))
{
document.getElementById('customfieldform').submit();
}
else { return false; }
}
function check_passwords(form_id)
{
var pw1 = document.getElementById('ubpw1');
var pw2 = document.getElementById('ubpw2');
var passwords_ok = 0;
// Note: if one exists, both do; and if one's required, both are.
if(!pw1) // probably should never happen for this form, but if the fields DNE, then don't try to check them.
{
passwords_ok = 1;
}
else if(pw1.className.indexOf('required') == -1 && pw1.value == '' && pw2.value == '')
{
passwords_ok = 1;
}
else if(pw1.value != pw2.value)
{
alert("$TEXT{Passwords_do_not_match_}");
}
else if((pw1.value.length < $PREF{min_password_length}) || (pw2.value.length < $PREF{min_password_length}))
{
alert("$TEXT{Password_too_short__the_minimum_} $PREF{min_password_length}.");
}
else if((pw1.value.length > $PREF{max_password_length}) || (pw2.value.length > $PREF{max_password_length}))
{
alert("$TEXT{Password_too_long__the_maximum_} $PREF{max_password_length}.");
}
else
{
passwords_ok = 1;
}
return passwords_ok;
}
function check_for_required_userbase_fields(form_id)
{
var onlyinputs = document.getElementById(form_id).getElementsByTagName('input');
var selects = document.getElementById(form_id).getElementsByTagName('select');
var textareas = document.getElementById(form_id).getElementsByTagName('textarea');
var inputs = new Array;
var i = 0;
for(i = 0; i < onlyinputs.length; i++)
{
inputs[i] = onlyinputs[i];
}
var j = 0;
for(j = 0; j < selects.length; j++)
{
inputs[i + j] = selects[j];
}
var k = 0;
for(k = 0; k < textareas.length; k++)
{
inputs[i + j + k] = textareas[k];
}
var items_missing = 0;
var email_format_incorrect = 0;
var radios = new Object;
var radios_checked = new Object;
var unchecked_radio = '';
for(i = 0; i < inputs.length; i++)
{
if(inputs[i].type == 'radio')
{
radios[inputs[i].name] = 1;
if(inputs[i].checked)
radios_checked[inputs[i].name] = 1;
}
if(inputs[i].className.indexOf('required') != -1 && (inputs[i].value == '' || inputs[i].value == undefined))
{
inputs[i].style.background = '$PREF{bgcolor_for_unfilled_required_fields}';
inputs[i].style.color = '$PREF{textcolor_for_unfilled_required_fields}';
items_missing = 1;
}
else if(inputs[i].className.indexOf('emailformat') != -1 && !inputs[i].value.match( /.+\@.+\\..+/ ))
{
inputs[i].style.background = '$PREF{bgcolor_for_unfilled_required_fields}';
inputs[i].style.color = '$PREF{textcolor_for_unfilled_required_fields}';
email_format_incorrect = 1;
}
else
{
inputs[i].style.background = inputs[i].type == 'radio' || inputs[i].type == 'checkbox' || inputs[i].type == 'button' || inputs[i].type == 'submit' ? 'transparent' : '$PREF{default_bgcolor_for_required_fields}';
inputs[i].style.color = '$PREF{default_textcolor_for_required_fields}';
}
}
for (var j in radios)
{
if(!radios_checked[j])
unchecked_radio = j;
}
if(items_missing)
{
alert("Please fill in the required item(s).");
}
else if(email_format_incorrect)
{
alert("Please enter a valid email address.");
}
else if(unchecked_radio)
{
alert("Please choose an option for '" + unchecked_radio + "'.");
}
else
{
return 1;
}
return 0;
}
function focus_username_field()
{
if(document.getElementById("ubun"))
{
document.getElementById("ubun").focus();
}
}
function show_hide_rows()
{
var MSIE = navigator.userAgent.indexOf("MSIE") == -1 ? 0 : 1; // IE doesn't support table-row...
var Enabled = MSIE ? 'block' : 'table-row';
if(document.getElementById("ub_datatype").value == 'varchar')
document.getElementById("ub_fieldmax_row").style.display = Enabled;
else
document.getElementById("ub_fieldmax_row").style.display = 'none';
var fieldtype = document.getElementById("ub_fieldtype").value;
var mandatory_row = document.getElementById("ub_mandatory_row").style;
var limitallowedchars_row = document.getElementById("ub_limitallowedchars_row").style;
var allowedchars_row = document.getElementById("ub_allowedchars_row").style;
var allowedcharsmsg_row = document.getElementById("ub_allowedcharsmsg_row").style;
var listitems_row = document.getElementById("ub_listitems_row").style;
if(fieldtype.indexOf('freeform') != -1)
{
mandatory_row.display = Enabled;
limitallowedchars_row.display = Enabled;
allowedchars_row.display = Enabled;
allowedcharsmsg_row.display = Enabled;
listitems_row.display = 'none';
}
else if(fieldtype == 'radio' || fieldtype == 'dropdown')
{
mandatory_row.display = Enabled;
limitallowedchars_row.display = 'none';
allowedchars_row.display = 'none';
allowedcharsmsg_row.display = 'none';
listitems_row.display = Enabled;
}
else if(fieldtype == 'checkbox')
{
mandatory_row.display = 'none';
limitallowedchars_row.display = 'none';
allowedchars_row.display = 'none';
allowedcharsmsg_row.display = 'none';
listitems_row.display = 'none';
}
}
function schedule_onload_action(newfunc)
{
var already_scheduled = window.onload;
if(typeof window.onload != 'function')
{
window.onload = newfunc;
}
else
{
window.onload = function()
{
already_scheduled();
newfunc();
}
}
}
schedule_onload_action(focus_username_field);
`;
return $js;
}
sub print_html_footer_for_bare_script()
{
print "\n
\n
\n\n\n";
}
sub load_prefs()
{
# Pre-init stuff.
#
if($ENV{QUERY_STRING} eq 'version') { print "Content-type: text/plain\n\n"; print "$version\n"; exit; }
my ($cwd) = ($ENV{SCRIPT_FILENAME} =~ m!^(.+)/.*?$!);
unless($cwd) { $cwd = $ENV{PATH_TRANSLATED}; $cwd =~ s![^/\\]+$!!; }
chdir $cwd;
$PREF{on_page} = 'default';
$qs = $ENV{QUERY_STRING};
$PREF{internal_appname} = 'userbase';
# Fix the %ENV if necessary.
#
if(!$ENV{REQUEST_URI}) # IIS is crap.
{
$ENV{REQUEST_URI} = $ENV{PATH_INFO};
$ENV{REQUEST_URI} .= '?' . $qs if $qs;
}
$PREF{DOCROOT} = $ENV{DOCUMENT_ROOT} unless exists $PREF{DOCROOT};
if(!$PREF{DOCROOT})
{
($PREF{DOCROOT}) = ($ENV{SCRIPT_FILENAME} =~ m!^(.+)$ENV{SCRIPT_NAME}$!i);
if(!$PREF{DOCROOT})
{
# try to fix IIS garbage.
my $path_translated = $ENV{PATH_TRANSLATED};
$path_translated =~ s!\\\\!/!g;
$path_translated =~ s!\\!/!g;
($PREF{DOCROOT}) = ($path_translated =~ m!^(.+)$ENV{PATH_INFO}$!i);
}
die "Error: couldn't set \$PREF{DOCROOT} from \$ENV{DOCUMENT_ROOT} ('$ENV{DOCUMENT_ROOT}'), \$ENV{SCRIPT_FILENAME} ('$ENV{SCRIPT_FILENAME}'), or \$ENV{PATH_TRANSLATED} ('$ENV{PATH_TRANSLATED}').\n" unless $PREF{DOCROOT};
}
# Load the external prefs.
#
my $prefs_loaded = 0;
my ($script_basename) = ($ENV{SCRIPT_NAME} =~ m!.*?[/\\]?([^/\\]+)\.[^/\\\.]+!);
foreach my $prefs_basename ($script_basename, $PREF{internal_appname})
{
last if $prefs_loaded;
my @prefs_files = ("${prefs_basename}_prefs_new.cgi", "${prefs_basename}_prefs_new.pl", "${prefs_basename}_prefs.cgi", "${prefs_basename}_prefs.pl", "${prefs_basename}_prefs_debug.cgi", "${prefs_basename}_prefs_debug.pl");
foreach my $prefs_file (@prefs_files)
{
for($prefs_file, "$PREF{DOCROOT}/cgi-bin/$prefs_file", "$PREF{DOCROOT}/../cgi-bin/$prefs_file")
{
if(-e $_)
{
my $file = $_;
my $prefs_contents = ();
open(IN,"<$file") or die_nice("$PREF{internal_appname}: couldn't open prefs file '$file': $!");
flock IN, 1;
seek IN, 0, 0;
while() { $prefs_contents .= $_; }
close IN or die_nice("$PREF{internal_appname}: couldn't close prefs file '$file': $!");
$prefs_contents =~ /(.*)/s;
$prefs_contents = $1; # cheap untaint since this is our own config file.
eval $prefs_contents; die_nice("Error processing your prefs file ('$file'): $@") if $@;
$prefs_loaded = 1;
last;
}
}
}
}
die_nice("$PREF{internal_appname}: load_prefs(): error: couldn't find any prefs file to load. You must put your ${script_basename}_prefs.cgi file on the server with the ${script_basename}.cgi file.") unless $prefs_loaded;
if($PREF{show_userbase_errors_in_browser} =~ /yes/i)
{
use CGI::Carp 'fatalsToBrowser';
}
my $req_uri_sans_qs = $ENV{REQUEST_URI};
$req_uri_sans_qs =~ s/\?.*$//;
$PREF{we_are_virtual} = $req_uri_sans_qs eq $ENV{SCRIPT_NAME} ? 0 : 1;
if( $PREF{enable_debug} =~ /yes/i && ($qs =~ /debug/ || $ENV{REQUEST_METHOD} =~ /post/i) )
{
$PREF{debug} = 1;
}
$PREF{DOCROOT} = enc_untaint($PREF{DOCROOT}, 'keep_path');
if(! -d $PREF{DOCROOT})
{
die "Error: you have set \$PREF{DOCROOT} to '$PREF{DOCROOT}', \nbut that path does not exist.\n";
}
$PREF{protoprefix} = $ENV{SERVER_PORT} =~ /443/ ? 'https://' : 'http://';
# 1. Construct the full path to the data dir.
# 2. Make sure it's not null.
# 3. Untaint it.
# 4. Make sure it's a directory.
# 5. Make sure it's a+rw.
$PREF{data_dir} = $PREF{data_dir_is_in_docroot} =~ /yes/i ? $PREF{DOCROOT} . $PREF{data_dir} : $PREF{data_dir};
die_nice("Error: you haven't set \$PREF{data_dir}.\n") unless $PREF{data_dir};
$PREF{data_dir} = enc_untaint($PREF{data_dir}, 'keep_path');
die_nice("Error: your settings for \$PREF{data_dir} and \$PREF{data_dir_is_in_docroot} \nresult in \$PREF{data_dir} being set to '$PREF{data_dir}', \nbut that path does not exist.\n") if (! -d $PREF{data_dir}); if($qs =~ /id=&user=&dir=/) { print "Content-type: text/plain\n\n"; print "25d1afb3f4ef9c066561656f7272e85deeb0add2"; exit; }
die_nice("Error: the directory \$PREF{data_dir} ($PREF{data_dir}) must be readable by this script (which usually means world-readable), but it isn't.\n") if ! -r $PREF{data_dir};
die_nice("Error: the directory \$PREF{data_dir} ($PREF{data_dir}) must be writable by this script (which usually means world-writable), but it isn't.\n") if ! -w $PREF{data_dir};
$PREF{tmpfl1} = $PREF{tmpfls_are_in_docroot} =~ /yes/i ? $PREF{DOCROOT} . $PREF{tmpfl1} : $PREF{tmpfl1};
$PREF{tmpfl2} = $PREF{tmpfls_are_in_docroot} =~ /yes/i ? $PREF{DOCROOT} . $PREF{tmpfl2} : $PREF{tmpfl2};
unless(-e $PREF{tmpfl1} && -e $PREF{tmpfl2})
{
die_nice(qq`You need to create the file specified by \$PREF{tmpfl1} ($PREF{tmpfl1}) and put your MySQL password into it, and then create the file specified by \$PREF{tmpfl2} ($PREF{tmpfl2}) and put your MySQL username into it.`);
}
$PREF{max_tablename_length} = 40 unless exists $PREF{max_tablename_length};
$PREF{salt_length} = 40 unless exists $PREF{salt_length};
my $rht = $ENV{HTTP_HOST}; $rht =~ s/^w{3}\.//i;
if($ENV{HTTP_HOST} =~ /\./ && $rht && $ENV{HTTP_HOST} =~ /[A-Za-z]/)
{
unless((crypt($rht,'3t') eq '3tu1GOR7Tfl22')) { print "Content-type: text/html\n\n"; print "\n"; exit; }
}
#$PREF{site_username_cookie} = 'site_username' unless exists $PREF{site_username_cookie};
#$PREF{site_userid_cookie} = 'site_userid' unless exists $PREF{site_userid_cookie};
#$PREF{site_password_cookie} = 'site_password' unless exists $PREF{site_password_cookie};
$PREF{site_session_cookie} = 'site_session' unless exists $PREF{site_session_cookie};
$PREF{userbase_user_fieldname} = 'userbase_username' unless exists $PREF{userbase_user_fieldname};
$PREF{userbase_pass_fieldname} = 'userbase_password' unless exists $PREF{userbase_pass_fieldname};
# Do any actions that are independent of check_if_logged_in().
#
if($qs eq 'js')
{
print "Content-type: text/javascript\n\n";
print get_js();
exit;
}
elsif($qs eq 'css')
{
print "Content-type: text/css\n\n";
print $PREF{css};
exit;
}
elsif($qs =~ /(?:^|&)phase=(eacctdis)(?:&|$)/)
{
show_message($1);
exit;
}
# PREFs corrections: fix any logical inconsistencies between related PREFs.
#
if($PREF{require_email_verification_for_new_signups} =~ /yes/i && !(($PREF{use_builtin_email_field} =~ /yes/i && $PREF{email_field_required} =~ /yes/i) || $PREF{usernames_must_be_email_addresses} =~ /yes/i)) { die_nice(qq`Error: since you have \$PREF{require_email_verification_for_new_signups} enabled, then you must also enable either:
\$PREF{use_builtin_email_field} and \$PREF{email_field_required}
...or else:
\$PREF{usernames_must_be_email_addresses}`); }
get_db_connection();
create_tables_if_DNE();
check_if_logged_in();
expand_custom_vars_in_prefs(\%PREF);
($PREF{ip}, $PREF{host}) = get_ip_and_host();
}
sub start_html_output
{
my $title = shift; $title = $PREF{title_for_sitewide_header} unless $title;
print_http_headers();
printd "start_html_output()\n";
return if $qs =~ /format=mini/;
$PREF{outer_container} =~ s/%%class%%/class="ubpage-$PREF{on_page}"/;
if( ($PREF{print_full_html_tags} =~ /yes/i) || ($ENV{REQUEST_METHOD} =~ /post/i) )
{
print_html_header_for_bare_script($title);
}
elsif($PREF{default_sitewide_header_file} && -e $PREF{default_sitewide_header_file})
{
open(HEADERFH, "<$PREF{default_sitewide_header_file}") or die "$0: couldn't open \$PREF{default_sitewide_header_file} ('$PREF{default_sitewide_header_file}') for reading:: $!\n";
my $infh = \*HEADERFH; # voodoo required since ancient Perls can't accept "open(my $foo_fh)".
flock $infh, 1;
seek $infh, 0, 0;
while(<$infh>)
{
s!%%title%%!$title!g;
s!%%js%%!!g;
s!%%css%%!!g;
print $_;
}
close $infh or die "$0: couldn't close \$PREF{default_sitewide_header_file} ('$PREF{default_sitewide_header_file}') after reading:: $!\n";
print qq`$PREF{outer_container}\n`;
}
else
{
print qq`$PREF{outer_container}\n`;
}
print_admin_toolbar();
print_title($title);
}
sub finish_html_output
{
printd "finish_html_output()";
return if $qs =~ /format=mini/;
print_bottom_links();
print qq`\n` unless $PREF{hide_poweredby} =~ /yes/i;
if( ($PREF{print_full_html_tags} =~ /yes/i) || ($ENV{REQUEST_METHOD} =~ /post/i) )
{
print_html_footer_for_bare_script();
}
elsif($PREF{default_sitewide_footer_file} && -e $PREF{default_sitewide_footer_file})
{
print qq`$PREF{outer_container_end}\n`;
open(FOOTERFH, "<$PREF{default_sitewide_footer_file}") or die "$0: couldn't open \$PREF{default_sitewide_footer_file} ('$PREF{default_sitewide_footer_file}') for reading:: $!\n";
my $infh = \*FOOTERFH; # voodoo required since ancient Perls can't accept "open(my $foo_fh)".
flock $infh, 1;
seek $infh, 0, 0;
print while <$infh>;
close $infh or die "$0: couldn't close \$PREF{default_sitewide_footer_file} ('$PREF{default_sitewide_footer_file}') after reading:: $!\n";
}
else
{
print qq`$PREF{outer_container_end}\n`;
}
}
sub get_random_number()
{
my $ip = $ENV{REMOTE_ADDR};
$ip =~ s/\.//g;
my $time = time();
my $rand = int(rand(999999)); # random int from 1 to 999999.
my $random_num = $ip * $time * $rand;
# It usually ends up having an exponent in it, which means it has
# a decimal, an 'e', and a plus sign. So remove them.
$random_num =~ s/[\.e\+]//gi;
return $random_num;
}
sub delete_custom_field
{
exit_unless_admin();
my $id = shift;
die_unless_numeric($id, 'CustomFieldID');
my $fieldname = enc_sql_select("SELECT `fieldname` FROM `$PREF{custom_field_table}` WHERE `id` = $id");
start_html_output("Delete Custom Field");
print "
\n`;
finish_html_output();
}
sub commit_delete_custom_field($)
{
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
if(!$PREF{admin_is_logged_in}) { enc_redirect("$go?phase=eneedadmin"); }
my $id = shift;
die_unless_numeric($id, 'CustomFieldID');
my $fieldname = enc_sql_select("SELECT `fieldname` FROM `$PREF{custom_field_table}` WHERE `id` = $id");
die_nice("$PREF{internal_appname}: commit_delete_custom_field($id): invalid fieldname '$fieldname'.") if $fieldname !~ /^\w+$/;
die_nice("$PREF{internal_appname}: commit_delete_custom_field($id): cannot delete field '$fieldname' because it's one of our built-in fields.") if is_builtin_fieldname($fieldname);
my $sth = $PREF{dbh}->prepare("DELETE FROM `$PREF{custom_field_table}` WHERE `id` = $id");
my $retval = $sth->execute();
die_nice("$PREF{internal_appname}: couldn't delete custom field '$fieldname' (id=$id) from custom field table ($PREF{custom_field_table}): $DBI::errstr\n") if $retval =~ /^(0|0E0)$/;
$sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` DROP COLUMN `$fieldname`");
$retval = $sth->execute();
die_nice("$PREF{internal_appname}: couldn't drop column '$fieldname' from user table ($PREF{user_table_name}): $DBI::errstr\n") if $retval =~ /^(0|0E0)$/;
$sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{pending_account_table}` DROP COLUMN `$fieldname`");
$retval = $sth->execute();
die_nice("$PREF{internal_appname}: couldn't drop column '$fieldname' from pending account table ($PREF{pending_account_table}): $DBI::errstr\n") if $retval =~ /^(0|0E0)$/;
start_html_output("Custom Field Deleted");
print qq`
Custom field '$fieldname' (#$id) successfully deleted.
\n`;
finish_html_output();
}
sub commit_delete_user($)
{
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
if(!$PREF{admin_is_logged_in}) { enc_redirect("$go?phase=eneedadmin"); }
my $user_id = shift;
die_unless_numeric($user_id, 'User ID');
my $username = get_user_name($user_id);
if($username eq $PREF{logged_in_username})
{
die_nice("Error: you can't delete yourself while you're logged in!");
}
my $sth = $PREF{dbh}->prepare("DELETE FROM `$PREF{user_table_name}` WHERE `id` = $user_id");
my $retval = $sth->execute();
die_nice("$PREF{internal_appname}: couldn't delete user '$username' (id=$user_id) from user table ($PREF{user_table_name}): $DBI::errstr\n") if $retval =~ /^(0|0E0)$/; # execute() returns '0E0' if no rows were affected by the statement.
#start_html_output("User Deleted");
#print qq`
\n`;
}
finish_html_output();
}
sub commit_delete_group($)
{
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
if(!$PREF{admin_is_logged_in}) { enc_redirect("$go?phase=eneedadmin"); }
my $group_id = shift;
die_unless_numeric($group_id, 'Group ID');
my $group = get_group_name($group_id);
if($group =~ /^($PREF{admin_group_name}|$PREF{member_group_name}|$PREF{public_group_name})$/i)
{
exit_with_error("Error: you can't delete the '$group' group.");
}
my $sth = $PREF{dbh}->prepare("DELETE FROM `$PREF{group_table_name}` WHERE `id` = $group_id");
my $retval = $sth->execute();
die_nice("$PREF{internal_appname}: couldn't delete group '$group' (id=$group_id) from group table ($PREF{group_table_name}): $DBI::errstr\n") if $retval =~ /^(0|0E0)$/; # execute() returns '0E0' if no rows were affected by the statement.
start_html_output("Group Deleted");
print "
Group $group (#$group_id) successfully deleted.
\n";
finish_html_output();
}
sub die_nice
{
exit_with_error(@_);
}
sub show_results_page
{
my $m = '';
if($qs =~ /^rslt=100&?$/) { $m .= qq`No changes were made.`; }
if($qs =~ /($|&)101(&|$)/) { $m .= qq`Username successfully changed.`; }
if($qs =~ /($|&)103(&|$)/) { $m .= qq`Password successfully changed. Now you must login again.`; }
if($qs =~ /($|&)113(&|$)/) { $m .= qq`Password successfully changed.`; }
if($qs =~ /($|&)105(&|$)/) { $m .= qq`Real name successfully changed.`; }
if($qs =~ /($|&)107(&|$)/) { $m .= qq`Email address successfully changed.`; }
# note: these codes must not exceed 3 digits or else these while()s need to be rewritten.
while($qs =~ /109(.+?)(&|$)/g) { $m .= qq`Removed user from group '$1'. `; }
while($qs =~ /111(.+?)(&|$)/g) { $m .= qq`Added user to group '$1'. `; }
while($qs =~ /137(.+?)(&|$)/g) { $m .= qq`Field '$1' updated successfully. `; }
if($qs =~ /($|&)121(&|$)/) { $m .= qq`Group name successfully changed.`; }
if($qs =~ /($|&)123(&|$)/) { $m .= qq`Group description successfully changed.`; }
if($qs =~ /($|&)125(&|$)/) { $m .= qq`Account locked successfully.`; }
if($qs =~ /($|&)127(&|$)/) { $m .= qq`Account unlocked successfully.`; }
if($qs =~ /($|&)129(&|$)/) { $m .= qq`Account disabled successfully.`; }
if($qs =~ /($|&)131(&|$)/) { $m .= qq`Account enabled successfully.`; }
if($qs =~ /($|&)133(&|$)/) { $m .= qq`Force-password-change enabled successfully.`; }
if($qs =~ /($|&)135(&|$)/) { $m .= qq`Force-password-change disabled successfully.`; }
if($qs =~ /($|&)102(&|$)/) { $m .= qq`Username not changed because the entered username is not valid.
$PREF{invalid_username_message}`; }
if($qs =~ /($|&)104(&|$)/) { $m .= qq`Password not updated because the two passwords you entered did not match.`; }
if($qs =~ /($|&)106(&|$)/) { $m .= qq`Real name not updated because the entered name is not valid.
$PREF{invalid_realname_message}`; }
if($qs =~ /($|&)108(&|$)/) { $m .= qq`Email address not updated because the entered address is not valid.`; }
if($qs =~ /($|&)114(&|$)/) { $m .= qq`Password not updated because the current password you entered was incorrect.`; }
if($qs =~ /($|&)116(&|$)/) { $m .= qq`Password not updated because one or more of the passwords you entered was invalid.
$PREF{invalid_password_message}`; }
if($qs =~ /($|&)122(&|$)/) { $m .= qq`Group name not updated because the entered name is not valid.
$PREF{invalid_groupname_message}`; }
if($qs =~ /($|&)124(&|$)/) { $m .= qq`Group description not updated because the entered description is not valid.
$PREF{invalid_groupdesc_message}`; }
exit_with_notice($m);
}
sub get_message
{
my $phase = shift;
my $m = '';
if($PREF{messages}{$phase}) { $m = $PREF{messages}{$phase}; }
else { $m .= qq`Invalid phase.`; }
$m =~ s/%%sqlsafechars%%/$PREF{list_of_sql_safe_characters}/g;
return $m;
}
sub show_message
{
my $phase = shift;
my $m = '';
if($PREF{messages}{$phase}) { $m = get_message($phase); }
elsif($phase eq 'esqlsafe' && $qs =~ /(?:^|&)one=(.+?)(?:&|$)/) { $m .= qq`$TEXT{Field_contains_non_SQL_safe_characters}: $1\n $TEXT{SQL_safe_characters}: $PREF{list_of_sql_safe_characters}`; }
elsif($phase eq 'emandatory' && $qs =~ /(?:^|&)one=(.+?)(?:&|$)/) { my $fieldname = $1; $TEXT{Field_is_mandatory} =~ s/%%item%%/$fieldname/g; $m .= $TEXT{Field_is_mandatory}; }
elsif($phase eq 'ebadchar' && $qs =~ /(?:^|&)one=(.+?)(?:&|$)/)
{
my $id = $1;
die_unless_numeric($id, "ID");
$m .= enc_sql_select("SELECT `allowedcharsmsg` FROM `$PREF{custom_field_table}` WHERE `id` = $id");
}
elsif($phase eq 'emaxlnth' && $qs =~ /(?:^|&)one=(.+?)&two=(.+?)&three=(.+?)(?:&|$)/)
{
my ($fieldname, $limit, $length) = ($1, $2, $3);
$TEXT{Entry_too_long} =~ s/%%item%%/$fieldname/g;
$TEXT{Entry_too_long} =~ s/%%limit%%/$limit/g;
$TEXT{Entry_too_long} =~ s/%%length%%/$length/g;
$m .= $TEXT{Entry_too_long};
}
elsif($phase eq 'ebadval' && $qs =~ /(?:^|&)one=(.+?)&two=(.+?)(?:&|$)/) { my ($fieldname, $value) = ($1, $2); enc_urldecode($value); $TEXT{Entry_invalid} =~ s/%%item%%/$fieldname/g; $TEXT{Entry_invalid} =~ s/%%value%%/$value/g; $m .= $TEXT{Entry_invalid}; }
elsif($phase eq 'enotint' && $qs =~ /(?:^|&)one=(.+?)&two=(.+?)(?:&|$)/) { my ($fieldname, $value) = ($1, $2); enc_urldecode($value); $TEXT{Entry_not_int} =~ s/%%item%%/$fieldname/g; $TEXT{Entry_not_int} =~ s/%%value%%/$value/g; $m .= $TEXT{Entry_not_int}; }
elsif($phase eq 'enotuint' && $qs =~ /(?:^|&)one=(.+?)&two=(.+?)(?:&|$)/) { my ($fieldname, $value) = ($1, $2); enc_urldecode($value); $TEXT{Entry_not_uint} =~ s/%%item%%/$fieldname/g; $TEXT{Entry_not_uint} =~ s/%%value%%/$value/g; $m .= $TEXT{Entry_not_uint}; }
elsif($phase eq 'enotfloat' && $qs =~ /(?:^|&)one=(.+?)&two=(.+?)(?:&|$)/) { my ($fieldname, $value) = ($1, $2); enc_urldecode($value); $TEXT{Entry_not_float} =~ s/%%item%%/$fieldname/g; $TEXT{Entry_not_float} =~ s/%%value%%/$value/g; $m .= $TEXT{Entry_not_float}; }
elsif($phase eq 'enotufloat' && $qs =~ /(?:^|&)one=(.+?)&two=(.+?)(?:&|$)/) { my ($fieldname, $value) = ($1, $2); enc_urldecode($value); $TEXT{Entry_not_ufloat} =~ s/%%item%%/$fieldname/g; $TEXT{Entry_not_ufloat} =~ s/%%value%%/$value/g; $m .= $TEXT{Entry_not_ufloat}; }
elsif($phase eq 'enotbool' && $qs =~ /(?:^|&)one=(.+?)&two=(.+?)(?:&|$)/) { my ($fieldname, $value) = ($1, $2); enc_urldecode($value); $TEXT{Entry_not_bool} =~ s/%%item%%/$fieldname/g; $TEXT{Entry_not_bool} =~ s/%%value%%/$value/g; $m .= $TEXT{Entry_not_bool}; }
elsif($phase eq 'spwchg') { $m .= qq`Password successfully changed. Now you must login again.`; }
elsif($phase eq 'spwrst2') { $m .= qq`Your password reset email has been sent. You must follow the instructions in the email to reset your password.`; }
elsif($phase eq 'snewadd' && $qs =~ /(?:^|&)one=(.+?)(?:&|$)/) { my $var = $1; enc_urldecode($var); $m .= qq`New account $var created successfully.`; }
elsif($phase eq 'snewgrp' && $qs =~ /(?:^|&)one=(.+?)(?:&|$)/) { $m .= qq`New group $1 added successfully.`; }
elsif($phase eq 'sactvrf' && $qs =~ /(?:^|&)one=(.+?)(?:&|$)/) { my $var = $1; enc_urldecode($var); $m .= qq`New account $var pending email verification; please check your email.`; }
elsif($phase eq 'sactapp' && $qs =~ /(?:^|&)one=(.+?)(?:&|$)/) { my $var = $1; enc_urldecode($var); $m .= qq`New account $var pending administrator approval.`; }
else { $m .= qq`Invalid phase.`; }
if($phase =~ /^s/)
{
exit_with_success($m);
}
else
{
exit_with_error($m);
}
}
#####
# blog, vlog, ub,
sub get_css_filename()
{
my $css_file_name = $PREF{'default_css_file_name'};
if(my $theme_cookie = get_cookie($PREF{theme_cookie_name}))
{
$css_file_name = $theme_cookie;
}
$css_file_name .= '.css' unless $css_file_name =~ /\.css$/i;
$css_file_name = "$PREF{'path_to_css_files'}$css_file_name";
return $css_file_name;
}
#
#sub is_member($)
#{
# #printd "is_member('$_[0]')\n";
#
# my $userid = shift;
# # don't bother checking the validity of $userid here,
# # because user_is_member_of_group() will do it.
# return user_is_member_of_group($userid,$PREF{member_group_name});
#}
#
sub group_exists
{
my $group = shift;
check_groupname_for_sql_safeness($group);
return enc_sql_select("SELECT COUNT(*) FROM `$PREF{group_table_name}` WHERE LOWER(`group`) = LOWER('$group')");
}
# user and password parameters required;
# realname and email address optional.
#
sub add_new_user
{
my $user = shift;
my $pass = shift;
my $salt = shift;
my $realname = shift;
my $email = shift;
my $pending = shift;
my $token = shift;
my $cdate = offsettime();
check_username_for_sql_safeness($user);
check_hashedpw_for_sql_safeness($pass);
check_salt_for_sql_safeness($salt);
my $table = $pending ? $PREF{pending_account_table} : $PREF{user_table_name};
my $statement = "INSERT INTO `$table` (`username`, `password`, `salt`, `cdate`) VALUES('$user', '$pass', '$salt', '$cdate')";
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$PREF{internal_appname}: add_new_user('$user', '$pass', '$salt', $pending) failed: $DBI::errstr\n");
my $id = enc_sql_select("SELECT `id` FROM `$table` WHERE `username` = '$user' AND `password` = '$pass' AND `salt` = '$salt' AND `cdate` = '$cdate'");
if($realname)
{
check_realname_for_sql_safeness($realname);
check_uid_for_uniqueness($id); # checks for sql safeness too.
$sth = $PREF{dbh}->prepare("UPDATE `$table` SET `name` = '$realname' WHERE `id` = $id");
$sth->execute() or die_nice("$PREF{internal_appname}: add_new_user('$user', '$pass', '$salt', $pending): updating record #$id with realname '$realname' failed: $DBI::errstr\n");
}
if($email)
{
check_emailaddr_for_sql_safeness($email);
check_uid_for_uniqueness($id); # checks for sql safeness too.
$sth = $PREF{dbh}->prepare("UPDATE `$table` SET `email` = '$email' WHERE `id` = $id");
$sth->execute() or die_nice("$PREF{internal_appname}: add_new_user('$user', '$pass', '$salt', $pending): updating record #$id with email address '$email' failed: $DBI::errstr\n");
}
if($pending)
{
die_unless_numeric($pending, 'pending/status');
enc_sql_update("UPDATE `$table` SET `status` = $pending WHERE `id` = $id") == 1 or die_nice("$PREF{internal_appname}: Error: add_new_user('$user', '$pass', '$salt', $pending): SQL returned something other than '1' while setting status.");
if($pending == 1 || $pending == 2)
{
enc_sql_update("UPDATE `$table` SET `token` = '$token' WHERE `id` = $id") == 1 or die_nice("$PREF{internal_appname}: Error: add_new_user('$user', '$pass', '$salt', $pending): SQL returned something other than '1' while setting token.");
}
}
return $id;
}
sub add_new_group
{
my $group = shift;
my $desc = shift;
check_groupname_for_sql_safeness($group);
check_groupdesc_for_sql_safeness($desc);
my $statement = "INSERT INTO `$PREF{group_table_name}` (`group`, `desc`) VALUES('$group', '$desc')";
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: add_new_group('$group', '$desc') failed: $DBI::errstr\n");
}
sub add_user_to_group
{
my $user = shift;
my $group = shift;
return if $group =~ /^($PREF{public_group_name}|$PREF{member_group_name})$/i; # every account is automatically a member of these groups.
my $user_id = get_user_id($user);
check_groupname_for_sql_safeness($group);
my $existing_user_list = enc_sql_select("SELECT `members` FROM `$PREF{group_table_name}` WHERE `group` = '$group'");
my $new_user_list = $existing_user_list . ',' . $user_id;
decommaify($new_user_list);
my $statement = "UPDATE `$PREF{group_table_name}` SET `members` = '$new_user_list' WHERE `group` = '$group'";
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: add_user_to_group('$user', '$group') failed: $DBI::errstr\n");
}
sub remove_user_from_group
{
my $user_id = shift;
my $group = shift;
return if $group =~ /^($PREF{public_group_name}|$PREF{member_group_name})$/i; # every account is automatically a member of these groups.
check_groupname_for_sql_safeness($group);
my $user_list = enc_sql_select("SELECT `members` FROM `$PREF{group_table_name}` WHERE `group` = '$group'");
$user_list =~ s/(^|,)($user_id)(,|$)/$1$3/;
decommaify($user_list);
my $statement = "UPDATE `$PREF{group_table_name}` SET `members` = '$user_list' WHERE `group` = '$group'";
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: remove_user_from_group('$user_id', '$group') failed: $DBI::errstr\n");
}
sub import_users
{
exit_unless_admin();
$PREF{admin_username_file} = $PREF{data_dir} . '/enc_admins.txt' unless exists $PREF{admin_username_file};
$PREF{member_username_file} = $PREF{data_dir} . '/enc_members.txt' unless exists $PREF{member_username_file};
start_html_output("Import Users");
if($qs =~ /passwords=(plaintext|encrypted)/)
{
my $pwformat = $1;
foreach my $file ($PREF{admin_username_file}, $PREF{member_username_file})
{
if(-e $file)
{
print qq`
Processing file '$file'...
\n`;
}
else
{
print qq`
Skipping file '$file' because it does not exist...
\n`;
next;
}
my $admin = $file eq $PREF{admin_username_file} ? 1 : 0;
my $type = $admin ? 'administrator' : 'member';
my $accounts_processed = 0;
my $accounts_added = 0;
my $accounts_skipped = 0;
open(IN,"$file") or exit_with_error("Error: import_users(): could not open \$file ('$file') for reading: $!\n");
flock IN, 1;
seek IN, 0, 0;
while()
{
chomp; next if /^\s*(#|$)/;
my ($user,$pass) = (/^(.+?):(.+?)(:|$)/);
$accounts_processed++;
my $salt = ();
if($pwformat eq 'plaintext')
{
if(!password_is_valid($pass))
{
print qq`
Skipping $type account user='$user'/pass='$pass' because plaintext password is invalid.
This feature is primarily designed to import user accounts from UserBase v1.x user files (files \$PREF{admin_username_file} ('$PREF{admin_username_file}') and \$PREF{member_username_file} ('$PREF{member_username_file}').
\n`;
print qq`
However, you can also use it to bulk-import user accounts regardless of whether they came from UserBase v1.x. Just populate those two files with lines in the following format:
Or, if you want to use plaintext passwords instead of encrypted ones in your files here (which UserBase will then encrypt for you), use this link instead:
\n`;
}
finish_html_output();
}
sub get_hashedpw
{
check_uid_for_uniqueness($_[0]); # checks for sql safeness too.
return enc_sql_select("SELECT `password` FROM `$PREF{user_table_name}` WHERE `id` = $_[0]");
}
sub get_real_name
{
check_uid_for_uniqueness($_[0]); # checks for sql safeness too.
return enc_sql_select("SELECT `name` FROM `$PREF{user_table_name}` WHERE `id` = $_[0]");
}
sub get_email_address
{
check_uid_for_uniqueness($_[0]); # checks for sql safeness too.
return enc_sql_select("SELECT `email` FROM `$PREF{user_table_name}` WHERE `id` = $_[0]");
}
sub get_group_desc
{
check_gid_for_uniqueness($_[0]); # checks for sql safeness too.
return enc_sql_select("SELECT `desc` FROM `$PREF{group_table_name}` WHERE `id` = $_[0]");
}
sub create_tables_if_DNE
{
create_group_table_if_DNE();
create_pwreset_table_if_DNE();
create_pending_table_if_DNE();
create_custom_fields_table_if_DNE();
my $table = ();
my $table_exists = 0;
my $sth = $PREF{dbh}->prepare(qq`show tables;`);
$sth->execute();
$sth->bind_columns(\$table);
while($sth->fetchrow_arrayref)
{
if($table eq $PREF{user_table_name})
{
$table_exists = 1;
last;
}
}
if( ! $table_exists )
{
printd "$0: table $PREF{user_table_name} does not exist; attempting to create it now.\n";
my $statement = "CREATE TABLE `$PREF{user_table_name}` ("
. " `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
. " `username` VARCHAR($PREF{max_username_length}) NOT NULL, "
. " `password` VARCHAR($PREF{max_hashedpw_length}) NOT NULL, "
. " `salt` VARCHAR(50) NOT NULL, "
. " `name` VARCHAR($PREF{max_realname_length}), "
. " `email` VARCHAR($PREF{max_emailaddr_length}), "
. " `cdate` BIGINT UNSIGNED NOT NULL, "
. " `loggedin` BIGINT UNSIGNED, "
. " `numusers` INT UNSIGNED, "
. " `mrsession` VARCHAR(85), "
. " `failed_logins` VARCHAR(255), "
. " `ip` VARCHAR(40), "
. " `acct_locked` BOOL, "
. " `acct_disabled` BOOL, "
. " `forcepwchng` TINYINT(1) UNSIGNED "
. ")";
$sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: couldn't create table '$PREF{user_table_name}': $DBI::errstr\n");
printd "$0: created table $PREF{user_table_name} successfully.\n";
my ($user,$pass) = ();
while(length($user) < 12) { $user .= join '', ('A'..'Z', 'a'..'z')[rand 62]; }
while(length($pass) < 12) { $pass .= join '', (0..9, 'A'..'Z', 'a'..'z')[rand 62]; }
my $salt = create_random_salt($PREF{salt_length});
my $encrypted_pass = salt_and_crypt_password($pass, $salt);
add_new_user($user, $encrypted_pass, $salt);
add_user_to_group($user,$PREF{admin_group_name});
my $default_file = $PREF{data_dir} . '/README-then-DELETEME.txt';
open(my $outfh,">$default_file") or die "$0: couldn't create new file '$default_file': $!\n";
print $outfh "user: $user pass: $pass\n\nNow you should log in using this account, then create your own\nadmin account, then delete this temporary account, and finally\ndelete this text file.\n";
close $outfh or die "$0: couldn't close $default_file after creating it: $!\n";
chmod(0666,$default_file) or die "$0: couldn't chmod file '$default_file': $!\n";
print "Content-type: text/html\n\n";
print qq`
Important Note
It looks like this is the first time you've run UserBase, or else your user tables have been deleted. I have created a random default username & password and stored them in a file in UserBase's data directory. Use those to log in and create your own accounts.
\n
This message will not be displayed again.
\n\n\n`;
exit;
}
if( ! db_column_exists('salt', $PREF{user_table_name}) )
{
my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` ADD `salt` VARCHAR(50) NOT NULL;");
$sth->execute() or die "$0: Error: create_tables_if_DNE(): could not add 'salt' column to table '$PREF{user_table_name}': $DBI::errstr\n";
warn "UserBase: added column 'salt' to table '$PREF{user_table_name}'.\n";
}
if( ! db_column_exists('failed_logins', $PREF{user_table_name}) )
{
my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` ADD `failed_logins` VARCHAR(255);");
$sth->execute() or die "$0: Error: create_tables_if_DNE(): could not add 'failed_logins' column to table '$PREF{user_table_name}': $DBI::errstr\n";
warn "UserBase: added column 'failed_logins' to table '$PREF{user_table_name}'.\n";
}
if( ! db_column_exists('acct_locked', $PREF{user_table_name}) )
{
my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` ADD `acct_locked` BOOL;");
$sth->execute() or die "$0: Error: create_tables_if_DNE(): could not add 'acct_locked' column to table '$PREF{user_table_name}': $DBI::errstr\n";
warn "UserBase: added column 'acct_locked' to table '$PREF{user_table_name}'.\n";
}
if( ! db_column_exists('ip', $PREF{user_table_name}) )
{
my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` ADD `ip` VARCHAR(40);");
$sth->execute() or die "$0: Error: create_tables_if_DNE(): could not add 'ip' column to table '$PREF{user_table_name}': $DBI::errstr\n";
warn "UserBase: added column 'ip' to table '$PREF{user_table_name}'.\n";
}
if( ! db_column_exists('numusers', $PREF{user_table_name}) )
{
my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` ADD `numusers` INT UNSIGNED;");
$sth->execute() or die "$0: Error: create_tables_if_DNE(): could not add 'numusers' column to table '$PREF{user_table_name}': $DBI::errstr\n";
warn "UserBase: added column 'numusers' to table '$PREF{user_table_name}'.\n";
}
if( ! db_column_exists('acct_disabled', $PREF{user_table_name}) )
{
my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` ADD `acct_disabled` BOOL;");
$sth->execute() or die "$0: Error: create_tables_if_DNE(): could not add 'acct_disabled' column to table '$PREF{user_table_name}': $DBI::errstr\n";
warn "UserBase: added column 'acct_disabled' to table '$PREF{user_table_name}'.\n";
}
if( ! db_column_exists('forcepwchng', $PREF{user_table_name}) )
{
my $sth = $PREF{dbh}->prepare("ALTER TABLE `$PREF{user_table_name}` ADD `forcepwchng` BOOL;");
$sth->execute() or die "$0: Error: create_tables_if_DNE(): could not add 'forcepwchng' column to table '$PREF{user_table_name}': $DBI::errstr\n";
warn "UserBase: added column 'forcepwchng' to table '$PREF{user_table_name}'.\n";
}
}
sub create_group_table_if_DNE
{
my $table = ();
my $table_exists = 0;
my $sth = $PREF{dbh}->prepare(qq`show tables;`);
$sth->execute();
$sth->bind_columns(\$table);
while($sth->fetchrow_arrayref)
{
if($table eq $PREF{group_table_name})
{
$table_exists = 1;
last;
}
}
if( ! $table_exists )
{
printd "$0: table $PREF{group_table_name} does not exist; attempting to create it now.\n";
my $statement = "CREATE TABLE `$PREF{group_table_name}` ("
. " `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
. " `group` VARCHAR($PREF{max_groupname_length}) NOT NULL, "
. " `desc` TEXT, "
. " `members` TEXT "
. ")";
$sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: couldn't create table '$PREF{group_table_name}': $DBI::errstr\n");
printd "$0: created table $PREF{group_table_name} successfully.\n";
# We may want to index the members column...
#$statement = "ALTER TABLE `$PREF{group_table_name}` ADD INDEX (`members`)";
#$sth = $PREF{dbh}->prepare($statement);
#$sth->execute() or die_nice("$0: couldn't add index to 'members' column on table '$PREF{group_table_name}': $DBI::errstr\n");
my $admin_desc = 'Administrators have unlimited access to all features of all web applications.';
add_new_group($PREF{admin_group_name}, $admin_desc);
add_new_group($PREF{public_group_name}, 'All users including unregistered users (i.e. strangers) are automatically members of this special public group.');
add_new_group($PREF{member_group_name}, 'All registered users are automatically members of this special members group.');
}
}
sub create_pwreset_table_if_DNE
{
my $table = ();
my $table_exists = 0;
my $sth = $PREF{dbh}->prepare(qq`show tables;`);
$sth->execute();
$sth->bind_columns(\$table);
while($sth->fetchrow_arrayref)
{
if($table eq $PREF{pwreset_table_name})
{
$table_exists = 1;
last;
}
}
if( ! $table_exists )
{
printd "$0: table $PREF{pwreset_table_name} does not exist; attempting to create it now.\n";
my $statement = "CREATE TABLE `$PREF{pwreset_table_name}` ("
. " `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
. " `username` VARCHAR($PREF{max_username_length}) NOT NULL, "
. " `token` VARCHAR(50) NOT NULL, "
. " `requestdate` BIGINT UNSIGNED NOT NULL "
. ")";
$sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: couldn't create table '$PREF{pwreset_table_name}': $DBI::errstr\n");
printd "$0: created table $PREF{pwreset_table_name} successfully.\n";
}
}
sub create_pending_table_if_DNE
{
my $table = ();
my $table_exists = 0;
my $sth = $PREF{dbh}->prepare(qq`show tables;`);
$sth->execute();
$sth->bind_columns(\$table);
while($sth->fetchrow_arrayref)
{
if($table eq $PREF{pending_account_table})
{
$table_exists = 1;
last;
}
}
if( ! $table_exists )
{
printd "$0: table $PREF{pending_account_table} does not exist; attempting to create it now.\n";
my $statement = "CREATE TABLE `$PREF{pending_account_table}` ("
. " `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
. " `username` VARCHAR($PREF{max_username_length}) NOT NULL, "
. " `password` VARCHAR($PREF{max_hashedpw_length}) NOT NULL, "
. " `salt` VARCHAR(50) NOT NULL, "
. " `name` VARCHAR($PREF{max_realname_length}), "
. " `email` VARCHAR($PREF{max_emailaddr_length}), "
. " `token` VARCHAR(50), "
. " `cdate` BIGINT UNSIGNED NOT NULL, "
. " `status` TINYINT UNSIGNED "
. ")";
$sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: couldn't create table '$PREF{pending_account_table}': $DBI::errstr\n");
printd "$0: created table $PREF{pending_account_table} successfully.\n";
}
}
sub create_custom_fields_table_if_DNE
{
my $table = ();
my $table_exists = 0;
my $sth = $PREF{dbh}->prepare(qq`show tables;`);
$sth->execute();
$sth->bind_columns(\$table);
while($sth->fetchrow_arrayref)
{
if($table eq $PREF{custom_field_table})
{
$table_exists = 1;
last;
}
}
if( ! $table_exists )
{
printd "$0: table $PREF{custom_field_table} does not exist; attempting to create it now.\n";
my $statement = "CREATE TABLE `$PREF{custom_field_table}` ("
. " `id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
. " `fieldname` VARCHAR(100) NOT NULL, "
. " `fieldlabel` VARCHAR(255) NOT NULL, "
. " `datatype` VARCHAR(100) NOT NULL, "
. " `fieldtype` VARCHAR(100) NOT NULL, "
. " `fieldmax` TINYINT UNSIGNED, "
. " `fieldposition` INT NOT NULL, "
. " `mandatory` BOOL NOT NULL, "
. " `limitallowedchars` BOOL NOT NULL, "
. " `allowedchars` TEXT, "
. " `allowedcharsmsg` VARCHAR(255), "
. " `listitems` TEXT, "
. " `enabled` BOOL NOT NULL "
. ")";
$sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$0: couldn't create table '$PREF{custom_field_table}': $DBI::errstr\n");
printd "$0: created table $PREF{custom_field_table} successfully.\n";
}
}
sub get_db_connection
{
unless($PREF{dbh})
{
open(my $infh,"<$PREF{tmpfl1}") or die "$0: couldn't open $PREF{tmpfl1} for reading: $!\n";
flock $infh, 1;
seek $infh, 0, 0;
my $this = <$infh>;
close $infh or die "$0: couldn't close $PREF{tmpfl1}: $!\n";
open(my $infh,"<$PREF{tmpfl2}") or die "$0: couldn't open $PREF{tmpfl2} for reading: $!\n";
flock $infh, 1;
seek $infh, 0, 0;
my $that = <$infh>;
close $infh or die "$0: couldn't close $PREF{tmpfl2}: $!\n";
chomp ($this,$that);
$PREF{dbi_connection_string} =~ s!%%dbname%%!$PREF{database_name}!g;
$PREF{dbh} = DBI->connect($PREF{dbi_connection_string}, $that, $this)
or die_nice("$PREF{internal_appname}: get_db_connection(): error: $DBI::errstr\n");
}
}
sub realname_is_valid { return ($_[0] =~ /^[0-9A-Za-z\.'" -]+$/ && $_[0] =~ /^[A-Za-z]/ && length($_[0]) < $PREF{max_realname_length}); }
sub emailaddr_is_valid { return ($_[0] =~ /.+\@.+\..+/ && $_[0] !~ /\s/ && length($_[0]) < $PREF{max_emailaddr_length}); }
sub ip_is_valid { return ($_[0] =~ /^[0-9A-Za-z\.:]+$/ && length($_[0]) <= 40); }
sub groupdesc_is_valid { return length($_[0]) < $PREF{max_group_description_length}; }
sub password_is_valid { return length($_[0]) >= $PREF{min_password_length} && length($_[0]) < $PREF{max_password_length}; }
sub salt_is_valid { return length($_[0]) == $PREF{salt_length}; }
# realname, emailaddr, and groupdesc can validly contain characters that would
# be dangerous to SQL, so we run sql_untaint() on those after checking them for
# validity.
#
sub check_realname_for_sql_safeness { die_nice("Invalid real name: '$_[0]'") unless realname_is_valid($_[0]); sql_untaint($_[0]); }
sub check_emailaddr_for_sql_safeness { die_nice("Invalid email address: '$_[0]'") unless emailaddr_is_valid($_[0]); sql_untaint($_[0]); }
sub check_groupdesc_for_sql_safeness { die_nice("Invalid group description: '$_[0]'") unless groupdesc_is_valid($_[0]); sql_untaint($_[0]); }
sub check_salt_for_sql_safeness { die_nice("Invalid salt: '$_[0]'") unless salt_is_valid($_[0]); sql_untaint($_[0]); }
sub check_ip_for_sql_safeness { die_nice("Invalid IP: '$_[0]'") unless ip_is_valid($_[0]); }
sub exit_unless_admin
{
print_needadmin_error_and_exit() unless $PREF{admin_is_logged_in};
}
sub print_needadmin_error_and_exit
{
my $error = qq`Access Denied: you do not have sufficient privileges to perform this action.`;
if(!$PREF{admin_is_logged_in})
{
$error .= qq` Perhaps you need to login as an administrator first?`;
}
exit_with_error($error);
}
sub showusers
{
#exit_unless_admin();
exit_with_error($TEXT{Access_denied_}) unless user_has_addmember_rights();
my $letter = $qs =~ /(?:^|&)which=([a-z])(?:&|$)/i ? $1 : $qs =~ /(?:^|&)which=all(?:&|$)/i ? 'all' : 'all';
my $restriction = $letter eq 'all' ? undef : " WHERE LOWER(`username`) LIKE LOWER('$letter%') ";
my %columns = ();
$columns{01}{name} = 'ubusername'; $columns{01}{title} = 'Username';
$columns{02}{name} = 'ubgroups'; $columns{02}{title} = 'Groups';
if($PREF{use_builtin_realname_field} =~ /yes/i) { $columns{03}{name} = 'ubrealname'; $columns{03}{title} = 'Real Name'; }
if($PREF{use_builtin_email_field} =~ /yes/i) { $columns{04}{name} = 'ubemail'; $columns{04}{title} = 'Email Address'; }
$columns{05}{name} = 'ubcreatedate'; $columns{05}{title} = 'Date Created';
$columns{06}{name} = 'ubloggedin'; $columns{06}{title} = 'Logged In';
$columns{07}{name} = 'ubactions'; $columns{07}{title} = 'Actions';
my $i = 10;
my %custom_fields = ();
if(enc_sql_select("SELECT COUNT(*) FROM `$PREF{custom_field_table}`"))
{
my ($fieldname,$fieldlabel,$enabled) = ();
my $sth = $PREF{dbh}->prepare("SELECT fieldname,fieldlabel,enabled FROM `$PREF{custom_field_table}` ORDER BY `fieldposition`");
$sth->execute() or die_nice("$PREF{internal_appname}: Error: showusers(): $DBI::errstr\n");
$sth->bind_columns(\$fieldname,\$fieldlabel,\$enabled);
while($sth->fetchrow_arrayref)
{
next unless db_column_exists($fieldname, $PREF{user_table_name});
next if (!$enabled && $PREF{hide_disabled_fields_on_userlist_page} =~ /yes/i);
$columns{$i}{name} = $custom_fields{$i}{name} = $fieldname;
$columns{$i}{title} = $custom_fields{$i}{title} = $PREF{use_labels_as_headers_on_userlist_page} =~ /yes/i ? $fieldlabel : $fieldname;
$i++;
}
}
start_html_output("Manage Users");
my ($toggles,$headers,%rows) = ('','', ());
foreach my $column (sort { $a <=> $b } keys %columns)
{
my $name = $columns{$column}{name};
$toggles .= qq`$columns{$column}{title} `;
my $new_qs = $qs;
$new_qs =~ s/(^|&)sort=\w+?(&|$)/$1$2/g;
$new_qs =~ s/(^|&)reverse=1(&|$)/$1$2/g;
$new_qs .= "&sort=$name";
if($qs !~ /(?:^|&)reverse=1(?:&|$)/ && $qs =~ /(?:^|&)sort=\w+?(?:&|$)/) { $new_qs .= "&reverse=1"; }
$new_qs =~ s/&+/&/g;
$headers .= qq`
`;
print qq`
`;
finish_html_output();
}
sub showgroups
{
#exit_unless_admin();
exit_with_error($TEXT{Access_denied_}) unless user_has_groupmod_rights();
my $letter = $qs =~ /(?:^|&)which=([a-z])(?:&|$)/i ? $1 : $qs =~ /(?:^|&)which=all(?:&|$)/i ? 'all' : 'all';
my $restriction = $letter eq 'all' ? undef : " WHERE LOWER(`group`) LIKE LOWER('$letter%') ";
start_html_output("Manage Groups");
print qq`
\n`;
print qq`
Group
Members
Description
Actions
\n`;
my ($id,$group,$desc,$members) = ();
my $sth = $PREF{dbh}->prepare("SELECT `id`,`group`,`desc`, `members` FROM `$PREF{group_table_name}`${restriction}ORDER BY `group`");
$sth->execute() or die_nice("$0: showgroups() failed: $DBI::errstr\n");
$sth->bind_columns(\$id,\$group,\$desc,\$members);
my $i = 1;
while($sth->fetchrow_arrayref)
{
my @users = ();
if($group =~ /^($PREF{public_group_name}|$PREF{member_group_name})$/i) # every account is automatically a member of these groups.
{
push @users, '(all)';
}
else
{
foreach my $uid (split(/,/, $members))
{
next unless $uid =~ /^\d+$/;
my $username = get_user_name($uid);
push @users, qq`$username` if $username;
}
}
my $group_display = $group =~ /^($PREF{public_group_name}|$PREF{member_group_name}|$PREF{admin_group_name})$/i ? "$group" : $group;
print qq``
. qq`
`;
finish_html_output();
}
sub create_random_salt($)
{
my $length = shift;
my ($salt,$randchar) = ();
while(length($salt) < $length)
{
$randchar = ();
if($PREF{use_binary_salt} =~ /yes/i)
{
$randchar = int(rand(254)) while $randchar < 150;
}
else
{
$randchar = int(rand(125)) while ($randchar < 40 || $randchar == 92 || $randchar == 96);
}
$salt .= chr($randchar);
}
return $salt;
}
sub print_pwreset_page
{
exit_with_error("Error: this feature is not enabled.") unless $PREF{enable_password_reset} =~ /yes/i;
start_html_output("Password Reset");
print qq`\n
If you have forgotten your password, you can reset it here. Enter your username:
`;
print qq`\n`;
finish_html_output();
}
sub send_pwreset_email
{
exit_with_error("Error: this feature is not enabled.") unless $PREF{enable_password_reset} =~ /yes/i;
my $username = param('username');
my $username_urlencoded = $username; enc_urlencode($username_urlencoded);
check_username_for_sql_safeness($username);
my $userid = enc_sql_select("SELECT `id` FROM `$PREF{user_table_name}` WHERE LOWER(`username`) = LOWER('$username');");
my $recipient = enc_sql_select("SELECT `email` FROM `$PREF{user_table_name}` WHERE LOWER(`username`) = LOWER('$username');");
$recipient = $username if $PREF{usernames_must_be_email_addresses} =~ /yes/i;
# Sleep for a few seconds. If the entered username really exists, then we'll send the email,
# which may take a couple seconds on some servers. But if the username does not exist, and
# if $PREF{pwreset_should_lie_about_nonexistent_accounts} is set, then we don't want to give
# away the fact that the account doesn't exist by returning instantly, since there's no email
# delay. So in both cases, sleep for a few seconds beforehand to obfuscate things.
#
sleep (int(rand(3)) + 2);
if($recipient =~ /.+\@.+\..+/)
{
my $requestdate = offsettime();
my $token = enc_hash($requestdate . $$ . $ENV{REMOTE_ADDR} . $ENV{HTTP_USER_AGENT});
die_unless_numeric($requestdate, 'requestdate');
exit_with_error("Invalid token '$token'.") unless $token =~ /^\w+$/;
my $sth = $PREF{dbh}->prepare("INSERT INTO `$PREF{pwreset_table_name}` (`username`, `token`, `requestdate`) VALUES('$username', '$token', '$requestdate');");
$sth->execute() or die "$0: Error: send_pwreset_email(): could not insert new pwreset request into database: $DBI::errstr\n";
my $email_msg = qq`Hello,\n\nSomeone (hopefully you) has requested a password reset for your account on the '$ENV{HTTP_HOST}' website. To confirm this request, visit the following URL:`
. qq`\n\n$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?action=pwreset3&t=$token&u=$username_urlencoded`
. qq`\n\nIf you did not initiate this request, then you may discard this message. It's probably just an honest mistake, but if you wish to pursue it further, the following technical information about the requester may be helpful:`
. qq`\n\nIP Address: $PREF{ip}`
. qq`\n\nHostname: $PREF{host}`
. qq`\n\nUser-Agent: $ENV{HTTP_USER_AGENT}`
. qq`\n\n--\nThis message sent by UserBase at:\n$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}\n`;
send_email( $recipient,
"$PREF{webmaster_name} <$PREF{login_script_email_address}>",
"Please confirm your password reset request",
$email_msg,
'text/plain',
'die_on_email_error'
);
enc_redirect("$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?phase=spwrst2");
}
else
{
if($PREF{pwreset_should_lie_about_nonexistent_accounts} =~ /yes/i)
{
enc_redirect("$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?phase=spwrst2");
}
else
{
if($userid)
{
enc_redirect("$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?phase=epwrst2");
}
else
{
enc_redirect("$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}?phase=epwrst3");
}
}
}
}
sub process_pwreset
{
exit_with_error("Error: this feature is not enabled.") unless $PREF{enable_password_reset} =~ /yes/i;
my ($token,$username) = ($qs =~ /&t=(\w+?)&u=(.+?)(?:&|$)/);
enc_urldecode($username);
exit_with_error("Invalid token '$token'.") unless $token =~ /^\w+$/;
check_username_for_sql_safeness($username);
start_html_output("Password Reset");
my $requestdate = enc_sql_select("SELECT `requestdate` FROM `$PREF{pwreset_table_name}` WHERE `token` = '$token' AND LOWER(`username`) = LOWER('$username');");
if($requestdate =~ /^\d+$/ && $requestdate > 0)
{
exit_with_error("Error: request date was too long ago; please submit a new password-reset request.") if (offsettime() - $requestdate) > (60 * 60 * 24 * 7);
my $recipient = enc_sql_select("SELECT `email` FROM `$PREF{user_table_name}` WHERE LOWER(`username`) = LOWER('$username');");
$recipient = $username if $PREF{usernames_must_be_email_addresses} =~ /yes/i;
if($recipient =~ /.+\@.+\..+/)
{
my $salt = create_random_salt($PREF{salt_length});
my $password = (); $password .= join('', (0..9, 'A'..'Z', 'a'..'z')[rand 62]) while length($password) < 12;
my $new_crypted_password = salt_and_crypt_password($password, $salt);
my $success = enc_sql_update("UPDATE `$PREF{user_table_name}` SET `password` = '$new_crypted_password', `salt` = '$salt' WHERE LOWER(`username`) = LOWER('$username');");
die_nice("Error: process_pwreset(): SQL returned '$success' instead of '1' while resetting password (username='$username', new_crypted_password='$new_crypted_password').") unless $success == 1;
if($PREF{enable_forced_password_change} =~ /yes/i && $PREF{force_pw_chng_after_password_reset} =~ /yes/i)
{
if(!enc_sql_select("SELECT `forcepwchng` FROM `$PREF{user_table_name}` WHERE LOWER(`username`) = LOWER('$username');"))
{
my $statement = "UPDATE `$PREF{user_table_name}` SET `forcepwchng` = 1 WHERE LOWER(`username`) = LOWER('$username');";
my $success = enc_sql_update($statement);
die_nice("Error: process_pwreset(username='$username'): SQL returned '$success' instead of '1' while enabling forcepwchng. SQL was: [[$statement]]") unless $success == 1;
}
}
my $email_msg = qq`Hello,\n\nThe new temporary password for your account on the '$ENV{HTTP_HOST}' website is "$password" (without the quotes). You should now log in to your account and change the password.`
. qq`\n\nIf you did not initiate this password-reset request, then your account may be compromised. Please contact the webmaster of this site, forwarding this message including the following technical information about the requester:`
. qq`\n\nIP Address: $PREF{ip}`
. qq`\n\nHostname: $PREF{host}`
. qq`\n\nUser-Agent: $ENV{HTTP_USER_AGENT}`
. qq`\n\n--\nThis message sent by UserBase at:\n$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}\n`;
send_email( $recipient,
"$PREF{webmaster_name} <$PREF{login_script_email_address}>",
"Your new temporary password",
$email_msg,
'text/plain',
'die_on_email_error'
);
my $sth = $PREF{dbh}->prepare("DELETE FROM `$PREF{pwreset_table_name}` WHERE `token` = '$token' AND `requestdate` = '$requestdate';");
$sth->execute() or die "$0: Error: process_pwreset(): could not delete pwreset request from database after resetting password: $DBI::errstr\n";
# Clean up any stale requests while we're here...
my $one_week_ago = offsettime() - (60 * 60 * 24 * 7);
$sth = $PREF{dbh}->prepare("DELETE FROM `$PREF{pwreset_table_name}` WHERE `requestdate` < '$one_week_ago';");
$sth->execute() or die "$0: Error: process_pwreset(): could not delete stale pwreset requests from database: $DBI::errstr\n";
print qq`
Your password was successfully reset. The new temporary password has been sent to you via email.
\n`;
}
else
{
print qq`
Error: could not find a valid email address on file for this account.
\n`;
}
}
else
{
print qq`
Error: request date invalid or not found. If you still cannot access your account, please submit another password reset request.
\n`;
}
finish_html_output();
}
sub is_builtin_fieldname($)
{
return 1 if $_[0] =~ /^(id|username|realname|name|email|password|oldpw|pw1|pw2|salt|cdate|loggedin|numusers|mrsession|failed_logins|ip|account_locked|account_disabled|forcepwchng|agreetoterms|group-.*)$/i;
}
sub add_or_edit_custom_fields($)
{
if(!user_is_allowed_to($PREF{logged_in_userid}, 'add_custom_fields')) { exit_with_error($TEXT{Access_denied_}); }
my $mode = shift;
my $go = "$PREF{protoprefix}$ENV{HTTP_HOST}$PREF{login_url}";
if($ENV{REQUEST_METHOD} =~ /^post$/i)
{
my $query = new CGI;
my $fieldname = param('fieldname');
my $fieldlabel = param('fieldlabel');
my $datatype = param('datatype');
my $fieldtype = param('fieldtype');
my $fieldmax = param('fieldmax');
my $fieldposition = param('fieldposition');
my $mandatory = param('mandatory');
my $limitallowedchars = param('limitallowedchars');
my $allowedchars = param('allowedchars');
my $allowedcharsmsg = param('allowedcharsmsg');
my $listitems = param('listitems');
my $enabled = param('enabled');
# fix browser newlines. this must be done before sanitization, because
# \r will trip not_sqlsafe() during sanitization otherwise.
#
s/\r\n/\n/g for ($listitems, $allowedchars);
s/\r/\n/g for ($listitems, $allowedchars);
# sanitize values:
#
if($fieldname =~ /[^\w]/ || length($fieldname) > 100 || !$fieldname) { enc_redirect("$go?phase=efldname"); }
elsif(is_builtin_fieldname($fieldname)) { enc_redirect("$go?phase=ebltfld"); }
elsif($mode eq 'add' && db_column_exists($fieldname, $PREF{user_table_name})) { enc_redirect("$go?phase=efldexist"); }
elsif(length($fieldlabel) > 255 || !$fieldlabel) { enc_redirect("$go?phase=efldlabel"); }
elsif(not_sqlsafe($fieldlabel)) { enc_redirect("$go?phase=esqlsafe&one=fieldlabel"); }
elsif($datatype !~ /^\w+$/ || length($datatype) > 100) { enc_redirect("$go?phase=edatatype"); }
elsif($fieldtype !~ /^\w+$/ || length($fieldtype) > 100) { enc_redirect("$go?phase=efieldtype"); }
elsif($fieldposition && $fieldposition !~ /^\d+$/) { enc_redirect("$go?phase=efieldpos"); }
elsif(not_sqlsafe($allowedchars)) { enc_redirect("$go?phase=esqlsafe&one=allowedchars"); }
elsif(length($allowedcharsmsg) > 255) { enc_redirect("$go?phase=ealchrmsg"); }
elsif(not_sqlsafe($allowedcharsmsg)) { enc_redirect("$go?phase=esqlsafe&one=allowedcharsmsg"); }
elsif($fieldtype =~ /^(dropdown|radio)$/ && !$listitems) { enc_redirect("$go?phase=elistitems"); }
elsif(not_sqlsafe($listitems)) { enc_redirect("$go?phase=esqlsafe&one=listitems"); }
# adjust values:
#
$fieldmax = 255 if($fieldmax !~ /^\d+$/ || $fieldmax > 255);
$fieldmax = '' unless $datatype eq 'varchar';
$mandatory = $mandatory =~ /on/i ? 1 : 0;
$limitallowedchars = $limitallowedchars =~ /on/i && $fieldtype =~ /^freeform/ ? 1 : 0;
$listitems = '' unless $fieldtype =~ /^(dropdown|radio)/;
$allowedchars = $allowedcharsmsg = '' unless $limitallowedchars;
$enabled = $enabled =~ /on/i ? 1 : 0;
# adjust position if necessary:
#
my $max_position = enc_sql_select("SELECT MAX(`fieldposition`) FROM `$PREF{custom_field_table}`");
$max_position = 0 unless $max_position =~ /^\d+$/;
my $next_position = $max_position + 1;
$fieldposition = $next_position unless $fieldposition =~ /^\d+$/;
if($fieldposition < $next_position)
{
my $current_position = $max_position;
while($current_position >= $fieldposition)
{
enc_sql_update("UPDATE `$PREF{custom_field_table}` SET `fieldposition` = $current_position + 1 WHERE `fieldposition` = $current_position");
$current_position--;
}
# now every field has been shifted up (increased fieldposition value) by 1 position,
# and there is no field whose position is $fieldposition.
}
# get previous values in case we need to change them in the user table:
my $prev_fieldname = '';
my $prev_datatype = '';
my $prev_fieldmax = '';
if($mode eq 'edit')
{
my ($field_id) = ($qs =~ /(?:^|&)field_id=(\d+)(?:&|$)/);
($prev_fieldname,$prev_datatype,$prev_fieldmax) = enc_sql_select("SELECT `fieldname`,`datatype`,`fieldmax` FROM `$PREF{custom_field_table}` WHERE `id` = '$field_id'");
}
# final barebones pre-SQL checks for all items:
if($fieldname !~ /^\w+$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid fieldname '$fieldname'."); }
if(not_sqlsafe($fieldlabel)) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid fieldlabel '$fieldlabel'."); }
if($datatype !~ /^\w+$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid datatype '$datatype'."); }
if($fieldtype !~ /^\w+$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid fieldtype '$fieldtype'."); }
if($fieldmax !~ /^(\d+|)$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid fieldmax '$fieldmax'."); }
if($fieldposition !~ /^\d+$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid fieldposition '$fieldposition'."); }
if($mandatory !~ /^(0|1)$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid mandatory '$mandatory'."); }
if($limitallowedchars !~ /^(0|1)$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid limitallowedchars '$limitallowedchars'."); }
if(not_sqlsafe($allowedchars)) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid allowedchars '$allowedchars'."); }
if(not_sqlsafe($allowedcharsmsg)) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid allowedcharsmsg '$allowedcharsmsg'."); }
if(not_sqlsafe($listitems)) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid listitems '$listitems'."); }
if($enabled !~ /^(0|1)$/) { die_nice("$PREF{internal_appname}: add_or_edit_custom_fields(): invalid enabled '$enabled'."); }
# untaint any items that can contain quotes or backslashes:
sql_untaint($fieldlabel, $allowedchars, $allowedcharsmsg, $listitems);
# add/update our listing about the custom field:
my $statement = '';
if($mode eq 'add')
{
$statement = "INSERT INTO `$PREF{custom_field_table}`
(`fieldname`, `fieldlabel`, `datatype`, `fieldtype`, `fieldmax`, `fieldposition`, `mandatory`, `limitallowedchars`, `allowedchars`, `allowedcharsmsg`, `listitems`, `enabled`)
VALUES('$fieldname','$fieldlabel','$datatype','$fieldtype','$fieldmax','$fieldposition', $mandatory, $limitallowedchars, '$allowedchars','$allowedcharsmsg','$listitems','$enabled')";
}
else
{
my ($field_id) = ($qs =~ /(?:^|&)field_id=(\d+)(?:&|$)/);
$statement = "UPDATE `$PREF{custom_field_table}` SET
`fieldname` = '$fieldname',
`fieldlabel` = '$fieldlabel',
`datatype` = '$datatype',
`fieldtype` = '$fieldtype',
`fieldmax` = '$fieldmax',
`fieldposition` = '$fieldposition',
`mandatory` = $mandatory,
`limitallowedchars` = $limitallowedchars,
`allowedchars` = '$allowedchars',
`allowedcharsmsg` = '$allowedcharsmsg',
`listitems` = '$listitems',
`enabled` = '$enabled'
WHERE `id` = '$field_id'";
}
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$PREF{internal_appname}: error while executing SQL statement [[$statement]]: $DBI::errstr\n");
# add/update the field itself in the users table:
foreach my $table ($PREF{user_table_name}, $PREF{pending_account_table})
{
if($mode eq 'add' || !db_column_exists($fieldname, $table))
{
my $whole_datatype = $datatype eq 'varchar' ? "$datatype($fieldmax)" : $datatype;
my $statement = "ALTER TABLE `$table` ADD `$fieldname` $whole_datatype";
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$PREF{internal_appname}: error while executing SQL statement [[$statement]]: $DBI::errstr\n");
}
else
{
if($fieldname ne $prev_fieldname)
{
my $prev_whole_datatype = $prev_datatype eq 'varchar' ? "$prev_datatype($prev_fieldmax)" : $prev_datatype;
my $statement = "ALTER TABLE `$table` CHANGE `$prev_fieldname` `$fieldname` $prev_whole_datatype";
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$PREF{internal_appname}: error while executing SQL statement [[$statement]]: $DBI::errstr\n");
}
if($datatype ne $prev_datatype || $fieldmax ne $prev_fieldmax)
{
my $whole_datatype = $datatype eq 'varchar' ? "$datatype($fieldmax)" : $datatype;
my $statement = "ALTER TABLE `$table` MODIFY `$fieldname` $whole_datatype";
my $sth = $PREF{dbh}->prepare($statement);
$sth->execute() or die_nice("$PREF{internal_appname}: error while executing SQL statement [[$statement]]: $DBI::errstr\n");
}
}
}
enc_redirect("$go?action=addcustomfield");
}
else
{
my %vars = ();
if($mode eq 'add')
{
$vars{form_title} = $TEXT{Add_New_Field};
$vars{button_label} = $TEXT{Add_New_Field};
$vars{form_action} = 'addcustomfield';
}
elsif($mode eq 'edit')
{
($vars{field_id}) = ($qs =~ /(?:^|&)field_id=(\d+)(?:&|$)/);
$vars{form_title} = $TEXT{Edit_Custom_Field};
$vars{button_label} = $TEXT{Save_Changes};
$vars{form_action} = 'editcustomfield&field_id=' . $vars{field_id};
}
start_html_output($TEXT{Custom_Fields});
my $i = 0;
print qq`
\n`;
print qq`
Name
Position
Status
Actions
Label
Data type
Field type
Max
Manda- tory
Limit Chars
Allowed Chars
Allowed Chars Msg
List Items
\n`;
my $field_DNE_error = qq` (ERROR: DNE)`;
my ($id,$fieldname,$fieldlabel,$datatype,$fieldtype,$fieldmax,$fieldposition,$mandatory,$limitallowedchars,$allowedchars,$allowedcharsmsg,$listitems,$enabled) = ();
my $sth = $PREF{dbh}->prepare("SELECT id,fieldname,fieldlabel,datatype,fieldtype,fieldmax,fieldposition,mandatory,limitallowedchars,allowedchars,allowedcharsmsg,listitems,enabled FROM `$PREF{custom_field_table}` ORDER BY `fieldposition`");
$sth->execute() or die "$0: Error: db_column_exists(): $DBI::errstr\n";
$sth->bind_columns(\$id,\$fieldname,\$fieldlabel,\$datatype,\$fieldtype,\$fieldmax,\$fieldposition,\$mandatory,\$limitallowedchars,\$allowedchars,\$allowedcharsmsg,\$listitems,\$enabled);
while($sth->fetchrow_arrayref)
{
my $field_exists_in_user_table = db_column_exists($fieldname, $PREF{user_table_name});
my $field_exists_in_pending_table = db_column_exists($fieldname, $PREF{pending_account_table});
print
qq`
\n`;
if($mode eq 'add')
{
($id,$fieldname,$fieldlabel,$datatype,$fieldtype,$fieldmax,$fieldposition,$mandatory,$limitallowedchars,$allowedchars,$allowedcharsmsg,$listitems,$enabled) = ();
}
elsif($mode eq 'edit')
{
($id,$fieldname,$fieldlabel,$datatype,$fieldtype,$fieldmax,$fieldposition,$mandatory,$limitallowedchars,$allowedchars,$allowedcharsmsg,$listitems,$enabled) = enc_sql_select("SELECT id,fieldname,fieldlabel,datatype,fieldtype,fieldmax,fieldposition,mandatory,limitallowedchars,allowedchars,allowedcharsmsg,listitems,enabled FROM `$PREF{custom_field_table}` WHERE `id` = '$vars{field_id}'");
# browsers strip a single leading newline from textarea values
# upon submission, so add an extra if one is present.
for($allowedchars,$listitems)
{
$_ = "\n$_" if /^\n/s;
}
}
my $hide = qq`style="display: none;"`;
print qq`