src/hg/instinct/webUserAdmin/webUserAdmin.c 1.13

1.13 2010/02/08 21:42:17 sbenz
Made checking for admin email more verbose
Index: src/hg/instinct/webUserAdmin/webUserAdmin.c
===================================================================
RCS file: /projects/compbio/cvsroot/kent/src/hg/instinct/webUserAdmin/webUserAdmin.c,v
retrieving revision 1.12
retrieving revision 1.13
diff -b -B -U 1000000 -r1.12 -r1.13
--- src/hg/instinct/webUserAdmin/webUserAdmin.c	13 Jan 2010 01:16:16 -0000	1.12
+++ src/hg/instinct/webUserAdmin/webUserAdmin.c	8 Feb 2010 21:42:17 -0000	1.13
@@ -1,1197 +1,1206 @@
 /* webUserAdmin - Administer web user account - signup, lost password, etc. */
 
 #include "common.h"
 #include "hash.h"
 #include "obscure.h"
 #include "hgConfig.h"
 #include "cheapcgi.h"
 #include "memalloc.h"
 #include "jksql.h"
 #include "htmshell.h"
 #include "cart.h"
 #include "hPrint.h"
 #include "hdb.h"
 #include "hui.h"
 #include "web.h"
 #include "ra.h"
 #include "hgColors.h"
 #include <crypt.h>
 #include "net.h"
 
 #include "webUserAdmin.h"
 #include "webUsers.h"
 #include "versionInfo.h"
 
 static char const rcsid[] = "$Id$";
 
 char *excludeVars[] = { "submit", "Submit", "debug", "fixMembers", "update", "webUser_password", NULL }; 
 /* The excludeVars are not saved to the cart. (We also exclude
  * any variables that start "near.do.") */
 
 /* ---- Global variables. ---- */
 struct cart *cart;	/* This holds cgi and other variables between clicks. */
 char *database;		/* Name of genome database - hg15, mm3, or the like. */
 struct hash *oldCart;	/* Old cart hash. */
 char *errMsg;           /* Error message to show user when form data rejected */
 char *myHostName;	/* The host name of this server */
 char *siteName;		/* The official name of this site */
 
 char *adminEmail;	/* admin's email address */
 
 /* -------- password functions ---- */
 
 void encryptPWD(char *password, char *salt, char *buf, int bufsize)
 /* encrypt a password */
 {
 /* encrypt user's password. */
 safef(buf,bufsize,crypt(password, salt));
 }
 
 void encryptNewPwd(char *password, char *buf, int bufsize)
 /* encrypt a new password */
 {
 unsigned long seed[2];
 char salt[] = "$1$........";
 const char *const seedchars =
 "./0123456789ABCDEFGHIJKLMNOPQRST"
 "UVWXYZabcdefghijklmnopqrstuvwxyz";
 int i;
 /* Generate a (not very) random seed. */
 seed[0] = time(NULL);
 seed[1] = getpid() ^ (seed[0] >> 14 & 0x30000);
 /* Turn it into printable characters from `seedchars'. */
 for (i = 0; i < 8; i++)
     salt[3+i] = seedchars[(seed[i/5] >> (i%5)*6) & 0x3f];
 encryptPWD(password, salt, buf, bufsize);
 }
 
 bool checkPwd(char *password, char *encPassword)
 /* check an encrypted password */
 {
+if(!password || !encPassword)
+	return FALSE;
 char encPwd[35] = "";
 encryptPWD(password, encPassword, encPwd, sizeof(encPwd));
 if (sameString(encPassword,encPwd))
     {
     return TRUE;
     }
 else
     {
     return FALSE;
     }
 }
 
 boolean checkPwdCharClasses(char *password)
 /* check that password uses at least 2 character classes */
 {
 /* [A-Z] [a-z] [0-9] [!@#$%^&*()] */
 int classes[4]={0,0,0,0};
 char c;
 while ((c=*password++))
     {
     if (c >= 'A' && c <= 'Z') classes[0] = 1;
     if (c >= 'a' && c <= 'z') classes[1] = 1;
     if (c >= '0' && c <= '9') classes[2] = 1;
     if (strchr("!@#$%^&*()",c)) classes[3] = 1;
     }
 return ((classes[0]+classes[1]+classes[2]+classes[3])>=2);
 }
 
 unsigned int randInt(unsigned int n)
 /* little randome number helper returns 0 to n-1 */
 {
 return (unsigned int) n * (rand() / (RAND_MAX + 1.0));
 }
 
 char *generateRandomPasswordExtended()
 /* Smiliar to generateRandomPassword(), but with finer timer resolution seed */
 /* Generate valid random password for users who have lost their old one.
  * Free the returned value.*/
 {
 char boundary[256];
 char punc[] = "!@#$%^&*()";
 char str[200];
 struct timeval tim;
 /* choose a new string for the boundary */
 /* Set initial seed */
 int i = 0;
 int r = 0;
 char c = ' ';
 boundary[0]=0;
 
 /* seed it with microsecond to get fine resolution */
 gettimeofday(&tim, NULL);
 srand((unsigned)tim.tv_usec);
 
 for(i=0;i<8;++i)
     {
     r = randInt(4);
     switch (r) 
 	{
 	case 0 :
     	    c = 'A' + randInt(26);
 	    break;
 	case 1 :
     	    c = 'a' + randInt(26);
 	    break;
 	case 2 :
     	    c = '0' + randInt(10);
 	    break;
 	default:
     	    c = punc[randInt(10)];
 	    break;
     	}
     boundary[i] = c;
     }
 boundary[i]=0;
 return cloneString(boundary);
 }
 
 char *generateRandomPassword()
 /* Generate valid random password for users who have lost their old one.
  * Free the returned value.*/
 {
 char boundary[256];
 char punc[] = "!@#$%^&*()";
 /* choose a new string for the boundary */
 /* Set initial seed */
 int i = 0;
 int r = 0;
 char c = ' ';
 boundary[0]=0;
 srand( (unsigned)time( NULL ) );
 for(i=0;i<8;++i)
     {
     r = randInt(4);
     switch (r) 
 	{
 	case 0 :
     	    c = 'A' + randInt(26);
 	    break;
 	case 1 :
     	    c = 'a' + randInt(26);
 	    break;
 	case 2 :
     	    c = '0' + randInt(10);
 	    break;
 	default:
     	    c = punc[randInt(10)];
 	    break;
     	}
     boundary[i] = c;
     }
 boundary[i]=0;
 return cloneString(boundary);
 }
 
 /* --- update passwords file ----- */
 
 void updatePasswordsFile(struct sqlConnection *conn)
 /* update the passwords file containing email:encryptedPassword */
 {
 struct sqlResult *sr;
 char **row;
  
 FILE *out = mustOpen("../conf/passwords", "w");
 
 sr = sqlGetResult(conn, 
 "select email,password from webUsers where activated='Y'"
 " and (expireDate='' or (current_date() < expireDate))");
 while ((row = sqlNextRow(sr)) != NULL)
     {
     fprintf(out,"%s:%s\n",row[0],row[1]);
     }
 sqlFreeResult(&sr);
 
 carefulClose(&out);
 }
 
 /* ---------- reverse DNS function --------- */
 
 #include <arpa/inet.h>
 #include <sys/socket.h>
 #include <netdb.h>
 
 char *reverseDns(char *ip)
 /* do reverse dns lookup on ip using getnamebyaddr,
  *  and then return a string to be freed that is the host */
 {
 struct hostent *hp;
 struct sockaddr_in sock;
 if (inet_aton(ip,&sock.sin_addr) == 0) return NULL;
 hp = gethostbyaddr(&sock.sin_addr,sizeof(sock.sin_addr),AF_INET);
 if (!hp) return NULL;
 return cloneString(hp->h_name); 
 }
 
 /* -------- functions ---- */
 
 void debugShowAllMembers(struct sqlConnection *conn)
 /* display all webUsers */
 {
 struct sqlResult *sr;
 char **row;
  
 hPrintf("<h1>Members</h1>");
 hPrintf("<table>");
 hPrintf("<th>email</th><th>password</th>");
 
 sr = sqlGetResult(conn, "select * from webUsers");
 while ((row = sqlNextRow(sr)) != NULL)
     {
     hPrintf("<tr><td>%s</td><td>%s</td></tr>",row[0],row[1]);
     }
 sqlFreeResult(&sr);
 
 hPrintf("</table>");
 }
 
 void lostPasswordPage(struct sqlConnection *conn)
 /* draw the lost password page */
 {
 hPrintf(
 "<h2>%s</h2>"
 "<p align=\"left\">"
 "</p>"
 "<font color=red>%s</font>"
 "<h3>Send Me A New Password</h3>"
 "<form method=post action=\"webUserAdmin\" name=lostPasswordForm >"
 "<table>"
 "<tr><td>E-mail</td><td><input type=text name=webUser_email size=20> "
   "(your e-mail is also your user-id)</td></tr>"
 "<tr><td>&nbsp;</td><td><input type=submit name=webUserAdmin.do.lostPassword value=submit>"
 "&nbsp;<input type=submit name=webUserAdmin.do.signupPage value=cancel></td></tr>"
 "</table>"
 "<br>"
 ,siteName
 , errMsg ? errMsg : ""
 );
 
 cartSaveSession(cart);
 
 hPrintf("</FORM>");
 
 }
 
 void lostPassword(struct sqlConnection *conn)
 /* process the lost password form */
 {
 char query[256];
 char cmd[256];
 char *email = cartUsualString(cart, "webUser_email", "");
 if (!email || sameString(email,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Email cannot be blank.");
     lostPasswordPage(conn);
     return;
     }
 safef(query,sizeof(query), "select password from webUsers where email='%s'", email);
 char *password = sqlQuickString(conn, query);
 if (!password)
     {
     freez(&errMsg);
     errMsg = cloneString("Email not found.");
     lostPasswordPage(conn);
     return;
     }
 freez(&password);
 password = generateRandomPassword();
 char encPwd[35] = "";
 encryptNewPwd(password, encPwd, sizeof(encPwd));
 
 safef(query,sizeof(query), "update webUsers set password='%s' where email='%s'", sqlEscapeString(encPwd), sqlEscapeString(email));
 sqlUpdate(conn, query);
 
 updatePasswordsFile(conn);
 
 safef(cmd,sizeof(cmd), 
 "echo 'Your new password is: %s' | mail -s \"Lost password\" %s"
 , password, email);
 int result = system(cmd);
 if (result == -1)
     {
     hPrintf(
     "<h2>%s</h2>"
     "<p align=\"left\">"
     "</p>"
     "<h3>Error emailing password to: %s</h3>"
     "Click <a href=webUserAdmin?webUserAdmin.do.signupPage=1>here</a> to return.<br>"
     , siteName
     , email
     );
     }
 else
     {
     hPrintf(
     "<h2>%s</h2>"
     "<p align=\"left\">"
     "</p>"
     "<h3>Password has been emailed to: %s</h3>"
     "Click <a href=webUserAdmin?webUserAdmin.do.signupPage=1>here</a> to return.<br>"
     , siteName
     , email
     );
     }
 
 freez(&password);
 }
 
 void changePasswordPage(struct sqlConnection *conn)
 /* change password page */
 {
 hPrintf(
 "<h2>%s</h2>"
 "<p align=\"left\">"
 "</p>"
 "<font color=red>%s</font>"
 "<h3>Change Password</h3>"
 "<form method=post action=\"webUserAdmin\" name=changePasswordForm >"
 "<table>"
 "<tr><td>E-mail</td><td><input type=text name=webUser_email size=20 value=\"%s\"> "
   "(your e-mail is also your user-id)</td></tr>"
 "<tr><td>Current Password</td><td><input type=password name=webUser_password value=\"\" size=10></td></tr>\n"
 "<tr><td>New Password</td><td><input type=password name=webUser_newPassword value=\"\" size=10></td></tr>\n"
 "<tr><td>Re-enter new Password</td><td><input type=password name=webUser_newPassword2 value=\"\" size=10></td></tr>\n"
 "<tr><td>&nbsp;</td><td><input type=submit name=webUserAdmin.do.changePassword value=submit>"
 "&nbsp;<input type=submit name=webUserAdmin.do.signupPage value=cancel></td></tr>"
 "</table>"
 "<br>"
 , siteName
 , errMsg ? errMsg : ""
 , cartUsualString(cart, "webUser_email", "")
 );
 
 cartSaveSession(cart);
 
 hPrintf("</FORM>");
 }
 
 void registerBatchWebUsersPage(struct sqlConnection *conn)
 /* activate a user page */
 {
 hPrintf(
 "<h2>%s</h2>"
 "<p align=\"left\">"
 "</p>"
 "<font color=red>%s</font>"
 "<h3>Register a batch of user emails</h3>"
 "<form method=post action=\"webUserAdmin\" name=registerBatchWebUsersForm >"
 "<table>"
 "<tr><td>E-mail list of the users to be registered: </td><td><input type=text name=webUser_emailsList size=20 value=\"%s\"> "
   "</td></tr>"
 "<tr><td>Admin Password</td><td><input type=password name=webUser_adminPassword value=\"\" size=10></td></tr>\n"
 "<tr><td>&nbsp;</td><td><input type=submit name=webUserAdmin.do.registerBatchWebUsers value=register>"
 "&nbsp;<input type=submit name=webUserAdmin.do.signupPage value=cancel></td></tr>"
 "</table>"
 "<br>"
 , siteName
 , errMsg ? errMsg : ""
 , cartUsualString(cart, "webUser_emailsList", "")
 );
 
 cartSaveSession(cart);
 
 hPrintf("</FORM>");
 }
 
 void registerBatchWebUsers(struct sqlConnection *conn)
 /* process activate web user form */
 {
 char query[256];
 char *emailsListFileName = cartUsualString(cart, "webUser_emailsList", "");
 char *adminPasswordEntered = cartUsualString(cart, "webUser_adminPassword", "");
 char cmd[1024];
 
 if (!emailsListFileName || sameString(emailsListFileName, ""))
     {
     freez(&errMsg);
     errMsg = cloneString("E-mail list file name cannot be blank.");
     registerBatchWebUsersPage(conn);
     return;
     }
 if (!adminPasswordEntered || sameString(adminPasswordEntered,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Admin password cannot be blank.");
     registerBatchWebUsersPage(conn);
     return;
     }
 
 safef(query,sizeof(query), "select password from webUsers where email='%s'", adminEmail);
 char *adminPassword = sqlQuickString(conn, query);
 if (!checkPwd(adminPasswordEntered, adminPassword))
     {
     freez(&errMsg);
     errMsg = cloneString("Wrong admin password.");
     registerBatchWebUsersPage(conn);
     return;
     }
 else
     {
     freez(&errMsg);
     struct lineFile *lf = lineFileOpen(emailsListFileName, TRUE);
     if (lf != NULL)
         {
         char   *line;
         int lineSize;
 
         while (lineFileNext(lf, &line, &lineSize))
 	    {
 	    char *password = generateRandomPasswordExtended();
 	    char encPwd[35] = "";
 	    char *webUserEmail;
 	    
 	    webUserEmail = line;
 	    safef(query,sizeof(query), "insert into webUsers set email='%s'", webUserEmail);
 	    sqlGetResult(conn, query);
 	    encryptNewPwd(password, encPwd, sizeof(encPwd));
 
 	    safef(query,sizeof(query), 
 		  "update webUsers set password='%s', name='%s', activated='Y' where email='%s'", 
 		  sqlEscapeString(encPwd), password, sqlEscapeString(webUserEmail));
 		  // !!! remove inserting password later by replacing the above 2 lines:
 		  //"update webUsers set password='%s', activated='Y'  where email='%s'", 
 		  //sqlEscapeString(encPwd), sqlEscapeString(webUserEmail));
 
 	    safef(cmd,sizeof(cmd), 
 	    "cat batchLet1.txt |sed -e 's/xx_password/%s/'|sed -e 's/xx_email/%s/g'|sed -e 's/xx_host/%s/'|sed -e 's/xx_adminEmail/%s/'|mail -s \"User account created for you at %s site, %s.\" %s", 
 	    password, webUserEmail, myHostName, adminEmail, siteName, myHostName, webUserEmail);
 	    int result = system(cmd);
 	    if (result == -1)
     		{			
     		errMsg = cloneString("Emailing failed during batch registration.");
         	registerBatchWebUsersPage(conn);
     		return;
     		}
 	    sqlUpdate(conn, query);
 	    
 	    updatePasswordsFile(conn);
 	    }
 
         errMsg = cloneString("The batch of emails registered.");
         registerBatchWebUsersPage(conn);
         return;
 	}
     else
     	{
     	errMsg = cloneString("E-mails list file, %s, could not be opened.");
     	registerBatchWebUsersPage(conn);
 	return;    
 	}		
     }	
 return;
 }
 
 void activateWebUserPage(struct sqlConnection *conn)
 /* activate a user page */
 {
 hPrintf(
 "<h2>%s</h2>"
 "<p align=\"left\">"
 "</p>"
 "<font color=red>%s</font>"
 "<h3>Activate a web user</h3>"
 "<form method=post action=\"webUserAdmin\" name=activateWebUserForm >"
 "<table>"
 "<tr><td>E-mail of the user to be activated: </td><td><input type=text name=webUser_email size=20 value=\"%s\"> "
   "</td></tr>"
 "<tr><td>Admin Password</td><td><input type=password name=webUser_adminPassword value=\"\" size=10></td></tr>\n"
 "<tr><td>&nbsp;</td><td><input type=submit name=webUserAdmin.do.activateWebUser value=activate>"
 "&nbsp;<input type=submit name=webUserAdmin.do.signupPage value=cancel></td></tr>"
 "</table>"
 "<br>"
 , siteName
 , errMsg ? errMsg : ""
 , cartUsualString(cart, "webUser_email", "")
 );
 
 cartSaveSession(cart);
 
 hPrintf("</FORM>");
 }
 
 void activateWebUser(struct sqlConnection *conn)
 /* process activate web user form */
 {
 char query[256];
 char *email = cartUsualString(cart, "webUser_email", "");
 char *adminPasswordEntered = cartUsualString(cart, "webUser_adminPassword", "");
 if (!email || sameString(email,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Email cannot be blank.");
     activateWebUserPage(conn);
     return;
     }
 if (!adminPasswordEntered || sameString(adminPasswordEntered,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Admin password cannot be blank.");
     activateWebUserPage(conn);
     return;
     }
 
 safef(query,sizeof(query), "select password from webUsers where email='%s'", adminEmail);
 char *adminPassword = sqlQuickString(conn, query);
-if (!checkPwd(adminPasswordEntered, adminPassword))
+if(!adminPassword)
+	{
+    freez(&errMsg);
+    errMsg = cloneString("Unable to find admin email in database.");
+    activateWebUserPage(conn);
+    return;	
+	}
+else if (!checkPwd(adminPasswordEntered, adminPassword))
     {
     freez(&errMsg);
     errMsg = cloneString("Wrong admin password.");
     activateWebUserPage(conn);
     return;
     }
 else
     {
     freez(&errMsg);
     safef(query,sizeof(query), "update webUsers set activated='Y' where email='%s'", email);
     sqlUpdate(conn, query);
     
     updatePasswordsFile(conn);
     cartRemove(cart, "webUser_adminPassword");
    
     char cmd[1024];
     safef(cmd,sizeof(cmd),
     "echo \"Hi,\n\n Your request for a user account at %s, %s, has been approved.\n\nWeb User Administrator\nUCSC Cancer Genomics Browser Team.\" | mail -s \"Your user account is approved at %s, %s.\" %s"
     , siteName, myHostName, siteName, myHostName, email);
     int result = system(cmd);
     if (result == -1)
     	{
     	hPrintf("<br>Note: sending approved email notice to the user %s failed\n", email);
         hPrintf("Click <a href=webUserAdmin?webUserAdmin.do.signupPage=1>here</a> to return.<br>\n");
     	return;
 	}
  
     errMsg = cloneString("The web user was activated successfully.");
     activateWebUserPage(conn);
 
     return;
     }
 
 safef(query,sizeof(query), "select password from webUsers where email='%s'", adminEmail);
 char *password = sqlQuickString(conn, query);
 if (!password)
     {
     freez(&errMsg);
     errMsg = cloneString("Admin email not found.");
     activateWebUserPage(conn);
     return;
     }
 }
 
 void changePassword(struct sqlConnection *conn)
 /* process the change password form */
 {
 char query[256];
 char *email = cartUsualString(cart, "webUser_email", "");
 char *currentPassword = cartUsualString(cart, "webUser_password", "");
 char *newPassword     = cartUsualString(cart, "webUser_newPassword", "");
 char *newPassword2    = cartUsualString(cart, "webUser_newPassword2", "");
 if (!email || sameString(email,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Email cannot be blank.");
     changePasswordPage(conn);
     return;
     }
 if (!currentPassword || sameString(currentPassword,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Current password cannot be blank.");
     changePasswordPage(conn);
     return;
     }
 if (!newPassword || sameString(newPassword,""))
     {
     freez(&errMsg);
     errMsg = cloneString("New password cannot be blank.");
     changePasswordPage(conn);
     return;
     }
 if (!sameWord(newPassword, newPassword2))
     {
     freez(&errMsg);
     errMsg = cloneString("The new password you entered and the one you re-entered must be the same.  Please try again.");
     changePasswordPage(conn);
     return;
     }
 safef(query,sizeof(query), "select password from webUsers where email='%s'", email);
 char *password = sqlQuickString(conn, query);
 if (!password)
     {
     freez(&errMsg);
     errMsg = cloneString("Email not found.");
     changePasswordPage(conn);
     return;
     }
 if (!checkPwd(currentPassword, password))
     {
     freez(&errMsg);
     errMsg = cloneString("Invalid current password.");
     changePasswordPage(conn);
     return;
     }
 freez(&password);
 if (!newPassword || sameString(newPassword,"") || (strlen(newPassword)<8))
     {
     freez(&errMsg);
     errMsg = cloneString("New password must be at least 8 characters long.");
     changePasswordPage(conn);
     return;
     }
 if (!checkPwdCharClasses(newPassword))
     {
     freez(&errMsg);
     errMsg = cloneString(
 	"Password must contain characters from 2 of the following 4 classes: "
 	"[A-Z] [a-z] [0-9] [!@#$%^&*()].");
     changePasswordPage(conn);
     return;
     }
 char encPwd[35] = "";
 encryptNewPwd(newPassword, encPwd, sizeof(encPwd));
 safef(query,sizeof(query), "update webUsers set password='%s' where email='%s'", sqlEscapeString(encPwd), sqlEscapeString(email));
 sqlUpdate(conn, query);
 
 hPrintf
     (
     "<h2>%s</h2>"
     "<p align=\"left\">"
     "</p>"
     "<h3>Password has been changed.</h3>"
     "Click <a href=webUserAdmin?webUserAdmin.do.signupPage=1>here</a> to return.<br>"
     , siteName
     , email
     );
     
 updatePasswordsFile(conn);
 
 cartRemove(cart, "webUser_password");
 cartRemove(cart, "webUser_password2");
 cartRemove(cart, "webUser_newPassword");
 cartRemove(cart, "webUser_newPassword2");
 }
 
 void updatePasswordUserInfoPage(struct sqlConnection *conn)
 /* change password and update user info page */
 {
 hPrintf(
 "<h2>%s</h2>"
 "<p align=\"left\">"
 "</p>"
 "<font color=red>%s</font>"
 "<h3>Change Password and Update User Info</h3>"
 "<form method=post action=\"webUserAdmin\" name=updatePasswordUserInfoForm >"
 "<table>"
 "<tr><td>E-mail</td><td>%s</td></tr>"
 //"<tr><td>E-mail</td><td><input type=text name=webUser_email size=20 value=\"%s\"> "
 //  "(your e-mail is also your user-id)</td></tr>"
 "<tr><td>Initial temporary password</td><td><input type=password name=webUser_password value=\"\" size=10></td></tr>\n"
 "<tr><td>New Password</td><td><input type=password name=webUser_newPassword value=\"\" size=10></td></tr>\n"
 "<tr><td>Name</td><td><input type=text name=webUser_name  size=20></td></tr>\n"
 "<tr><td>Phone</td><td><input type=text name=webUser_phone size=20></td></tr>\n"
 "<tr><td>Institution</td><td><input type=text name=webUser_institution size=40></td></tr>\n"
 "<tr><td>&nbsp;</td><td><input type=submit name=webUserAdmin.do.updatePasswordUserInfo value=submit>"
 "&nbsp;<input type=submit name=webUserAdmin.do.signupPage value=cancel></td></tr>"
 
 "</table>"
 "<br>"
 , siteName
 , errMsg ? errMsg : ""
 , cartUsualString(cart, "webUser_email", "")
 );
 cartSaveSession(cart);
 
 hPrintf("</FORM>");
 }
 
 void updatePasswordUserInfo(struct sqlConnection *conn)
 /* process the change password form */
 {
 char query[256];
 char *email = cartUsualString(cart, "webUser_email", "");
 char *currentPassword = cartUsualString(cart, "webUser_password", "");
 char *newPassword = cartUsualString(cart, "webUser_newPassword", "");
 if (!email || sameString(email,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Email cannot be blank.");
     updatePasswordUserInfoPage(conn);
     return;
     }
 if (!currentPassword || sameString(currentPassword,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Current password cannot be blank.");
     updatePasswordUserInfoPage(conn);
     return;
     }
 if (!newPassword || sameString(newPassword,""))
     {
     freez(&errMsg);
     errMsg = cloneString("New password cannot be blank.");
     updatePasswordUserInfoPage(conn);
     return;
     }
 safef(query,sizeof(query), "select password from webUsers where email='%s'", email);
 char *password = sqlQuickString(conn, query);
 if (!password)
     {
     freez(&errMsg);
     errMsg = cloneString("Email not found.");
     updatePasswordUserInfoPage(conn);
     return;
     }
 if (!checkPwd(currentPassword, password))
     {
     freez(&errMsg);
     errMsg = cloneString("Invalid current password.");
     updatePasswordUserInfoPage(conn);
     return;
     }
 freez(&password);
 if (!newPassword || sameString(newPassword,"") || (strlen(newPassword)<8))
     {
     freez(&errMsg);
     errMsg = cloneString("New password must be at least 8 characters long.");
     updatePasswordUserInfoPage(conn);
     return;
     }
 if (!checkPwdCharClasses(newPassword))
     {
     freez(&errMsg);
     errMsg = cloneString(
 	"Password must contain characters from 2 of the following 4 classes: "
 	"[A-Z] [a-z] [0-9] [!@#$%^&*()].");
     updatePasswordUserInfoPage(conn);
     return;
     }
 char encPwd[35] = "";
 encryptNewPwd(newPassword, encPwd, sizeof(encPwd));
 safef(query,sizeof(query), "update webUsers set password='%s' where email='%s'", sqlEscapeString(encPwd), sqlEscapeString(email));
 sqlUpdate(conn, query);
 
 /* update user info */
 char *name  = cartUsualString(cart, "webUser_name", "");
 char *phone = cartUsualString(cart, "webUser_phone", "");
 char *institution = cartUsualString(cart, "webUser_institution", "");
 safef(query,sizeof(query), "update webUsers set name='%s', phone='%s', institution='%s'  where email='%s'", sqlEscapeString(name), sqlEscapeString(phone), sqlEscapeString(institution), sqlEscapeString(email));
 sqlUpdate(conn, query);
 
 updatePasswordsFile(conn);
 
 hPrintf
     (
     "<h2>%s</h2>"
     "<p align=\"left\">"
     "</p>"
     "<h3>Password and user info have been updated.</h3>"
     "Click <a href=webUserAdmin?webUserAdmin.do.signupPage=1>here</a> to return.<br>"
     , siteName
     , email
     );
     
 cartRemove(cart, "webUser_password");
 cartRemove(cart, "webUser_password2");
 cartRemove(cart, "webUser_newPassword");
 cartRemove(cart, "webUser_newPassword2");
 }
 
 void signupPage(struct sqlConnection *conn)
 /* draw the signup page */
 {
 hPrintf("<h2>%s</h2>\n", siteName);
 hPrintf("<p align=\"left\">" "</p>"
 	"If you are already a member, click <a href=http://%s/>here</a> to access %s at %s.<br>\n",  
 	myHostName, siteName,  myHostName);
 hPrintf("To view your existing account, click <a href=");
 hPrintf("\"webUserAdmin?webUserAdmin.do.displayAccountPage=1\">here</a>.<br>\n");
 
 hPrintf("To change your password, click <a href=");
 hPrintf("\"webUserAdmin?webUserAdmin.do.changePasswordPage=1\">here</a>.<br>\n");
 
 hPrintf("To change your password and contact info, click <a href=");
 hPrintf("\"webUserAdmin?webUserAdmin.do.updatePasswordUserInfoPage=1\">here</a>.<br>\n");
 
 hPrintf("Lost your password? Click <a href=");
 hPrintf("\"webUserAdmin?webUserAdmin.do.lostPasswordPage=1\">here</a>.<br>\n");
 
 /* The following options are shown only for admin */
 if (sameWord(cartUsualString(cart, "adminId", ""), adminEmail))
     {
     hPrintf("<BR>To activate a web user? Click <a href=");
     hPrintf("\"webUserAdmin?webUserAdmin.do.activateWebUserPage=1\">here</a>.<br>\n");
     hPrintf("To register a batch of web users? Click <a href=");
     hPrintf("\"webUserAdmin?webUserAdmin.do.registerBatchWebUsersPage=1\">here</a>.<br>\n");
     }
 
 hPrintf("<font color=red>%s</font>", errMsg ? errMsg : "");
 hPrintf("<h3>Sign Up</h3>\n"
 "<form method=post action=\"webUserAdmin\" name=mainForm >\n"
 "NOTE: Your e-mail is also your user login ID.\n");
 hPrintf("<br>Password must be at least 8 characters long and it must contain characters");
 hPrintf("<br>from 2 of the following 4 classes: [A-Z] [a-z] [0-9] [!@#$%^&*()].<br><br>");
 
 hPrintf("<table>\n"
 "<tr><td>E-mail</td><td><input type=text name=webUser_email value=\"%s\"size=20>\n"
 "<tr><td>Password</td><td><input type=password name=webUser_password value=\"%s\" size=10></td></tr>\n"
 "<tr><td>Re-enter password</td><td><input type=password name=webUser_password2 value=\"%s\" size=10></td></tr>\n"
 "<tr><td>Name</td><td><input type=text name=webUser_name value=\"%s\" size=20></td></tr>\n"
 "<tr><td>Phone</td><td><input type=text name=webUser_phone value=\"%s\" size=20></td></tr>\n"
 "<tr><td>Institution</td><td><input type=text name=webUser_institution value=\"%s\" size=40></td></tr>\n"
 "<tr><td>&nbsp;</td><td><input type=submit name=webUserAdmin.do.signup value=submit></td></tr>\n"
 "</table>\n"
 "<br>\n"
 "Questions or problems? Please email <a href=\"mailto:%s\">%s</a>.<br>\n"
 , cartUsualString(cart, "webUser_email", "")
 , cartUsualString(cart, "webUser_password", "")
 , cartUsualString(cart, "webUser_password2", "")
 , cartUsualString(cart, "webUser_name", "")
 , cartUsualString(cart, "webUser_phone", "")
 , cartUsualString(cart, "webUser_institution", "")
 //, sameString("commercial",cartUsualString(cart, "webUser_type", "")) ? " checked" : ""
 //, sameString("academic",cartUsualString(cart, "webUser_type", "")) ? " checked" : ""
 , adminEmail
 , adminEmail
 );
 
 cartSaveSession(cart);
 
 hPrintf("</FORM>");
 }
 
 void signup(struct sqlConnection *conn)
 /* process the signup form */
 {
 char query[256];
 char *email = cartUsualString(cart, "webUser_email", "");
 if (!email || sameString(email,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Email cannot be blank.");
     signupPage(conn);
     return;
     }
 safef(query,sizeof(query), "select password from webUsers where email='%s'", email);
 char *password = sqlQuickString(conn, query);
 if (password)
     {
     freez(&errMsg);
     errMsg = cloneString("A user with this email address already exists.");
     signupPage(conn);
     freez(&password);
     return;
     }
 
 password = cartUsualString(cart, "webUser_password", "");
 if (!password || sameString(password,"") || (strlen(password)<8))
     {
     freez(&errMsg);
     errMsg = cloneString("Password must be at least 8 characters long.");
     signupPage(conn);
     return;
     }
 char *password2 = cartUsualString(cart, "webUser_password2", "");
 if (!sameWord(password, password2))
     {
     freez(&errMsg);
     errMsg = cloneString("The password you entered and the one you re-entered must be the same.  Please try again.");
     signupPage(conn);
     return;
     }
 if (!checkPwdCharClasses(password))
     {
     freez(&errMsg);
     errMsg = cloneString("Password must contain characters from 2 of the following 4 classes: [A-Z] [a-z] [0-9] [!@#$%^&*()].");
     signupPage(conn);
     return;
     }
 
 char *name = cartUsualString(cart, "webUser_name", "");
 if (!name || sameString(name,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Name cannot be blank.");
     signupPage(conn);
     return;
     }
 
 char *phone = cartUsualString(cart, "webUser_phone", "");
 if (!phone || sameString(phone,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Phone cannot be blank.");
     signupPage(conn);
     return;
     }
 
 char *institution = cartUsualString(cart, "webUser_institution", "");
 if (!institution || sameString(institution,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Institution cannot be blank.");
     signupPage(conn);
     return;
     }
 /*
 char *type = cartUsualString(cart, "webUser_type", "");
 if (!type || sameString(type,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Type cannot be blank.");
     signupPage(conn);
     return;
     }
 */
 
 char encPwd[35] = "";
 encryptNewPwd(password, encPwd, sizeof(encPwd));
 safef(query,sizeof(query), "insert into webUsers set "
     "email='%s',password='%s',activated='%s',name='%s',phone='%s',institution='%s'", 
     sqlEscapeString(email), sqlEscapeString(encPwd), "N", sqlEscapeString(name), sqlEscapeString(phone), sqlEscapeString(institution));
 sqlUpdate(conn, query);
 
 hPrintf(
 "<h2>%s</h2>\n"
 "<p align=\"left\">\n"
 "</p>\n"
 "<h3>Thank you for signing up.  Your request will be reviewed shortly."
 "<br>Upon its approval, you will be notified by an email."
 "</h3>\n"
 , siteName
 );
 
 char cmd[256];
 safef(cmd,sizeof(cmd),
 "echo \"%s just signed up.\nPlease authorize this person as appropriate. \" | mail -s \"Someone just signed up at %s.\" %s"
 , email, myHostName, adminEmail);
 int result = system(cmd);
 if (result == -1)
     {
     hPrintf("<br>Note: sending email notice to the reviwer for %s failed\n", email);
     }
 
 hPrintf(
 "Click <a href=webUserAdmin?webUserAdmin.do.signupPage=1>here</a> to return.<br>\n"
 );
 }
 
 /* ----- account login/display functions ---- */
 
 void displayAccountPage(struct sqlConnection *conn)
 /* draw the account login page */
 {
 char *email = cartUsualString(cart, "webUser_email", "");
 /* for password security, use cgi hash instead of cart */
 char *password = cgiUsualString("webUser_password", ""); 
 hPrintf(
 "<h2>%s</h2>"
 "<p align=\"left\">"
 "</p>"
 "<font color=red>%s</font>"
 "<h3>Account Login</h3>"
 "<form method=post action=\"webUserAdmin\" name=accountLoginForm >"
 "<table>"
 "<tr><td>E-mail</td><td><input type=text name=webUser_email value=\"%s\" size=20> "
 "<tr><td>Password</td><td><input type=password name=webUser_password value=\"%s\" size=10></td></tr>\n"
   "(your e-mail is also your user-id)</td></tr>"
 "<tr><td>&nbsp;</td><td><input type=submit name=webUserAdmin.do.displayAccount value=submit>"
 "&nbsp;<input type=submit name=webUserAdmin.do.signupPage value=cancel></td></tr>"
 "</table>"
 "<br>"
 , siteName
 , errMsg ? errMsg : ""
 , email
 , password
 );
 
 cartSaveSession(cart);
 
 hPrintf("</FORM>");
 }
 
 void displayAccount(struct sqlConnection *conn)
 /* display user account info */
 {
 struct sqlResult *sr;
 char **row;
 char query[256];
 char *email = cartUsualString(cart, "webUser_email", "");
 if (sameString(email,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Email cannot be blank.");
     displayAccountPage(conn);
     return;
     }
 /* for password security, use cgi hash instead of cart */
 char *password = cgiUsualString("webUser_password", ""); 
 if (sameString(password,""))
     {
     freez(&errMsg);
     errMsg = cloneString("Password cannot be blank.");
     displayAccountPage(conn);
     return;
     }
 
 safef(query,sizeof(query),"select * from webUsers where email='%s'", email);
 sr = sqlGetResult(conn, query);
 if ((row = sqlNextRow(sr)) == NULL)
     {
     freez(&errMsg);
     char temp[256];
     safef(temp,sizeof(temp),"Email %s not found.",email);
     errMsg = cloneString(temp);
     displayAccountPage(conn);
     return;
     }
 struct webUsers *m = webUsersLoad(row);
 sqlFreeResult(&sr);
 
 if (checkPwd(password,m->password))
     {
     hPrintf("<h1>Account Information</h1>\n",m->email);
     hPrintf("<table>\n");
     hPrintf("<tr><td align=right>user login ID:</td><td>%s</td><tr>\n",m->email);
     hPrintf("<tr><td align=right>name:</td><td>%s</td><tr>\n",m->name);
     hPrintf("<tr><td align=right>phone:</td><td>%s</td><tr>\n",m->phone);
     hPrintf("<tr><td align=right>institution:</td><td>%s</td><tr>\n",m->institution);
     hPrintf("<tr><td align=right>activated:</td><td>%s</td><tr>\n",m->activated);
     hPrintf("</table>\n");
     hPrintf("<br>\n");
 
     hPrintf("Return to <a href=\"webUserAdmin\">signup</A>.<br>\n");
     hPrintf("Go to <a href=\"/\">%s</A>.<br>\n", siteName);
     }
 else
     {
     hPrintf("<h1>Invalid User/Password</h1>\n",m->email);
     hPrintf("Return to <a href=\"webUserAdmin\">signup</A>.<br>\n");
     }
 
 webUsersFree(&m);
 }
 
 /*
 void upgradeMembersTable(struct sqlConnection* conn)
 / * one-time upgrade of webUsers table to store encrypted passwords * /
 {
 char query[256];
 
 safef(query,sizeof(query),"select email from webUsers");
 struct slName *email=NULL,*list = sqlQuickList(conn,query);
 for(email=list;email;email=email->next)
     {
 
     uglyf("email=%s<br>\n",email->name);
     
     safef(query,sizeof(query),"select password from webUsers where email='%s'", email->name);
     char *password = sqlQuickString(conn,query);
 
     uglyf("password=%s<br>\n",password);
     
     if (password)
 	{
 	if (!startsWith("$1$",password)) / * upgrade has not already been done * /
 	    {
 	    uglyf("does not start with $1$<br>\n");
 	    char encPwd[35] = "";
     	    encryptNewPwd(password, encPwd, sizeof(encPwd));
 	    safef(query,sizeof(query),"update webUsers set password = '%s' where email='%s'", 
 		sqlEscapeString(encPwd), sqlEscapeString(email->name));
 	    uglyf("query: %s<br>\n",query);
 	    sqlUpdate(conn,query);
 	    }
 	freez(&password);
 	}
 
     uglyf("<br>\n");
     
     }
 slFreeList(&list);
 }
 */
 
 void doMiddle(struct cart *theCart)
 /* Write the middle parts of the HTML page. 
  * This routine sets up some globals and then
  * dispatches to the appropriate page-maker. */
 {
 struct sqlConnection *conn = hAllocConnProfile("localDb", "membership");
 
 if(!siteName || !adminEmail)
 	errAbort("Missing required hg.conf variables.\n"
 			 "Please set the following:\n"
 			 "hostname, siteName, adminEmail");
 
 cart = theCart;
 if (cartVarExists(cart, "debug"))
     debugShowAllMembers(conn);
 else if (cartVarExists(cart, "update"))
     {
     updatePasswordsFile(conn);
     hPrintf(
     "<h2>%s</h2>"
     "<p align=\"left\">"
     "</p>"
     "<h3>Successfully updated the authentication file.</h3>"
     "Click <a href=webUserAdmin?webUserAdmin.do.signupPage=1>here</a> to return.<br>"
     , siteName
     );
     }
 else if (cartVarExists(cart, "webUserAdmin.do.lostPasswordPage"))
     lostPasswordPage(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.lostPassword"))
     lostPassword(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.changePasswordPage"))
     changePasswordPage(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.updatePasswordUserInfoPage"))
     updatePasswordUserInfoPage(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.updatePasswordUserInfo"))
     updatePasswordUserInfo(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.activateWebUserPage"))
     activateWebUserPage(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.activateWebUser"))
     activateWebUser(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.registerBatchWebUsersPage"))
     registerBatchWebUsersPage(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.registerBatchWebUsers"))
     registerBatchWebUsers(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.changePassword"))
     changePassword(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.displayAccountPage"))
     displayAccountPage(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.displayAccount"))
     displayAccount(conn);
 else if (cartVarExists(cart, "webUserAdmin.do.signup"))
     signup(conn);
 else
     signupPage(conn);
 
 hFreeConn(&conn);
 cartRemovePrefix(cart, "webUserAdmin.do.");
 }
 
 void usage()
 /* Explain usage and exit. */
 {
 errAbort(
   "webUserAdmin - administer web user account functions - a cgi script\n"
   "usage:\n"
   "   webUserAdmin\n"
   );
 }
 
 int main(int argc, char *argv[])
 /* Process command line. */
 {
 pushCarefulMemHandler(100000000);
 cgiSpoof(&argc, argv);
 htmlSetStyle(htmlStyleUndecoratedLink);
 htmlSetBgColor(HG_CL_OUTSIDE);
 oldCart = hashNew(10);
 siteName   = cfgOption("siteName");
 adminEmail = cfgOption("adminEmail");
 myHostName = getenv("HTTP_HOST");
 cartHtmlShell(siteName, doMiddle, hUserCookie(), excludeVars, oldCart);
 return 0;
 }