a3df543e964484070a202af9f2da33b2f91e8a00 chinhli Mon Mar 26 15:48:46 2012 -0700 Inithial hgLogin work diff --git src/hg/hgLogin/hgLogin.c src/hg/hgLogin/hgLogin.c new file mode 100644 index 0000000..5278460 --- /dev/null +++ src/hg/hgLogin/hgLogin.c @@ -0,0 +1,898 @@ +/* hgLogin - Administer UCSC Genome Browser membership - 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 + +#include "net.h" + +#include "hgLogin.h" +#include "gbMembers.h" + +#include "versionInfo.h" +char msg[2048] = ""; + + +char *excludeVars[] = { "submit", "Submit", "debug", "fixMembers", "update", "hgLogin_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 */ + +/* -------- 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 */ +/* XXXX TODO: use md5 in linked SSL */ +{ +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 */ +{ +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 *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 gbMembers 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 +#include +#include + +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 gbMembers */ +{ +struct sqlResult *sr; +char **row; + +hPrintf("

Members

"); +hPrintf(""); +hPrintf(""); + +sr = sqlGetResult(conn, "select * from members"); +while ((row = sqlNextRow(sr)) != NULL) + { + hPrintf("",row[0],row[1]); + } +sqlFreeResult(&sr); + +hPrintf("
emailpassword
%s%s
"); +} + + + +void lostPasswordPage(struct sqlConnection *conn) +/* draw the lost password page */ +{ +hPrintf( +"

UCSC Genome Browser

" +"

" +"

" +"%s" +"

Send Me A New Password

" +"
" +"" +"" +"" +"
E-mail " + "(your e-mail is also your user-id)
 " +" 
" +"
" +, errMsg ? errMsg : "" +); + +cartSaveSession(cart); + +hPrintf("
"); + +} + + +void lostPassword(struct sqlConnection *conn) +/* process the lost password form */ +{ +char query[256]; +char cmd[256]; +char *email = cartUsualString(cart, "hgLogin_email", ""); +if (!email || sameString(email,"")) + { + freez(&errMsg); + errMsg = cloneString("Email cannot be blank."); + lostPasswordPage(conn); + return; + } +safef(query,sizeof(query), "select password from gbMembers 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 gbMembers 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 GSID HIV password\" %s" +, password, email); +int result = system(cmd); +if (result == -1) + { + hPrintf( + "

UCSC Genome Browser

" + "

" + "

" + "

Error emailing password to: %s

" + "Click here to return.
" + , email + ); + } +else + { + hPrintf( + "

UCSC Genome Browser

" + "

" + "

" + "

Password has been emailed to: %s

" + "Click here to return.
" + , email + ); + } + +freez(&password); +} + +void changePasswordPage(struct sqlConnection *conn) +/* change password page */ +{ +hPrintf( +"

UCSC Genome Browser

" +"

" +"

" +"%s" +"

Change Password

" +"
" +"" +"" +"\n" +"\n" +"" +"
E-mail " + "(your e-mail is also your user-id)
Current Password
New Password
 " +" 
" +"
" +, errMsg ? errMsg : "" +, cartUsualString(cart, "hgLogin_email", "") +); + +cartSaveSession(cart); + +hPrintf("
"); + +} + +void changePassword(struct sqlConnection *conn) +/* process the change password form */ +{ +char query[256]; +char *email = cartUsualString(cart, "hgLogin_email", ""); +char *currentPassword = cartUsualString(cart, "hgLogin_password", ""); +char *newPassword = cartUsualString(cart, "hgLogin_newPassword", ""); +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; + } +safef(query,sizeof(query), "select password from gbMembers 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 gbMembers set password='%s' where email='%s'", sqlEscapeString(encPwd), sqlEscapeString(email)); +sqlUpdate(conn, query); + +hPrintf + ( + "

UCSC Genome Browser

" + "

" + "

" + "

Password has been changed.

" + "Click here to return.
" + , email + ); + +updatePasswordsFile(conn); + +cartRemove(cart, "hgLogin_password"); +cartRemove(cart, "hgLogin_newPassword"); +} + + + + +void signupPage(struct sqlConnection *conn) +/* draw the signup page */ +/* XXXX TODO: + cornfirm password, password help + like Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only. +optional real name */ + +{ +hPrintf( +"

UCSC Genome Browser

\n" +"

" +"

" +"GSID provides access to data from the 2003 VaxGen HIV vaccine phase III clinical trials on a yearly access-fee basis.
\n" +"Academic and non-profit researchers get a substantial discount.
\n" +"
\n" +"If you are already a member, click here to access UCSC Genome Browser.
\n" +"To view your existing account, click here.
\n" +"To change your password, click here.
\n" +"Lost your password? Click here.
\n" +"%s" +"

Sign up

\n" +"
\n" +"NOTE: Your e-mail is also your user-id.\n" +"\n" +"\n" +"\n" +"\n" +"\n" +"
Name
E-mail\n" +"
Password
Real name (optional)
 
\n" +"
\n" +, getenv("HTTP_HOST") +, errMsg ? errMsg : "" +, cartUsualString(cart, "hgLogin_user", "") +, cartUsualString(cart, "hgLogin_email", "") +, cartUsualString(cart, "hgLogin_password", "") +, cartUsualString(cart, "hgLogin_realName", "") +); + + +cartSaveSession(cart); + +hPrintf("
"); + +} + + +void signup(struct sqlConnection *conn) +/* process the signup form */ +{ +char query[256]; +char *user = cartUsualString(cart, "hgLogin_user", ""); +if (!user || sameString(user,"")) + { + freez(&errMsg); + errMsg = cloneString("User name cannot be blank."); + signupPage(conn); + return; + } +safef(query,sizeof(query), "select password from gbMembers where userName='%s'", user); +char *password = sqlQuickString(conn, query); +if (password) + { + freez(&errMsg); + errMsg = cloneString("A user with this name already exists."); + signupPage(conn); + freez(&user); + return; + } + +char *email = cartUsualString(cart, "hgLogin_email", ""); +if (!email || sameString(email,"")) + { + freez(&errMsg); + errMsg = cloneString("Email cannot be blank."); + signupPage(conn); + return; + } + +password = cartUsualString(cart, "hgLogin_password", ""); +if (!password || sameString(password,"") || (strlen(password)<8)) + { + freez(&errMsg); + errMsg = cloneString("Password must be at least 8 characters long."); + 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 *realName = cartUsualString(cart, "hgLogin_realName", ""); +if (!realName || sameString(realName,"")) + { + realName = " "; + } + +char encPwd[35] = ""; +encryptNewPwd(password, encPwd, sizeof(encPwd)); +safef(query,sizeof(query), "insert into gbMembers set " + "userName='%s',realName='%s',password='%s',email='%s', " + "lastUse=NOW(),activated='N',dateAuthenticated='9999-12-31 23:59:59'", + sqlEscapeString(user),sqlEscapeString(realName),sqlEscapeString(encPwd),sqlEscapeString(email)); +sqlUpdate(conn, query); + + +hPrintf( +"

UCSC Genome Browser

\n" +"

\n" +"

\n" +"

User %s successfully added.

\n" +, email +); + + +hPrintf( +"Click here to return.
\n" +); + + +} + + +/* ----- account login/display functions ---- */ + + +void displayLoginPage(struct sqlConnection *conn) +/* draw the account login page */ +{ +char *email = cartUsualString(cart, "hgLogin_email", ""); +/* for password security, use cgi hash instead of cart */ +char *password = cgiUsualString("hgLogin_password", ""); +hPrintf( +"

UCSC Genome Browser

" +"

" +"

" +"%s" +"

Account Login

" +"
" +"" +"" +"
" +"" +"
User Name
" +"
Password
 " +" 
" +"
" +, errMsg ? errMsg : "" +, email +, password +); + +cartSaveSession(cart); + +hPrintf("
"); + +} + + +/******* BEGIN dispalyLogin *************************/ +void displayLogin(struct sqlConnection *conn) +/* display user account info */ +{ +struct sqlResult *sr; +char **row; +char query[256]; +char *userName = cartUsualString(cart, "hgLogin_userName", ""); +if (sameString(userName,"")) + { + freez(&errMsg); + errMsg = cloneString("User name cannot be blank."); + displayLoginPage(conn); + return; + } +/************************************************************* +char *email = cartUsualString(cart, "hgLogin_email", ""); +if (sameString(email,"")) + { + freez(&errMsg); + errMsg = cloneString("Email cannot be blank."); + displayLoginPage(conn); + return; + } +****************************************************************/ +/* for password security, use cgi hash instead of cart */ +char *password = cgiUsualString("hgLogin_password", ""); +if (sameString(password,"")) + { + freez(&errMsg); + errMsg = cloneString("Password cannot be blank."); + displayLoginPage(conn); + return; + } + +safef(query,sizeof(query),"select * from gbMembers where userName='%s'", userName); +sr = sqlGetResult(conn, query); +if ((row = sqlNextRow(sr)) == NULL) + { + freez(&errMsg); + char temp[256]; + safef(temp,sizeof(temp),"User name %s not found.",userName); + errMsg = cloneString(temp); + displayLoginPage(conn); + return; + } +struct gbMembers *m = gbMembersLoad(row); +sqlFreeResult(&sr); + +/* TODO: check user name exist and activated */ +/* ..... */ + +if (checkPwd(password,m->password)) + { +hPrintf("

Login succesful !!!! calling displayLoginSuccess now.

\n"); + displayLoginSuccess(); +// htmlSetCookie("hgLogin_User", "chinhli", NULL, NULL, ".cse.ucsc.edu", FALSE); +// hPrintf(""); + return; +//htmlSetCookie("hgLogin_User", "Chin Li", NULL, NULL, +//".cse.ucsc.edu", FALSE); +// hPrintf("

Login Information for %s:

\n",m->email); +// hPrintf("\n"); +// hPrintf("\n",m->userName); +// hPrintf("\n",m->activated); +// hPrintf("
name:%s
activated:%s
\n"); +// hPrintf("
\n"); +// hPrintf("Return to Session.
\n"); +// hPrintf("Return to signup.
\n"); +// +// hPrintf("Go to UCSC Genome Browser.
\n"); + } +else + { + hPrintf("

Invalid User/Password

\n",m->email); + hPrintf("Return to signup.
\n"); + } + +gbMembersFree(&m); + +} +/******* end old dispalyLogin *************************/ + + + +void displayLoginSuccess() +/* display login success msg, and set cookie */ +{ +hPrintf( +"

UCSC Genome Browser

" +"

" +"

" +"%s" +"\n" +//"" +"\n" +); +hPrintf( +"" +); +} + + + +void displayUserInfo(struct sqlConnection *conn) +/* display user account info */ +{ +struct sqlResult *sr; +char **row; +char query[256]; +char *user = cartUsualString(cart, "hgLogin_user", ""); +/*************************************/ +if (sameString(user,"")) + { + freez(&errMsg); + errMsg = cloneString("User name cannot be blank."); + displayUserInfo(conn); + return; + } +/* for password security, use cgi hash instead of cart */ +char *password = cgiUsualString("hgLogin_password", ""); +if (sameString(password,"")) + { + freez(&errMsg); + errMsg = cloneString("Password cannot be blank."); + displayUserInfo(conn); + return; + } +safef(query,sizeof(query),"select * from gbMembers where username='%s'", user); +sr = sqlGetResult(conn, query); +if ((row = sqlNextRow(sr)) == NULL) + { + freez(&errMsg); + char temp[256]; + safef(temp,sizeof(temp),"User %s not found.",user); + errMsg = cloneString(temp); + displayUserInfo(conn); + return; + } +struct gbMembers *m = gbMembersLoad(row); +sqlFreeResult(&sr); + +if (checkPwd(password,m->password)) + { + + hPrintf("

UCSC Genome Browser User Information:

\n"); + hPrintf("\n"); + hPrintf("\n",m->userName); + + hPrintf("\n",m->realName); + hPrintf("\n",m->email); + hPrintf("
User name:%s
Real name:%s
E-mail:%s
\n"); + hPrintf("
\n"); + + + hPrintf("Return to signup.
\n"); + hPrintf("Go to UCSC Genome Browser.
\n"); + } +else + { + hPrintf("

Invalid User/Password

\n",m->userName); + hPrintf("Return to signup.
\n"); + } +/**************************************************/ +gbMembersFree(&m); + +} + +/* +void upgradeMembersTable(struct sqlConnection* conn) +/ * one-time upgrade of gbMembers table to store encrypted passwords * / +{ +char query[256]; + +safef(query,sizeof(query),"select email from members"); +struct slName *email=NULL,*list = sqlQuickList(conn,query); +for(email=list;email;email=email->next) + { + + uglyf("email=%s
\n",email->name); + + safef(query,sizeof(query),"select password from gbMembers where email='%s'", email->name); + char *password = sqlQuickString(conn,query); + + uglyf("password=%s
\n",password); + + if (password) + { + if (!startsWith("$1$",password)) / * upgrade has not already been done * / + { + uglyf("does not start with $1$
\n"); + char encPwd[35] = ""; + encryptNewPwd(password, encPwd, sizeof(encPwd)); + safef(query,sizeof(query),"update gbMembers set password = '%s' where email='%s'", + sqlEscapeString(encPwd), sqlEscapeString(email->name)); + uglyf("query: %s
\n",query); + sqlUpdate(conn,query); + } + freez(&password); + } + + uglyf("
\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 = hConnectCentral(); +cart = theCart; + + +if (cartVarExists(cart, "debug")) + debugShowAllMembers(conn); +/* remove after a while when it is no longer needed +else if (cartVarExists(cart, "fixMembers")) + { + upgradeMembersTable(conn); + updatePasswordsFile(conn); + hPrintf( + "

UCSC Genome Browser

" + "

" + "

" + "

Successfully updated the gbMembers table to store hashed passwords.

" + "Click here to return.
" + ); + } +*/ +else if (cartVarExists(cart, "update")) + { + updatePasswordsFile(conn); + hPrintf( + "

UCSC Genome Browser

" + "

" + "

" + "

Successfully updated the authentication file.

" + "Click here to return.
" + ); + } +else if (cartVarExists(cart, "hgLogin.do.lostPasswordPage")) + lostPasswordPage(conn); +else if (cartVarExists(cart, "hgLogin.do.lostPassword")) + lostPassword(conn); +else if (cartVarExists(cart, "hgLogin.do.changePasswordPage")) + changePasswordPage(conn); +else if (cartVarExists(cart, "hgLogin.do.changePassword")) + changePassword(conn); +else if (cartVarExists(cart, "hgLogin.do.displayUserInfo")) + displayUserInfo(conn); +else if (cartVarExists(cart, "hgLogin.do.displayLoginPage")) + displayLoginPage(conn); +else if (cartVarExists(cart, "hgLogin.do.displayLogin")) + displayLogin(conn); +else if (cartVarExists(cart, "hgLogin.do.signup")) + signup(conn); +else + signupPage(conn); + + +hDisconnectCentral(&conn); +cartRemovePrefix(cart, "hgLogin.do."); + +} + +void usage() +/* Explain usage and exit. */ +{ +errAbort( + "hgLogin - administer gsid hiv membership functions - a cgi script\n" + "usage:\n" + " hgLogin\n" + ); +} + +int main(int argc, char *argv[]) +/* Process command line. */ +{ +pushCarefulMemHandler(100000000); +cgiSpoof(&argc, argv); +htmlSetStyle(htmlStyleUndecoratedLink); +htmlSetBgColor(HG_CL_OUTSIDE); +oldCart = hashNew(10); +cartHtmlShell("UCSC Genome Browser Signup", doMiddle, hUserCookie(), excludeVars, oldCart); +return 0; +}