# all the user stuff
#
# kevin lenzo
# patrick cole
#

sub parseUserfile {
    $file = $param{'miscdir'}.$filesep.$param{'userList'};
    %user = ();
    @userList = ();

    open(FH, $file);
    while (<FH>) {
	if (!/^#/ && defined $_) {
	    if (/^UserEntry\s+(.+?)\s/) {
		push @userList, $1;
		$workname = $1;
		if (/\s*\{\s*/) {
		    while (<FH>) {
			if (/^\s*(\w+)\s+(.+);$/) {
			    $opt = $1; $val = $2;
			    $val =~ s/\"//g;
			    if ($opt =~ /^mask$/i) {
				push @{$workname."masks"}, $val;
			    } elsif ($opt =~ /^flags$/i) {
				$val =~ s/\+//;
				$user{$workname."flags"} = $val;
			    } else {
				$opt =~ tr/A-Z/a-z/;
				$user{$workname.$opt} = $val;
			    }
			} elsif (/^\s*\}\s*$/) {
			    last;
			}
		    }
		} else {
		    status("parse error: User Entry $workname without right brace");
		}
	    }
	}
    }

    my $u;
    foreach $u (@userList) {
	status("found user $user: flags +".$user{$u."flags"}) 
	    if $param{VERBOSITY} > 1;
	
	if ($param{VERBOSITY} > 2) {
	    my $h;
	    foreach $h (@{$u."masks"}) {
		status(" -> hostmask: $h");
	    }
	}
    }
}

sub IsFlag {
    my $flags = $_[0];
    my ($ret, $f, $o);
    my @ind = split //, $flags;

    foreach $f (split //, $uFlags) {
	foreach $o (@ind) {
	    if ($f eq $o) {
		$ret .= $f;
		last;
	    }
	}
    }
    $ret;
}

sub verifyUser {
    my $lnuh = $_[0];
    my ($u, $m);
    my $VerifWho;

    foreach $u (@userList) {
	foreach (@{$u."masks"}) {
	    $m = $_;
	    $m =~ s/\*/.*?/g;
	    $m =~ s/([\@\(\)\[\]])/\\$1/g;
	    if ($lnuh =~ /^$m$/i) {
		$VerifWho = $u;
		last;
	    }
	}
    }

    my $now = time();

    my $m = $message;
    if ($msgType !~ /public/) {
	$m = "<private message>";
    }
    $seen{lc $who} = $now.$;.$m;

    if ($VerifWho) {
	$uFlags = $user{$VerifWho."flags"};
	$uPasswd = $user{$VerifWho."pass"};
	$uTitle = $user{$VerifWho."title"};

	if (exists $seenVerif{$VerifWho} or 
	    (time()-$seenVerif{$VerifWho} > 360)) {
	    status("mask verified for $VerifWho");
	}

	$seenVerif{$VerifWho} = $now;
    }

    return $VerifWho;
}

sub mkpasswd {
    my $what = $_[0];
    my $salt = chr(33+rand(64)).chr(33+rand(64));
    $salt =~ s/:/;/g;

    return crypt($what, $salt);
}

sub ckuser {
    # returns user level if matched, zero otherwise
    my ($nuh, $plaintextpass) = @_;
    if (!$plaintextpass) {
	($nuh, $plaintextpass) = split(/\s+/, $nuh);
    }

    return '' unless $nuh;
    my ($level, $cryptedpass, $rest, $nuh2) = &userinfo($nuh);

    if (&ckpasswd($plaintextpass, $cryptedpass)) {
	# password matched for user nick!user@host
	&status("confirmed user: $nuh");
	return $level;
    } else {
	# no match
	return 0;
    }
}

sub ckpasswd {
    # returns true if arg1 encrypts to arg2
    my ($plain, $encrypted) = @_;
    if (!$encrypted) {
	($plain, $encrypted) = split(/\s+/, $plain, 2);
    }
    return '' unless ($plain && $encrypted);
    my $salt = substr($encrypted, 0, 2);
    return ($encrypted eq crypt($plain, $salt));
}

sub userinfo {
    my $lnuh = $_[0];
    my $k;

    if (!$lnuh) {
	$lnuh = $nuh;
    }
    foreach $k (keys %userList) {
	my $n = $k;
	$n =~ s/\*/.*/g;
	$n =~ s/([\@\(\)\[\]])/\\$1/g;

	if ($lnuh =~ /^$n$/i) {
	    # this may expand later
	    my ($userlevel, $pass, $rest) = split(/:/, $userList{$k}, 3);
	    return ($userlevel, $pass, $rest, $k);
	}
    }
    return ();
}

sub users {
    my @stuff;
    foreach (sort keys %userList) {
	push(@stuff, "$_ => $userList{$_}\n");
    }
    return @stuff;
}

sub adduser {
    my($nuh, $level, $plainpass, $rest) = @_;
    if (!$level) {
	($nuh, $level, $plainpass, $rest) = split(/\s+/, $nuh, 4);
    }
    if (!$plainpass && ($level =~ /\D/)) {
	my $x = $level;
	if ($plainpass =~ /^\D+/) {
	    $level = $plainpass;
	    $plainpass = $level;
	}
    }

    if (($level =~ /^\d+/) && $plainpass) {
	my $cryptedpass = mkpasswd($plainpass);
	my $i = join(":", $level, $cryptedpass, $rest);
	$userLevel{$nuh} = $i;
	&status("user $nuh added at level $i");
	return "user $nuh added at level $i";
    } else {
	&status("bad params to adduser");
	return '';
    }
}

sub writeUserFile {
    my $where = $_[0];
    chomp $where;
    if (!$where) {
	$where = $param{'miscdir'}.$filesep.$param{'userList'};
    }
    if (!$where) {
	return "no file given and no param set for writing user file\n";
    }
    if (open(UF, ">$where")) {
	foreach (sort keys %userLevel) {
	    print UF "$_:$userLevel{$_}\n";
	}
	close UF;
	&status("wrote user file to $where");
	return "wrote user file";
    } else {
	&status("failed to write user file to $where");
	return "couldn't write user file";
    }
}

sub changepass {
    my ($nuh, $oldpass, $newpass) = @_;

    if (&ckuser($nuh, $oldpass)) {
	my $cryptednew = mkpasswd($newpass);
	my ($level, $pass, $rest, $nuh2) = &userinfo($nuh);
	my $i = join(":", $level, $newpass, $rest);
	$userList{$nuh2} = $i;
	&status("password changed for $nuh");
	return "password changed for $nuh";
    } else {
	&status("password change failed for $nuh");
	return "password did not match you: $nuh";
    }
}

sub removeuser {
    my $nuh = $_[0];

    if ($userList{$nuh}) {
	delete $userList{$nuh};
	&status("deleted $nuh from userlist");
	return "deleted $nuh from the userlist";
    } else {
	return 'No match for $nuh';
    }
}

sub setlevel {
    my ($nuh, $newlevel) = @_;
    if (!$newlevel) {
	($nuh, $newlevel) = split(/\s+/, $nuh, 2);
    }
    my ($level, $pass, $rest, $nuh2) = &userinfo($nuh);
    if ($newlevel !~ /^\d+/) {
	return "bad user level: $newlevel";
    }
    if ($userList{$nuh}) {
	($level, $pass, $rest) = split(/:/, $userList{$nuh});
	$nuh2 = $nuh;
    }
    if ($nuh2) {
	my $i = join(":", $newlevel, $pass, $rest);
	$userList{$nuh2} = $i;
	&status("level for $nuh changed to $newlevel (was $level)");
    } else {
	&status("no match for $nuh");
    }
    0;
}

sub userProcessing {
    my $now = time();

    if ($VerifWho) {
	if ($msgType =~ /private/) {
	    my $unverified_message = "you must identify yourself; /msg $param{nick} <pass> <command>";

	    if (IsFlag("e") eq "e") { # eval
		if ($message =~ s/^(\S+) eval//) {
		    if (!exists $verified{$VerifWho}) {
			&status("unverified <$who> $message");
			&msg($who, $unverified_message);
			return 'NOREPLY';
		    } 
		    my ($pass, $m) = ($1, $message);
		    $_ = "";
		    &msg($who, "WARNING: exposed eval security risk");
		    $x = eval($m); 
		    &msg($who, $x);
		}
	    }

	    if (IsFlag("o") eq "o") { # owner flag
		if ($message =~ /^die/) {
		    if (!exists $verified{$VerifWho}) {
			&status("unverified <$who> $message");
			&msg($who, $unverified_message);
			return 'NOREPLY';
		    } 
		    &rawout("QUIT :$who");
		    &closeDBM("is", "are");
		    if (%seen) { dbmclose %seen }
		    if (%plusplus) { dbmclose %plusplus }
		    status("Dying by $who\'s request");
		    exit(0);
		}

		if ($message =~ /^reload$/i) {
		    if (!exists $verified{$VerifWho}) {
			&status("unverified <$who> $message");
			&msg($who, $unverified_message);
			return 'NOREPLY';
		    } 
		    &status("RELOAD <$who>");
		    my $loaded;
		    opendir DIR, $infobot_src_dir;
		    while ($file = readdir DIR) {
			next unless $file =~ /\.pl$/;
			next if $file =~ /^Process.pl$/;
			chomp $file;
			$loaded .= "$file ";
			do $file;
		    }
		    close DIR;
		    &msg($who, "reloaded");
		    return 'NOREPLY';
		}

		if ($message =~ /^rehash$/i) {
		    if (!exists $verified{$VerifWho}) {
			&status("unverified <$who> $message");
			&msg($who, $unverified_message);
			return 'NOREPLY';
		    } 
		    &status("REHASH <$who>\n");
		    &parseUserfile();
		    &loadParamFiles(@paramfiles);
		    &msg($who, "rehashed");
		    return 'NOREPLY';
		}

		if ($message =~ /^modes$/) {
		    if (!exists $verified{$VerifWho}) {
			&status("unverified <$who> $message");
			&msg($who, $unverified_message);
			return 'NOREPLY';
		    } 
		    my ($chan, $mode, $user, $msg, $m1);
		    foreach $chan (keys %channels) {
			my $msg = "$chan: ";
			foreach $mode (keys %{$channels{$chan}}) {
			    my $m1 = $msg." $mode: ";
			    foreach $user (keys %{$channels{$chan}{$mode}}) {
				$m1 .= "$user ";
			    }
			    &msg($who, $m1);
			}
		    }
		    return 'NOREPLY';
		}
	    }

	    if (IsFlag("p") eq "p") { # oP on channel
		if ($message =~ s/^op( me)?$//i or $message =~ s/^op //i) {
		    if (!exists $verified{$VerifWho}) {
			&status("unverified <$who> $message");
			&msg($who, $unverified_message);
			return 'NOREPLY';
		    } 
		    &status("trying to op $who at their request");
		    foreach $chan (keys %channels) {
			if ($message) {
			    &op($chan, $message);
			} else {
			    &op($chan, $who);
			}
		    }
		    return 'NOREPLY'; 
		}
	    }
	}
    } else {
	$uFlags = $user{"defaultflags"};
    }
}

1;

