/*
** Copyright (c) 2007 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/
**
*******************************************************************************
**
** Implementation of the Setup page
*/
#include
#include "config.h"
#include "setup.h"
/*
** Output a single entry for a menu generated using an HTML table.
** If zLink is not NULL or an empty string, then it is the page that
** the menu entry will hyperlink to. If zLink is NULL or "", then
** the menu entry has no hyperlink - it is disabled.
*/
void setup_menu_entry(
const char *zTitle,
const char *zLink,
const char *zDesc
){
@
setup_menu_entry("Users", "setup_ulist",
"Grant privileges to individual users.");
setup_menu_entry("Access", "setup_access",
"Control access settings.");
setup_menu_entry("Configuration", "setup_config",
"Configure the WWW components of the repository");
setup_menu_entry("Behavior", "setup_behavior",
"Configure the SCM behavior of the repository");
setup_menu_entry("Timeline", "setup_timeline",
"Timeline display preferences");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
setup_menu_entry("CSS", "setup_editcss",
"Edit the Cascading Style Sheet used by all pages of this repository");
setup_menu_entry("Header", "setup_header",
"Edit HTML text inserted at the top of every page");
setup_menu_entry("Footer", "setup_footer",
"Edit HTML text inserted at the bottom of every page");
setup_menu_entry("Shunned", "shun",
"Show artifacts that are shunned by this repository");
setup_menu_entry("Log", "rcvfromlist",
"A record of received artifacts and their sources");
setup_menu_entry("Stats", "stat",
"Display repository statistics");
@
style_footer();
}
/*
** WEBPAGE: setup_ulist
**
** Show a list of users. Clicking on any user jumps to the edit
** screen for that user.
*/
void setup_ulist(void){
Stmt s;
login_check_credentials();
if( !g.okAdmin ){
login_needed();
return;
}
style_submenu_element("Add", "Add User", "setup_uedit");
style_header("User List");
@
@
@ Users:
@
@
@
@
User ID
@
Capabilities
@
Contact Info
@
db_prepare(&s, "SELECT uid, login, cap, info FROM user ORDER BY login");
while( db_step(&s)==SQLITE_ROW ){
const char *zCap = db_column_text(&s, 2);
if( strstr(zCap, "s") ) zCap = "s";
@
Email: View sensitive data such as EMail addresses
@
f
@
New-Wiki: Create new wiki pages
@
g
@
Clone: Clone the repository
@
h
@
Hyperlinks: Show hyperlinks to detailed
@ repository history
@
i
@
Check-In: Commit new versions in the repository
@
j
@
Read-Wiki: View wiki pages
@
k
@
Write-Wiki: Edit wiki pages
@
m
@
Append-Wiki: Append to wiki pages
@
n
@
New-Tkt: Create new tickets
@
o
@
Check-Out: Check out versions
@
p
@
Password: Change your own password
@
r
@
Read-Tkt: View tickets
@
s
@
Setup/Super-user: Setup and configure this website
@
t
@
Tkt-Report: Create new bug summary reports
@
u
@
Reader: Inherit privileges of
@ user reader
@
v
@
Developer: Inherit privileges of
@ user developer
@
w
@
Write-Tkt: Edit tickets
@
z
@
Zip download: Download a baseline via the
@ /zip URL even without checkout
@ and history permissions
@
@
@
@
@ Every user, logged in or not, inherits the privileges of nobody.
@
@
@
@ Any human can login as anonymous since the password is
@ clearly displayed on the login page for them to type. The purpose
@ of requiring anonymous to log in is to prevent access by spiders.
@ Every logged-in user inherits the combined privileges of
@ anonymous and
@ nobody.
@
@
@
@ Users with privilege v inherit the combined privileges of
@ developer, anonymous, and nobody.
@
@
@
@
style_footer();
}
/*
** Return true if zPw is a valid password string. A valid
** password string is:
**
** (1) A zero-length string, or
** (2) a string that contains a character other than '*'.
*/
static int isValidPwString(const char *zPw){
if( zPw==0 ) return 0;
if( zPw[0]==0 ) return 1;
while( zPw[0]=='*' ){ zPw++; }
return zPw[0]!=0;
}
/*
** WEBPAGE: /setup_uedit
*/
void user_edit(void){
const char *zId, *zLogin, *zInfo, *zCap, *zPw;
char *oaa, *oas, *oar, *oaw, *oan, *oai, *oaj, *oao, *oap;
char *oak, *oad, *oac, *oaf, *oam, *oah, *oag, *oae;
char *oat, *oau, *oav, *oaz;
const char *inherit[128];
int doWrite;
int uid;
int higherUser = 0; /* True if user being edited is SETUP and the */
/* user doing the editing is ADMIN. Disallow editing */
/* Must have ADMIN privleges to access this page
*/
login_check_credentials();
if( !g.okAdmin ){ login_needed(); return; }
/* Check to see if an ADMIN user is trying to edit a SETUP account.
** Don't allow that.
*/
zId = PD("id", "0");
uid = atoi(zId);
if( zId && !g.okSetup && uid>0 ){
char *zOldCaps;
zOldCaps = db_text(0, "SELECT cap FROM user WHERE uid=%d",uid);
higherUser = zOldCaps && strchr(zOldCaps,'s');
}
if( P("can") ){
cgi_redirect("setup_ulist");
return;
}
/* If we have all the necessary information, write the new or
** modified user record. After writing the user record, redirect
** to the page that displays a list of users.
*/
doWrite = cgi_all("login","info","pw") && !higherUser;
if( doWrite ){
char zCap[50];
int i = 0;
int aa = P("aa")!=0;
int ad = P("ad")!=0;
int ae = P("ae")!=0;
int ai = P("ai")!=0;
int aj = P("aj")!=0;
int ak = P("ak")!=0;
int an = P("an")!=0;
int ao = P("ao")!=0;
int ap = P("ap")!=0;
int ar = P("ar")!=0;
int as = g.okSetup && P("as")!=0;
int aw = P("aw")!=0;
int ac = P("ac")!=0;
int af = P("af")!=0;
int am = P("am")!=0;
int ah = P("ah")!=0;
int ag = P("ag")!=0;
int at = P("at")!=0;
int au = P("au")!=0;
int av = P("av")!=0;
int az = P("az")!=0;
if( aa ){ zCap[i++] = 'a'; }
if( ac ){ zCap[i++] = 'c'; }
if( ad ){ zCap[i++] = 'd'; }
if( ae ){ zCap[i++] = 'e'; }
if( af ){ zCap[i++] = 'f'; }
if( ah ){ zCap[i++] = 'h'; }
if( ag ){ zCap[i++] = 'g'; }
if( ai ){ zCap[i++] = 'i'; }
if( aj ){ zCap[i++] = 'j'; }
if( ak ){ zCap[i++] = 'k'; }
if( am ){ zCap[i++] = 'm'; }
if( an ){ zCap[i++] = 'n'; }
if( ao ){ zCap[i++] = 'o'; }
if( ap ){ zCap[i++] = 'p'; }
if( ar ){ zCap[i++] = 'r'; }
if( as ){ zCap[i++] = 's'; }
if( at ){ zCap[i++] = 't'; }
if( au ){ zCap[i++] = 'u'; }
if( av ){ zCap[i++] = 'v'; }
if( aw ){ zCap[i++] = 'w'; }
if( az ){ zCap[i++] = 'z'; }
zCap[i] = 0;
zPw = P("pw");
if( !isValidPwString(zPw) ){
zPw = db_text(0, "SELECT pw FROM user WHERE uid=%d", uid);
}
zLogin = P("login");
if( uid>0 &&
db_exists("SELECT 1 FROM user WHERE login=%Q AND uid!=%d", zLogin, uid)
){
style_header("User Creation Error");
@ Login "%h(zLogin)" is already used by a different
@ user.
@
@
@ User %h(zLogin) has Setup privileges and you only have Admin privileges
@ so you are not permitted to make changes to %h(zLogin).
@
@
}
@
@ The Setup user can make arbitrary configuration changes.
@ An Admin user can add other users and change user privileges
@ and reset user passwords. Both automatically get all other privileges
@ listed below. Use these two settings with discretion.
@
@
@
@ The "•" mark indicates
@ the privileges of "nobody" that are available to all users
@ regardless of whether or not they are logged in.
@
@
@
@ The "•" mark indicates
@ the privileges of "anonymous" that are inherited by all logged-in users.
@
@
@
@ The "•" mark indicates
@ the privileges of "developer" that are inherited by all users with
@ the Developer privilege.
@
@
@
@ The "•" mark indicates
@ the privileges of "reader" that are inherited by all users with
@ the Reader privilege.
@
@
@
@ The Delete privilege give the user the ability to erase
@ wiki, tickets, and attachments that have been added by anonymous
@ users. This capability is intended for deletion of spam. The
@ delete capability is only in effect for 24 hours after the item
@ is first posted. The Setup user can delete anything at any time.
@
@
@
@ The History privilege allows a user to see most hyperlinks.
@ This is recommended ON for most logged-in users but OFF for
@ user "nobody" to avoid problems with spiders trying to walk every
@ historical version of every baseline and file.
@
@
@
@ The Zip privilege allows a user to see the "download as ZIP"
@ hyperlink and permits access to the /zip page. This allows
@ users to download ZIP archives without granting other rights like
@ Read or History. This privilege is recommended for
@ user nobody so that automatic package downloaders can obtain
@ the sources without going through the login procedure.
@
@
@
@ The Check-in privilege allows remote users to "push".
@ The Check-out privilege allows remote users to "pull".
@ The Clone privilege allows remote users to "clone".
@
@
@
@ The Read Wiki, New Wiki, Append Wiki, and
@ Write Wiki privileges control access to wiki pages. The
@ Read Tkt, New Tkt, Append Tkt, and
@ Write Tkt privileges control access to trouble tickets.
@ The Tkt Report privilege allows the user to create or edit
@ ticket report formats.
@
@
@
@ Users with the Password privilege are allowed to change their
@ own password. Recommended ON for most users but OFF for special
@ users "developer, "anonymous", and "nobody".
@
@
@
@ The EMail privilege allows the display of sensitive information
@ such as the email address of users and contact information on tickets.
@ Recommended OFF for "anonymous" and for "nobody" but ON for
@ "developer".
@
@
@
@ Login is prohibited if the password is an empty string.
@
@
@
@
Special Logins
@
@
@
@ No login is required for user "nobody". The capabilities
@ of the nobody user are inherited by all users, regardless of
@ whether or not they are logged in. To disable universal access
@ to the repository, make sure no user named "nobody" exists or
@ that the nobody user has no capabilities enabled.
@ The password for nobody is ignore. To avoid problems with
@ spiders overloading the server, it is recommended
@ that the 'h' (History) capability be turned off for the nobody
@ user.
@
@
@
@ Login is required for user "anonymous" but the password
@ is displayed on the login screen beside the password entry box
@ so anybody who can read should be able to login as anonymous.
@ On the other hand, spiders and web-crawlers will typically not
@ be able to login. Set the capabilities of the anonymous user
@ to things that you want any human to be able to do, but not any
@ spider. Every other logged-in user inherits the privileges of
@ anonymous.
@
@
@
@ The "developer" user is intended as a template for trusted users
@ with check-in privileges. When adding new trusted users, simply
@ select the Developer privilege to cause the new user to inherit
@ all privileges of the "developer" user. Similarly, the "reader"
@ user is a template for users who are allowed more access than anonymous,
@ but less than a developer.
@
@
@
style_footer();
}
/*
** Generate a checkbox for an attribute.
*/
static void onoff_attribute(
const char *zLabel, /* The text label on the checkbox */
const char *zVar, /* The corresponding row in the VAR table */
const char *zQParm, /* The query parameter */
int dfltVal /* Default value if VAR table entry does not exist */
){
const char *zVal = db_get(zVar, 0);
const char *zQ = P(zQParm);
int iVal;
if( zVal ){
iVal = atoi(zVal);
}else{
iVal = dfltVal;
}
if( zQ==0 && P("submit") ){
zQ = "off";
}
if( zQ ){
int iQ = strcmp(zQ,"on")==0 || atoi(zQ);
if( iQ!=iVal ){
login_verify_csrf_secret();
db_set(zVar, iQ ? "1" : "0", 0);
iVal = iQ;
}
}
if( iVal ){
@ %s(zLabel)
}else{
@ %s(zLabel)
}
}
/*
** Generate an entry box for an attribute.
*/
void entry_attribute(
const char *zLabel, /* The text label on the entry box */
int width, /* Width of the entry box */
const char *zVar, /* The corresponding row in the VAR table */
const char *zQParm, /* The query parameter */
char *zDflt /* Default value if VAR table entry does not exist */
){
const char *zVal = db_get(zVar, zDflt);
const char *zQ = P(zQParm);
if( zQ && strcmp(zQ,zVal)!=0 ){
login_verify_csrf_secret();
db_set(zVar, zQ, 0);
zVal = zQ;
}
@
@ %s(zLabel)
}
/*
** Generate a text box for an attribute.
*/
static void textarea_attribute(
const char *zLabel, /* The text label on the textarea */
int rows, /* Rows in the textarea */
int cols, /* Columns in the textarea */
const char *zVar, /* The corresponding row in the VAR table */
const char *zQP, /* The query parameter */
const char *zDflt /* Default value if VAR table entry does not exist */
){
const char *z = db_get(zVar, (char*)zDflt);
const char *zQ = P(zQP);
if( zQ && strcmp(zQ,z)!=0 ){
login_verify_csrf_secret();
db_set(zVar, zQ, 0);
z = zQ;
}
if( rows>0 && cols>0 ){
@
@ %s(zLabel)
}
}
/*
** WEBPAGE: setup_access
*/
void setup_access(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("Access Control Settings");
db_begin_transaction();
@
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_timeline
*/
void setup_timeline(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("Timeline Display Preferences");
db_begin_transaction();
@
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_behavior
*/
void setup_behavior(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("Fossil SCM Behavior");
db_begin_transaction();
@
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_config
*/
void setup_config(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
style_header("WWW Configuration");
db_begin_transaction();
@
db_end_transaction(0);
style_footer();
}
/*
** WEBPAGE: setup_editcss
*/
void setup_editcss(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
db_begin_transaction();
if( P("clear")!=0 ){
db_multi_exec("DELETE FROM config WHERE name='css'");
cgi_replace_parameter("css", zDefaultCSS);
}else{
textarea_attribute(0, 0, 0, "css", "css", zDefaultCSS);
}
style_header("Edit CSS");
@
@
@ Here is the default CSS:
@
@ %h(zDefaultCSS)
@
style_footer();
db_end_transaction(0);
}
/*
** WEBPAGE: setup_header
*/
void setup_header(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
db_begin_transaction();
if( P("clear")!=0 ){
db_multi_exec("DELETE FROM config WHERE name='header'");
cgi_replace_parameter("header", zDefaultHeader);
}else{
textarea_attribute(0, 0, 0, "header", "header", zDefaultHeader);
}
style_header("Edit Page Header");
@
@
@ Here is the default page header:
@
@ %h(zDefaultHeader)
@
style_footer();
db_end_transaction(0);
}
/*
** WEBPAGE: setup_footer
*/
void setup_footer(void){
login_check_credentials();
if( !g.okSetup ){
login_needed();
}
db_begin_transaction();
if( P("clear")!=0 ){
db_multi_exec("DELETE FROM config WHERE name='footer'");
cgi_replace_parameter("footer", zDefaultFooter);
}else{
textarea_attribute(0, 0, 0, "footer", "footer", zDefaultFooter);
}
style_header("Edit Page Footer");
@
@
@ Here is the default page footer:
@