/*
** Copyright (c) 2007 D. Richard Hipp
**
** This program is free software; you can redistribute it and/or
** modify it under the terms of the Simplified BSD License (also
** known as the "2-Clause License" or "FreeBSD License".)
**
** 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.
**
** Author contact information:
** drh@hwaci.com
** http://www.hwaci.com/drh/
**
*******************************************************************************
**
** Implementation of the Setup page
*/
#include "config.h"
#include
#include "setup.h"
/*
** Increment the "cfgcnt" variable, so that ETags will know that
** the configuration has changed.
*/
void setup_incr_cfgcnt(void){
static int once = 1;
if( once ){
once = 0;
db_unprotect(PROTECT_CONFIG);
db_multi_exec("UPDATE config SET value=value+1 WHERE name='cfgcnt'");
if( db_changes()==0 ){
db_multi_exec("INSERT INTO config(name,value) VALUES('cfgcnt',1)");
}
db_protect_pop();
}
}
/*
** 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
){
@
}
/*
** WEBPAGE: setup
**
** Main menu for the administrative pages. Requires Admin or Setup
** privileges. Links to sub-pages only usable by Setup users are
** shown only to Setup users.
*/
void setup_page(void){
int setup_user = 0;
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
}
setup_user = g.perm.Setup;
style_set_current_feature("setup");
style_header("Server Administration");
/* Make sure the header contains . Issue a warning
** if it does not. */
if( !cgi_header_contains("Configuration Error: Please add
@ <base href="$secureurl/$current_page"> after
@ <head> in the
@ HTML header!
}
#if !defined(_WIN32)
/* Check for /dev/null and /dev/urandom. We want both devices to be present,
** but they are sometimes omitted (by mistake) from chroot jails. */
if( access("/dev/null", R_OK|W_OK) ){
@
WARNING: Device "/dev/null" is not available
@ for reading and writing.
}
if( access("/dev/urandom", R_OK) ){
@
WARNING: Device "/dev/urandom" is not available
@ for reading. This means that the pseudo-random number generator used
@ by SQLite will be poorly seeded.
}
#endif
@
setup_menu_entry("Users", "setup_ulist",
"Grant privileges to individual users.");
if( setup_user ){
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("Security-Audit", "secaudit0",
"Analyze the current configuration for security problems");
if( setup_user ){
setup_menu_entry("Robot-Defense", "setup_robot",
"Settings for configure defense against robots");
setup_menu_entry("Settings", "setup_settings",
"Web interface to the \"fossil settings\" command");
}
setup_menu_entry("Timeline", "setup_timeline",
"Timeline display preferences");
if( setup_user ){
setup_menu_entry("Login-Group", "setup_login_group",
"Manage single sign-on between this repository and others"
" on the same server");
setup_menu_entry("Tickets", "tktsetup",
"Configure the trouble-ticketing system for this repository");
setup_menu_entry("Wiki", "setup_wiki",
"Configure the wiki for this repository");
setup_menu_entry("Interwiki Map", "intermap",
"Mapping keywords for interwiki links");
setup_menu_entry("Chat", "setup_chat",
"Configure the chatroom");
setup_menu_entry("Forum", "setup_forum",
"Forum config and metrics");
}
setup_menu_entry("Search","srchsetup",
"Configure the built-in search engine");
setup_menu_entry("URL Aliases", "waliassetup",
"Configure URL aliases");
if( setup_user ){
setup_menu_entry("Notification", "setup_notification",
"Automatic notifications of changes via outbound email");
setup_menu_entry("Transfers", "xfersetup",
"Configure the transfer system for this repository");
}
setup_menu_entry("Skins", "setup_skin_admin",
"Select and/or modify the web interface \"skins\"");
setup_menu_entry("Moderation", "setup_modreq",
"Enable/Disable requiring moderator approval of Wiki and/or Ticket"
" changes and attachments.");
setup_menu_entry("Ad-Unit", "setup_adunit",
"Edit HTML text for an ad unit inserted after the menu bar");
setup_menu_entry("URLs & Checkouts", "urllist",
"Show URLs used to access this repo and known check-outs");
if( setup_user ){
setup_menu_entry("Web-Cache", "cachestat",
"View the status of the expensive-page cache");
}
setup_menu_entry("Logo", "setup_logo",
"Change the logo and background images for the server");
setup_menu_entry("Shunned", "shun",
"Show artifacts that are shunned by this repository");
setup_menu_entry("Log Files", "setup-logmenu",
"A menu of available log files");
setup_menu_entry("Unversioned Files", "uvlist?byage=1",
"Show all unversioned files held");
setup_menu_entry("Stats", "stat",
"Repository Status Reports");
setup_menu_entry("Sitemap", "sitemap",
"Links to miscellaneous pages");
if( setup_user ){
setup_menu_entry("SQL", "admin_sql",
"Enter raw SQL commands");
setup_menu_entry("TH1", "admin_th1",
"Enter raw TH1 commands");
}
@
style_finish_page();
}
/*
** WEBPAGE: setup-logmenu
**
** Show a menu of available log renderings accessible to an administrator,
** together with a succinct explanation of each.
**
** This page is only accessible by administrators.
*/
void setup_logmenu_page(void){
Blob desc;
blob_init(&desc, 0, 0);
/* Administrator access only */
login_check_credentials();
if( !g.perm.Admin ){
login_needed(0);
return;
}
style_header("Log Menu");
@
setup_menu_entry("Admin Log", "admin_log",
"The admin log records configuration changes to the repository.\n"
"The admin log is stored in the \"admin_log\" table of the repository.\n"
);
setup_menu_entry("Artifact Log", "rcvfromlist",
"The artifact log records when new content is added to the repository.\n"
"The time and date and origin of the new content is entered into the\n"
"Log. The artifact log is always on and is stored in the \"rcvfrom\"\n"
"table of the repository.\n"
);
blob_appendf(&desc,
"The error log is a separate text file to which warning and error\n"
"messages are appended. A single error log can and often is shared\n"
"across multiple repositories.\n"
);
if( g.zErrlog==0 || fossil_strcmp(g.zErrlog,"-")==0 ){
blob_appendf(&desc,"The error log is disabled for this repository.");
}else{
blob_appendf(&desc,"In this repository, the error log is in the file"
"named \"%s\".", g.zErrlog);
}
setup_menu_entry("Error Log", "errorlog", blob_str(&desc));
blob_reset(&desc);
setup_menu_entry("Panic Log", "paniclog",
"The panic log is a filtering of the Error Log that shows only the\n"
"most important messages - assertion faults, segmentation faults, and\n"
"similar malfunctions."
);
setup_menu_entry("User Log", "user_log",
"The user log is a record of login attempts. The user log is stored\n"
"in the \"accesslog\" table of the respository.\n"
);
@
style_finish_page();
}
/*
** Generate a checkbox for an attribute.
*/
void onoff_attribute(
const char *zLabel, /* The text label on the checkbox */
const char *zVar, /* The corresponding row in the CONFIG table */
const char *zQParm, /* The query parameter */
int dfltVal, /* Default value if CONFIG table entry does not exist */
int disabled /* 1 if disabled */
){
const char *zQ = P(zQParm);
int iVal = db_get_boolean(zVar, dfltVal);
if( zQ==0 && !disabled && P("submit") ){
zQ = "off";
}
if( zQ ){
int iQ = fossil_strcmp(zQ,"on")==0 || atoi(zQ);
if( iQ!=iVal && cgi_csrf_safe(2) ){
db_protect_only(PROTECT_NONE);
db_set(zVar/*works-like:"x"*/, iQ ? "1" : "0", 0);
db_protect_pop();
setup_incr_cfgcnt();
admin_log("Set option [%q] to [%q].",
zVar, iQ ? "on" : "off");
iVal = iQ;
}
}
@
}
/*
** 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 CONFIG table */
const char *zQParm, /* The query parameter */
const char *zDflt, /* Default value if CONFIG table entry does not exist */
int disabled /* 1 if disabled */
){
const char *zVal = db_get(zVar, zDflt);
const char *zQ = P(zQParm);
if( zQ && fossil_strcmp(zQ,zVal)!=0 && cgi_csrf_safe(2) ){
const int nZQ = (int)strlen(zQ);
setup_incr_cfgcnt();
db_protect_only(PROTECT_NONE);
db_set(zVar/*works-like:"x"*/, zQ, 0);
db_protect_pop();
admin_log("Set entry_attribute %Q to: %.*s%s",
zVar, 20, zQ, (nZQ>20 ? "..." : ""));
zVal = zQ;
}
@ %s(zLabel)
}
/*
** Generate a text box for an attribute.
*/
const char *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 CONFIG table */
const char *zQP, /* The query parameter */
const char *zDflt, /* Default value if CONFIG table entry does not exist */
int disabled /* 1 if the textarea should not be editable */
){
const char *z = db_get(zVar, zDflt);
const char *zQ = P(zQP);
if( zQ && !disabled && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
const int nZQ = (int)strlen(zQ);
db_protect_only(PROTECT_NONE);
db_set(zVar/*works-like:"x"*/, zQ, 0);
db_protect_pop();
setup_incr_cfgcnt();
admin_log("Set textarea_attribute %Q to: %.*s%s",
zVar, 20, zQ, (nZQ>20 ? "..." : ""));
z = zQ;
}
if( rows>0 && cols>0 ){
@
if( *zLabel ){
@ %s(zLabel)
}
}
return z;
}
/*
** Generate a text box for an attribute.
*/
void multiple_choice_attribute(
const char *zLabel, /* The text label on the menu */
const char *zVar, /* The corresponding row in the CONFIG table */
const char *zQP, /* The query parameter */
const char *zDflt, /* Default value if CONFIG table entry does not exist */
int nChoice, /* Number of choices */
const char *const *azChoice /* Choices in pairs (VAR value, Display) */
){
const char *z = db_get(zVar, zDflt);
const char *zQ = P(zQP);
int i;
if( zQ && fossil_strcmp(zQ,z)!=0 && cgi_csrf_safe(2) ){
const int nZQ = (int)strlen(zQ);
db_unprotect(PROTECT_ALL);
db_set(zVar/*works-like:"x"*/, zQ, 0);
setup_incr_cfgcnt();
db_protect_pop();
admin_log("Set multiple_choice_attribute %Q to: %.*s%s",
zVar, 20, zQ, (nZQ>20 ? "..." : ""));
z = zQ;
}
@ %h(zLabel)
}
/*
** Insert code into the current page that allows the user to configure
** auto-hyperlink related robot defense settings.
*/
static void addAutoHyperlinkSettings(void){
static const char *const azDefenseOpts[] = {
"0", "Off",
"2", "UserAgent only",
"1", "UserAgent and Javascript",
};
multiple_choice_attribute(
"Enable hyperlinks base on User-Agent and/or Javascript",
"auto-hyperlink", "autohyperlink", "1",
count(azDefenseOpts)/2, azDefenseOpts);
@
Enable hyperlinks (the equivalent of the "h" permission) for all users,
@ including user "nobody", as long as the User-Agent string in the
@ HTTP header indicates that the request is coming from an actual human
@ being. If this setting is "UserAgent only" (2) then the
@ UserAgent string is the only factor considered. If the value of this
@ setting is "UserAgent And Javascript" (1) then Javascript is added that
@ runs after the page loads and fills in the href= values of <a>
@ elements. In either case, <a> tags are only generated if the
@ UserAgent string indicates that the request is coming from a human and
@ not a robot.
@
@
This setting is designed to give easy access to humans while
@ keeping out robots.
@ You do not normally want a robot to walk your entire repository because
@ if it does, your server will end up computing diffs and annotations for
@ every historical version of every file and creating ZIPs and tarballs of
@ every historical check-in, which can use a lot of CPU and bandwidth
@ even for relatively small projects.
@
@
The "UserAgent and Javascript" value for this setting provides
@ superior protection from robots. However, that setting also prevents
@ the visited/unvisited colors on hyperlinks from displaying correctly
@ on Safari-derived browsers. (Chrome and Firefox work fine.) Since
@ Safari is the underlying rendering engine on all iPhones and iPads,
@ this means that hyperlink visited/unvisited colors will not operate
@ on those platforms when "UserAgent and Javascript" is selected.
@
@
Additional parameters that control the behavior of Javascript:
@
entry_attribute("Delay in milliseconds before enabling hyperlinks", 5,
"auto-hyperlink-delay", "ah-delay", "50", 0);
@
onoff_attribute("Also require a mouse event before enabling hyperlinks",
"auto-hyperlink-mouseover", "ahmo", 0, 0);
@
@
For maximum robot defense, "Delay" should be at least 50 milliseconds
@ and "require a mouse event" should be turned on. These values only come
@ into play when the main auto-hyperlink settings is 2 ("UserAgent and
@ Javascript").
@
@
To see if Javascript-base hyperlink enabling mechanism is working,
@ visit the /test_env page (from a separate
@ web browser that is not logged in, even as "anonymous") and verify
@ that the "g.jsHref" value is "1".
@
(Properties: "auto-hyperlink", "auto-hyperlink-delay", and
@ "auto-hyperlink-mouseover"")
A Fossil website can have billions of pages in its tree, even for a
@ modest project. Many of those pages (examples: diffs and tarballs)
@ might be expensive to compute. A robot that tries to walk the entire
@ website can present a crippling CPU and bandwidth load.
@
@
The settings on this page are intended to help site administrators
@ defend the site against robots.
@
@
This repository (in the file named "%h(zSelfRepo)")
@ is not currently part of any login-group.
@ To join a login group, fill out the form below.
@
@
}else{
Stmt q;
int n = 0;
@
This repository (in the file "%h(zSelfRepo)")
@ is currently part of the "%h(zGroup)" login group.
@ Other repositories in that group are:
@
@
Project Name
@
Repository File
db_prepare(&q,
"SELECT value,"
" (SELECT value FROM config"
" WHERE name=('peer-name-' || substr(x.name,11)))"
" FROM config AS x"
" WHERE name GLOB 'peer-repo-*'"
" ORDER BY value"
);
while( db_step(&q)==SQLITE_ROW ){
const char *zRepo = db_column_text(&q, 0);
const char *zTitle = db_column_text(&q, 1);
n++;
@