From d883dbc516649e1e013f749b8aa2da36fb6b67b3 Mon Sep 17 00:00:00 2001
From: "Leigh B. Stoller" <stoller@flux.utah.edu>
Date: Sun, 20 Mar 2005 15:18:51 +0000
Subject: [PATCH] An initial checkpoint of the wiki support. This is just the
 core stuff; the rest of the code that ties it into emulab is still scattered
 around my devel tree and under test.

---
 wiki/GNUmakefile.in     |  63 ++++
 wiki/addwikiproj.in     | 187 +++++++++++
 wiki/addwikiuser.in     | 193 +++++++++++
 wiki/delwikiuser.in     | 150 +++++++++
 wiki/setwikigroups.in   | 167 ++++++++++
 wiki/usertemplate.in    |  20 ++
 wiki/webhometemplate.in |  20 ++
 wiki/wikiproxy.in       | 707 ++++++++++++++++++++++++++++++++++++++++
 wiki/wikisetup.in       | 254 +++++++++++++++
 9 files changed, 1761 insertions(+)
 create mode 100644 wiki/GNUmakefile.in
 create mode 100644 wiki/addwikiproj.in
 create mode 100644 wiki/addwikiuser.in
 create mode 100644 wiki/delwikiuser.in
 create mode 100644 wiki/setwikigroups.in
 create mode 100644 wiki/usertemplate.in
 create mode 100644 wiki/webhometemplate.in
 create mode 100644 wiki/wikiproxy.in
 create mode 100644 wiki/wikisetup.in

diff --git a/wiki/GNUmakefile.in b/wiki/GNUmakefile.in
new file mode 100644
index 0000000000..cdf9536049
--- /dev/null
+++ b/wiki/GNUmakefile.in
@@ -0,0 +1,63 @@
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2000-2005 University of Utah and the Flux Group.
+# All rights reserved.
+#
+
+SRCDIR		= @srcdir@
+TESTBED_SRCDIR	= @top_srcdir@
+OBJDIR		= ..
+SUBDIR		= wiki
+
+include $(OBJDIR)/Makeconf
+
+SBIN_SCRIPTS		= addwikiuser addwikiproj wikisetup delwikiuser \
+			  setwikigroups
+
+CTRL_SBIN_SCRIPTS	= wikiproxy
+CTRL_LIB_FILES		= usertemplate webhometemplate
+
+#
+# Force dependencies on the scripts so that they will be rerun through
+# configure if the .in file is changed.
+# 
+all:	$(SBIN_SCRIPTS) $(CTRL_SBIN_SCRIPTS) $(CTRL_LIB_FILES)
+
+include $(TESTBED_SRCDIR)/GNUmakerules
+
+install: $(addprefix $(INSTALL_SBINDIR)/, $(SBIN_SCRIPTS)) \
+	 $(addprefix $(INSTALL_DIR)/opsdir/sbin/, $(CTRL_SBIN_SCRIPTS)) \
+	 $(addprefix $(INSTALL_DIR)/opsdir/lib/wiki/, $(CTRL_LIB_FILES))
+
+boss-install: install
+
+post-install: 
+	chown root $(INSTALL_SBINDIR)/addwikiuser
+	chmod u+s $(INSTALL_SBINDIR)/addwikiuser
+	chown root $(INSTALL_SBINDIR)/delwikiuser
+	chmod u+s $(INSTALL_SBINDIR)/delwikiuser
+	chown root $(INSTALL_SBINDIR)/addwikiproj
+	chmod u+s $(INSTALL_SBINDIR)/addwikiproj
+	chown root $(INSTALL_SBINDIR)/addwikiproj
+	chmod u+s $(INSTALL_SBINDIR)/addwikiproj
+	chown root $(INSTALL_SBINDIR)/setwikigroups
+	chmod u+s $(INSTALL_SBINDIR)/setwikigroups
+
+#
+# Control node installation (okay, plastic)
+#
+control-install:	$(addprefix $(INSTALL_SBINDIR)/, $(CTRL_SBIN_SCRIPTS))\
+		$(addprefix $(INSTALL_LIBDIR)/wiki/, $(CTRL_LIB_FILES))\
+
+clean:
+	rm -f *.o core
+
+$(INSTALL_DIR)/opsdir/lib/wiki/%: %
+	@echo "Installing $<"
+	-mkdir -p $(INSTALL_DIR)/opsdir/lib/wiki
+	$(INSTALL_DATA) $< $@
+
+$(INSTALL_DIR)/opsdir/sbin/%: %
+	@echo "Installing $<"
+	-mkdir -p $(INSTALL_DIR)/opsdir/sbin
+	$(INSTALL) $< $@
diff --git a/wiki/addwikiproj.in b/wiki/addwikiproj.in
new file mode 100644
index 0000000000..81dd76d547
--- /dev/null
+++ b/wiki/addwikiproj.in
@@ -0,0 +1,187 @@
+#!/usr/bin/perl -wT
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2005 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use Getopt::Std;
+
+#
+# Add a project to the wiki on ops.
+#
+sub usage()
+{
+    print STDOUT "Usage: addwikiproj <pid>\n";
+    exit(-1);
+}
+my $optlist = "d";
+my $debug   = 0;
+
+#
+# Configure variables
+#
+my $TB		= "@prefix@";
+my $TBOPS       = "@TBOPSEMAIL@";
+my $CONTROL     = "@USERNODE@";
+my $BOSSNODE	= "@BOSSNODE@";
+my $WIKISUPPORT = @WIKISUPPORT@;
+my $SSH         = "$TB/bin/sshtb";
+my $WIKIPROXY   = "$TB/sbin/wikiproxy";
+
+#
+# Untaint the path
+# 
+$ENV{'PATH'} = "/bin:/usr/bin";
+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;
+
+#
+# We don't want to run this script unless its the real version.
+#
+if ($EUID != 0) {
+    die("*** $0:\n".
+	"    Must be setuid! Maybe its a development version?\n");
+}
+
+#
+# This script is setuid, so please do not run it as root. Hard to track
+# what has happened.
+# 
+if ($UID == 0) {
+    die("*** $0:\n".
+	"    Please do not run this as root! Its already setuid!\n");
+}
+
+#
+# If no wiki support, just exit. 
+#
+if (! $WIKISUPPORT) {
+    print "WIKI support is not enabled. Exit ...\n";
+    exit(0);
+}
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (@ARGV != 1) {
+    usage();
+}
+my $pid = $ARGV[0];
+
+#
+# Untaint args.
+#
+if ($pid =~ /^([-\w]+)$/) {
+    $pid = $1;
+}
+else {
+    die("Bad data in pid: $pid");
+}
+
+#
+# This script always does the right thing, so no permission checks.
+# In fact, all it does it call over to ops to run a script over there.
+# Note that the proxy will just update the password if the user already
+# exist in the wiki. 
+#
+# Look in the DB to see if there is already a wikiname defined. If
+# we use that. Otherwise have to form one from the pid. Ick.
+#
+my $query_result =
+    DBQueryFatal("select wikiname from groups where pid='$pid' and pid=gid");
+
+if (!$query_result->numrows) {
+    fatal("No such pid $pid in the DB!");
+}
+my ($wikiname) = $query_result->fetchrow_array();
+
+if (!defined($wikiname)) {
+    #
+    # The wikirules for web names are slightly easier to deal with.
+    # Note that there will also be a *Group name created from the token.
+    #
+    $wikiname = ucfirst($pid);
+
+    if ($wikiname =~ /[-_]/) {
+	my @tokens = split(/[-_]/, $wikiname);
+
+	$wikiname = "";
+
+	#
+	# Make sure the first letter of each token is a caps.
+	# 
+	foreach my $token (@tokens) {
+	    $wikiname .= ucfirst($token);
+	}
+    }
+
+    #
+    # Check to make sure the wikiname does not violate the wikirules!
+    # If it does, just skip. User will have to plug in a new name.
+    #
+    if (! ($wikiname =~ /^[A-Z]+[A-Za-z0-9]*$/)) {
+	print "Bad WikiName for Project $pid: $wikiname\n";
+	next;
+    }
+
+    #
+    # We are not likely to get dups for a project name, but make sure
+    # anyway.
+    #
+    $query_result =
+	DBQueryFatal("select pid from groups ".
+		     "where wikiname='$wikiname'");
+
+    if ($query_result->numrows) {
+	fatal("The wikiname for project $pid ($wikiname) is already in use!");
+    }
+    print "Selecting wikiname '$wikiname' for project $pid\n";
+
+    DBQueryFatal("update groups set wikiname='$wikiname' ".
+		 "where pid='$pid' and pid=gid");
+}
+
+#
+# For ssh.
+#
+$UID = $EUID;
+
+if ($CONTROL ne $BOSSNODE) {
+    my $optarg = ($debug ? "-d" : "");
+	
+    print "Adding project $pid to the wiki on $CONTROL.\n";
+
+    if (system("$SSH -host $CONTROL $WIKIPROXY ".
+	       "  $optarg addproject $pid $wikiname")) {
+	fatal("$WIKIPROXY failed on $CONTROL!");
+    }
+}
+exit(0);
+
+sub fatal($)
+{
+    my($mesg) = $_[0];
+
+    die("*** $0:\n".
+	"    $mesg\n");
+}
diff --git a/wiki/addwikiuser.in b/wiki/addwikiuser.in
new file mode 100644
index 0000000000..7b96696824
--- /dev/null
+++ b/wiki/addwikiuser.in
@@ -0,0 +1,193 @@
+#!/usr/bin/perl -wT
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2005 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use Getopt::Std;
+
+#
+# Add a user to the wiki on ops. Also allow update of password.
+#
+sub usage()
+{
+    print STDOUT "Usage: addwikiuser [-u] <uid>\n";
+    exit(-1);
+}
+my $optlist = "ud";
+my $update  = 0;
+my $debug   = 0;
+
+#
+# Configure variables
+#
+my $TB		= "@prefix@";
+my $TBOPS       = "@TBOPSEMAIL@";
+my $CONTROL     = "@USERNODE@";
+my $BOSSNODE	= "@BOSSNODE@";
+my $WIKISUPPORT = @WIKISUPPORT@;
+my $SSH         = "$TB/bin/sshtb";
+my $WIKIPROXY   = "$TB/sbin/wikiproxy";
+
+#
+# Untaint the path
+# 
+$ENV{'PATH'} = "/bin:/usr/bin";
+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;
+
+#
+# We don't want to run this script unless its the real version.
+#
+if ($EUID != 0) {
+    die("*** $0:\n".
+	"    Must be setuid! Maybe its a development version?\n");
+}
+
+#
+# This script is setuid, so please do not run it as root. Hard to track
+# what has happened.
+# 
+if ($UID == 0) {
+    die("*** $0:\n".
+	"    Please do not run this as root! Its already setuid!\n");
+}
+
+#
+# If no wiki support, just exit. 
+#
+if (! $WIKISUPPORT) {
+    print "WIKI support is not enabled. Exit ...\n";
+    exit(0);
+}
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"u"})) {
+    $update = 1;
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (@ARGV != 1) {
+    usage();
+}
+my $user = $ARGV[0];
+
+#
+# Untaint args.
+#
+if ($user =~ /^([-\w]+)$/) {
+    $user = $1;
+}
+else {
+    die("Bad data in user: $user.");
+}
+
+#
+# This script always does the right thing, so no permission checks.
+# In fact, all it does it call over to ops to run a script over there.
+# Note that adduser will just update the password if the user already
+# exist in the wiki. 
+#
+
+#
+# Look in the DB to see if there is already a wikiname defined. If
+# we use that. Otherwise have to form one from the user name. Ick.
+#
+my $query_result =
+    DBQueryFatal("select wikiname,usr_name,usr_email ".
+		 "from users where uid='$user'");
+
+if (!$query_result->numrows) {
+    fatal("No such user $user in the DB!");
+}
+my ($wikiname,$usr_name,$usr_email) = $query_result->fetchrow_array();
+
+if (!defined($wikiname)) {
+    my @tokens = split(/\s+|-/, $usr_name);
+
+    #
+    # Build a wikiname from the tokens. Lowercase each token, then
+    # captialize it, then run them all together. Oh, get rid of any
+    # non alphanum characters.
+    #
+    $wikiname = "";
+
+    foreach my $token (@tokens) {
+	$token = ucfirst(lc($token));
+	$token =~ s/\.//g;
+	$wikiname .= $token;
+    }
+
+    #
+    # Check to make sure the wikiname does not violate the wikirules!
+    # If it does, just skip. User will have to plug in a new name.
+    #
+    if (! ($wikiname =~ /^[A-Z]+[a-z]+[A-Z]+[A-Za-z0-9]*$/)) {
+	fatal("Bad WikiName: $wikiname. Not setting up account");
+    }
+
+    #
+    # Make sure that no other user has the same wikiname but a different
+    # email address. 
+    #
+    $query_result =
+	DBQueryFatal("select uid,usr_name from users ".
+		     "where wikiname='$wikiname' and usr_email!='$usr_email'");
+
+    if ($query_result->numrows) {
+	fatal("The wikiname for $user ($wikiname) is already in use!");
+    }
+    print "Selecting wikiname '$wikiname' for user $user\n";
+
+    DBQueryFatal("update users set wikiname='$wikiname' where uid='$user'");
+}
+
+#
+# For ssh.
+#
+$UID = $EUID;
+
+if ($CONTROL ne $BOSSNODE) {
+    my $optarg = ($debug ? "-d" : "");
+	
+    if ($update) {
+	print "Updating $user wiki info on $CONTROL.\n";
+    }
+    else {
+	print "Adding user $user to the wiki on $CONTROL.\n";
+    }
+
+    if (system("$SSH -host $CONTROL $WIKIPROXY ".
+	       "  $optarg adduser $user $wikiname")) {
+	fatal("$WIKIPROXY failed on $CONTROL!");
+    }
+}
+exit(0);
+
+sub fatal($)
+{
+    my($mesg) = $_[0];
+
+    die("*** $0:\n".
+	"    $mesg\n");
+}
diff --git a/wiki/delwikiuser.in b/wiki/delwikiuser.in
new file mode 100644
index 0000000000..3a99d4edc4
--- /dev/null
+++ b/wiki/delwikiuser.in
@@ -0,0 +1,150 @@
+#!/usr/bin/perl -wT
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2005 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use Getopt::Std;
+
+#
+# Delete a user from the wiki
+#
+sub usage()
+{
+    print STDOUT "Usage: delwikiuser <uid>\n";
+    exit(-1);
+}
+my $optlist = "d";
+my $debug   = 0;
+
+#
+# Configure variables
+#
+my $TB		= "@prefix@";
+my $TBOPS       = "@TBOPSEMAIL@";
+my $CONTROL     = "@USERNODE@";
+my $BOSSNODE	= "@BOSSNODE@";
+my $WIKISUPPORT = @WIKISUPPORT@;
+my $SSH         = "$TB/bin/sshtb";
+my $WIKIPROXY   = "$TB/sbin/wikiproxy";
+
+#
+# Untaint the path
+# 
+$ENV{'PATH'} = "/bin:/usr/bin";
+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;
+
+#
+# We don't want to run this script unless its the real version.
+#
+if ($EUID != 0) {
+    die("*** $0:\n".
+	"    Must be setuid! Maybe its a development version?\n");
+}
+
+#
+# This script is setuid, so please do not run it as root. Hard to track
+# what has happened.
+# 
+if ($UID == 0) {
+    die("*** $0:\n".
+	"    Please do not run this as root! Its already setuid!\n");
+}
+
+#
+# If no wiki support, just exit. 
+#
+if (! $WIKISUPPORT) {
+    print "WIKI support is not enabled. Exit ...\n";
+    exit(0);
+}
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (@ARGV != 1) {
+    usage();
+}
+my $user = $ARGV[0];
+
+#
+# Untaint args.
+#
+if ($user =~ /^([-\w]+)$/) {
+    $user = $1;
+}
+else {
+    die("Bad data in user: $user.");
+}
+
+#
+# This script always does the right thing, so no permission checks.
+# In fact, all it does it call over to ops to run a script over there.
+# Note that adduser will just update the password if the user already
+# exist in the wiki. 
+#
+
+#
+# Look in the DB to see if there is already a wikiname defined. If
+# we use that. Otherwise have to form one from the user name. Ick.
+#
+my $query_result =
+    DBQueryFatal("select wikiname ".
+		 "from users where uid='$user'");
+
+if (!$query_result->numrows) {
+    fatal("No such user $user in the DB!");
+}
+my ($wikiname) = $query_result->fetchrow_array();
+
+if (!defined($wikiname)) {
+    print "There is no wikiname defined in the DB. ".
+	"Must not have a wiki account!\n";
+    exit(0);
+}
+
+#
+# For ssh.
+#
+$UID = $EUID;
+
+if ($CONTROL ne $BOSSNODE) {
+    my $optarg = ($debug ? "-d" : "");
+	
+    print "Removing user $user from the wiki on $CONTROL.\n";
+
+    if (system("$SSH -host $CONTROL $WIKIPROXY ".
+	       "  $optarg deluser $user $wikiname")) {
+	fatal("$WIKIPROXY failed on $CONTROL!");
+    }
+}
+exit(0);
+
+sub fatal($)
+{
+    my($mesg) = $_[0];
+
+    die("*** $0:\n".
+	"    $mesg\n");
+}
diff --git a/wiki/setwikigroups.in b/wiki/setwikigroups.in
new file mode 100644
index 0000000000..43c4fe6228
--- /dev/null
+++ b/wiki/setwikigroups.in
@@ -0,0 +1,167 @@
+#!/usr/bin/perl -wT
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2005 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use Getopt::Std;
+
+#
+# Set the wiki groups for a user. Currently we just do the projects.
+#
+sub usage()
+{
+    print STDOUT "Usage: setwikigroups <uid>\n";
+    exit(-1);
+}
+my $optlist = "d";
+my $debug   = 0;
+my @glist   = ();
+
+#
+# Configure variables
+#
+my $TB		= "@prefix@";
+my $TBOPS       = "@TBOPSEMAIL@";
+my $CONTROL     = "@USERNODE@";
+my $BOSSNODE	= "@BOSSNODE@";
+my $WIKISUPPORT = @WIKISUPPORT@;
+my $SSH         = "$TB/bin/sshtb";
+my $WIKIPROXY   = "$TB/sbin/wikiproxy";
+
+#
+# Untaint the path
+# 
+$ENV{'PATH'} = "/bin:/usr/bin";
+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;
+
+#
+# We don't want to run this script unless its the real version.
+#
+if ($EUID != 0) {
+    die("*** $0:\n".
+	"    Must be setuid! Maybe its a development version?\n");
+}
+
+#
+# This script is setuid, so please do not run it as root. Hard to track
+# what has happened.
+# 
+if ($UID == 0) {
+    die("*** $0:\n".
+	"    Please do not run this as root! Its already setuid!\n");
+}
+
+#
+# If no wiki support, just exit. 
+#
+if (! $WIKISUPPORT) {
+    print "WIKI support is not enabled. Exit ...\n";
+    exit(0);
+}
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (@ARGV != 1) {
+    usage();
+}
+my $user = $ARGV[0];
+
+#
+# Untaint args.
+#
+if ($user =~ /^([-\w]+)$/) {
+    $user = $1;
+}
+else {
+    die("Bad data in user: $user.");
+}
+
+#
+# This script always does the right thing, so no permission checks.
+# In fact, all it does is call over to ops to run a script over there.
+# Note that adduser will just update the password if the user already
+# exist in the wiki. 
+#
+my $query_result =
+    DBQueryFatal("select p.pid,g.wikiname,p.trust from group_membership as p ".
+		 "left join groups as g on g.pid=p.pid and g.gid=p.gid ".
+		 "where uid='$user' and p.pid=g.gid and trust!='none'");
+
+while (my ($pid,$wikiname,$trust) = $query_result->fetchrow_array()) {
+    if (!defined($wikiname)) {
+	print "There is no wikiname defined in the DB for project $pid!\n";
+	next;
+    }
+    push(@glist, $wikiname);
+
+    #
+    # Add to the admin group for the project if proj/group root.
+    # This admin project name is hardwired in the wikiproxy. Sorry.
+    #
+    if ($trust eq "project_root" || $trust eq "group_root") {
+	push(@glist, "${wikiname}Admin");
+    }
+}
+
+# Admin users ... TBAdmin() test does not work for this test ...
+$query_result =
+    DBQueryFatal("select wikiname,admin from users where uid='$user'");
+my ($wikiname,$isadmin) = $query_result->fetchrow_array();
+if ($isadmin) {
+    push(@glist, "TWikiAdmin");
+}
+if (!defined($wikiname)) {
+    print "There is no wikiname defined in the DB. ".
+	"Must not have a wiki account!\n";
+    exit(0);
+}
+exit(0)
+    if (! @glist);
+
+#
+# For ssh.
+#
+$UID = $EUID;
+
+if ($CONTROL ne $BOSSNODE) {
+    my $optarg = ($debug ? "-d" : "");
+	
+    print "Setting wikigroups for $user on $CONTROL.\n";
+
+    if (system("$SSH -host $CONTROL $WIKIPROXY ".
+	       "  $optarg setgroups $wikiname @glist")) {
+	fatal("$WIKIPROXY failed on $CONTROL!");
+    }
+}
+exit(0);
+
+sub fatal($)
+{
+    my($mesg) = $_[0];
+
+    die("*** $0:\n".
+	"    $mesg\n");
+}
diff --git a/wiki/usertemplate.in b/wiki/usertemplate.in
new file mode 100644
index 0000000000..200a0e76ff
--- /dev/null
+++ b/wiki/usertemplate.in
@@ -0,0 +1,20 @@
+This is your TWiki home page. Feel free to customize it!  If you are
+not familiar with the %WIKITOOLNAME% collaboration platform, please
+visit %TWIKIWEB%.WelcomeGuest first. See the links at your left for
+more information on how to write and create TWiki pages.
+
+__These are your Projects/Groups__
+	* Projects/Groups:
+
+__Personal Preferences (details in %TWIKIWEB%.TWikiVariables)__
+	* Horizontal size of text edit box:
+		* Set EDITBOXWIDTH = 70
+	* Vertical size of text edit box:
+		* Set EDITBOXHEIGHT = 17
+	* Default state of the __link__ check box in the attach file page:
+		* Set ATTACHLINKBOX = 
+
+__Permissions (leave these alone unless you know what you are doing, or ask us)__
+	* Set ALLOWTOPICVIEW = 
+	* Set ALLOWTOPICCHANGE = 
+	* Set ALLOWTOPICRENAME = 
diff --git a/wiki/webhometemplate.in b/wiki/webhometemplate.in
new file mode 100644
index 0000000000..82511a5b99
--- /dev/null
+++ b/wiki/webhometemplate.in
@@ -0,0 +1,20 @@
+Welcome to the home of <b>%WEB%</b>. This is a web-based collaboration
+area for your project (or group).  If you are not familiar with the
+%WIKITOOLNAME% collaboration platform, please visit
+%TWIKIWEB%.WelcomeGuest first. See the links at your left for more
+information on how to write and create TWiki pages.
+
+	* YourFirstWikiTopic
+	* 
+
+
+__TWiki groups that are relevant to your Project/Group__
+
+	* [[%MAINWEB%.%WEB%Group][%WEB%Group]]
+	* [[%MAINWEB%.%WEB%AdminGroup][%WEB%AdminGroup]]
+
+
+__Site Tools of the %WEB% Web__
+
+%INCLUDE{"%TWIKIWEB%.WebSiteTools"}%
+
diff --git a/wiki/wikiproxy.in b/wiki/wikiproxy.in
new file mode 100644
index 0000000000..72c316f2bb
--- /dev/null
+++ b/wiki/wikiproxy.in
@@ -0,0 +1,707 @@
+#!/usr/bin/perl -w
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2005 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use Getopt::Std;
+use Errno;
+    
+#
+# A wrapper for messing with the wiki support from boss.
+#
+sub usage()
+{
+    print "Usage: wikiproxy adduser <uid> <wikiname> or\n";
+    print "       wikiproxy deluser <uid> <wikiname> or\n";
+    print "       wikiproxy addproject <pid> <wikiname> or\n";
+    print "       wikiproxy setgroups <wikiuser> <wikigroup> ...\n";
+    exit(-1);
+}
+my $optlist = "d";
+my $debug   = 0;
+
+#
+# Configure variables
+#
+my $TB       = "@prefix@";
+my $TBOPS    = "@TBOPSEMAIL@";
+
+# Should be configure variables.
+my $WIKIDIR  = "/usr/local/www/data/twiki";
+
+# And other stuff.
+my $USERTEMPLATE = "$TB/lib/wiki/usertemplate";
+my $HOMETEMPLATE = "$TB/lib/wiki/webhometemplate";
+my $WIKIDATADIR  = "$WIKIDIR/data";
+my $WIKIUSERDIR  = "$WIKIDATADIR/Main";
+my $WIKIGROUPDIR = "$WIKIDATADIR/Main";
+my $WIKIARCHIVE  = "$WIKIDATADIR/_archive";
+my $WIKIPASSWD   = "$WIKIDIR/data/.htpasswd";
+my $WIKIUSER     = "nobody";
+my $WIKIGROUP    = "nobody";
+my $CI		 = "ci";
+    
+#
+# Turn off line buffering on output
+#
+$| = 1;
+
+#
+# Untaint the path
+# 
+$ENV{'PATH'} = "/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin";
+delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
+
+#
+# Only real root, cause the script has to read/write a pid file that
+# cannot be accessed by the user.
+#
+if ($UID != 0) {
+    die("*** $0:\n".
+	"    Must be root to run this script!\n");
+}
+
+#
+# Testbed Support libraries
+#
+use lib "@prefix@/lib";
+use libtestbed;
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (! @ARGV) {
+    usage();
+}
+
+my $action = shift(@ARGV);
+
+if ($action eq "adduser") {
+    exit(AddWikiUser(@ARGV));
+}
+elsif ($action eq "deluser") {
+    exit(DelWikiUser(@ARGV));
+}
+elsif ($action eq "addproject") {
+    exit(AddWikiProject(@ARGV));
+}
+elsif ($action eq "setgroups") {
+    exit(SetWikiGroups(@ARGV));
+}
+else {
+    die("*** $0:\n".
+	"    Do not know what to do with '$action'!\n");
+}
+exit(0);
+
+#
+# Add entry (or update password) for a user.
+#
+sub AddWikiUser(@)
+{
+    usage()
+	if (@_ != 2);
+
+    my ($user, $wikiname) = @_;
+
+    chdir("$WIKIUSERDIR") or
+	fatal("Could not chdir to $WIKIUSERDIR");
+
+    #
+    # Grab the password hash. 
+    #
+    my ($name,$passwd) = getpwnam($user);
+    fatal("No such user $user in the password file")
+	if (!defined($name));
+
+    #
+    # If the password file does not have the entry, just tack it onto
+    # the end of the file. Otherwise we have to get fancier so we 
+    # change the password atomically. It appears that the TWiki code
+    # does not lock the password file when it makes it own changes! 
+    #
+    if (system("egrep -q -s '^${wikiname}:' $WIKIPASSWD")) {
+	print "Adding $user to $WIKIPASSWD\n"
+	    if ($debug);
+	
+	open(PWD, ">> $WIKIPASSWD") or
+	    fatal("Could not open $WIKIPASSWD for appending");
+	print PWD "${wikiname}:${passwd}\n";
+	close(PWD);
+    }
+    else {
+	#
+	# Open up the file and read it, creating a new version.
+	#
+	my $data = "";
+	
+	print "Updating $wikiname in $WIKIPASSWD\n"
+	    if ($debug);
+	
+	open(PWD, "$WIKIPASSWD") or
+	    fatal("Could not open $WIKIPASSWD for reading");
+	
+	while (<PWD>) {
+	    if ($_ =~ /^${wikiname}:.*$/) {
+		$data .= "${wikiname}:${passwd}\n";
+	    }
+	    else {
+		$data .= $_;
+	    }
+	}
+	close(PWD);
+
+	open(PWD, "> ${WIKIPASSWD}.$$") or
+	    fatal("Could not open ${WIKIPASSWD}.$$ for writing");
+	print PWD $data;
+	close(PWD);
+
+	system("chown ${WIKIUSER}:${WIKIGROUP} ${WIKIPASSWD}.$$") == 0
+	    or fatal("Could not chown ${WIKIPASSWD}.$$");
+
+	rename("${WIKIPASSWD}.$$", $WIKIPASSWD)
+	    or fatal("Could not rename ${WIKIPASSWD}.$$");
+    }
+
+    #
+    # Create the stub topic file for the user if it does not exist.
+    #
+    return 0
+	if (-e "${WIKIUSERDIR}/${wikiname}.txt");
+
+    print "Creating ${WIKIUSERDIR}/${wikiname}.txt\n"
+	if ($debug);
+    
+    system("cp -p ${USERTEMPLATE} ${wikiname}.txt") == 0
+	or fatal("Could not copy ${USERTEMPLATE} to ${wikiname}.txt");
+
+    #
+    # Set the initial groups list to the user.
+    #
+    open(PREFS, "${wikiname}.txt") or
+	fatal("Could not open ${wikiname}.txt for reading");
+
+    while (<PREFS>) {
+	if ($_ =~ /^(.* Set ALLOWTOPIC.*)\r$/ ||
+	    $_ =~ /^(.* Set ALLOWTOPIC.*)$/) {
+	    $data .= $1 . "%MAINWEB%.${wikiname}\n";
+	}
+	else {
+	    $data .= $_;
+	}
+    }
+    close(PREFS);
+
+    #
+    # Now write the file out.
+    #
+    open(PREFS, "> ${wikiname}.txt") or
+	fatal("Could not open ${wikiname}.txt for writing");
+    print PREFS $data;
+    close(PREFS);
+    
+    #
+    # Check it in (locked).
+    #
+    CI("${wikiname}.txt", "AddWikiUser $user $wikiname") == 0
+        or fatal("Could not ci ${wikiname}.txt");
+    
+    return 0;
+}
+
+#
+# Delete entry for a user.
+#
+sub DelWikiUser(@)
+{
+    usage()
+	if (@_ != 2);
+
+    my ($user, $wikiuser) = @_;
+
+    chdir("$WIKIUSERDIR") or
+	fatal("Could not chdir to $WIKIUSERDIR");
+
+    #
+    # First, find groups the user is currently a member of, and remove.
+    #
+    my $currentgroups =
+	`egrep -l -s 'GROUP = .*${wikiuser}.*' *Group.txt`;
+    $currentgroups =~ s/Group.txt//g;
+
+    if (!$?) {
+	my @glist = split(/\s+/, $currentgroups);
+	    
+	print "Current groups for $wikiuser are @glist\n"
+	    if ($debug);
+
+	foreach my $group (@glist) {
+	    my $wikiname = "${group}Group";
+	    my $data     = "";
+
+	    print "Removing $wikiuser from $group\n";
+
+	    open(GRP, "${wikiname}.txt") or
+		fatal("DelWikiUser: ".
+		      "Could not open ${wikiname}.txt (read)\n");
+
+	    while (<GRP>) {
+		if ($_ =~ /^(\s*\*\s*Set\s*GROUP\s*=\s*)(.*)$/) {
+		    my @tokens = split(/,\s*/, $2);
+
+		    # Cut out the user.
+		    @tokens = grep {$_ ne $wikiuser} @tokens;
+
+		    # And reform the group list.
+		    $data .= $1 . join(", ", @tokens) . "\n";
+		}
+		else {
+		    $data .= $_;
+		}
+	    }
+	    close(GRP);
+
+	    #
+	    # Now write the new text back out to the file.
+	    #
+	    open(GRP, ">${wikiname}.txt") or
+		fatal("SetWikiGroups: ".
+		      "Could not open ${wikiname}.txt (write)\n");
+	    print GRP $data;
+	    close(GRP);
+	    
+	    #
+	    # Check it in (locked).
+	    #
+	    CI("${wikiname}.txt", "DelWikiUser $user $wikiuser") == 0
+		or fatal("Could not ci ${wikiname}.txt");
+	}
+    }
+    
+    #
+    # Remove user from the password file.
+    #
+    print "Removing $user from $WIKIPASSWD\n"
+	if ($debug);
+	
+    open(PWD, "$WIKIPASSWD") or
+	fatal("Could not open $WIKIPASSWD for reading");
+	
+    while (<PWD>) {
+	if ($_ =~ /^$user:.*$/) {
+	    next;
+	}
+	else {
+	    $data .= $_;
+	}
+    }
+    close(PWD);
+
+    open(PWD, "> ${WIKIPASSWD}.$$") or
+	fatal("Could not open ${WIKIPASSWD}.$$ for writing");
+    print PWD $data;
+    close(PWD);
+
+    system("chown ${WIKIUSER}:${WIKIGROUP} ${WIKIPASSWD}.$$") == 0
+	or fatal("Could not chown ${WIKIPASSWD}.$$");
+
+    rename("${WIKIPASSWD}.$$", $WIKIPASSWD)
+	or fatal("Could not rename ${WIKIPASSWD}.$$");
+
+    #
+    # Archive the user file. Removing users is kinda bogus cause I do not
+    # attempt to remove any other files the user created. We could figure
+    # this out of course, but not bothering right now.
+    #
+    if (! -e $WIKIARCHIVE) {
+	mkdir($WIKIARCHIVE, 0775) or
+	    fatal("Could not mkdir $WIKIDATADIR!");
+
+	system("chown ${WIKIUSER}:${WIKIGROUP} $WIKIARCHIVE") == 0
+	    or fatal("Could not chown $WIKIARCHIVE");
+    }
+    my $wikifile = "${wikiuser}.txt";    
+    my $oldname  = "${wikifile},v";
+    my $newname  = "${oldname}-" . TBDateTimeFSSafe();
+
+    print "Renaming $oldname to $WIKIARCHIVE/$newname\n"
+	if ($debug);
+
+    rename($oldname, "$WIKIARCHIVE/$newname")
+	or fatal("Could not rename $oldname to $WIKIARCHIVE/$newname");
+    unlink($wikifile)
+	or fatal("Could not remove $wikifile");
+
+    return 0;
+}
+
+#
+# Create a "web" for a new project.
+#
+sub AddWikiProject(@)
+{
+    my $data = "";
+    
+    usage()
+	if (@_ != 2);
+
+    my ($pid, $wikiname) = @_;
+
+    chdir("$WIKIDATADIR") or
+	fatal("Could not chdir to $WIKIDATADIR");
+
+    # Skip if already there. 
+    return 0
+	if (-d $wikiname);
+
+    print "Creating ${WIKIDATADIR}/${wikiname}\n"
+	if ($debug);
+
+    #
+    # Copy the _default directory to the new WEB name. This "creates"
+    # the new wiki web. 
+    #
+    system("/usr/site/bin/hier cp _default $wikiname") == 0
+	or fatal("Could not copy _default to $wikiname");
+
+    system("cp -p ${HOMETEMPLATE} ${wikiname}/WebHome.txt") == 0
+	or fatal("Could not copy ${HOMETEMPLATE} to ${wikiname}/WebHome.txt");
+
+    #
+    # We also need a group for this project to restrict who can access it.
+    #
+    AddWikiGroup($pid, "${wikiname}Group");
+
+    #
+    # And another group for the project leaders and group roots, who are
+    # allowed to change the preferences for the web.
+    #
+    AddWikiGroup("${pid}Root", "${wikiname}RootGroup");
+
+    #
+    # Now finish up in the new web directory.
+    # 
+    chdir("$WIKIDATADIR/$wikiname") or
+	fatal("Could not chdir to $WIKIDATADIR/$wikiname");
+
+    #
+    # Remove all the RCS control files. They are old and pointless.
+    # Create new ones instead. 
+    #
+    system("rm -f *.txt,v") == 0 or
+	fatal("Could not remove old RCS control files!");
+
+    system("chown ${WIKIUSER}:${WIKIGROUP} *.txt")
+	== 0 or fatal("Could not chown *.txt to ${WIKIUSER}:${WIKIGROUP}");
+
+    system("sudo -u nobody ci -l '-mInitial Revision by wikiproxy:\n".
+	   "AddWikiProject $pid $wikiname' ".
+	   "      '-t-Initial Revision by wikiproxy' *.txt") == 0
+        or fatal("Could not create new RCS control files.");
+
+    #
+    # Now edit the web preferences file to set who is allowed to access
+    # this web. Basically, only people in the project (wiki group) and
+    # the admin group.
+    #
+    open(PREFS, "WebPreferences.txt") or
+	fatal("Could not open WebPreferences.txt for reading");
+
+    while (<PREFS>) {
+	if ($_ =~ /^.* Set (ALLOWWEB.*) = \r$/ ||
+	    $_ =~ /^.* Set (ALLOWWEB.*) = $/) {
+	    $data .= "\t\t* Set $1 = " . "%MAINWEB%.${wikiname}Group\n";
+	}
+	elsif ($_ =~ /^(.* Set ALLOWTOPIC.*)\r$/ ||
+	       $_ =~ /^(.* Set ALLOWTOPIC.*)$/) {
+	    $data .= $1 . "%MAINWEB%.${wikiname}RootGroup\n";
+	}
+	elsif ($_ =~ /^(.* Set NOSEARCHALL.*)\r$/ ||
+	       $_ =~ /^(.* Set NOSEARCHALL.*)$/) {
+	    $data .= $1 . "on\n";
+	}
+	else {
+	    $data .= $_;
+	}
+    }
+    close(PREFS);
+
+    #
+    # Now write the file out and check it in.
+    #
+    open(PREFS, ">WebPreferences.txt") or
+	fatal("Could not open WebPreferences.txt for writing");
+    print PREFS $data;
+    close(PREFS);
+
+    CI("WebPreferences.txt", "AddWikiProject $pid $wikiname") == 0
+        or fatal("Could not ci WebPreferences.txt");
+
+    return 0;
+}
+
+#
+# Create a wiki group. 
+#
+sub AddWikiGroup(@)
+{
+    usage()
+	if (@_ != 2);
+
+    my ($group, $wikiname) = @_;
+
+    chdir("$WIKIGROUPDIR") or
+	fatal("Could not chdir to $WIKIGROUPDIR");
+
+    # Skip if already there. 
+    return 0
+	if (-d "${wikiname}.txt");
+
+    print "Creating ${WIKIGROUPDIR}/${wikiname}.txt\n"
+	if ($debug);
+
+    open(GRP, "> ${wikiname}.txt") or
+	fatal("Could not open ${WIKIGROUPDIR}/${wikiname}.txt for writing");
+
+    print GRP "*Project/Group Members for ${wikiname}*\n";
+    print GRP "\n";
+    print GRP "\t* Set GROUP = \n";
+    print GRP "\t* Set ALLOWTOPICCHANGE = %MAINWEB%.TWikiAdminGroup\n";
+    print GRP "\t* Set ALLOWTOPICRENAME = %MAINWEB%.TWikiAdminGroup\n";
+    print GRP "\n";
+    print GRP "__Any changes you make to this file will be LOST!__\n";
+    close(GRP);
+
+    #
+    # Check it in (locked).
+    #
+    CI("${wikiname}.txt", "AddWikiGroup $group $wikiname") == 0
+        or fatal("Could not ci ${wikiname}.txt");
+
+    return 0;
+}
+
+#
+# Set the wiki groups for a user.
+#
+sub SetWikiGroups(@)
+{
+    usage()
+	if (@_ < 1);
+
+    my $wikiuser  = shift(@_);
+    my @groups    = @_;
+    my %grouphash = ();
+    
+    foreach my $group (@groups) {
+	$grouphash{$group} = $group;
+    }
+
+    chdir("$WIKIGROUPDIR") or
+	fatal("Could not chdir to $WIKIGROUPDIR");
+
+    #
+    # First, find groups the user is currently a member of, and remove
+    # from those groups not supposed to be in.
+    #
+    my $currentgroups =
+	`egrep -l -s 'GROUP = .*${wikiuser}.*' *Group.txt`;
+    $currentgroups =~ s/Group.txt//g;
+
+    if (!$?) {
+	my @glist = split(/\s+/, $currentgroups);
+	    
+	print "Current groups for $wikiuser are @glist\n"
+	    if ($debug);
+
+	foreach my $group (@glist) {
+	    my $wikiname = "${group}Group";
+	    my $data     = "";
+
+	    next
+		if (exists($grouphash{$group}));
+	    
+	    print "Removing $wikiuser from $group\n";
+
+	    open(GRP, "${wikiname}.txt") or
+		fatal("SetWikiGroups: ".
+		      "Could not open ${wikiname}.txt (read)\n");
+
+	    while (<GRP>) {
+		if ($_ =~ /^(\s*\*\s*Set\s*GROUP\s*=\s*)(.*)$/) {
+		    my @tokens = split(/,\s*/, $2);
+
+		    # Cut out the user.
+		    @tokens = grep {$_ ne $wikiuser} @tokens;
+
+		    # And reform the group list.
+		    $data .= $1 . join(", ", @tokens) . "\n";
+		}
+		else {
+		    $data .= $_;
+		}
+	    }
+	    close(GRP);
+
+	    #
+	    # Now write the new text back out to the file.
+	    #
+	    open(GRP, ">${wikiname}.txt") or
+		fatal("SetWikiGroups: ".
+		      "Could not open ${wikiname}.txt (write)\n");
+	    print GRP $data;
+	    close(GRP);
+	    
+	    #
+	    # Check it in (locked).
+	    #
+	    CI("${wikiname}.txt", "SetWikiGroups $wikiuser @groups") == 0
+		or fatal("Could not ci ${wikiname}.txt");
+	}
+    }
+
+    #
+    # Now add user to other groups.
+    #
+    foreach my $group (@groups) {
+	my $wikiname = "${group}Group";
+	my $data     = "";
+	    
+	# Error if no such wikigroup
+	fatal("No group file for wiki group $wikiname!")
+	    if (! -e "${wikiname}.txt");
+
+	next
+	    if (!system("egrep -q -s 'GROUP = .*${wikiuser}.*' ".
+			"${wikiname}.txt\n"));
+	    
+	print "Adding $wikiuser to wikigroup ${wikiname}.txt\n";
+
+	open(GRP, "${wikiname}.txt") or
+	    fatal("SetWikiGroups: Could not open ${wikiname}.txt (read)\n");
+
+	while (<GRP>) {
+	    if ($_ =~ /^(\s*\*\s*Set\s*GROUP\s*=)\s*$/) {
+		# First name in the group.
+		$data .= $1 . " $wikiuser\n";
+	    }
+	    elsif ($_ =~ /^(\s*\*\s*Set\s*GROUP\s*=\s*)(.*)$/) {
+		$data .= $1 . "${2}, $wikiuser\n";
+	    }
+	    else {
+		$data .= $_;
+	    }
+	}
+	close(GRP);
+
+	#
+	# No write the new text back out to the file.
+	#
+	open(GRP, ">${wikiname}.txt") or
+	    fatal("SetWikiGroups: Could not open ${wikiname}.txt (write)\n");
+	print GRP $data;
+	close(GRP);
+
+	#
+	# Check it in (locked).
+	#
+	CI("${wikiname}.txt", "SetWikiGroups $wikiuser @groups") == 0
+	    or fatal("Could not ci ${wikiname}.txt");
+    }
+
+    #
+    # And finally, change the home page for the user. This is basically
+    # to avoid searching the entire wiki for the membership.
+    #
+    $data = "";
+    
+    open(PREFS, "${wikiuser}.txt") or
+	fatal("Could not open ${wikiuser}.txt for reading");
+
+    while (<PREFS>) {
+	if ($_ =~ /^(\s*\* Projects\/Groups:).*$/) {
+	    $data .= $1 . "\n";
+
+	    #
+	    # Add a list of groups.
+	    #
+	    foreach my $group (@groups) {
+		if (-d "../$group") {
+		    $data .= "\t\t* [[${group}.WebHome][${group}]]\n";
+		}
+		else {
+		    $data .= "\t\t* [[%MAINWEB%.${group}Group][${group}Group]]\n";
+		}
+	    }
+
+	    #
+	    # And skip the current ones.
+	    #
+	    while (<PREFS>) {
+		last
+		    if ($_ =~ /^\s$/);
+	    }
+	    $data .= "\n";
+	}
+	else {
+	    $data .= $_;
+	}
+    }
+    close(PREFS);
+
+    #
+    # Now write the file out.
+    #
+    open(PREFS, "> ${wikiuser}.txt") or
+	fatal("Could not open ${wikiuser}.txt for writing");
+    print PREFS $data;
+    close(PREFS);
+    
+    #
+    # Check it in (locked).
+    #
+    CI("${wikiuser}.txt", "SetWikiGroups $wikiuser @groups") == 0 
+        or fatal("Could not ci ${wikiuser}.txt");
+    
+    return 0;
+}
+
+sub CI($$) {
+    my ($file, $comment) = @_;
+
+    system("chown ${WIKIUSER}:${WIKIGROUP} $file")
+	== 0 or fatal("Could not chown $file to ${WIKIUSER}:${WIKIGROUP}");
+
+    my $cmd = "sudo -u nobody ci -l '-mRevision by wikiproxy:\n$comment'".
+	"      '-t-Revision by wikiproxy' $file 2>&1";
+    my $output = `$cmd`;
+    if ($? && $output =~ /no lock set by/ ) {
+	my $cmd = "rcs -u -M $file";
+	my $output = `$cmd`;
+	return -1
+	    if ($?);
+	system("sudo -u nobody rcs -l $file");
+	return -1
+	    if ($?);
+        return system($cmd);	
+    }
+    return $? >> 8;
+}
+     
+sub fatal($)
+{
+    my($mesg) = $_[0];
+
+    die("*** $0:\n".
+	"    $mesg\n");
+}
diff --git a/wiki/wikisetup.in b/wiki/wikisetup.in
new file mode 100644
index 0000000000..ffc2cb678e
--- /dev/null
+++ b/wiki/wikisetup.in
@@ -0,0 +1,254 @@
+#!/usr/bin/perl -w
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2005 University of Utah and the Flux Group.
+# All rights reserved.
+#
+use English;
+use Getopt::Std;
+
+#
+# Initial wiki setup. Create wiki accounts for all users and projects.
+#
+sub usage()
+{
+    print STDOUT "Usage: wikisetup\n";
+    exit(-1);
+}
+my $optlist  = "d";
+my $debug    = 0;
+my $impotent = 0;
+
+#
+# Configure variables
+#
+my $TB		= "@prefix@";
+my $TBOPS       = "@TBOPSEMAIL@";
+my $CONTROL     = "@USERNODE@";
+my $BOSSNODE	= "@BOSSNODE@";
+my $WIKISUPPORT = @WIKISUPPORT@;
+my $SSH         = "$TB/bin/sshtb";
+my $WIKIPROXY   = "$TB/sbin/wikiproxy";
+my $ADDWIKIUSER = "$TB/sbin/addwikiuser";
+my $ADDWIKIPROJ = "$TB/sbin/addwikiproj";
+my $SETWIKIGROUPS = "$TB/sbin/setwikigroups";
+
+#
+# Untaint the path
+# 
+$ENV{'PATH'} = "/bin:/usr/bin";
+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;
+
+#
+# If no wiki support, just exit. 
+#
+if (! $WIKISUPPORT) {
+    print "WIKI support is not enabled. Exit ...\n";
+    exit(0);
+}
+
+#
+# Only testbed admins.
+#
+if (!TBAdmin($UID)) {
+    die("*** $0:\n".
+	"    Must be a testbed admin to run this script\n");
+}
+
+#
+# Parse command arguments. Once we return from getopts, all that should be
+# left are the required arguments.
+#
+%options = ();
+if (! getopts($optlist, \%options)) {
+    usage();
+}
+if (defined($options{"d"})) {
+    $debug = 1;
+}
+if (@ARGV) {
+    usage();
+}
+
+my %wikipids   = ();	# Indexed by pid, gives wikiname.
+my %wikinames  = ();	# Indexed by wikiname.
+
+#
+# Attempt to form unique wiki IDs for all users and projects. What a
+# pain in the ass this is going to be.
+#
+# First the projects. Only main group.
+#
+$query_result =
+    DBQueryFatal("select pid from groups where pid=gid ".
+		 "and (pid='testbed' or pid='emulab-ops' or pid='tbres' or ".
+		 "     pid='utahstud')");
+
+while (my ($pid) = $query_result->fetchrow_array()) {
+    #
+    # The wikirules for web names are slightly easier to deal with.
+    # Note that there will also be a *Group name created from the token.
+    #
+    my $wikiname = ucfirst($pid);
+
+    if ($wikiname =~ /[-_]/) {
+	my @tokens = split(/[-_]/, $wikiname);
+
+	$wikiname = "";
+
+	#
+	# Make sure the first letter of each token is a caps.
+	# 
+	foreach my $token (@tokens) {
+	    $wikiname .= ucfirst($token);
+	}
+    }
+
+    #
+    # Check to make sure the wikiname does not violate the wikirules!
+    # If it does, just skip. User will have to plug in a new name.
+    #
+    if (! ($wikiname =~ /^[A-Z]+[A-Za-z0-9]*$/)) {
+	print "Bad WikiName for Project $pid: $wikiname\n";
+	next;
+    }
+
+    #
+    # We are not likely to get dups for a project name, but make sure
+    # anyway.
+    #
+    if (exists($wikinames{$wikiname})) {
+	my $other_pid   = $wikinames{$wikiname};
+
+	print "Duplicate WikiName for Project: $wikiname $pid $other_pid\n";
+	next;
+    }
+    $wikipids{$pid} = $wikiname;
+}
+#
+# Now we are going to loop over the names, insert them into the DB, and
+# then call addwikiuser to actually do the work over on ops.
+#
+foreach my $pid (keys(%wikipids)) {
+    my $wikiname = $wikipids{$pid};
+
+    print "Creating Wiki for project $pid ($wikiname)\n"
+	if ($debug);
+
+    if (!$impotent) {
+	DBQueryFatal("update groups set wikiname='$wikiname' ".
+		     "where pid='$pid' and pid=gid");
+
+	system("$ADDWIKIPROJ $pid") == 0
+	    or fatal("Could not add wiki web for $pid ($wikiname)");
+    }
+}
+
+#
+# Now the users.
+# 
+$query_result =
+    DBQueryFatal("select distinct g.uid,u.usr_name,u.usr_email ".
+		 "  from group_membership as g ".
+		 "left join users as u on u.uid=g.uid ".
+		 "where u.status='active' and ".
+		 "     (g.pid='testbed' or g.pid='emulab-ops' or ".
+		 "      g.pid='tbres' or g.pid='utahstud')");
+
+my %wikiuids   = ();	# Indexed by user uid, gives wikiname.
+my %emailaddrs = ();	# Indexed by user uid, gives user email address.
+# Clear this for next loop.
+%wikinames = ();
+
+while (my ($uid,$name,$email) = $query_result->fetchrow_array()) {
+    $emailaddrs{$uid} = $email;
+
+    #
+    # Split the user name up into tokens. 
+    #
+    my @tokens = split(/\s+|-/, $name);
+
+    #
+    # Build a wikiname from the tokens. Lowercase each token, then
+    # captialize it, then run them all together. Oh, get rid of any
+    # non alphanum characters.
+    #
+    my $wikiname = "";
+
+    foreach my $token (@tokens) {
+	$token = ucfirst(lc($token));
+	$token =~ s/\.//g;
+	$wikiname .= $token;
+    }
+    #print "$wikiname\n";
+
+    #
+    # Check to make sure the wikiname does not violate the wikirules!
+    # If it does, just skip. User will have to plug in a new name.
+    #
+    if (! ($wikiname =~ /^[A-Z]+[a-z]+[A-Z]+[A-Za-z0-9]*$/)) {
+	print "Bad WikiName: $wikiname \n";
+	next;
+    }
+    #
+    # Look to see if this wikiname exists. If it does, and the email
+    # address is the same, then fine. If the email address is different,
+    # then that is a problem. Skip.
+    #
+    if (exists($wikinames{$wikiname})) {
+	my $other_uid   = $wikinames{$wikiname};
+	my $other_email = $emailaddrs{$other_uid};
+
+	if ($email ne $other_email) {
+	    print "Duplicate WikiName: $wikiname $uid $other_uid\n";
+	    next;
+	}
+    }
+    $wikinames{$wikiname} = $uid;
+    $wikiuids{$uid}       = $wikiname;
+    $emailaddrs{$uid}     = $email;
+}
+
+#
+# Now we are going to loop over the names, insert them into the DB, and
+# then call addwikiuser to actually do the work over on ops.
+#
+foreach my $uid (keys(%wikiuids)) {
+    my $wikiname = $wikiuids{$uid};
+
+    print "Creating Wiki for home page $uid ($wikiname)\n"
+	if ($debug);
+    
+    if (!$impotent) {
+	DBQueryFatal("update users set wikiname='$wikiname' ".
+		     "where uid='$uid'");
+
+	system("$ADDWIKIUSER $uid") == 0
+	    or fatal("Could not add wiki account for $uid ($wikiname)");
+
+	system("$SETWIKIGROUPS $uid") == 0
+	    or fatal("Could not set wiki groups for $uid ($wikiname)");
+    }
+}
+
+exit(0);
+
+sub fatal($)
+{
+    my($mesg) = $_[0];
+
+    die("*** $0:\n".
+	"    $mesg\n");
+}
-- 
GitLab