From 4f49b16079b27d7ce633619e4cddfdf2bffbc952 Mon Sep 17 00:00:00 2001
From: cashmere
Date: Fri, 4 Sep 2026 09:05:24 +0200
Subject: initial commit
---
src/setup.c | 2575 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 2575 insertions(+)
create mode 100644 src/setup.c
(limited to 'src/setup.c')
diff --git a/src/setup.c b/src/setup.c
new file mode 100644
index 0000000..fb86474
--- /dev/null
+++ b/src/setup.c
@@ -0,0 +1,2575 @@
+/*
+** 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 neither NULL nor 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 /* Caution! Rendered using %s. May contain raw HTML. */
+){
+ @
+}
+
+
+
+/*
+** 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");
+ setup_menu_entry("Tarballs and ZIPs", "setup_download",
+ "Preferences for auto-generated tarballs and ZIP files");
+ 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");
+#if 0 /* Disabled for now. Does this even work? */
+ setup_menu_entry("Transfers", "xfersetup",
+ "Configure the transfer system for this repository");
+#endif
+ }
+ 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;
+ int bErrLog; /* True if Error Log enabled */
+ blob_init(&desc, 0, 0);
+
+ /* Administrator access only */
+ login_check_credentials();
+ if( !g.perm.Admin ){
+ login_needed(0);
+ return;
+ }
+ style_header("Log Menu");
+ @
+
+ if( db_get_boolean("admin-log",1)==0 ){
+ blob_appendf(&desc,
+ "The admin log records configuration changes to the repository.\n"
+ "Disabled: Turn on the "
+ " admin-log setting to enable."
+ );
+ setup_menu_entry("Admin Log", 0, blob_str(&desc));
+ blob_reset(&desc);
+ }else{
+ setup_menu_entry("Admin Log", "admin_log",
+ "The admin log records configuration changes to the repository\n"
+ "in the \"admin_log\" table.\n"
+ );
+ }
+ setup_menu_entry("Xfer Log", "rcvfromlist",
+ "The artifact log records when new content is added in the\n"
+ "\"rcvfrom\" table.\n"
+ );
+ if( db_get_boolean("access-log",1) ){
+ setup_menu_entry("User Log", "user_log",
+ "Login attempts recorded in the \"accesslog\" table."
+ );
+ }else{
+ blob_appendf(&desc,
+ "Login attempts recorded in the \"accesslog\" table.\n"
+ "Disabled: Turn on the "
+ "access-log setting to enable."
+ );
+ setup_menu_entry("User Log", 0, blob_str(&desc));
+ blob_reset(&desc);
+ }
+
+ blob_appendf(&desc,
+ "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,"Disabled: "
+ "To enable the error log ");
+ if( fossil_strcmp(g.zCmdName, "cgi")==0 ){
+ blob_appendf(&desc,
+ "make an entry like \"errorlog: FILENAME\""
+ " in the CGI script at %h",
+ P("SCRIPT_FILENAME")
+ );
+ }else{
+ blob_appendf(&desc,
+ " add the \"--errorlog FILENAME\" option to the\n"
+ "\"%h %h\" command that launched the server.",
+ g.argv[0], g.zCmdName
+ );
+ }
+ bErrLog = 0;
+ }else{
+ blob_appendf(&desc,"In this repository, the error log is the file "
+ "named \"%s\".", g.zErrlog);
+ bErrLog = 1;
+ }
+ setup_menu_entry("Error Log", bErrLog ? "errorlog" : 0, blob_str(&desc));
+ blob_reset(&desc);
+
+ @
+ 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", "HTTP Header Only",
+ "1", "HTTP Header And Javascript",
+ };
+ multiple_choice_attribute(
+ "Enable hyperlinks base on HTTP Header and/or Javascript",
+ "auto-hyperlink", "autohyperlink", "1",
+ count(azDefenseOpts)/2, azDefenseOpts);
+ @
+ 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);
+ @
Enable hyperlinks (the equivalent of the "h" permission) for all users,
+ @ including user "nobody" if the request appears to be from a human.
+ @ Disabling hyperlinks helps prevent robots from walking your site and
+ @ soaking up all your CPU and bandwidth.
+ @ If this setting is "HTTP Header Only" (2) then only the HTTP header
+ @ content (including the UserAgent string and the fossil-client-ok cookie)
+ @ is considered when enabling hyperlinks. If the value of this
+ @ setting is "HTTP Header 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 not generated unless the
+ @ UserAgent string and/or the fossil-client-ok cookie indicate that the
+ @ client is likely human.
+ @ (Property: "auto-hyperlink")
+ @
+ @
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").
+ @ (Properties: "auto-hyperlink-delay" and "auto-hyperlink-mouseover")
+ @
+ @
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".
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 administrators
+ @ defend against abusive robots.
+ @
+ @