home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
rtsi.com
/
2014.01.www.rtsi.com.tar
/
www.rtsi.com
/
OS9
/
FAQ
/
discus_admin_1357211388
/
source
/
fcn-acct.pl
< prev
next >
Wrap
Text File
|
2009-11-06
|
15KB
|
444 lines
# FILE: fcn-acct.pl
# DESCRIPTION: Account (moderator and user) management functions
#-------------------------------------------------------------------------------
# DISCUS COPYRIGHT NOTICE
#
# Discus is copyright (c) 2002 by DiscusWare, LLC, all rights reserved.
# The use of Discus is governed by the Discus License Agreement which is
# available from the Discus WWW site at:
# http://www.discusware.com/discus/license
#
# Pursuant to the Discus License Agreement, this copyright notice may not be
# removed or altered in any way.
#-------------------------------------------------------------------------------
use strict;
use vars qw($GLOBAL_OPTIONS $DCONF $PARAMS);
###
### update_registered_users_file
###
### Updates the total number of registered users on the board
###
sub update_registered_users_file {
my ($newcount) = @_;
if ($newcount =~ /^\+(\d+)/) {
return undef if $1 == 0;
my $plus = $1;
my $o = readfile("$DCONF->{admin_dir}/data/regusers.txt", "update_registered_users_file", { no_unlock => 1, create => 1 });
my $c = $o->[0]; $c += $plus;
writefile("$DCONF->{admin_dir}/data/regusers.txt", [ $c ], "update_registered_users_file", { no_lock => 1, create => 1 });
} elsif ($newcount =~ /^\-(\d+)/) {
return undef if $1 == 0;
my $minus = $1;
my $o = readfile("$DCONF->{admin_dir}/data/regusers.txt", "update_registered_users_file", { no_unlock => 1, create => 1 });
my $c = $o->[0]; $c -= $minus;
writefile("$DCONF->{admin_dir}/data/regusers.txt", [ $c ], "update_registered_users_file", { no_lock => 1, create => 1 });
} elsif ($newcount >= 0) {
writefile("$DCONF->{admin_dir}/data/regusers.txt", [ $newcount ], "update_registered_users_file", { no_unlock => 1, no_lock => 1, create => 1 });
}
}
###
### write_account_file
###
### Writes out users.txt, passwd.txt, or databases. Use of this routine to write
### the user base makes the rest of the functions independent of whether you are
### using the MySQL back end, flat text files, or separated text files.
###
sub write_account_file {
my ($database, $array_ref, $param_ref) = @_;
$database = ( $database eq "passwd" ? "passwd" : "users" );
my $ca = 0;
if (-e "$DCONF->{admin_dir}/source/custom-acct.pl") {
dreq("custom-acct");
$ca = 1;
}
if ($DCONF->{pro} && $GLOBAL_OPTIONS->{database} == 1) {
dreq("sql-acct-PRO");
my @u = sql_write_account_file($database, $array_ref, $param_ref);
custom_write_account_file($database, $array_ref, $param_ref) if $ca;
return @u;
}
my @successes = (); my @failures = ();
my @files = ("$DCONF->{admin_dir}/$database.txt");
if ($DCONF->{pro} && $GLOBAL_OPTIONS->{split_user_files} && $database =~ m|users|) {
dreq("userfile");
@files = userfile_build_from_array($array_ref, $database);
}
undef my $cat_ref;
my $upd_or_del = undef;
my $add_rec = undef;
foreach my $record (@{ $array_ref }) {
my $u = $record->{user};
my $f = $files[0];
if ($DCONF->{pro} && $GLOBAL_OPTIONS->{split_user_files} && $database =~ m|users|) {
$f = userfile_which_file($database, $u);
}
if ($record->{action} eq "delete") {
$cat_ref->{$u}->{action} = "D";
$upd_or_del->{$f}++;
} elsif ($record->{action} eq "update") {
$cat_ref->{$u}->{action} = "U";
$cat_ref->{$u}->{data} = $record;
$upd_or_del->{$f}++;
} elsif ($record->{action} eq "add") {
push(@{ $add_rec->{$f} }, $record);
}
}
while (my $fn = shift @files) {
undef my $username_exists;
if ($upd_or_del->{$fn}) {
my $rf = readfile($fn, "write_account_file", { no_unlock => 1, zero_ok => 1, create => 1 }) if ! $param_ref->{fastupdate};
$rf = readfile($fn, "write_account_file", { no_lock => 1, no_unlock => 1, zero_ok => 1, create => 1 }) if $param_ref->{fastupdate};
my @nf = ();
foreach my $rec (@{ $rf }) {
my $h = user_line_as_hash($rec);
$h->{database} = $fn =~ /passwd/ ? "passwd" : "users";
if (! defined $cat_ref->{ $h->{user} }) {
push @nf, $rec;
next;
}
$username_exists->{$h->{user}} = 1;
my $u = $h->{user};
if ($cat_ref->{$u}->{action} eq "D") {
push (@successes, $h);
next;
} elsif ($cat_ref->{$u}->{action} eq "U") {
foreach my $dk (keys(%{ $cat_ref->{$u}->{data} })) {
$h->{$dk} = $cat_ref->{$u}->{data}->{$dk} if defined $cat_ref->{$u}->{data}->{$dk};
}
$rec = generate_user_line($h);
push (@successes, $h);
push (@nf, $rec);
} else {
push (@nf, $rec);
}
}
writefile($fn, \@nf, "write_account_file", { no_lock => 1, zero_ok => 1, create => 1 }) if ! $param_ref->{fastupdate};
writefile($fn, \@nf, "write_account_file", { no_unlock => 1, no_lock => 1, zero_ok => 1, create => 1 }) if $param_ref->{fastupdate};
} else {
my $rf = readfile($fn, "write_account_file", { zero_ok => 1, create => 1 });
foreach my $rec (@{ $rf }) {
my $h = user_line_as_hash($rec);
$username_exists->{$h->{user}} = 1;
}
}
if (defined $add_rec->{$fn}) {
my @add_lines = ();
foreach my $uref (@{ $add_rec->{$fn} }) {
my $un = prepare_userpass($uref->{user});
if ($username_exists->{$un}) {
$uref->{error_code} = 1;
push @failures, $uref;
next;
}
$username_exists->{$un} = 1;
push @add_lines, generate_user_line($uref);
push @successes, $uref;
}
if (scalar(@add_lines)) {
appendfile($fn, \@add_lines, "write_account_file", { no_lock => 1, zero_ok => 1, create => 1 }) if ! $param_ref->{fastupdate};
appendfile($fn, \@add_lines, "write_account_file", { no_unlock => 1, no_lock => 1, zero_ok => 1, create => 1 }) if $param_ref->{fastupdate};
}
}
}
custom_write_account_file($database, $array_ref, $param_ref) if $ca;
return (\@successes, \@failures);
}
###
### delete_account
###
### Deletes one or more moderator or user accounts, and returns arrays of
### accounts successfully deleted and accounts that couldn't be deleted.
###
sub delete_account {
my ($reff, $dbh_orig, $database) = @_;
dreq("authpass");
$database = "users" if $database eq "";
my @array = ();
foreach my $acct (keys(%{ $reff })) {
push @array, { user => $acct, action => 'delete' };
}
my ($success, $failure) = write_account_file($database, \@array, { dbh_orig => $dbh_orig });
update_registered_users_file(join("", "-", scalar(@{$success})));
if ($DCONF->{pro}) {
dreq("fcn-prfl-PRO");
delete_enhanced_profile_record( $reff, $database );
}
return ($success, $failure);
}
###
### update_account
###
### Prepares one or more updates for the user or moderator database. Also performs
### some simple error checking on the fields (require that e-mail address is valid,
### etc.).
###
sub update_account {
my ($updates, $database, $param_ref) = @_;
my $timecache = time;
$database = ($database eq "" ? "users" : $database);
my @updates = ( ref $updates eq "ARRAY" ? @{ $updates } : ( $updates ) );
undef my $ud;
foreach my $u (@updates) {
$u->{pass} = defined $u->{new_password} ? crypt($u->{new_password}, pick_random_salt()) : undef;
$u->{pass} = $u->{force_pass} if defined $u->{force_pass};
if (defined $u->{email}) {
$u->{email} = trim($u->{email});
$u->{email} = "" if $u->{email} !~ m|^([\w\-\+\.]+)\@([\w\-\+\.]+)$|;
}
if ($u->{fullname} ne "") {
$u->{fullname} =~ s/[<>:]//g;
$u->{fullname} =~ s/\s+/ /g;
$u->{fullname} = undef if $u->{fullname} eq "";
}
$u->{action} = "update";
$u->{database} = $database;
}
return write_account_file($database, \@updates, $param_ref);
}
###
### add_account
###
### Adds one or more user or moderator accounts to the system, returning the successes
### and failures as array references. The first argument, $ah, is an array of hash
### references, with each hash ref being one new account. Some of the notable entries
### in each hash are:
### user => Username of account being created
###
### pass1 & pass2 => Unencrypted passwords, generally from form input
### OR pass => Unencrypted final password
### OR encrypted_password => Encrypted password
### OR givepassword => Makes Discus assign a random password
###
### email => E-mail address
### fullname => Full name of users
### groups => Slash-delimited group list
###
### You must set $args->{database} to either "users" (for users) or "passwd" (for
### moderators). Discus figures out the right file or database table to read/write.
###
### Within returns, Discus will also set for you:
### encrypted_password => Encrypted password
### final_password => Unencrypted password, for if you used random password
###
sub add_account {
my ($ah, $args) = @_;
my $timecache = time; srand($timecache);
dreq("authpass");
undef my @newlines;
undef my @failed;
undef my @successes;
my @ah = ref $ah eq "HASH" ? ( $ah ) : @{ $ah };
H: foreach my $hash (@ah) {
undef my $newhash;
my $username = prepare_userpass($hash->{'user'});
if (length($username) > 100 || length($username) < 1) {
$hash->{error_code} = 2;
$hash->{'final_password'} = $hash->{'pass'} if defined $hash->{'pass'};
push @failed, \%{$hash};
next H;
}
$newhash->{'user'} = $username;
if ($hash->{'pass1'} ne "") {
($hash->{'pass1'}, $hash->{'pass2'}) = prepare_userpass_p($hash->{'pass1'}, $hash->{'pass2'});
if ($hash->{'pass1'} ne $hash->{'pass2'}) {
$hash->{error_code} = 3;
push (@failed, $hash);
next H;
}
my ($text, $salt) = pick_random_password();
my $password = crypt($hash->{'pass1'}, $salt);
$newhash->{'final_password'} = $hash->{'pass1'};
$newhash->{'encrypted_password'} = $password;
} elsif ($hash->{'pass'} ne "") {
($hash->{'pass'}) = prepare_userpass_p($hash->{pass});
if (length($hash->{pass}) < 1) {
$hash->{error_code} = 3;
push (@failed, $hash);
next H;
}
my $password = crypt($hash->{'pass'}, pick_random_salt());
$newhash->{'final_password'} = $hash->{'pass'};
$newhash->{'encrypted_password'} = $password;
} elsif ($hash->{'exactpass'} ne "") {
$newhash->{'final_password'} = $hash->{'exactpass'};
$newhash->{'encrypted_password'} = $hash->{'exactpass'};
} else {
if ($args->{'givepassword'} == 0) {
$hash->{error_code} = 4;
push (@failed, $hash);
next H;
}
my ($text, $salt) = pick_random_password();
($text) = prepare_userpass_p($text);
my $password = crypt($text, $salt);
$newhash->{'final_password'} = $text;
$newhash->{'encrypted_password'} = $password;
}
$newhash->{'pass'} = $newhash->{'encrypted_password'};
$newhash->{'pass'} = "*$newhash->{pass}||*!" if $hash->{forcechange};
$newhash->{'encrypted_password'} = undef;
if ($hash->{'email'} =~ m|^([\w\-\+\.]+)\@([\w\-\+\.]+)$|) {
$newhash->{'email'} = $hash->{'email'};
} else {
$newhash->{'email'} = "email";
}
if ($hash->{'fullname'} eq "") {
$newhash->{'fullname'} = "fullname";
} else {
$newhash->{'fullname'} = $hash->{'fullname'};
$newhash->{'fullname'} =~ s/\n//g;
$newhash->{'fullname'} =~ s/[:<>\\]//g;
}
$newhash->{'edit'} = $hash->{'edit'} eq "" ? 1 : $hash->{'edit'};
$newhash->{'ctime'} = $hash->{'ctime'} == 0 ? $timecache : $hash->{'ctime'};
$newhash->{'atime'} = ! defined $hash->{'atime'} ? 0 : $hash->{'atime'};
$newhash->{'stime'} = ! defined $hash->{'stime'} ? 0 : $hash->{'stime'};
$newhash->{'notify'} = $hash->{'notify'} eq "" ? $GLOBAL_OPTIONS->{'default_notify_string'} : $hash->{'notify'};
$newhash->{'groups'} = $hash->{'groups'} eq "" ? "/" : $hash->{'groups'};
$newhash->{'action'} = "add";
$newhash->{additional} = $hash->{additional};
push (@newlines, $newhash);
}
my ($success, $failure) = write_account_file($args->{database}, \@newlines);
my @u = @{$failure}; push @u, @failed; $failure = \@u;
update_registered_users_file(join("", "+", scalar(@{$success})));
return ($success, $failure) if $args->{no_enhanced};
if ($DCONF->{pro}) {
my @a = ();
my %failures = map { $_->{user}, 1 } @{$failure};
foreach my $x (@newlines) {
next if defined $failures{$x->{user}};
my $u = {};
$u->{username} = $x->{user};
$u->{database} = $args->{database};
$u->{personal} = $x->{additional};
$u->{status} = $args->{database} eq "passwd" ? 9 : 3;
$u->{posts} = 0;
my %j = map { $_, 1 } split(//, $GLOBAL_OPTIONS->{default_prefs}); $u->{pref} = \%j;
push @a, $u;
}
if (scalar @a) {
dreq("fcn-prfl-PRO");
update_enhanced_profile_file(\@a);
}
}
return ($success, $failure);
}
###
### pick_random_password
###
### Chooses a 4-10 letter password randomly.
###
sub pick_random_password {
my ($arg) = @_;
my $salt = pick_random_salt() if $arg == 0;
my $text = "";
for (my $i = 1; $i <= (6 + int(rand(3))); $i++) {
my $char = int(rand(36));
if ($char > 26) {
$char -= 26;
$text .= $char;
} else {
$text .= ('A' .. 'Z')[$char];
}
}
return ($text, $salt) if $arg == 0;
return $text if $arg == 1;
}
###
### pick_random_salt
###
### Picks a 2-letter salt
###
sub pick_random_salt {
my @salt = ();
for (my $i=1; $i<=2; $i++) {
push (@salt, int(rand(26))+65);
}
my $salt = pack('c2', @salt);
return $salt;
}
###
### user_copy
###
### Copies one or more users from one group to another
###
sub user_copy {
my ($oldgroup, $newgroup, $users, $move) = @_;
dreq("authpass");
my $p = undef;
if ($users ne "" && $users ne "*" && $users ne "0") {
foreach my $x (split(/,/, $users)) {
$p->{$x} = 1;
}
}
my $raf = read_account_file("users", $p, { returnformat => 'single_array', no_lock => 1, no_unlock => 1 });
my @u = ();
foreach my $x (@{$raf}) {
next if $x->{groups} !~ m|/$oldgroup/|;
next if $x->{groups} =~ m|/$newgroup/|;
my @g = grep(/\S/, split(/\//, $x->{groups}));
push @g, $newgroup;
my %i = map { $_, 1 } @g;
$i{$oldgroup} = 0 if $move;
my $j = join("/", grep($i{$_} == 1, keys(%i)));
$j = "/$j" if $j !~ m|^/|;
$j = "$j/" if $j !~ m|/$|;
push @u, { user => $x->{user}, groups => $j };
}
update_account(\@u, "users") if scalar(@u);
return { copied => scalar(@u) };
}
###
### remove_account_group
###
### Removes accounts from a particular group
###
sub remove_account_group {
my ($reff, $dbh_orig, $database, $group) = @_;
dreq("authpass");
my $deletes = {};
my $raf = read_account_file("users", $reff, { returnformat => 'single_array', no_lock => 1, no_unlock => 1 });
my @u = ();
foreach my $x (@{$raf}) {
next if $x->{groups} !~ m|/$group/|;
if ($x->{groups} eq "/$group/") {
$deletes->{$x->{user}} += 1;
next;
}
my @g = grep(/\S/, split(/\//, $x->{groups}));
my %i = map { $_, 1 } @g;
$i{$group} = 0;
my $j = join("/", grep($i{$_} == 1, keys(%i)));
$j = "/$j" if $j !~ m|^/|;
$j = "$j/" if $j !~ m|/$|;
push @u, { user => $x->{user}, groups => $j };
}
update_account(\@u, "users") if scalar(@u);
if (scalar keys %{$deletes}) {
delete_account($deletes, $dbh_orig, $database);
}
}
1;