From 16aaa101aef3844cd58bd0a9e0c29520e5eb2c30 Mon Sep 17 00:00:00 2001
From: "Leigh B. Stoller" <stoller@flux.utah.edu>
Date: Tue, 16 Jan 2007 19:49:29 +0000
Subject: [PATCH] Move the bulk (or guts) of newuser and newproject from the
 web interface to the backend. There are new scripts that can be called from
 the command line:

	newuser xmlfile
	newproj xmlfile

They both run from small xmlfiles that are generated by the web
interface from the form data. I also moved user verification to the
backend so that we do not have duplicated email functions, but that
was a small change.

Upon error, the xmlfile is saved and sent to tbops so that we can
rerun the command by hand, rather then force user to fill out form
again. I also do a better job of putting the form back up intact when
there are internal errors.

If the user provides an initial public key, that is put into the xml
file as well and addpubkey is called from newuser instead of the web
interface. A more general change to addpukey is that it is now
*always* called as "nobody". This script was a morass of confusion
cause of having to call it as nobody before the user actually
exists. In fact, another of my ongoing projects is to reduce the
number of scripts called as a particular user, but thats a story for
another day. Anyway, the script is always called as nobody, but we
pass along the implied user in the environment so that it can do
permission checks.
---
 Makeconf.in               |   3 +-
 account/GNUmakefile.in    |   6 +-
 account/addpubkey.in      |  33 +--
 account/newproj.in        | 306 ++++++++++++++++++++++++++
 account/newuser.in        | 449 ++++++++++++++++++++++++++++++++++++++
 account/tbacct.in         |  40 +++-
 db/User.pm.in             |   4 +-
 sql/database-fill.sql     |   8 +
 www/defs.php3.in          |  20 +-
 www/deletepubkey.php3     |   4 +-
 www/joinproject.php3      | 180 ++++-----------
 www/newproject.php3       | 332 ++++++----------------------
 www/project_defs.php      |  70 ++++++
 www/showpubkeys.php3      |   6 +-
 www/user_defs.php         |  75 +++++++
 www/verifyusr.php3        | 185 +---------------
 www/widearea_register.php |   6 +-
 17 files changed, 1106 insertions(+), 621 deletions(-)
 create mode 100644 account/newproj.in
 create mode 100644 account/newuser.in

diff --git a/Makeconf.in b/Makeconf.in
index 3eb468f5ff..1219243d9c 100644
--- a/Makeconf.in
+++ b/Makeconf.in
@@ -1,6 +1,6 @@
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
 # All rights reserved.
 # 
 # Most of the configure-substitution magic is done here.
@@ -37,6 +37,7 @@ TBAUDITEMAIL	= @TBAUDITEMAIL@
 TBACTIVEARCHIVE = @TBACTIVEARCHIVE@
 TBUSERSARCHIVE  = @TBUSERSARCHIVE@
 TBERRORSEMAIL	= @TBERRORSEMAIL@
+TBAPPROVALEMAIL	= @TBAPPROVALEMAIL@
 BOSSNODE        = @BOSSNODE@
 USERNODE        = @USERNODE@
 FSNODE          = @FSNODE@
diff --git a/account/GNUmakefile.in b/account/GNUmakefile.in
index 8d675becc5..2b31de4d4f 100644
--- a/account/GNUmakefile.in
+++ b/account/GNUmakefile.in
@@ -12,8 +12,10 @@ UNIFIED         = @UNIFIED_BOSS_AND_OPS@
 
 include $(OBJDIR)/Makeconf
 
-SBIN_STUFF	= tbacct addsfskey addpubkey mkusercert quotamail genpubkeys
-LIBEXEC_STUFF	= webtbacct webaddsfskey webaddpubkey webmkusercert
+SBIN_STUFF	= tbacct addsfskey addpubkey mkusercert quotamail genpubkeys \
+		  newuser newproj
+LIBEXEC_STUFF	= webtbacct webaddsfskey webaddpubkey webmkusercert \
+		  webnewuser webnewproj
 
 # These scripts installed setuid, with sudo. 
 SETUID_BIN_SCRIPTS   = 
diff --git a/account/addpubkey.in b/account/addpubkey.in
index f2f47288f6..9d5dcc6470 100644
--- a/account/addpubkey.in
+++ b/account/addpubkey.in
@@ -171,22 +171,25 @@ if (defined($user)) {
     $user_email = $target_user->email();
     $user_dbid  = $target_user->dbid();
     $user_uid   = $target_user->uid();
+    $USERUID    = $user_uid;
 }
 
 #
-# If invoked as "nobody" its for a user with no actual account.
+# If invoked as "nobody" we came from the web interface. We have to have
+# a credential in the environment, unless its just a key verification
+# operation, which anyone can do. 
 # 
 if (getpwuid($UID) eq "nobody") {
-    if ($initmode || $genmode) {
-	fatal("Bad usage as 'nobody'");
+    $this_user = User->ImpliedUser();
+    
+    if (($initmode || $genmode || !$verify) && !defined($this_user)) {
+	fatal("Bad usage from web interface");
     }
     $nobody = 1;
 }
 else {
-    $USERUID = getpwnam($user);
-
-    # Map invoking user to object.
-    $this_user = User->LookupByUnixId($UID);
+    # From the command line; map invoking user to object.
+    $this_user = User->ThisUser();
 
     if (! defined($this_user)) {
 	fatal("You ($UID) do not exist!");
@@ -194,16 +197,17 @@ else {
 }
 
 #
-# Initmode or genmode, do it and exit.
+# Initmode or genmode, do it and exit. Eventually get rid of the switch
+# to the target user.
 #
 if ($initmode) {
     # Drop root privs, switch to target user.
-    $EUID = $USERUID;
+    $EUID = $UID = $USERUID;
     exit InitUser();
 }
 if ($genmode) {
     # Drop root privs, switch to target user.
-    $EUID = $USERUID;
+    $EUID = $UID = $USERUID;
     exit GenerateKeyFile();
 }
 
@@ -235,10 +239,12 @@ else {
 #
 if (!$verify) {
     # If its the user himself, then we can generate a new authkeys file. 
-    if (!$nobody && !TBAdmin() && !$target_user->SameUser($this_user)) {
+    if (!$target_user->SameUser($this_user) && !TBAdmin()) {
 	fatal("You are not allowed to set pubkeys for $target_user\n");
     }
     if (-d "$HOMEDIR/$user_uid/.ssh") {
+	# Drop root privs, switch to target user.
+	$EUID = $UID = $USERUID;
 	$genmode = 1;
     }
     
@@ -250,11 +256,6 @@ if (!$verify) {
     AuditStart(0);
 }
 
-# Drop root privs, switching to user.
-if (!$nobody) {
-    $EUID = $USERUID;
-}
-
 #
 # Grab the first line of the file. Parse it to see if its in the
 # format we like (openssh), either protocol 1 or 2.
diff --git a/account/newproj.in b/account/newproj.in
new file mode 100644
index 0000000000..68a8b659ec
--- /dev/null
+++ b/account/newproj.in
@@ -0,0 +1,306 @@
+#!/usr/bin/perl -wT
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use strict;
+use Getopt::Std;
+use XML::Simple;
+use Data::Dumper;
+
+#
+# Create a new user from a XML description. 
+#
+sub usage()
+{
+    print("Usage: newproj <xmlfile>\n");
+    print("       newproj -m <pid_idx>\n");
+    exit(-1);
+}
+my $optlist = "dm:";
+my $debug   = 1;
+my $resend;
+
+#
+# Configure variables
+#
+my $TB		= "@prefix@";
+my $TBOPS       = "@TBOPSEMAIL@";
+my $TBAPPROVAL  = "@TBAPPROVALEMAIL@";
+my $TBAUDIT	= "@TBAUDITEMAIL@";
+my $TBBASE      = "@TBBASE@";
+my $TBWWW       = "@TBWWW@";
+
+#
+# This script is setuid, so please do not run it as root. Hard to track
+# what has happened.
+#
+if ($EUID == 0) {
+    die("*** $0:\n".
+	"    Please do not run this as root!\n");
+}
+
+#
+# Untaint the path
+#
+$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin";
+delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+#
+# Turn off line buffering on output
+#
+$| = 1;
+
+#
+# Load the Testbed support stuff.
+#
+use lib "@prefix@/lib";
+use libdb;
+use libtestbed;
+use Project;
+use User;
+
+# Protos
+sub fatal($);
+sub UserError($);
+
+# Locals
+my $SAVEUID	= $UID;
+my $xmlfile;
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+my %options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (defined($options{"m"})) {
+    $resend = $options{"m"};
+
+    if ($resend =~ /^(\d*)$/) {
+	$resend = $1;
+    }
+    else {
+	fatal("Bad characters in -m option: $resend");
+    }
+}
+else {
+    usage()
+	if (@ARGV != 1);
+
+    $xmlfile  = shift(@ARGV);
+}
+
+#
+# Map invoking user to object. 
+# If invoked as "nobody" we are coming from the web interface and there
+# is no current user context.
+#
+my $this_user;
+
+if (getpwuid($UID) ne "nobody") {
+    $this_user = User->ThisUser();
+
+    if (! defined($this_user)) {
+	fatal("You ($UID) do not exist!");
+    }
+    fatal("You must have admin privledges to create new projects!")
+	if (!TBAdmin());
+}
+
+#
+# Resend email message and exit.
+#
+if (defined($resend)) {
+    my $project = Project->Lookup($resend);
+    fatal("Could not map project $resend to object!")
+	if (!defined($project));
+
+    exit($project->SendNewProjectEmail());
+}
+
+#
+# Check the filename when invoked from the web interface; must be a
+# file in /tmp.
+#
+if (! defined($this_user)) {
+    if ($xmlfile =~ /^([-\w\.\/]+)$/) {
+	$xmlfile = $1;
+    }
+    else {
+	fatal("Bad data in pathname: $xmlfile");
+    }
+
+    # Use realpath to resolve any symlinks.
+    my $translated = `realpath $xmlfile`;
+    if ($translated =~ /^(\/tmp\/[-\w\.\/]+)$/) {
+	$xmlfile = $1;
+    }
+    else {
+	fatal("Bad data in translated pathname: $xmlfile");
+    }
+}
+
+#
+# These are the fields that we allow to come in from the XMLfile.
+#
+my %required = ("name"		    => "pid",
+		"leader"	    => "head_uid",
+		"short description" => "name",
+		"URL"		    => "URL",
+		"funders"	    => "funders",
+		"long description"  => "why",
+		"public"	    => "public",
+		"num_pcs"	    => "num_pcs",
+		"linkedtous"	    => "linked_to_us");
+		
+my %optional = ("members"	 => "num_members",
+		"ron"            => "num_ron",
+		"plab"           => "num_pcplab",
+		"whynotpublic"	 => "public_whynot",
+		"user_interface" => "default_user_interface");
+
+#
+# This script is not audited cause we want the output to be sent back
+# to the web interface. Thats okay since we send email from the script
+# anyway.
+#
+
+#
+# Must wrap the parser in eval since it exits on error.
+#
+my $xmlparse = eval { XMLin($xmlfile,
+			    VarAttr => 'name',
+			    ContentKey => '-content',
+			    SuppressEmpty => undef); };
+fatal($@)
+    if ($@);
+
+
+#
+# Make sure all the required arguments were provided.
+#
+foreach my $key (keys(%required)) {
+    
+    fatal("Missing required attribute '$key'")
+	if (! exists($xmlparse->{'attribute'}->{"$key"}));
+}
+
+#
+# We build up an array of arguments to pass to User->Create() as we check
+# the attributes.
+#
+my %newproj_args = ();
+
+foreach my $key (keys(%{ $xmlparse->{'attribute'} })) {
+    my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'};
+
+    if ($debug) {
+	print STDERR "Project attribute: '$key' -> '$value'\n";
+    }
+
+    my $dbslot;
+
+    # Must be in the allowed lists above, with exceptions handled below
+    if (exists($required{$key})) {
+	$dbslot = $required{$key};
+	next
+	    if (!defined($dbslot));
+	fatal("Null value for required field $key")
+	    if (!defined($value));
+    }
+    elsif (exists($optional{$key})) {
+	$dbslot = $optional{$key};
+	next
+	    if (!defined($dbslot) || !defined($value));
+    }
+    else {
+	fatal("Invalid attribute in XML: '$key' -> '$value'\n");
+    }
+
+    # Now check that the value is legal.
+    if (! TBcheck_dbslot($value, "projects", $dbslot,
+			 TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) {
+	UserError("Illegal data: $key - $value");
+    }
+
+    #
+    # Do a taint check to avoid warnings, since the values are abviously okay.
+    #
+    if ($value =~ /^(.*)$/) {
+	$value = $1;
+    }
+    $newproj_args{$dbslot} = $value;
+}
+
+#
+# Now do special checks.
+#
+UserError("Project already exists; pick another name!")
+    if (Project->Lookup($newproj_args{'pid'}));
+
+my $leader = User->Lookup($newproj_args{'head_uid'});
+UserError("Project leader does not exist!")
+    if (!defined($leader));
+   
+#
+# Now safe to create the project. Move the pid out of the argument array
+# since its actually an argument to the Create() routine. Ditto for the
+# project leader.
+#
+my $new_pid = $newproj_args{'pid'};
+delete($newproj_args{'pid'});
+delete($newproj_args{'head_uid'});
+
+my $newproj = Project->Create($new_pid, $leader, \%newproj_args);
+if (!defined($newproj)) {
+    fatal("Could not create new project!");
+}
+my $new_idx = $newproj->pid_idx();
+
+#
+# See if we are in an initial Emulab setup. If so, no email sent.
+#
+my $firstinitstate;
+TBGetSiteVar("general/firstinit/state", \$firstinitstate);
+
+#
+# Send the email notification.
+#
+$newproj->SendNewProjectEmail($firstinitstate eq "createproject");
+
+# The web interface requires this line to be printed!
+print "User $new_pid/$new_idx has been created\n";
+exit(0);
+
+sub fatal($) {
+    my($mesg) = $_[0];
+
+    print STDERR "*** $0:\n".
+	         "    $mesg\n";
+    exit(-1);
+}
+sub UserError($) {
+    my($mesg) = $_[0];
+
+    print $mesg;
+    exit(1);
+}
+
+sub escapeshellarg($)
+{
+    my ($str) = @_;
+
+    $str =~ s/(')/'\\''/g;
+    return $str;
+}
+	   
+
+	       
diff --git a/account/newuser.in b/account/newuser.in
new file mode 100644
index 0000000000..f8ed0f3a1c
--- /dev/null
+++ b/account/newuser.in
@@ -0,0 +1,449 @@
+#!/usr/bin/perl -wT
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2000-2007 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use strict;
+use Getopt::Std;
+use XML::Simple;
+use Data::Dumper;
+
+#
+# Create a new user from a XML description. 
+#
+sub usage()
+{
+    print("Usage: newuser -t <type> <xmlfile>\n");
+    exit(-1);
+}
+my $optlist = "dt:";
+my $debug   = 1;
+my $type    = "";
+
+#
+# Configure variables
+#
+my $TB		= "@prefix@";
+my $TBOPS       = "@TBOPSEMAIL@";
+my $TBAPPROVAL  = "@TBAPPROVALEMAIL@";
+my $TBAUDIT	= "@TBAUDITEMAIL@";
+my $TBBASE      = "@TBBASE@";
+my $TBWWW       = "@TBWWW@";
+my $WIKISUPPORT = @WIKISUPPORT@;
+my $TBADMINGROUP= "@TBADMINGROUP@";
+my $checkpass   = "$TB/libexec/checkpass";
+my $addpubkey   = "$TB/sbin/addpubkey";
+
+#
+# This script is setuid, so please do not run it as root. Hard to track
+# what has happened.
+#
+if ($EUID == 0) {
+    die("*** $0:\n".
+	"    Please do not run this as root!\n");
+}
+
+#
+# Untaint the path
+#
+$ENV{'PATH'} = "$TB/bin:$TB/sbin:/bin:/usr/bin:/usr/bin:/usr/sbin";
+delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+#
+# Turn off line buffering on output
+#
+$| = 1;
+
+#
+# Load the Testbed support stuff.
+#
+use lib "@prefix@/lib";
+use libdb;
+use libtestbed;
+use User;
+
+# Protos
+sub fatal($);
+sub UserError($);
+sub escapeshellarg($);
+
+# Locals
+my $SAVEUID	= $UID;
+my $keyfile;
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+my %options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (defined($options{"t"})) {
+    $type = $options{"t"};
+}
+if (@ARGV != 1) {
+    usage();
+}
+my $xmlfile  = shift(@ARGV);
+
+#
+# Map invoking user to object. 
+# If invoked as "nobody" we are coming from the web interface and there
+# is no current user context.
+#
+my $this_user;
+
+if (getpwuid($UID) ne "nobody") {
+    $this_user = User->ThisUser();
+
+    if (! defined($this_user)) {
+	fatal("You ($UID) do not exist!");
+    }
+    fatal("You must have admin privledges to create new users")
+	if (!TBAdmin());
+}
+else {
+    #
+    # Check the filename when invoked from the web interface; must be a
+    # file in /tmp.
+    #
+    if ($xmlfile =~ /^([-\w\.\/]+)$/) {
+	$xmlfile = $1;
+    }
+    else {
+	fatal("Bad data in pathname: $xmlfile");
+    }
+
+    # Use realpath to resolve any symlinks.
+    my $translated = `realpath $xmlfile`;
+    if ($translated =~ /^(\/tmp\/[-\w\.\/]+)$/) {
+	$xmlfile = $1;
+    }
+    else {
+	fatal("Bad data in translated pathname: $xmlfile");
+    }
+}
+
+#
+# These are the fields that we allow to come in from the XMLfile.
+#
+my %required = ("name"		=> "usr_name",
+		"email"		=> "usr_email",
+		"address"	=> "usr_addr",
+		"city"		=> "usr_city",
+		"state"		=> "usr_state",
+		"zip"		=> "usr_zip",
+		"country"	=> "usr_country",
+		"phone"		=> "usr_phone",
+		"title"		=> "usr_title",
+		"affiliation"	=> "usr_affil",
+		"password"	=> undef,
+		"wikiname"	=> "wikiname");
+
+my %optional = ("login"		=> "uid",
+		"address2"	=> "usr_addr2",
+		"URL"		=> "usr_URL",
+		"shell"		=> "usr_shell",
+		"pubkey"        => undef);
+
+#
+# Must wrap the parser in eval since it exits on error.
+#
+my $xmlparse = eval { XMLin($xmlfile,
+			    VarAttr => 'name',
+			    ContentKey => '-content',
+			    SuppressEmpty => undef); };
+fatal($@)
+    if ($@);
+
+
+#
+# Make sure all the required arguments were provided.
+#
+foreach my $key (keys(%required)) {
+    
+    fatal("Missing required attribute '$key'")
+	if (! exists($xmlparse->{'attribute'}->{"$key"}));
+}
+
+#
+# We build up an array of arguments to pass to User->Create() as we check
+# the attributes.
+#
+my %newuser_args = ();
+
+foreach my $key (keys(%{ $xmlparse->{'attribute'} })) {
+    my $value = $xmlparse->{'attribute'}->{"$key"}->{'value'};
+
+    if ($debug) {
+	print STDERR "User attribute: '$key' -> '$value'\n";
+    }
+
+    my $dbslot;
+
+    # Must be in the allowed lists above, with exceptions handled below
+    if (exists($required{$key})) {
+	$dbslot = $required{$key};
+	next
+	    if (!defined($dbslot));
+	fatal("Null value for required field $key")
+	    if (!defined($value));
+    }
+    elsif (exists($optional{$key})) {
+	$dbslot = $optional{$key};
+	next
+	    if (!defined($dbslot) || !defined($value));
+    }
+    else {
+	fatal("Invalid attribute in XML: '$key' -> '$value'\n");
+    }
+
+    # Now check that the value is legal.
+    if (! TBcheck_dbslot($value, "users", $dbslot,
+			 TBDB_CHECKDBSLOT_WARN|TBDB_CHECKDBSLOT_ERROR)) {
+	UserError("Illegal data: $key - $value");
+    }
+
+    #
+    # Do a taint check to avoid warnings, since the values are abviously okay.
+    #
+    if ($value =~ /^(.*)$/) {
+	$value = $1;
+    }
+    $newuser_args{$dbslot} = $value;
+}
+
+#
+# Now do special checks.
+#
+
+#
+# If user selected his own uid, must be unique.
+#
+if (exists($newuser_args{'uid'})) {
+    UserError("User already exists; pick another login name!")
+	if (User->Lookup($newuser_args{'uid'}));
+    
+    UserError("Reserved user name; pick another login name!")
+	if (getpwnam($newuser_args{'uid'}));
+}
+
+#
+# User name must be at least two tokens.
+#
+my @foo = split(/\s+/, $newuser_args{'usr_name'});
+
+UserError("User name must be more then a single token!")
+    if (@foo < 2);
+
+#
+# Wikiname must be unique.
+#
+if ($WIKISUPPORT) {
+    UserError("Wikiname already in use; please pick another!")
+	if (User->LookupByWikiName($newuser_args{'wikiname'}));
+}
+
+#
+# And the email address has to be unique.
+#
+UserError("Email address already in use; please pick another!")
+    if (User->LookupByEmail($newuser_args{'usr_email'}));
+
+#
+# Check the password.
+#
+my $pswd = $xmlparse->{'attribute'}->{'password'}->{'value'};
+	
+my $checkpass_args = escapeshellarg($pswd);
+$checkpass_args   .= " " .
+    (exists($newuser_args{'uid'}) ? $newuser_args{'uid'} : "ignored");
+$checkpass_args   .= escapeshellarg($newuser_args{'usr_name'} . ":" .
+				    $newuser_args{'usr_email'});
+    
+my $pwokay = `$checkpass $checkpass_args`;
+if ($?) {
+    chomp($pwokay);
+    
+    if (! ($pwokay =~ /^ok$/)) {
+	UserError("$pwokay");
+    }
+    fatal("Checkpass failed with $?");
+}
+$newuser_args{'usr_pswd'} = crypt($pswd, "\$1\$" . substr(time(), 0, 8));
+
+#
+# Do a check on the pubkey if supplied. The safest thing to do is generate
+# a temporary file and pass that to addpubkey to check.
+#
+if (exists($xmlparse->{'attribute'}->{'pubkey'})) {
+    $keyfile = TBMakeTempFile("addpubkey");
+    fatal("Could not create tempfile")
+	if ($?);
+
+    open(KEY, ">> $keyfile") or
+	fatal("Could not open $keyfile");
+    print KEY $xmlparse->{'attribute'}->{'pubkey'}->{'value'};
+    close($keyfile);
+
+    my $result = `$addpubkey -n -f $keyfile`;
+    chomp($result);
+    UserError("Could not parse public key")
+	if ($?);
+}
+
+#
+# Now safe to create the user. Move the uid out of the argument array
+# since its actually an argument to the Create() routine.
+#
+my $new_uid;
+
+if (exists($newuser_args{'uid'})) {
+    $new_uid = $newuser_args{'uid'};
+    delete($newuser_args{'uid'});
+}
+
+#
+# The type modifier comes in on the command line since this is available
+# only from the web interface or locally. The usual case is to create a
+# normal user.
+#
+my $flags = 0;
+if ($type eq "webonly") {
+    $flags = $User::NEWUSER_FLAGS_WEBONLY;
+}
+elsif ($type eq "wikionly") {
+    $flags = $User::NEWUSER_FLAGS_WIKIONLY;
+}
+elsif ($type eq "leader") {
+    $flags = $User::NEWUSER_FLAGS_PROJLEADER;
+}
+my $newuser = User->Create($new_uid, $flags, \%newuser_args);
+if (!defined($newuser)) {
+    fatal("Could not create new user!");
+}
+
+#
+# See if we are in an initial Emulab setup. If so, no email sent.
+#
+my $firstinitstate;
+if (TBGetSiteVar("general/firstinit/state", \$firstinitstate)) {
+    #
+    # The first user gets admin status and some extra groups, etc.
+    #
+    DBQueryFatal("update users set ".
+		 "  admin=1,status='". $User::USERSTATUS_UNAPPROVED . "' " .
+		 "where uid='$new_uid'");
+
+    DBQueryFatal("insert into unixgroup_membership set ".
+		 "uid='$new_uid', gid='wheel'");
+    
+    DBQueryFatal("insert into unixgroup_membership set ".
+		 "uid='$new_uid', gid='$TBADMINGROUP'");
+
+    exit(0)
+	if ($firstinitstate eq "createproject");
+}
+
+#
+# Send the email notification.
+#
+my $key       = $newuser->verify_key();
+my $usr_uid   = $newuser->uid();
+my $usr_name  = $newuser->name();
+my $usr_email = $newuser->email();
+	    
+SENDMAIL("$usr_name '$usr_uid' <$usr_email>",
+	 "Your New User Key",
+	 "\n".
+	 "Dear $usr_name ($usr_uid):\n\n".
+	 "This is your account verification key: $key\n\n".
+	 "Please use this link to verify your user account:\n".
+	 "\n".
+	 "    ${TBBASE}/login.php3?vuid=$usr_uid&key=$key\n".
+	 "\n".
+	 ($type eq "wikionly" ?
+	  "Once you have verified your account, you will be able to access\n".
+	  "the Wiki. You MUST verify your account first!"
+	  :
+	  ($type eq "webonly" ?
+	   "Once you have verified your account, Testbed Operations will be\n".
+	   "able to approve you. You MUST verify your account first!"
+	   :
+	   ($type eq "leader" ?
+	  "You will then be verified as a user. When you have been both\n".
+	  "verified and approved by Testbed Operations, you will be marked\n".
+	  "as an active user and granted full access to your account.\n".
+	  "You MUST verify your account before your project can be approved!\n"
+	    :
+	    "Once you have verified your account, the project leader will\n".
+	    "be able to approve you.\n\n".
+	    "You MUST verify your account before the project leader can ".
+	    "approve you\n".
+	    "After project approval, you will be marked as an active user,\n".
+	    "and will be granted full access to your user account."))) .
+	 "\n\n".
+	 "Thanks,\n".
+	 "Testbed Operations\n",
+	 "$TBAPPROVAL",
+	 "Bcc: $TBAUDIT");
+
+#
+# Do we have a keyfile? If so, rerun addpubkey for real now that the
+# user is created and email is sent.
+#
+my $user_uid = $newuser->uid();
+my $user_idx = $newuser->uid_idx();
+
+if (defined($keyfile)) {
+    # Set the implied user for addpubkey.
+    $ENV{'HTTP_INVOKING_USER'} = $user_idx;
+    my $result   = `$addpubkey -u $user_uid -f $keyfile`;
+    chomp($result);
+    fatal("Could not parse public key: $result")
+	if ($?);
+    unlink($keyfile)
+	if (! $debug);
+    
+}
+
+# The web interface requires this line to be printed!
+print "User $user_uid/$user_idx has been created\n";
+exit(0);
+
+sub fatal($) {
+    my($mesg) = $_[0];
+
+    unlink($keyfile)
+	if (defined($keyfile) && !$debug);
+
+    print STDERR "*** $0:\n".
+	         "    $mesg\n";
+    exit(-1);
+}
+sub UserError($) {
+    my($mesg) = $_[0];
+
+    unlink($keyfile)
+	if (defined($keyfile) && !$debug);
+
+    print $mesg;
+    exit(1);
+}
+
+sub escapeshellarg($)
+{
+    my ($str) = @_;
+
+    $str =~ s/(')/'\\''/g;
+    return $str;
+}
+	   
+
+	       
diff --git a/account/tbacct.in b/account/tbacct.in
index 535bb243db..18867c07f9 100644
--- a/account/tbacct.in
+++ b/account/tbacct.in
@@ -24,7 +24,8 @@ use Getopt::Std;
 sub usage()
 {
     print("Usage: tbacct [-f] [-b] ".
-	  "<add|del|mod|passwd|wpasswd|email|freeze|thaw> <user> [args]\n");
+	  "<add|del|mod|passwd|wpasswd|email|freeze|thaw|verify> ".
+	  "<user> [args]\n");
     exit(-1);
 }
 my $optlist = "fb";
@@ -135,6 +136,7 @@ sub UpdateWindowsPassword();
 sub UpdateUser(;$);
 sub FreezeUser();
 sub ThawUser();
+sub VerifyUser();
 sub UpdateEmail();
 sub CheckDotFiles();
 sub GenerateSFSKey();
@@ -181,7 +183,7 @@ if ($user =~ /^([-\w]+)$/i) {
 else {
     die("Tainted argument: $user\n");
 }
-if ($cmd =~ /^(add|del|mod|freeze|passwd|wpasswd|thaw|email)$/) {
+if ($cmd =~ /^(add|del|mod|freeze|passwd|wpasswd|thaw|email|verify)$/) {
     $cmd = $1;
 }
 else {
@@ -211,7 +213,7 @@ if (getpwuid($UID) eq "nobody") {
     $this_user = $target_user;
 }
 else {
-    $this_user = User->LookupByUnixId($UID);
+    $this_user = User->ThisUser();
 
     if (! defined($this_user)) {
 	fatal("You ($UID) do not exist!");
@@ -302,6 +304,10 @@ SWITCH: for ($cmd) {
 	ThawUser();
 	last SWITCH;
     };
+    /^verify$/ && do {
+	VerifyUser();
+	last SWITCH;
+    };
 }
 
 # Always do this!
@@ -867,6 +873,34 @@ sub ThawUser()
     return UpdateUser(0);
 }
 
+#
+# Verify a user. Converts status and sends email
+#
+sub VerifyUser()
+{
+    #
+    # Only admin people can do this unless its the user himself.
+    #
+    if (! $target_user->SameUser($this_user) && ! TBAdmin()) {
+	fatal("You do not have permission to verify user $user.");
+    }
+
+    if ($target_user->status() ne USERSTATUS_NEWUSER) {
+	fatal("$user is not a newuser! Cannot verify the account!");
+    }
+
+    my $newstatus = ($target_user->wikionly() ?
+		     USERSTATUS_ACTIVE() : USERSTATUS_UNAPPROVED());
+
+    $target_user->SetStatus($newstatus) == 0 or
+	fatal("Could not set user status to '$newstatus' for $target_user");
+
+    $target_user->SendVerifiedEmail() == 0 or
+	fatal("Could not send verified email for $target_user");
+
+    return 0;
+}
+
 #
 # Check dot files. We do this over and over ...
 #
diff --git a/db/User.pm.in b/db/User.pm.in
index 6db265932b..d2ee7bbc8a 100644
--- a/db/User.pm.in
+++ b/db/User.pm.in
@@ -618,9 +618,9 @@ sub SetPassword($$$)
 }
 
 #
-# User approved; find users groups and send email.
+# User verified; find users groups and send email.
 #
-sub SendApprovalEmail($)
+sub SendVerifiedEmail($)
 {
     my ($self) = @_;
 
diff --git a/sql/database-fill.sql b/sql/database-fill.sql
index 779cbb59a1..b0bb8c7260 100644
--- a/sql/database-fill.sql
+++ b/sql/database-fill.sql
@@ -573,7 +573,15 @@ REPLACE INTO table_regex VALUES ('nseconfigs','vname','text','redirect','virt_no
 REPLACE INTO table_regex VALUES ('nseconfigs','nseconfig','text','regex','^[\\040-\\176\\012\\011\\015]*$',0,16777215,NULL);
 REPLACE INTO table_regex VALUES ('os_info','osname','text','regex','^[-\\w\\.+]+$',2,20,NULL);
 REPLACE INTO table_regex VALUES ('projects','newpid','text','regex','^[a-zA-Z][-a-zA-Z0-9]+$',2,12,NULL);
+REPLACE INTO table_regex VALUES ('projects','head_uid','text','redirect','users:uid',0,0,NULL);
+REPLACE INTO table_regex VALUES ('projects','name','text','redirect','default:tinytext',0,256,NULL);
+REPLACE INTO table_regex VALUES ('projects','funders','text','redirect','default:tinytext',0,256,NULL);
+REPLACE INTO table_regex VALUES ('projects','public','int','redirect','default:tinyint',0,1,NULL);
+REPLACE INTO table_regex VALUES ('projects','linked_to_us','int','redirect','default:tinyint',0,1,NULL);
+REPLACE INTO table_regex VALUES ('projects','public_whynot','text','redirect','default:tinytext',0,256,NULL);
+REPLACE INTO table_regex VALUES ('projects','default_user_interface','text','regex','^(emulab|plab)$',2,12,NULL);
 REPLACE INTO table_regex VALUES ('projects','pid','text','regex','^[-\\w]+$',2,12,NULL);
+REPLACE INTO table_regex VALUES ('projects','URL','text','redirect','default:tinytext',0,0,NULL);
 REPLACE INTO table_regex VALUES ('reserved','vname','text','redirect','virt_nodes:vname',1,32,NULL);
 REPLACE INTO table_regex VALUES ('users','uid','text','regex','^[a-zA-Z][\\w]+$',2,8,NULL);
 REPLACE INTO table_regex VALUES ('users','usr_phone','text','regex','^[-\\d\\(\\)\\+\\.x ]+$',7,64,NULL);
diff --git a/www/defs.php3.in b/www/defs.php3.in
index 2eb167313d..eaf2b6adef 100644
--- a/www/defs.php3.in
+++ b/www/defs.php3.in
@@ -340,20 +340,16 @@ function SUEXEC($uid, $gid, $cmdandargs, $action) {
     return $suexec_retval;
 }
 
-function ADDPUBKEY($uid, $cmdandargs) {
+#
+# We invoke addpubkey as user nobody all the time. The implied user is passed
+# along in an HTTP_ variable (see tbauth). This avoids a bunch of confusion
+# that results from new users who do not have a context yet. 
+#
+function ADDPUBKEY($cmdandargs) {
     global $TBSUEXEC_PATH;
 
-    #
-    # Complication. User might not have an actual account if setting or
-    # changing his own pubkeys. webonly, unapproved, and unverified users
-    # can still muck with their personal info. So, just invoke as user
-    # nobody. We will get audit email in case we need to track what has
-    # happened. 
-    #
-    if (!$uid || !HASREALACCOUNT($uid)) {
-	$uid = "nobody";
-    }
-    return SUEXEC($uid, "nobody", $cmdandargs, 0);
+    return SUEXEC("nobody", "nobody", "webaddpubkey $cmdandargs",
+		  SUEXEC_ACTION_CONTINUE);
 }
 
 #
diff --git a/www/deletepubkey.php3 b/www/deletepubkey.php3
index d31ae28c24..676d763b5e 100644
--- a/www/deletepubkey.php3
+++ b/www/deletepubkey.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2003, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2003, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 include("defs.php3");
@@ -139,7 +139,7 @@ DBQueryFatal("delete from user_pubkeys ".
 # will complain and die!
 #
 if (HASREALACCOUNT($target_uid)) {
-    ADDPUBKEY($uid, "webaddpubkey -w $target_uid");
+    ADDPUBKEY("-w $target_uid");
 }
 
 header("Location: " . CreateURL("showpubkeys", $target_user));
diff --git a/www/joinproject.php3 b/www/joinproject.php3
index 469ee48732..e09dee65a0 100644
--- a/www/joinproject.php3
+++ b/www/joinproject.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2003, 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 include("defs.php3");
@@ -676,73 +676,6 @@ if (count($errors)) {
     return;
 }
 
-# Okay, do pubkey checks.
-if (!$returning && !$forwikionly) {
-    #
-    # Pub key provided in form (paste in).
-    #
-    if (isset($formfields[usr_key]) &&
-	strcmp($formfields[usr_key], "")) {
-        #
-        # This is passed off to the shell, so taint check it.
-        # 
-	if (! preg_match("/^[-\w\s\.\@\+\/\=]*$/", $formfields[usr_key])) {
-	    $errors["PubKey"] = "Invalid characters";
-	}
-	else {
-            #
-            # Replace any embedded newlines first.
-            #
-	    $formfields[usr_key] =
-		ereg_replace("[\n]", "", $formfields[usr_key]);
-	    $usr_key = $formfields[usr_key];
-
-            #
-            # Verify key format.
-            #
-	    if (ADDPUBKEY(null, "webaddpubkey -n -k '$usr_key' ")) {
-		$errors["Pubkey Format"] =
-		    "Could not be parsed. Is it a public key?";
-	    }
-	    else {
-		$addpubkeyargs = "-k '$usr_key' ";
-	    }
-	}
-    }
-
-    #
-    # If usr provided a file for the key, it overrides the paste in text.
-    #
-    if (isset($_FILES['usr_keyfile']) &&
-	$_FILES['usr_keyfile']['name'] != "" &&
-	$_FILES['usr_keyfile']['name'] != "none") {
-
-	$localfile = $_FILES['usr_keyfile']['tmp_name'];
-
-	if (! stat($localfile)) {
-	    $errors["PubKey File"] = "No such file";
-	}
-        # Taint check shell arguments always! 
-	elseif (! preg_match("/^[-\w\.\/]*$/", $localfile)) {
-	    $errors["PubKey File"] = "Invalid characters";
-	}
-	else {
-	    chmod($localfile, 0644);
-
-            #
-            # Verify key format.
-            #
-	    if (ADDPUBKEY(null, "webaddpubkey -n $localfile ")) {
-		$errors["Pubkey Format"] =
-		    "Could not be parsed. Is it a public key?";
-	    }
-	    else {
-		$addpubkeyargs = "$localfile";
-	    }
-	}
-    }
-}
-
 #
 # Need the user, project and group objects for the rest of this.
 #
@@ -769,84 +702,57 @@ if (count($errors)) {
 }
 
 #
-# Create a new user.
+# Create a new user. We do this by creating a little XML file to pass to
+# the newuser script.
 #
 if (! $returning) {
-    #
-    # Certain of these values must be escaped or otherwise sanitized.
-    #
-    $joining_uid       = ($USERSELECTUIDS ? $formfields[joining_uid] : null);
-    $usr_name          = addslashes($formfields[usr_name]);
-    $usr_email         = $formfields[usr_email];
-    $password1         = $formfields[password1];
-    $password2         = $formfields[password2];
-    $wikiname          = ($WIKISUPPORT ? $formfields[wikiname] : "");
-
-    if (!$forwikionly) {
-	$usr_affil         = addslashes($formfields[usr_affil]);
-	$usr_title         = addslashes($formfields[usr_title]);
-	$usr_addr          = addslashes($formfields[usr_addr]);
-	$usr_city          = addslashes($formfields[usr_city]);
-	$usr_state         = addslashes($formfields[usr_state]);
-	$usr_zip           = addslashes($formfields[usr_zip]);
-	$usr_country       = addslashes($formfields[usr_country]);
-	$usr_phone         = $formfields[usr_phone];
+    $args = array();
+    $args["name"]	   = $formfields[usr_name];
+    $args["email"]         = $formfields[usr_email];
+    $args["address"]       = $formfields[usr_addr];
+    $args["address2"]      = $formfields[usr_addr2];
+    $args["city"]          = $formfields[usr_city];
+    $args["state"]         = $formfields[usr_state];
+    $args["zip"]           = $formfields[usr_zip];
+    $args["country"]       = $formfields[usr_country];
+    $args["phone"]         = $formfields[usr_phone];
+    $args["shell"]         = 'tcsh';
+    $args["title"]         = $formfields[usr_title];
+    $args["affiliation"]   = $formfields[usr_affil];
+    $args["password"]      = $formfields[password1];
+    $args["wikiname"]      = ($WIKISUPPORT ? $formfields[wikiname] : "");
+
+    if (isset($formfields[usr_URL]) &&
+	$formfields[usr_URL] != $HTTPTAG && $formfields[usr_URL] != "") {
+	$args["URL"] = $formfields[usr_URL];
     }
-    else {
-	$usr_affil         = "";
-	$usr_title         = "";
-	$usr_addr          = "";
-	$usr_city          = "";
-	$usr_state         = "";
-	$usr_zip           = "";
-	$usr_country       = "";
-	$usr_phone         = "";
+    if ($USERSELECTUIDS) {
+	$args["login"] = $formfields[joining_uid];
     }
 
-    if (! isset($formfields[usr_URL]) ||
-	strcmp($formfields[usr_URL], "") == 0 ||
-	strcmp($formfields[usr_URL], $HTTPTAG) == 0) {
-	$usr_URL = "";
-    }
-    else {
-	$usr_URL = addslashes($formfields[usr_URL]);
-    }
+    # Backend verifies pubkey and returns error.
+    if (!$forwikionly) {
+	if (isset($_FILES['usr_keyfile']) &&
+	    $_FILES['usr_keyfile']['name'] != "" &&
+	    $_FILES['usr_keyfile']['name'] != "none") {
 
-    if (! isset($formfields[usr_addr2])) {
-	$usr_addr2 = "";
-    }
-    else {
-	$usr_addr2 = addslashes($formfields[usr_addr2]);
+	    $localfile = $_FILES['usr_keyfile']['tmp_name'];
+	    $args["pubkey"] = file_get_contents($localfile);
+	}
+	elseif (isset($formfields['usr_key']) &&
+		$formfields['usr_key'] != "") {
+	    $args["pubkey"] = $formfields[usr_key];
+	}
     }
-
-    $args = array();
-    $args["usr_expires"]   = date("Y:m:d", time() + (86400 * 120));
-    $args["usr_name"]	   = $usr_name;
-    $args["usr_email"]     = $usr_email;
-    $args["usr_addr"]      = $usr_addr;
-    $args["usr_addr2"]     = $usr_addr2;
-    $args["usr_city"]      = $usr_city;
-    $args["usr_state"]     = $usr_state;
-    $args["usr_zip"]       = $usr_zip;
-    $args["usr_country"]   = $usr_country;
-    $args["usr_URL"]       = $usr_URL;
-    $args["usr_phone"]     = $usr_phone;
-    $args["usr_shell"]     = 'tcsh';
-    $args["usr_title"]     = $usr_title;
-    $args["usr_affil"]     = $usr_affil;
-    $args["usr_pswd"]      = crypt("$password1");
-    $args["wikiname"]      = $wikiname;
-
-    if (! ($user = User::NewUser($joining_uid,
-				 ($forwikionly ? TBDB_NEWACCOUNT_WIKIONLY : 0),
-				 $args))) {
-	TBERROR("Could not create new user '$usr_email'!", 1);
+    if (! ($user = User::NewNewUser(($forwikionly ? TBDB_NEWACCOUNT_WIKIONLY : 0),
+				    $args,
+				    $error)) != 0) {
+	$errors["Error Creating User"] = $error;
+	SPITFORM($formfields, $returning, $errors);
+	PAGEFOOTER();
+	return;
     }
     $joining_uid = $user->uid();
-
-    if (!$forwikionly && isset($addpubkeyargs)) {
-	ADDPUBKEY($joining_uid, "webaddpubkey -u $joining_uid $addpubkeyargs");
-    }
 }
 
 #
diff --git a/www/newproject.php3 b/www/newproject.php3
index 11605f10f1..8234fcf115 100755
--- a/www/newproject.php3
+++ b/www/newproject.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2003, 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 include("defs.php3");
@@ -869,74 +869,6 @@ if (count($errors)) {
     return;
 }
 
-# Okay, do pubkey checks.
-if (!$returning) {
-    #
-    # Pub Key.
-    #
-    if (isset($formfields[usr_key]) &&
-	strcmp($formfields[usr_key], "")) {
-        #
-        # This is passed off to the shell, so taint check it.
-        # 
-	if (! preg_match("/^[-\w\s\.\@\+\/\=]*$/", $formfields[usr_key])) {
-	    $errors["PubKey"] = "Invalid characters";
-	}
-	else {
-            #
-            # Replace any embedded newlines first.
-            #
-	    $formfields[usr_key] =
-		ereg_replace("[\n]", "", $formfields[usr_key]);
-	    $usr_key = $formfields[usr_key];
-
-            #
-            # Verify key format.
-            #
-	    if (ADDPUBKEY(null, "webaddpubkey -n -k '$usr_key' ")) {
-		$errors["Pubkey Format"] =
-		    "Could not be parsed. Is it a public key?";
-	    }
-	    else {
-		$addpubkeyargs = "-k '$usr_key' ";
-	    }
-	}
-    }
-
-    #
-    # If usr provided a file for the key, it overrides the paste in text.
-    #
-    if (isset($_FILES['usr_keyfile']) &&
-	$_FILES['usr_keyfile']['name'] != "" &&
-	$_FILES['usr_keyfile']['name'] != "none") {
-
-	$localfile = $_FILES['usr_keyfile']['tmp_name'];
-
-	if (! stat($localfile)) {
-	    $errors["PubKey File"] = "No such file";
-	}
-        # Taint check shell arguments always! 
-	elseif (! preg_match("/^[-\w\.\/]*$/", $localfile)) {
-	    $errors["PubKey File"] = "Invalid characters";
-	}
-	else {
-	    chmod($localfile, 0644);
-
-            #
-            # Verify key format.
-            #
-	    if (ADDPUBKEY(null, "webaddpubkey -n $localfile ")) {
-		$errors["Pubkey Format"] =
-		    "Could not be parsed. Is it a public key?";
-	    }
-	    else {
-		$addpubkeyargs = "$localfile";
-	    }
-	    
-	}
-    }
-}
-
 # Done with sanity checks!
 if (count($errors)) {
     SPITFORM($formfields, $returning, $errors);
@@ -948,235 +880,105 @@ if (count($errors)) {
 # Certain of these values must be escaped or otherwise sanitized.
 #
 if (!$returning) {
-    $proj_head_uid     = (($USERSELECTUIDS ||
-			   $FirstInitState == "createproject") ?
-			  $formfields[proj_head_uid] : null);
-    $usr_title         = addslashes($formfields[usr_title]);
-    $usr_name          = addslashes($formfields[usr_name]);
-    $usr_affil         = addslashes($formfields[usr_affil]);
-    $usr_email         = $formfields[usr_email];
-    $usr_addr          = addslashes($formfields[usr_addr]);
-    $usr_city          = addslashes($formfields[usr_city]);
-    $usr_state         = addslashes($formfields[usr_state]);
-    $usr_zip           = addslashes($formfields[usr_zip]);
-    $usr_country       = addslashes($formfields[usr_country]);
-    $usr_phone         = $formfields[usr_phone];
-    $password1         = $formfields[password1];
-    $password2         = $formfields[password2];
-    $wikiname          = ($WIKISUPPORT ? $formfields[wikiname] : "");
-    $usr_returning     = "No";
-
-    if (! isset($formfields[usr_URL]) ||
-	strcmp($formfields[usr_URL], "") == 0 ||
-	strcmp($formfields[usr_URL], $HTTPTAG) == 0) {
-	$usr_URL = "";
+    $args = array();
+    $args["name"]	   = $formfields[usr_name];
+    $args["email"]         = $formfields[usr_email];
+    $args["address"]       = $formfields[usr_addr];
+    $args["address2"]      = $formfields[usr_addr2];
+    $args["city"]          = $formfields[usr_city];
+    $args["state"]         = $formfields[usr_state];
+    $args["zip"]           = $formfields[usr_zip];
+    $args["country"]       = $formfields[usr_country];
+    $args["phone"]         = $formfields[usr_phone];
+    $args["shell"]         = 'tcsh';
+    $args["title"]         = $formfields[usr_title];
+    $args["affiliation"]   = $formfields[usr_affil];
+    $args["password"]      = $formfields[password1];
+    $args["wikiname"]      = ($WIKISUPPORT ? $formfields[wikiname] : "");
+
+    if (isset($formfields[usr_URL]) &&
+	$formfields[usr_URL] != $HTTPTAG && $formfields[usr_URL] != "") {
+	$args["URL"] = $formfields[usr_URL];
     }
-    else {
-	$usr_URL = addslashes($formfields[usr_URL]);
+    if ($USERSELECTUIDS || $FirstInitState == "createproject") {
+	$args["login"] = $formfields[proj_head_uid];
     }
-    
-    if (! isset($formfields[usr_addr2])) {
-	$usr_addr2 = "";
+
+    # Backend verifies pubkey and returns error.
+    if (isset($_FILES['usr_keyfile']) &&
+	$_FILES['usr_keyfile']['name'] != "" &&
+	$_FILES['usr_keyfile']['name'] != "none") {
+
+	$localfile = $_FILES['usr_keyfile']['tmp_name'];
+	$args["pubkey"] = file_get_contents($localfile);
     }
-    else {
-	$usr_addr2 = addslashes($formfields[usr_addr2]);
+    elseif (isset($formfields['usr_key']) &&
+	    $formfields['usr_key'] != "") {
+	$args["pubkey"] = $formfields[usr_key];
     }
 
-    $args = array();
-    $args["usr_expires"]   = $proj_expires;
-    $args["usr_name"]	   = $usr_name;
-    $args["usr_email"]     = $usr_email;
-    $args["usr_addr"]      = $usr_addr;
-    $args["usr_addr2"]     = $usr_addr2;
-    $args["usr_city"]      = $usr_city;
-    $args["usr_state"]     = $usr_state;
-    $args["usr_zip"]       = $usr_zip;
-    $args["usr_country"]   = $usr_country;
-    $args["usr_URL"]       = $usr_URL;
-    $args["usr_phone"]     = $usr_phone;
-    $args["usr_shell"]     = 'tcsh';
-    $args["usr_title"]     = $usr_title;
-    $args["usr_affil"]     = $usr_affil;
-    $args["usr_pswd"]      = crypt("$password1");
-    $args["wikiname"]      = $wikiname;
-
-    if (! ($leader = User::NewUser($proj_head_uid,
-				   TBDB_NEWACCOUNT_PROJLEADER, $args))) {
-	TBERROR("Could not create new user '$usr_email'!", 1);
+    if (! ($leader = User::NewNewUser(TBDB_NEWACCOUNT_PROJLEADER,
+				      $args,
+				      $error)) != 0) {
+	$errors["Error Creating User"] = $error;
+	SPITFORM($formfields, $returning, $errors);
+	PAGEFOOTER();
+	return;
     }
     # If null; used below
     $proj_head_uid = $leader->uid();
-
-    if (isset($addpubkeyargs)) {
-	ADDPUBKEY($proj_head_uid,
-		  "webaddpubkey -u $proj_head_uid $addpubkeyargs");
-    }
 }
 else {
     $leader = $this_user;
-    $usr_title	   = $leader->title();
-    $usr_name	   = $leader->name();
-    $usr_affil	   = $leader->affil();
-    $usr_email	   = $leader->email();
-    $usr_addr	   = $leader->addr();
-    $usr_addr2     = $leader->addr2();
-    $usr_city	   = $leader->city();
-    $usr_state	   = $leader->state();
-    $usr_zip	   = $leader->zip();
-    $usr_country   = $leader->country();
-    $usr_phone	   = $leader->phone();
-    $usr_URL       = $leader->URL();
-    $wikiname      = $leader->wikiname();
-    $usr_returning = "Yes";
 }
 
-# And the project details.
-$pid               = $formfields[pid];
-$proj_name	   = addslashes($formfields[proj_name]);
-$proj_URL          = addslashes($formfields[proj_URL]);
-$proj_funders      = addslashes($formfields[proj_funders]);
-$proj_whynotpublic = addslashes($formfields[proj_whynotpublic]);
-$proj_members      = $formfields[proj_members];
-$proj_pcs          = $formfields[proj_pcs];
-$proj_why	   = addslashes($formfields[proj_why]);
-$proj_expires      = date("Y:m:d", time() + (86400 * 120));
+#
+# Now for the new Project
+#
+$args = array();
+$args["name"]              = $formfields["pid"];
+$args["short description"] = $formfields["proj_name"];
+$args["URL"]               = $formfields["proj_URL"];
+$args["members"]           = $formfields["proj_members"];
+$args["num_pcs"]           = $formfields["proj_pcs"];
+$args["long description"]  = $formfields["proj_why"];
+$args["funders"]           = $formfields["proj_funders"];
+$args["whynotpublic"]      = $formfields["proj_whynotpublic"];
 
 if (!isset($formfields[proj_public]) ||
-    strcmp($formfields[proj_public], "checked")) {
-    $proj_public = "No";
-    $public = 0;
+    $formfields[proj_public] != "checked") {
+    $args["public"] = 0;
 }
 else {
-    $proj_public = "Yes";
-    $public = 1;
+    $args["public"] = 1;
 }
 if (!isset($formfields[proj_linked]) ||
-    strcmp($formfields[proj_linked], "checked")) {
-    $proj_linked = "No";
-    $linked = 0;
+    $formfields[proj_linked] != "checked") {
+    $args["linkedtous"] = 0;
 }
 else {
-    $proj_linked = "Yes";
-    $linked = 1;
+    $args["linkedtous"] = 1;
 }
 if (isset($formfields[proj_plabpcs]) &&
     $formfields[proj_plabpcs] == "checked") {
-    $proj_plabpcs = "Yes";
-    $plabpcs = 1;
-}
-else {
-    $proj_plabpcs = "No";
-    $plabpcs = 0;
+    $args["plab"] = 1;
 }
 if (isset($formfields[proj_ronpcs]) &&
     $formfields[proj_ronpcs] == "checked") {
-    $proj_ronpcs = "Yes";
-    $ronpcs = 1;
-}
-else {
-    $proj_ronpcs = "No";
-    $ronpcs = 0;
+    $args["ron"] = 1;
 }
 
-
-#
-# Now for the new Project
-#
-$args = array();
-$args["expires"]       = $proj_expires;
-$args["name"]	       = $proj_name;
-$args["URL"]           = $proj_URL;
-$args["num_members"]   = $proj_members;
-$args["num_pcs"]       = $proj_pcs;
-$args["why"]           = $proj_why;
-$args["funders"]       = $proj_funders;
-$args["num_pcplab"]    = $plabpcs;
-$args["num_ron"]       = $ronpcs;
-$args["public"]        = $public;
-$args["public_whynot"] = $proj_whynotpublic;
-$args["linked_to_us"]  = $linked;
-
-if (! ($project = Project::NewProject($pid, $leader, $args))) {
-	TBERROR("Could not create new project '$pid'!", 1);
+if (! ($project = Project::NewNewProject($leader, $args, $error))) {
+    $errors["Error Creating Project"] = $error;
+    SPITFORM($formfields, $returning, $errors);
+    PAGEFOOTER();
+    return;
 }
 
 #
-# If a new user, do not send the full blown message until verified.
-#
-if ($returning || $FirstInitState) {
-    $unix_gid  = $project->unix_gid();
-    $unix_name = $project->unix_name();
-
-    #
-    # The mail message to the approval list.
-    # 
-    TBMAIL($TBMAIL_APPROVAL,
-	   "New Project '$pid' ($proj_head_uid)",
-	   "'$usr_name' wants to start project '$pid'.\n".
-	   "\n".
-	   "Name:            $usr_name ($proj_head_uid)\n".
-	   "Returning User?: $usr_returning\n".
-	   "Email:           $usr_email\n".
-	   "User URL:        $usr_URL\n".
-	   "Project:         $proj_name\n".
-	   "Expires:         $proj_expires\n".
-	   "Project URL:     $proj_URL\n".
-	   "Public URL:      $proj_public\n".
-	   "Why Not Public:  $proj_whynotpublic\n".
-	   "Link to Us?:     $proj_linked\n".
-	   "Funders:         $proj_funders\n".
-	   "Job Title:       $usr_title\n".
-	   "Affiliation:     $usr_affil\n".
-	   "Address 1:       $usr_addr\n".
-	   "Address 2:       $usr_addr2\n".
-	   "City:            $usr_city\n".
-	   "State:           $usr_state\n".
-	   "ZIP/Postal Code: $usr_zip\n".
-	   "Country:         $usr_country\n".
-	   "Phone:           $usr_phone\n".
-	   "Members:         $proj_members\n".
-	   "PCs:             $proj_pcs\n".
-	   "Planetlab PCs:   $proj_plabpcs\n".
-	   "RON PCs:         $proj_ronpcs\n".
-	   "Unix GID:        $unix_name ($unix_gid)\n".
-	   "Reasons:\n$proj_why\n\n".
-	   "Please review the application and when you have made a \n".
-	   "decision, go to $TBWWW and\n".
-	   "select the 'Project Approval' page.\n\n".
-	   "They are expecting a result within 72 hours.\n", 
-	   "From: $usr_name '$proj_head_uid' <$usr_email>\n".
-	   "Reply-To: $TBMAIL_APPROVAL\n".
-	   "Errors-To: $TBMAIL_WWW");
-}
-else {
-    TBMAIL($TBMAIL_APPROVAL,
-	   "New Project '$pid' ($proj_head_uid)",
-	   "'$usr_name' wants to start project '$pid'.\n".
-	   "\n".
-	   "Name:            $usr_name ($proj_head_uid)\n".
-	   "Email:           $usr_email\n".
-	   "Returning User?: No\n".
-	   "\n".
-	   "No action is necessary until the user has verified the account.\n",
-	   "From: $usr_name '$proj_head_uid' <$usr_email>\n".
-	   "Reply-To: $TBMAIL_APPROVAL\n".
-	   "Errors-To: $TBMAIL_WWW");
-}
-
+# Need to do some extra work for the first project; eventually move to backend
+# 
 if ($FirstInitState) {
-    #
-    # The first user gets admin status and some extra groups, etc.
-    #
-    DBQueryFatal("update users set ".
-		 "  admin=1,status='". TBDB_USERSTATUS_UNAPPROVED . "' " .
-		 "where uid='$proj_head_uid'");
-
-    DBQueryFatal("insert into unixgroup_membership set ".
-		 "uid='$proj_head_uid', gid='wheel'");
-    
-    DBQueryFatal("insert into unixgroup_membership set ".
-		 "uid='$proj_head_uid', gid='$TBADMINGROUP'");
-
     Group::Initialize($proj_head_uid, $pid);
     
     #
diff --git a/www/project_defs.php b/www/project_defs.php
index d84ccd0225..f259d31c16 100644
--- a/www/project_defs.php
+++ b/www/project_defs.php
@@ -207,6 +207,76 @@ class Project
 	return $newproject;
     }
 
+    function NewNewProject($leader, $args, &$error) {
+	global $suexec_output, $suexec_output_array;
+
+        #
+        # Generate a temporary file and write in the XML goo.
+        #
+	$xmlname = tempnam("/tmp", "newproj");
+	if (! $xmlname) {
+	    TBERROR("Could not create temporary filename", 0);
+	    $error = "Transient error; please try again later.";
+	    return null;
+	}
+	if (! ($fp = fopen($xmlname, "w"))) {
+	    TBERROR("Could not open temp file $xmlname", 0);
+	    $error = "Transient error; please try again later.";
+	    return null;
+	}
+
+	# Need to say who is going to be leading this project.
+	$args["leader"] = $leader->uid();
+
+	fwrite($fp, "<project>\n");
+	foreach ($args as $name => $value) {
+	    fwrite($fp, "<attribute name=\"$name\">");
+	    fwrite($fp, "  <value>" . htmlspecialchars($value) . "</value>");
+	    fwrite($fp, "</attribute>\n");
+	}
+	fwrite($fp, "</project>\n");
+	fclose($fp);
+	chmod($xmlname, 0666);
+
+	$retval = SUEXEC("nobody", "nobody", "webnewproj $xmlname",
+			 SUEXEC_ACTION_IGNORE);
+
+	if ($retval) {
+	    if ($retval < 0) {
+		$error = "Transient error; please try again later.";
+		SUEXECERROR(SUEXEC_ACTION_CONTINUE);
+	    }
+	    else {
+		$error = $suexec_output;
+	    }
+	    return null;
+	}
+
+        #
+        # Parse the last line of output. Ick.
+        #
+	unset($matches);
+	
+	if (!preg_match("/^User\s+(\w+)\/(\d+)\s+/",
+			$suexec_output_array[count($suexec_output_array)-1],
+			$matches)) {
+	    $error = "Transient error; please try again later.";
+	    SUEXECERROR(SUEXEC_ACTION_CONTINUE);
+	    return null;
+	}
+	$pid_idx = $matches[2];
+	$newproj = Project::Lookup($pid_idx);
+	if (! $newproj) {
+	    $error = "Transient error; please try again later.";
+	    TBERROR("Could not lookup new project $pid_idx", 0);
+	    return null;
+	}
+	# Unlink this here, so that the file is left behind in case of error.
+	# We can then create the project by hand from the xmlfile, if desired.
+	unlink($xmlname);
+	return $newproj;
+    }    
+
     #
     # Access Check, which for now uses the global function to avoid duplication
     # until all code is changed.
diff --git a/www/showpubkeys.php3 b/www/showpubkeys.php3
index 51cc369b74..5cd9257342 100644
--- a/www/showpubkeys.php3
+++ b/www/showpubkeys.php3
@@ -1,7 +1,7 @@
 <?php
 #
 # EMULAB-COPYRIGHT
-# Copyright (c) 2000-2003, 2005, 2006 University of Utah and the Flux Group.
+# Copyright (c) 2000-2003, 2005, 2006, 2007 University of Utah and the Flux Group.
 # All rights reserved.
 #
 include("defs.php3");
@@ -324,7 +324,7 @@ if (count($errors)) {
 # Okay, first run the script in verify mode to see if the key is
 # parsable. If it is, then do it for real.
 #
-if (ADDPUBKEY($uid, "webaddpubkey -n -u $target_uid $addpubkeyargs")) {
+if (ADDPUBKEY("-n -u $target_uid $addpubkeyargs")) {
     $errors["Pubkey Format"] = "Could not be parsed. Is it a public key?";
     SPITFORM($formfields, $errors);
     PAGEFOOTER();
@@ -333,7 +333,7 @@ if (ADDPUBKEY($uid, "webaddpubkey -n -u $target_uid $addpubkeyargs")) {
 #
 # Insert key, update authkeys files and nodes if appropriate.
 #
-ADDPUBKEY($uid, "webaddpubkey -u $target_uid $addpubkeyargs");
+ADDPUBKEY("-u $target_uid $addpubkeyargs");
 
 #
 # Redirect back, avoiding a POST in the history.
diff --git a/www/user_defs.php b/www/user_defs.php
index 5ede5dea96..4c3354e2be 100644
--- a/www/user_defs.php
+++ b/www/user_defs.php
@@ -427,6 +427,81 @@ class User
 	return $newuser;
     }
 
+    function NewNewUser($flags, $args, &$error) {
+	global $suexec_output, $suexec_output_array;
+	$typearg = "";
+
+	if ($flags & TBDB_NEWACCOUNT_PROJLEADER)
+	    $typearg = "-t leader";
+	elseif ($flags & TBDB_NEWACCOUNT_WIKIONLY)
+	    $typearg = "-t wikionly";
+	elseif ($flags & TBDB_NEWACCOUNT_WEBONLY)
+	    $typearg = "-t webonly";
+
+        #
+        # Generate a temporary file and write in the XML goo.
+        #
+	$xmlname = tempnam("/tmp", "newuser");
+	if (! $xmlname) {
+	    TBERROR("Could not create temporary filename", 0);
+	    $error = "Transient error; please try again later.";
+	    return null;
+	}
+	if (! ($fp = fopen($xmlname, "w"))) {
+	    TBERROR("Could not open temp file $xmlname", 0);
+	    $error = "Transient error; please try again later.";
+	    return null;
+	}
+
+	fwrite($fp, "<user>\n");
+	foreach ($args as $name => $value) {
+	    fwrite($fp, "<attribute name=\"$name\">");
+	    fwrite($fp, "  <value>" . htmlspecialchars($value) . "</value>");
+	    fwrite($fp, "</attribute>\n");
+	}
+	fwrite($fp, "</user>\n");
+	fclose($fp);
+	chmod($xmlname, 0666);
+
+	$retval = SUEXEC("nobody", "nobody", "webnewuser $typearg $xmlname",
+			 SUEXEC_ACTION_IGNORE);
+
+	if ($retval) {
+	    if ($retval < 0) {
+		$error = "Transient error; please try again later.";
+		SUEXECERROR(SUEXEC_ACTION_CONTINUE);
+	    }
+	    else {
+		$error = $suexec_output;
+	    }
+	    return null;
+	}
+
+        #
+        # Parse the last line of output. Ick.
+        #
+	unset($matches);
+	
+	if (!preg_match("/^User\s+(\w+)\/(\d+)\s+/",
+			$suexec_output_array[count($suexec_output_array)-1],
+			$matches)) {
+	    $error = "Transient error; please try again later.";
+	    SUEXECERROR(SUEXEC_ACTION_CONTINUE);
+	    return null;
+	}
+	$uid_idx = $matches[2];
+	$newuser = User::Lookup($uid_idx);
+	if (! $newuser) {
+	    $error = "Transient error; please try again later.";
+	    TBERROR("Could not lookup new user $uid_idx", 0);
+	    return null;
+	}
+	# Unlink this here, so that the file is left behind in case of error.
+	# We can then create the user by hand from the xmlfile, if desired.
+	unlink($xmlname);
+	return $newuser;	    
+    }    
+
     #
     # Delete a user, but JUST from the users table. 
     #
diff --git a/www/verifyusr.php3 b/www/verifyusr.php3
index 134e01327b..156647d6ea 100644
--- a/www/verifyusr.php3
+++ b/www/verifyusr.php3
@@ -49,185 +49,19 @@ if ($key != $this_user->verify_key()) {
 	      "Please enter the correct key.", 1);
 }
 
-function INFORMLEADERS($this_user) {
-    global $TBWWW, $TBMAIL_APPROVAL, $TBMAIL_AUDIT, $TBMAIL_WWW;
+if ($status == TBDB_USERSTATUS_NEWUSER) {
+    STARTBUSY("Verifying $uid");
 
-    #
-    # Grab user info.
-    #
-    $usr_email   = $this_user->email();
-    $usr_URL     = $this_user->URL();
-    $usr_addr    = $this_user->addr();
-    $usr_addr2	 = $this_user->addr2();
-    $usr_city	 = $this_user->city();
-    $usr_state	 = $this_user->state();
-    $usr_zip	 = $this_user->zip();
-    $usr_country = $this_user->country();
-    $usr_name    = $this_user->name();
-    $usr_phone   = $this_user->phone();
-    $usr_title   = $this_user->title();
-    $usr_affil   = $this_user->affil();
-    $uid_idx     = $this_user->uid_idx();
-    $uid         = $this_user->uid();
-
-    #
-    # Get the list of all project/groups this users has tried to join
-    # but whose membership messages where delayed until the user verified
-    # himself.
-    #
-    $group_result =
-	DBQueryFatal("select gid_idx from group_membership ".
-		     "where uid_idx='$uid_idx' and trust='none'");
-				     
-     while ($row = mysql_fetch_array($group_result)) {
-	 $gid_idx = $row["gid_idx"];
-
-	 if (! ($group = Group::Lookup($gid_idx))) {
-	     TBERROR("Could not find group record for $gid_idx", 1);
-	 }
-	 $project     = $group->Project();
-	 $projleader  = $project->GetLeader();
-	 $groupleader = $group->GetLeader();
-	 $pid         = $project->pid();
-	 $gid         = $group->gid();
-
-	 if ($this_user->SameUser($projleader)) {
-	     #
-	     # Project leader verifying himself. 
-	     # 
-	     $proj_name		= $project->name();
-	     $proj_URL          = $project->URL();
-	     $proj_funders      = $project->funders();
-	     $proj_public       = ($project->public() ? "Yes" : "No");
-	     $proj_linked       = ($project->linked_to_us() ? "Yes" : "No");
-	     $proj_whynotpublic = $project->public_whynot();
-	     $proj_members      = $project->num_members();
-	     $proj_pcs          = $project->num_pcs();
-	     $proj_plabpcs      = $project->num_pcplab();
-	     $proj_ronpcs       = $project->num_ron();
-	     $proj_why		= $project->why();
-	     $unix_gid          = $group->unix_gid();
-	     $unix_name         = $group->unix_name();
-	     
-	     TBMAIL($TBMAIL_APPROVAL,
-		"New Project '$pid' ($uid)",
-		"'$usr_name' wants to start project '$pid'.\n".
-		"\n".
-		"Name:            $usr_name ($uid)\n".
-		"Returning User?: No\n".
-		"Email:           $usr_email\n".
-		"User URL:        $usr_URL\n".
-		"Project:         $proj_name\n".
-		"Project URL:     $proj_URL\n".
-		"Public URL:      $proj_public\n".
-		"Why Not Public:  $proj_whynotpublic\n".
-		"Linked to Us?:   $proj_linked\n".
-		"Funders:         $proj_funders\n".
-		"Job Title:       $usr_title\n".
-		"Affiliation:     $usr_affil\n".
-		"Address 1:       $usr_addr\n".
-		"Address 2:       $usr_addr2\n".
-		"City:            $usr_city\n".
-		"State:           $usr_state\n".
-		"ZIP/Postal Code: $usr_zip\n".
-		"Country:         $usr_country\n".
-		"Phone:           $usr_phone\n".
-		"Members:         $proj_members\n".
-		"PCs:             $proj_pcs\n".
-		"Planetlab PCs:   $proj_plabpcs\n".
-		"RON PCs:         $proj_ronpcs\n".
-		"Unix GID:        $unix_name ($unix_gid)\n".
-		"Reasons:\n$proj_why\n\n".
-		"Please review the application and when you have made \n".
-		"a decision, go to $TBWWW and\n".
-		"select the 'Project Approval' page.\n\n".
-		"They are expecting a result within 72 hours.\n", 
-		"From: $usr_name '$uid' <$usr_email>\n".
-		"Reply-To: $TBMAIL_APPROVAL\n".
-		"Errors-To: $TBMAIL_WWW");
-	 }
-	 else {
-	     $leader_name  = $groupleader->name();
-	     $leader_email = $groupleader->email();
-	     $leader_uid   = $groupleader->uid();
-	     $allleaders   = $group->LeaderMailList();
-	     
-	     TBMAIL("$leader_name '$leader_uid' <$leader_email>",
-		"$uid $pid Project Join Request",
-		"$usr_name is trying to join your group $gid in project $pid.".
-		"\n".
-		"\n".
-		"Contact Info:\n".
-		"Name:            $usr_name\n".
-		"Emulab ID:       $uid\n".
-		"Email:           $usr_email\n".
-		"User URL:        $usr_URL\n".
-		"Job Title:       $usr_title\n".
-		"Affiliation:     $usr_affil\n".
-		"Address 1:       $usr_addr\n".
-		"Address 2:       $usr_addr2\n".
-		"City:            $usr_city\n".
-		"State:           $usr_state\n".
-		"ZIP/Postal Code: $usr_zip\n".
-		"Country:         $usr_country\n".
-		"Phone:           $usr_phone\n".
-		"\n".
-		"Please return to $TBWWW,\n".
-		"log in, select the 'New User Approval' page, and enter\n".
-		"your decision regarding $usr_name's membership in your\n".
-		"project.\n\n".
-		"Thanks,\n".
-		"Testbed Operations\n",
-		"From: $TBMAIL_APPROVAL\n".
-		"Cc: $allleaders\n".
-		"Bcc: $TBMAIL_AUDIT\n".
-		"Errors-To: $TBMAIL_WWW");
-	 }
-     }
-}
-
-if (strcmp($status, TBDB_USERSTATUS_UNVERIFIED) == 0) {
-    $this_user->SetStatus(TBDB_USERSTATUS_ACTIVE);
+    SUEXEC("nobody", "nobody", "webtbacct verify $uid", SUEXEC_ACTION_DIE);
     
-    TBMAIL($TBMAIL_AUDIT,
-	   "User '$uid' has been verified",
-	   "\n".
-	   "User '$uid' has been verified.\n".
-           "Status has been changed from 'unverified' to 'active'\n".
-	   "\n".
-	   "Testbed Operations\n",
-	   "From: $TBMAIL_OPS\n".
-	   "Errors-To: $TBMAIL_WWW");
-
-    echo "<p>".
-         "Because your membership has already been approved, ".
-	 "you are now an active user of emulab. ".
-	 "Click on the 'Home' link at your left, and any options ".
-	 "that are now available to you will appear.\n";
-}
-elseif (strcmp($status, TBDB_USERSTATUS_NEWUSER) == 0) {
-    $newstatus = ($wikionly ?
-		  TBDB_USERSTATUS_ACTIVE : TBDB_USERSTATUS_UNAPPROVED);
-    
-    $this_user->SetStatus($newstatus);
-
-    TBMAIL($TBMAIL_AUDIT,
-	   "User '$uid' has been verified",
-	   "\n".
-	   "User '$uid' has been verified.\n".
-           "Status has been changed from 'newuser' to '$newstatus'\n".
-	   "\n".
-	   "Testbed Operations\n",
-	   "From: $TBMAIL_OPS\n".
-	   "Errors-To: $TBMAIL_WWW");
-
     if ($wikionly) {
 	#
 	# For wikionly accounts, build the account now.
 	# Just builds the wiki account of course (nothing else).
 	#
-	SUEXEC("nobody", $TBADMINGROUP, "webtbacct add $uid",
-	       SUEXEC_ACTION_DIE);
+	SUEXEC("nobody", "nobody", "webtbacct add $uid", SUEXEC_ACTION_DIE);
+
+	STOPBUSY();
 
 	#
 	# The backend sets the actual WikiName
@@ -235,13 +69,14 @@ elseif (strcmp($status, TBDB_USERSTATUS_NEWUSER) == 0) {
 	$this_user->Refresh();
 	$wikiname = $this_user->wikiname();
 
-	echo "You have been verified. You may now access the Wiki at<br>".
+	echo "<br>".
+	    "You have been verified. You may now access the Wiki at<br>".
 	    "<a href='$WIKIURL/$wikiname'>$WIKIURL/$wikiname</a>\n";
     }
     else {
-	INFORMLEADERS($this_user);
+	STOPBUSY();
 
-	echo "<p>".
+	echo "<br>".
 	     "You have now been verified. However, your application ".
 	    "has not yet been approved. You will receive ".
 	    "email when that has been done.\n";
diff --git a/www/widearea_register.php b/www/widearea_register.php
index 81d23a88b5..151e364c72 100644
--- a/www/widearea_register.php
+++ b/www/widearea_register.php
@@ -717,7 +717,7 @@ if (!$returning) {
             #
             # Verify key format.
             #
-	    if (ADDPUBKEY(null, "webaddpubkey -n -k '$usr_key' ")) {
+	    if (ADDPUBKEY("-n -k '$usr_key' ")) {
 		$errors["Pubkey Format"] =
 		    "Could not be parsed. Is it a public key?";
 	    }
@@ -749,7 +749,7 @@ if (!$returning) {
             #
             # Verify key format.
             #
-	    if (ADDPUBKEY(null, "webaddpubkey -n $localfile ")) {
+	    if (ADDPUBKEY("-n $localfile ")) {
 		$errors["Pubkey Format"] =
 		    "Could not be parsed. Is it a public key?";
 	    }
@@ -867,7 +867,7 @@ if (!$returning) {
     $usr_dbid = $user->dbid();
 
     if (isset($addpubkeyargs)) {
-	ADDPUBKEY($usr_uid, "webaddpubkey -u $usr_uid $addpubkeyargs");
+	ADDPUBKEY("-u $usr_uid $addpubkeyargs");
     }
 }
 else {
-- 
GitLab