#!/usr/bin/perl
#
# Catch22 (llama@osoal.org.nz)
# One Short Of A Llama [http://web.osoal.org.nz]

# create table users
# (
#    loginaddr  varchar(80),
#    logintime  int unsigned default 0,
#    uid        int unsigned auto_increment primary key,
#    gid        int unsigned,
#    nickname   varchar(30),
#    passwd     varchar(30),
#    email      varchar(50),
#    fakeemail  varchar(50),
#    realname   varchar(80),
#    url        varchar(50),
#    whois      tinytext,
#    boxes      tinytext,
#    threads    int,
#    comments   int
# );

#
use strict;
use DBI;
use OneShortOfALlama;

#
sub user_menu
{
    # menu
    print('<div align="center">
           [ <a href="', $ENV{'SCRIPT_NAME'}, '?select=Modify"><b>Preferences</b></a>
           | <a href="', $ENV{'SCRIPT_NAME'}, '?select=Logout"><b>Logout</b></a>
           | <a href="', $ENV{'SCRIPT_NAME'}, '?select=Mail+Password"><b>Mail Password</b></a> ]
           </div>
           <br>
           <br>', "\n\n");
}

sub whois_user
{
    my($dbh, $gid, $buf, $sth, $nickname, $realname, $url, $whois, $thread, $email, $fakeemail,
       $subject, $pid, $replies, $time, $loginaddr, $logintime, $load, $wuid, $wgid);

    #
    $dbh = shift(@_);
    $gid = shift(@_);
    
    unless ($buf = shift(@_))
    {
	print('You must supply a nickname/uid to search for');
	return ();
    }

    #
    ($wuid, $wgid, $nickname, $realname, $email, $fakeemail, $url, $whois, $loginaddr, $logintime)
	= $dbh->selectrow_array('select uid, gid, nickname, realname, email, fakeemail, url, whois,
                                        loginaddr, from_unixtime(logintime, "%W %D %M %T, %Y")
                                 from osoal.users
                                 where (' . ($buf =~ m/^\d+$/ ? 'uid' : 'nickname') . ' = ' . $dbh->quote($buf) . ')');

	#
    if ($wuid)
    {
	print('Whois Information for <b>', $nickname, '</b> (', $wuid);
	
	if ($gid == 1)
	{
	    print("/$wgid");
	}

	print(')
               <br>
               <br>

               <table align="center">
               <tr>
                   <td align="left">Email</td>
                   <td align="right">(', $fakeemail || 'not specified', ')');

	if ($gid == 1)
	{
	    print('<br>(', $email, ')');
	}

	print('</td>
               </tr>

               <tr>
                   <td align="left">Realname</td>
                   <td align="right">', $realname || '(not specified)', '</td>
               </tr>

               <tr>
                   <td align="left">URL</td>
                   <td align="right">', $url ? "<a href=\"$url\"><b>$url</b></a>" : '(not specified)', '</td>
               </tr>

               <tr>
                   <td align="left">Whois</td>
                   <td align="right">', $whois || '(not specified)', '<td>
               </tr>

               <tr>
                   <td align="left">Comments</td>
                   <td align="right">', "\n\n");

	#
	$sth = $dbh->prepare('select pid, thread, replies, nickname,
                                     from_unixtime(time, "%W %D %M %l:%i %p, %Y"), subject
                              from osoal.comments where (uid = ' . $wuid . ') order by time desc limit 5');
	$sth->execute();

	if ($sth->rows())
	{
	    $sth->bind_columns(undef(), \$pid, \$thread, \$replies, \$nickname, \$time, \$subject);
			
	    while ($sth->fetch())
	    {
		$load = $dbh->selectrow_array("select count(*) from osoal.comments
                                               where (thread = $thread && pid >= $pid)");

		print('<br>
                       <a href="/comments.pl?pid=', $pid, '&load=', $load, '#', $pid, '"><b>', $nickname, '</b></a>
                       <em>', $subject, '</em><br> <small>', $time, '</small>
                       (<small>', $replies, ' repl', ($replies == 1 ? 'y' : 'ies'), '</small>)<br>', "\n");

	
	    }

	    print('<br>', "\n");
	}
	else
	{
	    print('(This user has not posted any comments)');
	}

	print('</td>
               </tr>', "\n\n");
		
	if ($gid == 1)
	{
	    print('<tr>
                   <td align="left">Login Address</td>
                   <td align="right">', $loginaddr || '(unknown)', '</td>
                   </tr>

                   <tr>
                   <td align="left">Login Time</td>
                   <td align="right">', $logintime || '(unknown)', '</td>
                   </tr>');
	}

	#
	print('</table>', "\n\n");
    }
    else
    {
	print('You may not request whois information on this user');
    }
}

sub login_user
{
    my($nickname, $passwd, $uid, $gid);

    #
    $nickname = shift(@_);
    $passwd   = shift(@_);

    #
    ($uid, $gid) = authenticate($nickname, crypt($passwd, $nickname));
	
    if ($uid)
    {
	print('Set-Cookie: osoal=nickname&', encode($nickname), '&passwd&', encode(crypt($passwd, $nickname)),
	      '; path=/; domain=', $ENV{'HTTP_HOST'}, "\n");
    }

    return ($uid);
}

sub update_user
{
    my($logintime, $loginaddr, $uid, $dbh);

    #
    $dbh = shift(@_);
    $uid = shift(@_);

    #
    ($loginaddr, $logintime)
	= $dbh->selectrow_array("select loginaddr, from_unixtime(logintime, '%W %D %M %T, %Y')
                                 from osoal.users where (uid = $uid)");
    
    $dbh->do('update users set loginaddr = ' . $dbh->quote(nslookup($ENV{'REMOTE_ADDR'})) . ', logintime = ' . time() . "
              where (uid = $uid)");
    
    if ($logintime)
    {
	print('<br>
               Last login from ', $loginaddr, ' on ', $logintime, "\n\n");
    }
}

sub create_user
{
    my($nickname, $passwd, $email, $uid, $dbh, $sth);

    #
    $dbh      = shift(@_);
    $nickname = shift(@_);
    $email    = shift(@_);

    #
    unless ($nickname && $email)
    {
	print('You must supply a nickname and a password to create an account');
	return ();
    }

    # trim unecessary spaces
    $nickname =~ s/\n|\r//g;
    $nickname =~ s/^\s*(\w+)\s*$/$1/;

    #
    unless ($nickname =~ m/^[A-Za-z]{1}/)
    {
	print('Nickname must start with a letter');
	return ();
    }

    unless ($email =~ m/^\S+?\@[\w-\.]+?\.\w{2,3}$/)
    {
	print('Invalid email address');
	return ();
    }

    #
    $uid = $dbh->selectrow_array('select uid from osoal.users where
                                  (nickname = ' . $dbh->quote($nickname) . ' || email = ' . $dbh->quote($email) . ')');

    if (!$uid)
    {
	$passwd = crypt($email, rand(time()));
	$sth    = $dbh->prepare('insert into osoal.users (nickname, passwd, gid, email, contact, boxes, threads, comments)
                                 values (?, ?, 2, ?, 0, ?, 1, 10)');
	$sth->execute($nickname, $passwd, $email, 'Vote Lastposts Finger Login Horoscopes');
	$uid    = $sth->{'mysql_insertid'};

	#
	sendmail('admin@osoal.org.nz',
		 $email,
		 join("\n",
		      "From: <admin\@osoal.org.nz>",
		      "To: <$email>",
		      "Subject: One Short Of A Llama Account Password",
		      "",
		      "The account $nickname ($uid) has been created with the password $passwd",
		      "You may configure your account via http://web.osoal.org.nz/login.pl",
		      "Nickname/password authentication is based on HTTP cookies so you must",
		      "ensure that your browser supports cookies and that any web proxies you",
		      "use do not filter them out.",
		      "",
		      "One Short Of A Llama will not give the information in your account",
		      "out to any third party. A working email address is required for",
		      "account administration. If you have any questions, mail: admin\@osoal.org.nz",
		      "",
		      "Catch22              | llama\@osoal.org.nz",
		      "One Short Of A Llama | http://web.osoal.org.nz"));

	print("User <b>$nickname</b> ($uid) created, the password for this account
               has been mailed to ($email)");
    }
    else
    {
	print('Permission Denied; You may not create this user');
    }
}

sub mail_password
{
    my($uid, $passwd, $email, $nickname, $dbh);

    #
    $dbh      = shift(@_);
    $nickname = shift(@_);
	
    #
    ($uid, $passwd, $email)
	= $dbh->selectrow_array('select uid, passwd, email from osoal.users
                                 where (nickname = ' . $dbh->quote($nickname) . ')');
	
    if ($uid)
    {
	unless (sendmail('admin@osoal.org.nz',
			 $email,
			 join("\n",
			      "From: <admin\@osoal.org.nz>",
			      "To: <$email>",
			      "Subject: One Short Of A Llama Account Password",
			      "",
			      "Someone (possibly not you) from " . nslookup($ENV{'REMOTE_ADDR'}),
			      "requested via a web form that the password for the $nickname user",
			      "account be mailed to its owner. If this was not you please ignore",
			      "this message, otherwise the password is: $passwd",
			      "",
			      "Catch22              | llama\@osoal.org.nz",
			      "One Short Of A Llama | http://web.osoal.org.nz")))
	{
	    print('Unable to send password');
	    return ();
	}

	print("Password for <b>$nickname</b> ($uid) has been mailed");
    }
    else
    {
	print('Permission Denied; You may not request that the password for this account be mailed');
    }
}

sub verify_password
{
    my($passwd, $passwdcheck);

    #
    $passwd      = shift(@_);
    $passwdcheck = shift(@_);

    #
    unless (length($passwd) > 5)
    {
	return ('Password must be longer than 5 characters');
    }

    unless ($passwd eq $passwdcheck)
    {
	return ('Passwords do not match');
    }

    return (0);
}

sub modify_user
{
    my($nickname, $realname, $email, $contact, $fakeemail, $threads,
       $comments, $url, $whois, $dbh, $uid, $boxes, $box);

    #
    $dbh = shift(@_);
    $uid = shift(@_);

    #
    ($nickname, $realname, $email, $contact, $fakeemail, $url, $whois, $boxes, $threads, $comments)
	= $dbh->selectrow_array("select nickname, realname, email, contact, fakeemail,
                                        url, whois, boxes, threads, comments
                                 from users where (uid = $uid)");

    #
    print('Updating Preferences for <b>', $nickname, '</b> (', $uid, ')
           <br>
           <form method="POST" action="', $ENV{'SCRIPT_NAME'}, '">
           <table align="center">
           <tr>
               <td align="left">Realname</td>
               <td align="right">
                   <input type="text" name="realname" value="', $realname, '" size="40"></td>
           </tr>

           <tr>
           <td align="left">Email</td>
           <td align="right">
               <input type="text" name="email" value="', $email, '" size="40"></td>
           </tr>

           <tr>
           <td align="right" colspan="2">
               <input type="checkbox" name="contact" value="x"', ($contact ? ' checked' : ''), '>
                  I authorize One Short Of A Llama to send me mail
           </td>
           </tr>

           <tr>
           <td align="left">FakeEmail</td>
           <td align="right">
               <input type="text" name="fakeemail" value="', $fakeemail, '" size="40"></td>
           </tr>

           <tr>
           <td align="left">URL</td>
           <td align="right">
               <input type="text" name="url" value="', $url, '" size="40"></td>
           </tr>

           <tr>
           <td align="left">Whois</td>
           <td align="right">
               <textarea name="whois" cols="38" rows="6" wrap="virtual">', $whois, '</textarea></td>
           </tr>

           <tr>
           <td align="left">Boxes</td>
           <td align="right">
               <table width="100%">
               <tr>');

	#
	for $box ('Vote', 'Lastposts', 'Finger')
	{
		user_boxes($boxes, $box);
	}

	print('</tr>
           <tr>');

	for $box ('Horoscopes', 'Login', '')
	{
		user_boxes($boxes, $box);
	}

	#
	print('</tr>
               </table></td>
           </tr>

           <tr>
           <td align="left">Comments</td>
           <td align="right">
               <table width="100%">
               <tr>
                   <td><select name="threads">
                       <option value="0"', ($threads == 0 ? ' selected' : ''), '>Flattened</option>
                       <option value="1"', ($threads == 1 ? ' selected' : ''), '>Threaded</option>
                       <option value="2"', ($threads == 2 ? ' selected' : ''), '>Nested</option>
                       </select></td>
                   <td>Display <input type="text" name="comments" value="', $comments, '" size="3"> Comments</td>
               </tr>
               </table></td>
           </tr>

           <tr>
           <td align="left">Password</td>
           <td align="right">
               <input type="password" name="passwd" value="" size="40">
               <div align="center"><small>(retype password for authentication)</small></div>
               <input type="password" name="passwdcheck" value="" size="40">
           </td>
           </tr>

           <tr>
               <td align="left"><input type="submit" name="select" value="Apply"></td>
               <td align="right"><img src="/pics/icons/oneshortofallama.png" alt=""></td>
           </tr>
           </table>
           </form>', "\n\n");
}

sub user_boxes
{
	my($boxes, $box);

	$boxes = shift(@_);
	$box   = shift(@_);

	#
	print('<td>');

	if ($box)
	{
		print('<input type="checkbox" name="boxes" value="', $box, '"');

		if ($boxes =~ m/$box/)
		{
			print(' checked');
		}

		print('> ', $box);
	}
	else
	{
		print('&nbsp;');
	}

	print('</td>');
}

sub apply_user
{
	my($uid, @columns, $i, $dbh, $param);

	#
	$dbh   = shift(@_);
	$uid   = shift(@_);
	$param = shift(@_);

	# email
	if ($param->{'email'})
	{
		if ($param->{'email'} =~ m/^\S+?\@[\w-\.]+?\.\w{2,3}$/)
		{
			push(@columns, 'email = ' . $dbh->quote($param->{'email'}));
		}
		else
		{
			print('Invalid Email Address');
		}
	}

	#
	unless ($param->{'threads'} =~ m/^[012]$/)
	{
		$param->{'threads'} = 1;
	}

	unless ($param->{'comments'} =~ m/^\d+$/ || $param->{'comments'} == -1)
	{
		$param->{'comments'} = 10;
	}

	$param->{'contact'} = ($param->{'contact'} ? 1 : 0);

	for $i (qw/realname url whois contact fakeemail boxes threads comments/)
	{
		# dont check for greater than zero length here otherwise we wont be
		# able to nullify the optional parameters
		push(@columns, "$i = " . $dbh->quote($param->{$i}));
	}

	# passwd
	if ($param->{'passwd'})
	{
		if ($i = verify_password($param->{'passwd'}, $param->{'passwdcheck'}))
		{
			print($i);
		}
		else
		{
			push(@columns, 'passwd = ' . $dbh->quote($param->{'passwd'}));
		}
	}

	#
	if (@columns)
	{
		$dbh->do("update osoal.users set " . join(', ', @columns) . " where (uid = $uid)");
	}
}

#
#
#

sub main
{
	my($param, $cookie, $uid, $gid, $dbh);
	$param = variables();

	#
	if ($param->{'select'} eq 'Login')
	{
		if (login_user($param->{'nickname'}, $param->{'passwd'}))
		{
			print('Content-type: text/html

                   <html>
                   <head>
                       <meta http-equiv="refresh" content="0;
                             url=', $ENV{'SCRIPT_NAME'}, '?select=Cookie">
                       <title>One Short Of A Llama:Login</title>
                   </head>
                   <body bgcolor="#ffffff"></body>
                   <table width="100%" height="100%">

                   <tr valign="center">
                       <td align="center">Activating Cookie...</td>
                   </tr>

                   </table>
                   </body>
                   </html>');

			return ();
		}
	}

	else
	{
		$cookie      = cookie('osoal');
		($uid, $gid) = authenticate($cookie->{'nickname'}, $cookie->{'passwd'});

		#
		if ($param->{'select'} eq 'Logout')
		{
			# logout user
			print("Set-Cookie: osoal=; expires=Thu, 1-Jan-1970 12:00:00 GMT; path=/; domain=$ENV{'HTTP_HOST'}\n");
		}

		elsif ($param->{'select'} eq 'Apply' && $uid)
		{
			# check password
			unless (verify_password($param->{'passwd'}, $param->{'passwdcheck'}))
			{
				print('Set-Cookie: osoal=nickname&', encode($cookie->{'nickname'}),
					  '&passwd&', encode(crypt($param->{'passwd'}, $cookie->{'nickname'})),
					  '; path=/ domain=', $ENV{'HTTP_HOST'}, "\n");
			}
		}
	}

	#
	header('Login', 'login.png', '/');
	print('<div align="center">
           <table width="600" cellspacing="0" cellpadding="0" border="0">
           <tr>
           <td>', "\n\n");

	#
	if ($dbh = DBI->connect("dbi:mysql:$MySQLserver;database=osoal",
				$MySQLuser, $MySQLpasswd))
	{
		# whois user
		if ($param->{'select'} eq 'Whois')
		{
			whois_user($dbh, $gid, $param->{'uid'} || $param->{'nickname'});
		}

		# create form
		elsif ($param->{'select'} eq 'Create')
		{
			print('<form method="POST" action="', $ENV{'SCRIPT_NAME'}, '">
                   <table align="center">

                   <tr>
                   <td align="left">Nickname</td>
                   <td align="right">
                       <input type="text" name="nickname" value="" size="20"></td>
                   </tr>

                   <tr>
                   <td align="left">Email</td>
                   <td align="right">
                       <input type="text" name="email" value="" size="20"></td>
                   </tr>

                   <tr>
                   <td align="left">
                       <input type="submit" name="select" value="Submit"></td>
                   <td align="right">
                       <img src="/pics/icons/oneshortofallama.png" alt=""></td>
                   </tr>

                   </table>
                   </form>

                    <div align="center">
                    Your initial password will be automatically generated and
                    sent to your email address.
                    </div>', "\n\n");
		}

                # create account
		elsif ($param->{'select'} eq 'Submit')
		{
		    #create_user($dbh, $param->{'nickname'}, $param->{'email'});
		}

		# mail password
		elsif ($param->{'select'} eq 'Mail Password')
		{
			if (($cookie->{'nickname'} && $uid) || $param->{'nickname'})
			{
				if ($uid)
				{
					user_menu();
				}

				mail_password($dbh, $param->{'nickname'} || $cookie->{'nickname'});
			}
			else
			{
				print("Permission Denied; You did not supply a nickname");
			}
		}

		# user maintence
		else
		{
			if ($uid)
			{
				unless ($param->{'select'} eq 'Logout')
				{
					user_menu();
				}

				# successful login
				if ($param->{'select'} eq 'Cookie')
				{
					# user cookie{'nickname'} because of redirect
					print('Login successful <b>', $cookie->{'nickname'}, '</b> (', $uid, ")\n");
					update_user($dbh, $uid);
				}

				# modify menu
				elsif ($param->{'select'} eq 'Modify')
				{
					modify_user($dbh, $uid);
				}

				# apply new preferences
				elsif ($param->{'select'} eq 'Apply')
				{
					apply_user($dbh, $uid, $param);
					modify_user($dbh, $uid);
				}

				# logout
				elsif ($param->{'select'} eq 'Logout')
				{
					print('Logged out <b>', $cookie->{'nickname'}, '</b> (', $uid, ")\n");
				}
				
				# authenticated
				else
				{
					print('Logged in <b>', $cookie->{'nickname'}, '</b> (', $uid, ")\n");
				}
			}
			
			else
			{
				if ($param->{'nickname'} && $param->{'passwd'})
				{
					print('Your nickname/password pair is invalid<br><br>');
				}

				elsif ($cookie->{'nickname'} && $cookie->{'passwd'})
				{
					print('Your cookie is invalid, try logging in again');
				}

				elsif ($param->{'select'} eq 'Cookie')
				{
					print('Your browser did not accept the cookie. Unable to complete authentication handshaking<br><br>');
				}

				# login form
				print('<form method="POST" action="', $ENV{'SCRIPT_NAME'}, '">
                       <table align="center">

                       <tr>
                       <td align="left">Nickname</td>
                       <td align="right">
                           <input type="text" name="nickname" value="" size=20"></td>
                       </tr>

                       <tr>
                       <td align="left">Password</td>
                       <td align="right">
                           <input type="password" name="passwd" value="" size="20"></td>
                       </tr>

                       <tr>
                       <td colspan="2">
                           <input type="submit" name="select" value="Login">
                           <input type="submit" name="select" value="Mail Password">
                           <input type="submit" name="select" value="Create">
                       </td>
                       </tr>

                       </table>
                       </form>', "\n\n");
			}
		}

		$dbh->disconnect();
	}

	#
	print('</td>
           </tr>
           </table>
           </div>', "\n\n");
	footer();
}

#
main();

