/*
** Copyright (c) 2002 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public
** License as published by the Free Software Foundation; either
** version 2 of the License, or (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
** General Public License for more details.
**
** You should have received a copy of the GNU General Public
** License along with this library; if not, write to the
** Free Software Foundation, Inc., 59 Temple Place - Suite 330,
** Boston, MA 02111-1307, USA.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** This file contains code for generating the login and logout screens.
*/
#define _XOPEN_SOURCE
#include <unistd.h>
#include "config.h"
#include "login.h"
#include <time.h>
/*
** Return the name of the login cookie
*/
static char *login_cookie_name(void){
return mprintf("%s_login", g.zName);
}
/*
** WEBPAGE: /login
** WEBPAGE: /logout
**
** Generate the login page
*/
void login_page(void){
const char *zUsername, *zPasswd, *zGoto;
const char *zNew1, *zNew2;
char *zErrMsg = "";
char *z;
login_check_credentials();
zUsername = P("u");
zPasswd = P("p");
zGoto = P("g");
if( P("out")!=0 ){
const char *zCookieName = login_cookie_name();
cgi_set_cookie(zCookieName, "", 0, 0);
db_execute("DELETE FROM cookie WHERE cookie='%q'", zCookieName);
cgi_redirect(PD("nxp","index"));
return;
}
if( !g.isAnon && zPasswd && (zNew1 = P("n1"))!=0 && (zNew2 = P("n2"))!=0 ){
z = db_short_query("SELECT passwd FROM user WHERE id='%q'", g.zUser);
if( z==0 || z[0]==0 || strcmp(crypt(zPasswd,z),z)!=0 ){
sleep(1);
zErrMsg =
"<p><font color=\"red\">\n"
"You entered an incorrect old password while attempting to change\n"
"your password. Your password is unchanged.\n"
"</font></p>\n"
;
}else if( strcmp(zNew1,zNew2)!=0 ){
zErrMsg =
"<p><font color=\"red\">\n"
"The two copies of your new passwords do not match.\n"
"Your password is unchanged.\n"
"</font></p>\n"
;
}else{
db_execute(
"UPDATE user SET passwd='%q' WHERE id='%q'",
crypt(zNew1,zPasswd), g.zUser
);
if( g.scm.pxUserWrite ) g.scm.pxUserWrite(0);
cgi_redirect("index");
return;
}
}
if( zUsername!=0 && zPasswd!=0 && strcmp(zUsername,"anonymous")!=0 ){
z = db_short_query("SELECT passwd FROM user WHERE id='%q'", zUsername);
if( z==0 || z[0]==0 || strcmp(crypt(zPasswd,z),z)!=0 ){
sleep(1);
zErrMsg =
"<p><font color=\"red\">\n"
"You entered an incorrect username and/or password\n"
"</font></p>\n"
;
}else{
time_t now;
char *zDigest;
const char *zAddr;
const char *zAgent;
char zHash[200];
char zRawDigest[16];
MD5Context ctx;
time(&now);
bprintf(zHash,sizeof(zHash),"%d%d%.19s", getpid(), (int)now, zPasswd);
MD5Init(&ctx);
MD5Update(&ctx, zHash, strlen(zHash));
MD5Final(zRawDigest, &ctx);
zDigest = encode64(zRawDigest, 16);
zAddr = getenv("REMOTE_ADDR");
if( zAddr==0 ) zAddr = "0.0.0.0";
zAgent = getenv("HTTP_USER_AGENT");
if( zAgent==0 ) zAgent = "Unknown";
db_execute(
"BEGIN;"
"DELETE FROM cookie WHERE expires<=%d;"
"INSERT INTO cookie(cookie,user,expires,ipaddr,agent)"
" VALUES('%q','%q',%d,'%q','%q');"
"COMMIT;",
now, zDigest, zUsername, now+3600*24, zAddr, zAgent
);
cgi_set_cookie(login_cookie_name(), zDigest, 0, 0);
cgi_redirect(PD("nxp","index"));
return;
}
free(z);
}
common_standard_menu("login", 0);
common_add_help_item("CvstracLogin");
common_header("Login/Logout");
cgi_printf("%s\n"
"<form action=\"login\" method=\"POST\">\n",zErrMsg);
if( P("nxp") ){
cgi_printf("<input type=\"hidden\" name=\"nxp\" value=\"%h\">\n",P("nxp"));
}
cgi_printf("<table align=\"left\" hspace=\"10\">\n"
"<tr>\n"
" <td align=\"right\">User ID:</td>\n"
" <td><input type=\"text\" name=\"u\" value=\"\" size=30></td>\n"
"</tr>\n"
"<tr>\n"
" <td align=\"right\">Password:</td>\n"
" <td><input type=\"password\" name=\"p\" value=\"\" size=30></td>\n"
"</tr>\n"
"<tr>\n"
" <td></td>\n"
" <td><input type=\"submit\" name=\"in\" value=\"Login\"></td>\n"
"</tr>\n"
"</table>\n");
if( g.isAnon ){
cgi_printf("<p>To login\n");
}else{
cgi_printf("<p>You are current logged in as <b>%h</b></p>\n"
"<p>To change your login to a different user\n",g.zUser);
}
cgi_printf("enter the user-id and password at the left and press the\n"
"\"Login\" button. Your user name will be stored in a browser cookie.\n"
"You must configure your web browser to accept cookies in order for\n"
"the login to take.</p>\n");
if( db_exists("SELECT id FROM user WHERE id='anonymous'") ){
cgi_printf("<p>This server is configured to allow limited access to users\n"
"who are not logged in.</p>\n");
}
if( !g.isAnon ){
cgi_printf("<br clear=\"both\"><hr>\n"
"<p>To log off the system (and delete your login cookie)\n"
" press the following button:<br>\n"
"<input type=\"submit\" name=\"out\" value=\"Logout\"></p>\n");
}
cgi_printf("</form>\n");
if( !g.isAnon && g.okPassword ){
cgi_printf("<br clear=\"both\"><hr>\n"
"<p>To change your password, enter your old password and your\n"
"new password twice below then press the \"Change Password\"\n"
"button.</p>\n"
"<form action=\"login\" method=\"POST\">\n"
"<table>\n"
"<tr><td align=\"right\">Old Password:</td>\n"
"<td><input type=\"password\" name=\"p\" size=30></td></tr>\n"
"<tr><td align=\"right\">New Password:</td>\n"
"<td><input type=\"password\" name=\"n1\" size=30></td></tr>\n"
"<tr><td align=\"right\">Repeat New Password:</td>\n"
"<td><input type=\"password\" name=\"n2\" size=30></td></tr>\n"
"<tr><td></td>\n"
"<td><input type=\"submit\" value=\"Change Password\"></td></tr>\n"
"</table>\n"
"</form>\n");
}
common_footer();
}
/*
** This routine examines the login cookie to see if it exists and
** contains a valid password hash. If the login cookie checks out,
** it then sets g.zUser to the name of the user and set g.isAnon to 0.
**
** Permission variable are set as appropriate:
**
** g.okRead User can read bug reports and change histories
** g.okDelete User can delete wiki, tickets, and attachments
** g.okCheckout User can read from the repository
** g.okWrite User can change bug reports
** g.okCheckin User can checking changes to the repository
** g.okAdmin User can add or delete other user and create new reports
** g.okSetup User can change CVSTrac options
** g.okPassword User can change his password
** g.okQuery User can enter or edit SQL report formats.
**
** g.okRdWiki User can read wiki pages
** g.okWiki User and create or modify wiki pages
**
*/
void login_check_credentials(void){
const char *zCookie;
time_t now;
char **azResult, *z;
int i;
const char *zUser;
const char *zPswd;
const char *zAddr; /* The IP address of the browser making this request */
const char *zAgent; /* The type of browser */
g.zUser = g.zHumanName = "anonymous";
g.okPassword = 0;
g.okRead = 0;
g.okNewTkt = 0;
g.okWrite = 0;
g.okAdmin = 0;
g.okSetup = 0;
g.okCheckout = 0;
g.okCheckin = 0;
g.okRdWiki = 0;
g.okWiki = 0;
g.okDelete = 0;
g.okQuery = 0;
g.isAnon = 1;
time(&now);
/*
** Check to see if there is an anonymous user. Everybody gets at
** least the permissions that anonymous enjoys.
*/
z = db_short_query("SELECT capabilities FROM user WHERE id='anonymous'");
if( z && z[0] ){
for(i=0; z[i]; i++){
switch( z[i] ){
case 'd': g.okDelete = 1; break;
case 'i': g.okCheckin = g.okCheckout = 1; break;
case 'j': g.okRdWiki = 1; break;
case 'k': g.okWiki = g.okRdWiki = 1; break;
case 'n': g.okNewTkt = 1; break;
case 'o': g.okCheckout = 1; break;
case 'p': g.okPassword = 1; break;
case 'q': g.okQuery = 1; break;
case 'r': g.okRead = 1; break;
case 'w': g.okWrite = g.okRead = 1; break;
}
}
}
g.okTicketLink = atoi(db_config("anon_ticket_linkinfo","0"));
g.okCheckinLink = atoi(db_config("anon_checkin_linkinfo","0"));
g.noFollow = atoi(db_config("nofollow_link","0"));
/*
** Next check to see if the user specified by "U" and "P" query
** parameters or by the login cookie exists
*/
if( (zUser = P("U"))!=0 && (zPswd = P("P"))!=0 ){
z = db_short_query("SELECT passwd FROM user WHERE id='%q'", zUser);
if( z==0 || z[0]==0 || strcmp(crypt(zPswd,z),z)!=0 ){
return;
}
}else if( (zCookie = P(login_cookie_name()))!=0 && zCookie[0]!=0 ){
zAddr = getenv("REMOTE_ADDR");
if( zAddr==0 ) zAddr = "0.0.0.0";
zAgent = getenv("HTTP_USER_AGENT");
if( zAgent==0 ) zAgent = "Unknown";
zUser = db_short_query(
"SELECT user FROM cookie "
"WHERE cookie='%q' "
" AND ipaddr='%q' "
" AND agent='%q' "
" AND expires>%d",
zCookie, zAddr, zAgent, now);
if( zUser==0 ){
return;
}
}else{
return;
}
/* If we reach here, it means that the user named "zUser" checks out.
** Set up appropriate permissions.
*/
azResult = db_query(
"SELECT name, capabilities FROM user "
"WHERE id='%q'", zUser
);
if( azResult[0]==0 ){
return; /* Should never happen... */
}
g.isAnon = 0;
g.zHumanName = azResult[0];
g.zUser = zUser;
cgi_logfile(0, g.zUser);
for(i=0; azResult[1][i]; i++){
switch( azResult[1][i] ){
case 's': g.okSetup = g.okDelete = 1;
case 'a': g.okAdmin = g.okRead = g.okWrite = g.okQuery =
g.okNewTkt = g.okPassword = 1;
case 'i': g.okCheckin = g.okCheckout = 1; break;
case 'd': g.okDelete = 1; break;
case 'j': g.okRdWiki = 1; break;
case 'k': g.okWiki = g.okRdWiki = 1; break;
case 'n': g.okNewTkt = 1; break;
case 'o': g.okCheckout = 1; break;
case 'p': g.okPassword = 1; break;
case 'q': g.okQuery = 1; break;
case 'r': g.okRead = 1; break;
case 'w': g.okWrite = g.okRead = 1; break;
}
}
g.okTicketLink = atoi(db_config("ticket_linkinfo","1"));
g.okCheckinLink = atoi(db_config("checkin_linkinfo","0"));
}
/*
** Call this routine when the credential check fails. It causes
** a redirect to the "login" page.
*/
void login_needed(void){
const char *zUrl = getenv("REQUEST_URI");
if( zUrl==0 ) zUrl = "index";
cgi_redirect(mprintf("login?nxp=%T", zUrl));
}
syntax highlighted by Code2HTML, v. 0.9.1