4898794edd81be5285ea6e544acbedeaeb31bf78 max Tue Nov 23 08:10:57 2021 -0800 Fixing pointers to README file for license in all source code files. refs #27614 diff --git src/hg/gsid/gsidMember/gsidMember.c src/hg/gsid/gsidMember/gsidMember.c index e7fd008..2676d68 100644 --- src/hg/gsid/gsidMember/gsidMember.c +++ src/hg/gsid/gsidMember/gsidMember.c @@ -1,1282 +1,1282 @@ /* gsidMember - Administer GSID HIV membership - signup, paypal, lost password, etc. */ /* Copyright (C) 2013 The Regents of the University of California - * See README in this or parent directory for licensing information. */ + * See kent/LICENSE or http://genome.ucsc.edu/license/ for licensing information. */ #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 "gsidMember.h" #include "members.h" #include "bio.h" #include "paypalSignEncrypt.h" #include "versionInfo.h" char *excludeVars[] = { "submit", "Submit", "debug", "fixMembers", "update", "gsidM_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 */ { 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, NOSQLINJ "select email,password from members 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); } /* -------- paypal functions ------ */ void appendSqlField(struct dyString* dy, char *varName, struct cgiVar *cgiVars) /* append the next field to the sql insert statement */ { boolean isFirstField = dy->stringSize == 0; if (isFirstField) sqlDyStringPrintf(dy,"insert into transactions set "); sqlDyStringPrintf(dy,"%-s%s='%s'", isFirstField ? "" : ", ", varName, cgiUsualString(varName,"")); struct cgiVar *this = NULL; for(this=cgiVars;this;this=this->next) { if (sameString(this->name,varName)) this->saved = TRUE; } } void processIpn(struct sqlConnection *conn) /* process Instant Payment Notification * Steps: * verify server source name * write to log * compose post pack to paypal.com or sandbox * todo: optimize this with https * verify response * write transaction to ipn table * mark user as paid if transaction is completed * (matching on email) * activate user account if completed by updating "passwords" file. */ { /* save the ipn post variables received to the log regardless */ struct cgiVar *this=NULL, *cgiVars = cgiVarList(); FILE *f=mustOpen("ipn.log","a"); struct dyString *dy=newDyString(256); struct dyString *dyVerify = NULL; char *paypalServer = cfgOption("paypalServer"); dyStringAppend(dy,"../cgi-bin/webscr?cmd=_notify-validate"); for(this=cgiVars;this;this=this->next) { fprintf(f,"%s=%s\n", this->name, this->val); char *encodedVal = cgiEncode(this->val); dyStringPrintf(dy,"&%s=%s", this->name, encodedVal); freeMem(encodedVal); this->saved = FALSE; /* clear now, use later */ } fflush(f); /* verify ipn sender ip */ char *remoteAddr=getenv("REMOTE_ADDR"); //66.135.197.164 ipn.sandbox.paypal.com fprintf(f,"REMOTE_ADDR=%s\n", remoteAddr); fflush(f); char *paypalIpn = reverseDns(remoteAddr); if (!sameString(paypalIpn,cfgOption("paypalIpnServer"))) { fprintf(f,"Error: invalid REMOTE_ADDR %s=%s is not paypal ipn %s\n", remoteAddr, paypalIpn, cfgOption("paypalIpnServer")); fflush(f); goto cleanup; } /* verify via post back to paypal */ fprintf(f,"about to attempt POST to [%s]:\n",dy->string); fflush(f); dyVerify = bio(paypalServer, dy->string, "all.pem", NULL); /* certs to verify OK */ if (!dyVerify) { fprintf(f,"Error: unable to post verification to %s\n",dy->string); fflush(f); goto cleanup; } //struct lineFile *lf = netLineFileMayOpen(dy->string); struct lineFile *lf = lineFileOnString("response",TRUE,cloneString(dyVerify->string)); fprintf(f,"POST verification response:\n"); fflush(f); char *line = NULL; boolean verified = FALSE; /* skip http response header */ while (lineFileNext(lf, &line, NULL)) { if (sameString(line,"")) break; } while (lineFileNext(lf, &line, NULL)) { fprintf(f,"%s\n",line); fflush(f); if (sameString(line,"VERIFIED")) verified = TRUE; } lineFileClose(&lf); if (!verified) { fprintf(f,"NOT VERIFIED (txn_id=%s)\n",cgiOptionalString("txn_id")); fflush(f); goto cleanup; } fprintf(f,"VERIFIED (txn_id=%s)\n",cgiOptionalString("txn_id")); fflush(f); /* append to transactions table */ dyStringClear(dy); appendSqlField(dy,"invoice",cgiVars); appendSqlField(dy,"receiver_email",cgiVars); appendSqlField(dy,"item_name",cgiVars); appendSqlField(dy,"item_number",cgiVars); appendSqlField(dy,"quantity",cgiVars); appendSqlField(dy,"payment_status",cgiVars); appendSqlField(dy,"pending_reason",cgiVars); appendSqlField(dy,"payment_date",cgiVars); appendSqlField(dy,"mc_gross",cgiVars); appendSqlField(dy,"mc_fee",cgiVars); appendSqlField(dy,"shipping",cgiVars); appendSqlField(dy,"tax",cgiVars); appendSqlField(dy,"mc_currency",cgiVars); appendSqlField(dy,"txn_id",cgiVars); appendSqlField(dy,"txn_type",cgiVars); appendSqlField(dy,"first_name",cgiVars); appendSqlField(dy,"last_name",cgiVars); appendSqlField(dy,"address_street",cgiVars); appendSqlField(dy,"address_city",cgiVars); appendSqlField(dy,"address_state",cgiVars); appendSqlField(dy,"address_zip",cgiVars); appendSqlField(dy,"address_country",cgiVars); appendSqlField(dy,"address_status",cgiVars); appendSqlField(dy,"residence_country",cgiVars); appendSqlField(dy,"payer_email",cgiVars); appendSqlField(dy,"payer_id",cgiVars); appendSqlField(dy,"payer_status",cgiVars); appendSqlField(dy,"payment_type",cgiVars); appendSqlField(dy,"payment_gross",cgiVars); appendSqlField(dy,"payment_fee",cgiVars); appendSqlField(dy,"business",cgiVars); appendSqlField(dy,"referrer_id",cgiVars); appendSqlField(dy,"receiver_id",cgiVars); appendSqlField(dy,"charset",cgiVars); appendSqlField(dy,"custom",cgiVars); appendSqlField(dy,"notify_version",cgiVars); appendSqlField(dy,"verify_sign",cgiVars); struct dyString *dyOther=newDyString(256); /* catchall for fields we did not anticipate, or future fields */ for(this=cgiVars;this;this=this->next) { if (!this->saved) dyStringPrintf(dyOther,"%s=%s\\n", this->name, this->val); /* remove these vars from the cart for better security/privacy */ cartRemove(cart, this->name); } sqlDyStringPrintf(dy,", otherFields='%s'", dyOther->string); dyStringFree(&dyOther); //debug TODO: clean that out of trash //writeGulp("../trash/debug.sql", dy->string, dy->stringSize); sqlUpdate(conn,dy->string); char *invoice = cgiUsualString("invoice",""); char *paymentDate = cgiUsualString("payment_date",""); /* handle expiration date */ /* Could use <time.h> function strptime, but perhaps getdate with the external file datemsk is more flexible for GSID in the long run. struct tm *tm; AllocVar(tm); char *dateOk=strptime(paymentDate, "%H:%M:%S %b %d, %Y PST", tm); if (!dateOk) strptime(paymentDate, "%H:%M:%S %b %d, %Y PDT", tm); if (!dateOk) */ setenv("DATEMSK","./datemsk", TRUE); /* required by getdate() */ struct tm *tm=getdate(paymentDate); if (!tm) { fprintf(f,"error: getdate(%s) returned null struct tm*\n", paymentDate); fflush(f); goto cleanup; } /* set expiration date to one year ahead */ char expireDate[11]; /* note: tm returns year rel 1900, mon and day are 0 based */ safef(expireDate,sizeof(expireDate),"%4d-%02d-%02d",1900+tm->tm_year+1,tm->tm_mon+1,tm->tm_mday+1); /* use invoice# to map back to user's email */ char query[256]; sqlSafef(query,sizeof(query), "select email from invoices where id=%s", invoice); char *email = sqlQuickString(conn, query); if (!email) { fprintf(f,"error: unable to use invoice# %s to map back to user's email.\n", invoice); fflush(f); goto cleanup; return; } /* see if payment_status is completed */ char *paymentStatus = cgiUsualString("payment_status",""); if (!sameString("Completed",paymentStatus)) { fprintf(f,"Note: payment status not 'Completed' %s\n",dy->string); fflush(f); /* send payer an email confirming */ char cmd[256]; safef(cmd,sizeof(cmd), "echo \"We received your payment through PayPal. However your account is not yet activated.\nPayment status is %s %s. When your payment status is completed your account will be activated and you will receive another email. Thank you.\" | mail -s \"Payment received for GSID HIV Data Browser access.\" %s" , paymentStatus , cgiUsualString("payment_reason","") , email); int result = system(cmd); if (result == -1) { fprintf(f,"Note: sending email notice of non-activated account to %s failed\n", email); fflush(f); goto cleanup; } goto cleanup; } /* Write payment info to the members table * email field has been stored in the invoice field */ dyStringClear(dy); sqlDyStringPrintf(dy,"update members set " "activated='Y'," //P means paied but not activated until GA //"activated='P'," "amountPaid='%s'," "datePaid='%s'," "expireDate='%s'" " where email='%s'" , cgiUsualString("payment_gross","") , paymentDate , expireDate , email ); //debug TODO: clean that out of trash //writeGulp("../trash/debug.sql", dy->string, dy->stringSize); sqlUpdate(conn,dy->string); updatePasswordsFile(conn); /* send payer an email confirming */ char cmd[256]; safef(cmd,sizeof(cmd), "echo \"We received your payment through Paypal. Your account is now activated.\nPlease go to http://%s/ to access the site. \" | mail -s \"Payment received for GSID HIV Data Browser access.\" %s" , getenv("HTTP_HOST"), email); int result = system(cmd); if (result == -1) { fprintf(f,"Note: sending email notice of activated account to %s failed\n", email); fflush(f); goto cleanup; } cleanup: freez(&paypalIpn); fprintf(f,"\n"); carefulClose(&f); dyStringFree(&dy); dyStringFree(&dyVerify); } /* -------- functions ---- */ void debugShowAllMembers(struct sqlConnection *conn) /* display all members */ { struct sqlResult *sr; char **row; hPrintf("<h1>Members</h1>"); hPrintf("<table>"); hPrintf("<th>email</th><th>password</th>"); sr = sqlGetResult(conn, NOSQLINJ "select * from members"); 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>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<span style='color:red;'>%s</span>" "<h3>Send Me A New Password</h3>" "<form method=post action=\"gsidMember\" name=lostPasswordForm >" "<table>" "<tr><td>E-mail</td><td><input type=text name=gsidM_email size=20> " "(your e-mail is also your user-id)</td></tr>" "<tr><td> </td><td><input type=submit name=gsidMember.do.lostPassword value=submit>" " <input type=submit name=gsidMember.do.signupPage value=cancel></td></tr>" "</table>" "<br>" , 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, "gsidM_email", ""); if (!email || sameString(email,"")) { freez(&errMsg); errMsg = cloneString("Email cannot be blank."); lostPasswordPage(conn); return; } sqlSafef(query,sizeof(query), "select password from members 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)); sqlSafef(query,sizeof(query), "update members set password='%s' where email='%s'", encPwd, 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( "<h2>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<h3>Error emailing password to: %s</h3>" "Click <a href=gsidMember?gsidMember.do.signupPage=1>here</a> to return.<br>" , email ); } else { hPrintf( "<h2>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<h3>Password has been emailed to: %s</h3>" "Click <a href=gsidMember?gsidMember.do.signupPage=1>here</a> to return.<br>" , email ); } freez(&password); } void changePasswordPage(struct sqlConnection *conn) /* change password page */ { hPrintf( "<h2>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<span style='color:red;'>%s</span>" "<h3>Change Password</h3>" "<form method=post action=\"gsidMember\" name=changePasswordForm >" "<table>" "<tr><td>E-mail</td><td><input type=text name=gsidM_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=gsidM_password value=\"\" size=10></td></tr>\n" "<tr><td>New Password</td><td><input type=password name=gsidM_newPassword value=\"\" size=10></td></tr>\n" "<tr><td> </td><td><input type=submit name=gsidMember.do.changePassword value=submit>" " <input type=submit name=gsidMember.do.signupPage value=cancel></td></tr>" "</table>" "<br>" , errMsg ? errMsg : "" , cartUsualString(cart, "gsidM_email", "") ); cartSaveSession(cart); hPrintf("</FORM>"); } void changePassword(struct sqlConnection *conn) /* process the change password form */ { char query[256]; char *email = cartUsualString(cart, "gsidM_email", ""); char *currentPassword = cartUsualString(cart, "gsidM_password", ""); char *newPassword = cartUsualString(cart, "gsidM_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; } sqlSafef(query,sizeof(query), "select password from members 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)); sqlSafef(query,sizeof(query), "update members set password='%s' where email='%s'", encPwd, email); sqlUpdate(conn, query); hPrintf ( "<h2>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<h3>Password has been changed.</h3>" "Click <a href=gsidMember?gsidMember.do.signupPage=1>here</a> to return.<br>" , email ); updatePasswordsFile(conn); cartRemove(cart, "gsidM_password"); cartRemove(cart, "gsidM_newPassword"); } void signupPage(struct sqlConnection *conn) /* draw the signup page */ { hPrintf( "<h2>GSID HIV Data Browser</h2>\n" "<p align=\"left\">" "</p>" "GSID provides access to data from the 2003 VaxGen HIV vaccine phase III clinical trials on a yearly access-fee basis.<br>\n" "Academic and non-profit researchers get a substantial discount. <br>\n" "<br>\n" "If you are already a member, click <a href=https://%s/>here</a> to access GSID HIV Data Browser.<br>\n" "To view your existing account, click <a href=\"gsidMember?gsidMember.do.displayAccountPage=1\">here</a>.<br>\n" "To change your password, click <a href=\"gsidMember?gsidMember.do.changePasswordPage=1\">here</a>.<br>\n" "Lost your password? Click <a href=\"gsidMember?gsidMember.do.lostPasswordPage=1\">here</a>.<br>\n" "<span style='color:red;'>%s</span>" "<h3>Sign up</h3>\n" "<form method=post action=\"gsidMember\" name=mainForm >\n" "NOTE: Your e-mail is also your user-id.\n" "<table>\n" "<tr><td>E-mail</td><td><input type=text name=gsidM_email value=\"%s\"size=20>\n" "<tr><td>Password</td><td><input type=password name=gsidM_password value=\"%s\" size=10></td></tr>\n" "<tr><td>Name</td><td><input type=text name=gsidM_name value=\"%s\" size=20></td></tr>\n" "<tr><td>Phone</td><td><input type=text name=gsidM_phone value=\"%s\" size=20></td></tr>\n" "<tr><td>Institution</td><td><input type=text name=gsidM_institution value=\"%s\" size=40></td></tr>\n" "<tr><td>Type</td><td><input type=radio name=gsidM_type value=commercial%s>Commercial $%s.00 USD</td></tr>\n" "<tr><td> </td><td><input type=radio name=gsidM_type value=academic%s>Academic $%s.00 USD</td></tr>\n" "<tr><td> </td><td><input type=submit name=gsidMember.do.signup value=submit></td></tr>\n" "</table>\n" "<br>\n" "Questions? Call (650) 228-7900.<br>\n" , getenv("HTTP_HOST") , errMsg ? errMsg : "" , cartUsualString(cart, "gsidM_email", "") , cartUsualString(cart, "gsidM_password", "") , cartUsualString(cart, "gsidM_name", "") , cartUsualString(cart, "gsidM_phone", "") , cartUsualString(cart, "gsidM_institution", "") , sameString("commercial",cartUsualString(cart, "gsidM_type", "")) ? " checked" : "" , cfgOption("paypalCommercialFee") , sameString("academic",cartUsualString(cart, "gsidM_type", "")) ? " checked" : "" , cfgOption("paypalAcademicFee") ); cartSaveSession(cart); hPrintf("</FORM>"); } void drawPaymentButton(struct sqlConnection *conn, char *type) { char query[256]; char *email = cartUsualString(cart, "gsidM_email", ""); sqlSafef(query, sizeof(query), "insert into invoices values(default, '%s')", email); sqlUpdate(conn, query); int id = sqlLastAutoId(conn); char invoice[20]; safef(invoice, sizeof(invoice), "%d", id); //char buttonFile[256]; //safef(buttonFile,sizeof(buttonFile),"./%s.button",type); // TODO may move the button file later char buttonHtml[4096]; //readInGulp(buttonFile, &buttonHtml, NULL); char buttonData[4096]; char *paypalServer = cfgOption("paypalServer"); char *httpHost=getenv("HTTP_HOST"); char *paypalEmail = cfgOption("paypalEmail"); safef(buttonData, sizeof(buttonData), "cmd=_xclick\n" "business=%s\n" "invoice=%s\n" "item_name=GSID HIV Yearly %s Access Fee\n" "item_number=%s\n" "amount=%s.00\n" "no_shipping=2\n" "return=https://%s/cgi-bin-signup/gsidMember?gsidMember.do.paypalThanks=1\n" "cancel_return=https://%s/cgi-bin-signup/gsidMember?gsidMember.do.paypalCancel=1\n" "notify_url=https://%s/cgi-bin-signup/gsidMember\n" "no_note=1\n" "currency_code=USD\n" "lc=US\n" "bn=PP-BuyNowBF\n" "cert_id=%s\n" , paypalEmail , invoice , sameString("commercial",type) ? "Commercial" : "Academic" , sameString("commercial",type) ? "001" : "002" , sameString("commercial",type) ? cfgOption("paypalCommercialFee") : cfgOption("paypalAcademicFee") , httpHost , httpHost , httpHost , cfgOption("gsidCertId") ); //debug TODO: clean that out of trash //writeGulp("../trash/debug.buttonData", buttonData, strlen(buttonData)); //fprintf(stderr, "debug: about to encrypt buttonData=[%s]\n", buttonData);fflush(stderr); char *buttonEncrypted = sign_and_encryptFromFiles(buttonData, "gsid_key.pem", "gsid_cert.pem", cfgOption("paypalCert"), FALSE); if (buttonEncrypted) { //eraseWhiteSpace(buttonEncrypted); //debug TODO: clean that out of trash //writeGulp("../trash/debug.buttonEnc", buttonEncrypted, strlen(buttonEncrypted)); } else { fprintf(stderr, "error: sign_and_encrypt failed on buttonData=[%s]\n", buttonData); } safef(buttonHtml,sizeof(buttonHtml), "<form action=\"https://%s/cgi-bin/webscr\" method=\"post\">\n" "<input type=\"hidden\" name=\"cmd\" value=\"_s-xclick\">\n" "<input type=\"image\" src=\"https://%s/en_US/i/btn/x-click-but23.gif\" border=\"0\" name=\"submit\" alt=\"Make payments with PayPal - it's fast, free and secure!\">\n" "<img alt=\"\" border=\"0\" src=\"https://%s/en_US/i/scr/pixel.gif\" width=\"1\" height=\"1\">\n" "<input type=\"hidden\" name=\"encrypted\" value=\"%s\">\n" "</form>\n" , paypalServer , paypalServer , paypalServer , buttonEncrypted ); if (buttonEncrypted) /* paypalSignEncrypt.c uses malloc to avoid common.h */ { free(buttonEncrypted); buttonEncrypted = NULL; } //debug //fprintf(stderr, "encrypted button form: [%s]\n", buttonHtml); hPrintf( "Pay yearly %s access fee to activate your account.<br>\n" "%s\n" "Your payment will be processed by PayPal.<br>\n" "You can use a variety of payment methods including credit card.<br>\n" "You do not need to be a PayPal member.<br>\n" "Be sure to click the \"Return to Merchant\" link when done paying.<br>\n" "<br>\n" , type , buttonHtml ); } void signup(struct sqlConnection *conn) /* process the signup form */ { char query[256]; char *email = cartUsualString(cart, "gsidM_email", ""); if (!email || sameString(email,"")) { freez(&errMsg); errMsg = cloneString("Email cannot be blank."); signupPage(conn); return; } sqlSafef(query,sizeof(query), "select password from members where email='%s'", email); char *password = sqlQuickString(conn, query); if (password) { freez(&errMsg); errMsg = cloneString("A user with this email already exists."); signupPage(conn); freez(&password); return; } password = cartUsualString(cart, "gsidM_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 *name = cartUsualString(cart, "gsidM_name", ""); if (!name || sameString(name,"")) { freez(&errMsg); errMsg = cloneString("Name cannot be blank."); signupPage(conn); return; } char *phone = cartUsualString(cart, "gsidM_phone", ""); if (!phone || sameString(phone,"")) { freez(&errMsg); errMsg = cloneString("Phone cannot be blank."); signupPage(conn); return; } char *institution = cartUsualString(cart, "gsidM_institution", ""); if (!institution || sameString(institution,"")) { freez(&errMsg); errMsg = cloneString("Institution cannot be blank."); signupPage(conn); return; } char *type = cartUsualString(cart, "gsidM_type", ""); if (!type || sameString(type,"")) { freez(&errMsg); errMsg = cloneString("Type cannot be blank."); signupPage(conn); return; } char encPwd[35] = ""; encryptNewPwd(password, encPwd, sizeof(encPwd)); sqlSafef(query,sizeof(query), "insert into members set " "email='%s',password='%s',activated='%s',name='%s',phone='%s',institution='%s',type='%s'", email, encPwd, "N", name, phone, institution, type); sqlUpdate(conn, query); hPrintf( "<h2>GSID HIV Data Browser</h2>\n" "<p align=\"left\">\n" "</p>\n" "<h3>User %s successfully added.</h3>\n" , email ); drawPaymentButton(conn, type); hPrintf( "Click <a href=gsidMember?gsidMember.do.signupPage=1>here</a> to return.<br>\n" ); } void paypalThanks() /* thank the user for their payment and welcome them */ { char *paypalServer = cfgOption("paypalServer"); char *status = cgiUsualString("st", ""); if (sameString(status,"Completed")) { hPrintf( "<p>\n" "<CENTER><H1>Thanks For signing up for GSID HIV Data Browser</H1></CENTER>\n" "<br>\n" // ); "Your account is now activated and ready to use.<br>\n" // disable the following statement with temporary Beta release message /* hPrintf(" We received your payment. Your account is now created. \n"); hPrintf("<br><br><B>At the moment, the system is available only to our authorized Beta test users.</B>"); hPrintf(" We will notify you as soon as our Beta test phase is completed. "); hPrintf(" Upon its official release,"); hPrintf(" we will activate your account to enable you to log in.<br>"); hPrintf( */ "<br>\n" "Thank you for your payment. " "Your transaction has been completed, and a receipt for your purchase has been emailed to you.<br>\n" "You may log into PayPal at http://%s/us to view details of this transaction.\n" "<br>\n" "<br>\n" "<big>\n" "Go to <a href=\"/\">GSID HIV Data Browser</A>\n" "</big>\n" "<br>\n" "<br>\n" , paypalServer ); } else { hPrintf( "<p>\n" "<CENTER><H1>Thanks For signing up for GSID HIV Data Browser</H1></CENTER>\n" "<br>\n" "Thank you for your payment. " "However, your account is not activated yet (status=%s).<br>\n" "<br>\n" "When your transaction has been completed, a notice will be emailed to you.<br>\n" "You may log into PayPal at http://%s/us to view details of this transaction.\n" "<br>\n" "<br>\n" "<big>\n" "When your transaction is completed, you may go to <a href=\"/\">GSID HIV Data Browser</A>\n" "</big>\n" "<br>\n" "<br>\n" , status , paypalServer ); } } void paypalCancel() /* the user has cancelled their payment before completion */ { hPrintf( "<p>\n" "<CENTER><H1>GSID HIV Data Browser - Payment Cancelled</H1></CENTER>\n" "<br>\n" "Because you cancelled your PayPal payment, your account has not been activated.<br>\n" "<br>\n" "<big>\n" "Go to <a href=\"/hiv-signup-html/\">GSID HIV Data Browser</A> to sign up.\n" "</big>\n" "<br>\n" "<br>\n" ); } /* ----- account login/display functions ---- */ void displayAccountPage(struct sqlConnection *conn) /* draw the account login page */ { char *email = cartUsualString(cart, "gsidM_email", ""); /* for password security, use cgi hash instead of cart */ char *password = cgiUsualString("gsidM_password", ""); hPrintf( "<h2>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<span style='color:red;'>%s</span>" "<h3>Account Login</h3>" "<form method=post action=\"gsidMember\" name=accountLoginForm >" "<table>" "<tr><td>E-mail</td><td><input type=text name=gsidM_email value=\"%s\" size=20> " "<tr><td>Password</td><td><input type=password name=gsidM_password value=\"%s\" size=10></td></tr>\n" "(your e-mail is also your user-id)</td></tr>" "<tr><td> </td><td><input type=submit name=gsidMember.do.displayAccount value=submit>" " <input type=submit name=gsidMember.do.signupPage value=cancel></td></tr>" "</table>" "<br>" , 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, "gsidM_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("gsidM_password", ""); if (sameString(password,"")) { freez(&errMsg); errMsg = cloneString("Password cannot be blank."); displayAccountPage(conn); return; } sqlSafef(query,sizeof(query),"select * from members 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 members *m = membersLoad(row); sqlFreeResult(&sr); if (checkPwd(password,m->password)) { hPrintf("<h1>Account Information for %s:</h1>\n",m->email); hPrintf("<table>\n"); 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>type:</td><td>%s</td><tr>\n",m->type); hPrintf("<tr><td align=right>amount paid:</td><td>$%8.2f</td><tr>\n",m->amountPaid); hPrintf("<tr><td align=right>expiration:</td><td>%s</td><tr>\n",m->expireDate); hPrintf("<tr><td align=right>activated:</td><td>%s</td><tr>\n",m->activated); hPrintf("</table>\n"); hPrintf("<br>\n"); /* add payment button if needed */ char *currentDate=sqlQuickString(conn, NOSQLINJ "select current_date()"); if (!sameString(m->activated,"Y") || strcmp(currentDate,m->expireDate)>0) { drawPaymentButton(conn, m->type); } freez(¤tDate); hPrintf("Return to <a href=\"gsidMember\">signup</A>.<br>\n"); hPrintf("Go to <a href=\"/\">GSID HIV Data Browser</A>.<br>\n"); } else { hPrintf("<h1>Invalid User/Password</h1>\n",m->email); hPrintf("Return to <a href=\"gsidMember\">signup</A>.<br>\n"); } membersFree(&m); } /* void upgradeMembersTable(struct sqlConnection* conn) / * one-time upgrade of members table to store encrypted passwords * / { char query[256]; sqlSafef(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<br>\n",email->name); sqlSafef(query,sizeof(query),"select password from members 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)); sqlSafef(query,sizeof(query),"update members set password = '%s' where email='%s'", encPwd, 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; cart = theCart; //hSetDb("membership"); conn = hAllocConn("membership"); 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( "<h2>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<h3>Successfully updated the members table to store hashed passwords.</h3>" "Click <a href=gsidMember?gsidMember.do.signupPage=1>here</a> to return.<br>" ); } */ else if (cartVarExists(cart, "update")) { updatePasswordsFile(conn); hPrintf( "<h2>GSID HIV Data Browser</h2>" "<p align=\"left\">" "</p>" "<h3>Successfully updated the authentication file.</h3>" "Click <a href=gsidMember?gsidMember.do.signupPage=1>here</a> to return.<br>" ); } else if (cgiVarExists("verify_sign")) processIpn(conn); else if (cartVarExists(cart, "gsidMember.do.paypalThanks")) paypalThanks(); else if (cartVarExists(cart, "gsidMember.do.paypalCancel")) paypalCancel(); else if (cartVarExists(cart, "gsidMember.do.lostPasswordPage")) lostPasswordPage(conn); else if (cartVarExists(cart, "gsidMember.do.lostPassword")) lostPassword(conn); else if (cartVarExists(cart, "gsidMember.do.changePasswordPage")) changePasswordPage(conn); else if (cartVarExists(cart, "gsidMember.do.changePassword")) changePassword(conn); else if (cartVarExists(cart, "gsidMember.do.displayAccountPage")) displayAccountPage(conn); else if (cartVarExists(cart, "gsidMember.do.displayAccount")) displayAccount(conn); else if (cartVarExists(cart, "gsidMember.do.signup")) signup(conn); else signupPage(conn); hFreeConn(&conn); cartRemovePrefix(cart, "gsidMember.do."); } void usage() /* Explain usage and exit. */ { errAbort( "gsidMember - administer gsid hiv membership functions - a cgi script\n" "usage:\n" " gsidMember\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("GSID HIV Data Browser Signup", doMiddle, hUserCookie(), excludeVars, oldCart); return 0; }