diff --git a/Makeconf.in b/Makeconf.in
index 3eb468f5ff3e212f13aa0d4b798e0a3235c5567c..1219243d9c1de781ceb012cb36e337ff090ae170 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 8d675becc5fba56ae6fa65ed36364a0af1798654..2b31de4d4fb06dd5d95140883da9ec90b2804648 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 f2f47288f6aceb10063cf9683a448f2bc57ddd08..9d5dcc6470f5f5c3a0a637d558e23ea432dd8c6e 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 0000000000000000000000000000000000000000..68a8b659ec39246c954a16666ec1a4947f305cbe
--- /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 0000000000000000000000000000000000000000..f8ed0f3a1c88b12a4c7a77d0e3759781c52332a0
--- /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 535bb243db0fd23a6c8a0bd80848d65124197fd3..18867c07f939eb8d77d76aa166c8718a2befde96 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 6db265932b59c2bdd73f706c0f2f8ab12a4070af..d2ee7bbc8aa280e9b9b703761c1c8002727a62fa 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 779cbb59a191386a7203d1ca6c5502a5933746a4..b0bb8c726024d42716f49ef81d68c75f6dd3bb9e 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 2eb167313d2141524070dbc27be3c039b2ee39ca..eaf2b6adef66b596763077ce7fb249bf3bed94fa 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 d31ae28c24501c6541d9c4afbf2af597b79095b8..676d763b5e0c79bf146342d92ae6789679454541 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 469ee4873200af94f0278e83c3e292d24121b1ad..e09dee65a0334858159fc6c82d7c099147dcc675 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 11605f10f11ecc3dcc09d429aae65f1578981d3b..8234fcf115d38a7c22adb215e83153dbca346836 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 d84ccd0225a60e6e622995257713c4aebb50a6a0..f259d31c16d1ebd60fe9f991e68af7d376851a07 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 51cc369b741212b00e84f0bd287b7b56a594fabf..5cd9257342ba0216ea7dc950204ca8c7addb0e9b 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 5ede5dea96337a745bdc1d8ea11dd37a4c393919..4c3354e2be302dd29f5d5cf2c3ebafa507311f77 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 134e01327b754f6891b099ff35bcc438cc7b4162..156647d6ea0555aefba7f769f6dd4a167e20d896 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 81d23a88b506ad18f1c251fceb541a89749ccf03..151e364c72e47ed8c90cca45f21b5a707d507f17 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 {