dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Copyright (c) 2006 D. Richard Hipp dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is free software; you can redistribute it and/or dbda8d6ce9 2007-07-21 drh: ** modify it under the terms of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License version 2 as published by the Free Software Foundation. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This program is distributed in the hope that it will be useful, dbda8d6ce9 2007-07-21 drh: ** but WITHOUT ANY WARRANTY; without even the implied warranty of dbda8d6ce9 2007-07-21 drh: ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU dbda8d6ce9 2007-07-21 drh: ** General Public License for more details. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** You should have received a copy of the GNU General Public dbda8d6ce9 2007-07-21 drh: ** License along with this library; if not, write to the dbda8d6ce9 2007-07-21 drh: ** Free Software Foundation, Inc., 59 Temple Place - Suite 330, dbda8d6ce9 2007-07-21 drh: ** Boston, MA 02111-1307, USA. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Author contact information: dbda8d6ce9 2007-07-21 drh: ** drh@hwaci.com dbda8d6ce9 2007-07-21 drh: ** http://www.hwaci.com/drh/ dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ******************************************************************************* dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** This module codes the main() procedure that runs first when the dbda8d6ce9 2007-07-21 drh: ** program is invoked. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #include "config.h" dbda8d6ce9 2007-07-21 drh: #include "main.h" dbda8d6ce9 2007-07-21 drh: #include <string.h> dbda8d6ce9 2007-07-21 drh: #include <time.h> dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: #if INTERFACE dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Number of elements in an array dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define count(X) (sizeof(X)/sizeof(X[0])) dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Size of a UUID in characters dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define UUID_SIZE 40 dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** All global variables are in this structure. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: struct Global { dbda8d6ce9 2007-07-21 drh: int argc; char **argv; /* Command-line arguments to the program */ dbda8d6ce9 2007-07-21 drh: int isConst; /* True if the output is unchanging */ dbda8d6ce9 2007-07-21 drh: sqlite3 *db; /* The connection to the databases */ dbda8d6ce9 2007-07-21 drh: int configOpen; /* True if the config database is open */ dbda8d6ce9 2007-07-21 drh: long long int now; /* Seconds since 1970 */ dbda8d6ce9 2007-07-21 drh: int repositoryOpen; /* True if the main repository database is open */ dbda8d6ce9 2007-07-21 drh: char *zRepositoryName; /* Name of the repository database */ dbda8d6ce9 2007-07-21 drh: int localOpen; /* True if the local database is open */ dbda8d6ce9 2007-07-21 drh: char *zLocalRoot; /* The directory holding the local database */ dbda8d6ce9 2007-07-21 drh: int minPrefix; /* Number of digits needed for a distinct UUID */ dbda8d6ce9 2007-07-21 drh: int fSqlTrace; /* True if -sqltrace flag is present */ dbda8d6ce9 2007-07-21 drh: int fSqlPrint; /* True if -sqlprint flag is present */ dbda8d6ce9 2007-07-21 drh: int fHttpTrace; /* Trace outbound HTTP requests */ dbda8d6ce9 2007-07-21 drh: char *zPath; /* Name of webpage being served */ dbda8d6ce9 2007-07-21 drh: char *zExtra; /* Extra path information past the webpage name */ dbda8d6ce9 2007-07-21 drh: char *zBaseURL; /* Full text of the URL being served */ dbda8d6ce9 2007-07-21 drh: const char *zContentType; /* The content type of the input HTTP request */ dbda8d6ce9 2007-07-21 drh: int iErrPriority; /* Priority of current error message */ dbda8d6ce9 2007-07-21 drh: char *zErrMsg; /* Text of an error message */ dbda8d6ce9 2007-07-21 drh: Blob cgiIn; /* Input to an xfer www method */ dbda8d6ce9 2007-07-21 drh: int cgiPanic; /* Write error messages to CGI */ dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: int urlIsFile; /* True if a "file:" url */ dbda8d6ce9 2007-07-21 drh: char *urlName; /* Hostname for http: or filename for file: */ dbda8d6ce9 2007-07-21 drh: int urlPort; /* TCP port number for http: */ dbda8d6ce9 2007-07-21 drh: char *urlPath; /* Pathname for http: */ e621b6dbe3 2007-07-30 drh: char *urlUser; /* User id for http: */ e621b6dbe3 2007-07-30 drh: char *urlPasswd; /* Password for http: */ dbda8d6ce9 2007-07-21 drh: char *urlCanonical; /* Canonical representation of the URL */ dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: const char *zLogin; /* Login name. "" if not logged in. */ dbda8d6ce9 2007-07-21 drh: int noPswd; /* Logged in without password (on 127.0.0.1) */ dbda8d6ce9 2007-07-21 drh: int userUid; /* Integer user id */ dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* Information used to populate the RCVFROM table */ dbda8d6ce9 2007-07-21 drh: int rcvid; /* The rcvid. 0 if not yet defined. */ dbda8d6ce9 2007-07-21 drh: char *zIpAddr; /* The remote IP address */ dbda8d6ce9 2007-07-21 drh: char *zNonce; /* The nonce used for login */ dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* permissions used by the server */ dbda8d6ce9 2007-07-21 drh: int okRead; /* xfer outbound */ dbda8d6ce9 2007-07-21 drh: int okWrite; /* xfer inbound */ dbda8d6ce9 2007-07-21 drh: int okSetup; /* use Setup screens on web interface */ dbda8d6ce9 2007-07-21 drh: int okRdTkt; /* view tickets via web */ dbda8d6ce9 2007-07-21 drh: int okWrTkt; /* make changes to tickets via web */ 66f4caa379 2007-07-23 drh: int okNewTkt; /* create new tickets */ 66f4caa379 2007-07-23 drh: int okApndTkt; /* append to tickets via the web */ dbda8d6ce9 2007-07-21 drh: int okRdWiki; /* view wiki via web */ dbda8d6ce9 2007-07-21 drh: int okWrWiki; /* edit wiki via web */ 66f4caa379 2007-07-23 drh: int okNewWiki; /* create new wiki via web */ 66f4caa379 2007-07-23 drh: int okApndWiki; /* append to wiki via web */ dbda8d6ce9 2007-07-21 drh: int okPassword; /* change password */ dbda8d6ce9 2007-07-21 drh: int okAdmin; /* administrative permission */ dbda8d6ce9 2007-07-21 drh: int okDelete; /* delete wiki or tickets */ dbda8d6ce9 2007-07-21 drh: int okQuery; /* create new reports */ fd36718ad9 2007-07-31 drh: int okHistory; /* access historical information */ dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: FILE *fDebug; /* Write debug information here, if the file exists */ dbda8d6ce9 2007-07-21 drh: }; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Macro for debugging: dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #define CGIDEBUG(X) if( g.fDebug ) cgi_debug X dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: #endif dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: Global g; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** The table of web pages supported by this application is generated dbda8d6ce9 2007-07-21 drh: ** automatically by the "mkindex" program and written into a file dbda8d6ce9 2007-07-21 drh: ** named "page_index.h". We include that file here to get access dbda8d6ce9 2007-07-21 drh: ** to the table. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: #include "page_index.h" dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Search for a function whose name matches zName. Write a pointer to dbda8d6ce9 2007-07-21 drh: ** that function into *pxFunc and return 0. If no match is found, dbda8d6ce9 2007-07-21 drh: ** return 1. If the command is ambiguous return 2; dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** The NameMap structure and the tables we are searching against are dbda8d6ce9 2007-07-21 drh: ** defined in the page_index.h header file which is automatically dbda8d6ce9 2007-07-21 drh: ** generated by mkindex.c program. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static int name_search( dbda8d6ce9 2007-07-21 drh: const char *zName, /* The name we are looking for */ dbda8d6ce9 2007-07-21 drh: const NameMap *aMap, /* Search in this array */ dbda8d6ce9 2007-07-21 drh: int nMap, /* Number of slots in aMap[] */ dbda8d6ce9 2007-07-21 drh: void (**pxFunc)(void) /* Write pointer to handler function here */ dbda8d6ce9 2007-07-21 drh: ){ dbda8d6ce9 2007-07-21 drh: int upr, lwr, cnt, m, i; dbda8d6ce9 2007-07-21 drh: int n = strlen(zName); dbda8d6ce9 2007-07-21 drh: lwr = 0; dbda8d6ce9 2007-07-21 drh: upr = nMap-1; dbda8d6ce9 2007-07-21 drh: while( lwr<=upr ){ dbda8d6ce9 2007-07-21 drh: int mid, c; dbda8d6ce9 2007-07-21 drh: mid = (upr+lwr)/2; dbda8d6ce9 2007-07-21 drh: c = strcmp(zName, aMap[mid].zName); dbda8d6ce9 2007-07-21 drh: if( c==0 ){ dbda8d6ce9 2007-07-21 drh: *pxFunc = aMap[mid].xFunc; dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: }else if( c<0 ){ dbda8d6ce9 2007-07-21 drh: upr = mid - 1; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: lwr = mid + 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: for(m=cnt=0, i=upr-2; i<=upr+3 && i<nMap; i++){ dbda8d6ce9 2007-07-21 drh: if( i<0 ) continue; dbda8d6ce9 2007-07-21 drh: if( strncmp(zName, aMap[i].zName, n)==0 ){ dbda8d6ce9 2007-07-21 drh: m = i; dbda8d6ce9 2007-07-21 drh: cnt++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( cnt==1 ){ dbda8d6ce9 2007-07-21 drh: *pxFunc = aMap[m].xFunc; dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: return 1+(cnt>1); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** This procedure runs first. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: int main(int argc, char **argv){ dbda8d6ce9 2007-07-21 drh: const char *zCmdName; dbda8d6ce9 2007-07-21 drh: void (*xFunc)(void); dbda8d6ce9 2007-07-21 drh: int rc; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: g.now = time(0); dbda8d6ce9 2007-07-21 drh: g.argc = argc; dbda8d6ce9 2007-07-21 drh: g.argv = argv; dbda8d6ce9 2007-07-21 drh: if( getenv("GATEWAY_INTERFACE")!=0 ){ dbda8d6ce9 2007-07-21 drh: zCmdName = "cgi"; dbda8d6ce9 2007-07-21 drh: }else if( argc<2 ){ dbda8d6ce9 2007-07-21 drh: fprintf(stderr, "Usage: %s COMMAND ...\n", argv[0]); dbda8d6ce9 2007-07-21 drh: exit(1); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: g.fSqlTrace = find_option("sqltrace", 0, 0)!=0; dbda8d6ce9 2007-07-21 drh: g.fSqlPrint = find_option("sqlprint", 0, 0)!=0; dbda8d6ce9 2007-07-21 drh: g.fHttpTrace = find_option("httptrace", 0, 0)!=0; dbda8d6ce9 2007-07-21 drh: g.zLogin = find_option("user", "U", 1); dbda8d6ce9 2007-07-21 drh: zCmdName = argv[1]; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: rc = name_search(zCmdName, aCommand, count(aCommand), &xFunc); dbda8d6ce9 2007-07-21 drh: if( rc==1 ){ dbda8d6ce9 2007-07-21 drh: fprintf(stderr,"%s: unknown command: %s\n" dbda8d6ce9 2007-07-21 drh: "%s: use \"commands\" or \"test-commands\" for help\n", dbda8d6ce9 2007-07-21 drh: argv[0], zCmdName, argv[0]); dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: }else if( rc==2 ){ dbda8d6ce9 2007-07-21 drh: fprintf(stderr,"%s: ambiguous command prefix: %s\n" dbda8d6ce9 2007-07-21 drh: "%s: use \"commands\" or \"test-commands\" for help\n", dbda8d6ce9 2007-07-21 drh: argv[0], zCmdName, argv[0]); dbda8d6ce9 2007-07-21 drh: return 1; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: xFunc(); dbda8d6ce9 2007-07-21 drh: return 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Print an error message, rollback all databases, and quit. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void fossil_panic(const char *zFormat, ...){ dbda8d6ce9 2007-07-21 drh: char *z; dbda8d6ce9 2007-07-21 drh: va_list ap; dbda8d6ce9 2007-07-21 drh: va_start(ap, zFormat); dbda8d6ce9 2007-07-21 drh: z = vmprintf(zFormat, ap); dbda8d6ce9 2007-07-21 drh: va_end(ap); dbda8d6ce9 2007-07-21 drh: if( g.cgiPanic ){ dbda8d6ce9 2007-07-21 drh: g.cgiPanic = 0; 396cc2a4eb 2007-07-30 drh: cgi_printf("<p><font color=\"red\">%h</font></p>", z); dbda8d6ce9 2007-07-21 drh: style_footer(); dbda8d6ce9 2007-07-21 drh: cgi_reply(); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: fprintf(stderr, "%s: %s\n", g.argv[0], z); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: db_force_rollback(); dbda8d6ce9 2007-07-21 drh: exit(1); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: void fossil_fatal(const char *zFormat, ...){ dbda8d6ce9 2007-07-21 drh: char *z; dbda8d6ce9 2007-07-21 drh: va_list ap; dbda8d6ce9 2007-07-21 drh: va_start(ap, zFormat); dbda8d6ce9 2007-07-21 drh: z = vmprintf(zFormat, ap); dbda8d6ce9 2007-07-21 drh: va_end(ap); dbda8d6ce9 2007-07-21 drh: if( g.cgiPanic ){ dbda8d6ce9 2007-07-21 drh: g.cgiPanic = 0; 396cc2a4eb 2007-07-30 drh: cgi_printf("<p><font color=\"red\">%h</font></p>", z); dbda8d6ce9 2007-07-21 drh: style_footer(); dbda8d6ce9 2007-07-21 drh: cgi_reply(); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: fprintf(stderr, "%s: %s\n", g.argv[0], z); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: db_force_rollback(); dbda8d6ce9 2007-07-21 drh: exit(1); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Print a usage comment and quit dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void usage(const char *zFormat){ dbda8d6ce9 2007-07-21 drh: fprintf(stderr, "Usage: %s %s %s\n", g.argv[0], g.argv[1], zFormat); dbda8d6ce9 2007-07-21 drh: exit(1); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Remove n elements from g.argv beginning with the i-th element. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void remove_from_argv(int i, int n){ dbda8d6ce9 2007-07-21 drh: int j; dbda8d6ce9 2007-07-21 drh: for(j=i+n; j<g.argc; i++, j++){ dbda8d6ce9 2007-07-21 drh: g.argv[i] = g.argv[j]; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: g.argc = i; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Look for a command-line option. If present, return a pointer. dbda8d6ce9 2007-07-21 drh: ** Return NULL if missing. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** hasArg==0 means the option is a flag. It is either present or not. dbda8d6ce9 2007-07-21 drh: ** hasArg==1 means the option has an argument. Return a pointer to the dbda8d6ce9 2007-07-21 drh: ** argument. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: const char *find_option(const char *zLong, const char *zShort, int hasArg){ dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: const char *zReturn = 0; dbda8d6ce9 2007-07-21 drh: assert( hasArg==0 || hasArg==1 ); dbda8d6ce9 2007-07-21 drh: for(i=2; i<g.argc; i++){ dbda8d6ce9 2007-07-21 drh: char *z = g.argv[i]; dbda8d6ce9 2007-07-21 drh: if( z[0]!='-' ) continue; dbda8d6ce9 2007-07-21 drh: z++; dbda8d6ce9 2007-07-21 drh: if( z[0]=='-' ){ dbda8d6ce9 2007-07-21 drh: if( z[1]==0 ){ dbda8d6ce9 2007-07-21 drh: remove_from_argv(i, 1); dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: z++; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( strcmp(z,zLong)==0 || (zShort!=0 && strcmp(z,zShort)==0) ){ dbda8d6ce9 2007-07-21 drh: zReturn = g.argv[i+hasArg]; dbda8d6ce9 2007-07-21 drh: remove_from_argv(i, 1+hasArg); dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: return zReturn; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Print a list of words in multiple columns. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void multi_column_list(const char **azWord, int nWord){ dbda8d6ce9 2007-07-21 drh: int i, j, len; dbda8d6ce9 2007-07-21 drh: int mxLen = 0; dbda8d6ce9 2007-07-21 drh: int nCol; dbda8d6ce9 2007-07-21 drh: int nRow; dbda8d6ce9 2007-07-21 drh: for(i=0; i<nWord; i++){ dbda8d6ce9 2007-07-21 drh: len = strlen(azWord[i]); dbda8d6ce9 2007-07-21 drh: if( len>mxLen ) mxLen = len; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: nCol = 80/(mxLen+2); dbda8d6ce9 2007-07-21 drh: if( nCol==0 ) nCol = 1; dbda8d6ce9 2007-07-21 drh: nRow = (nWord + nCol - 1)/nCol; dbda8d6ce9 2007-07-21 drh: for(i=0; i<nRow; i++){ dbda8d6ce9 2007-07-21 drh: const char *zSpacer = ""; dbda8d6ce9 2007-07-21 drh: for(j=i; j<nWord; j+=nRow){ dbda8d6ce9 2007-07-21 drh: printf("%s%-*s", zSpacer, mxLen, azWord[j]); dbda8d6ce9 2007-07-21 drh: zSpacer = " "; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: printf("\n"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: commands dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** List all commands whose name does not start with "test-" dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void cmd_cmd_list(void){ dbda8d6ce9 2007-07-21 drh: int i, nCmd; dbda8d6ce9 2007-07-21 drh: const char *aCmd[count(aCommand)]; dbda8d6ce9 2007-07-21 drh: for(i=nCmd=0; i<count(aCommand); i++){ dbda8d6ce9 2007-07-21 drh: if( strncmp(aCommand[i].zName,"test",4)==0 ) continue; dbda8d6ce9 2007-07-21 drh: if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue; dbda8d6ce9 2007-07-21 drh: aCmd[nCmd++] = aCommand[i].zName; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: multi_column_list(aCmd, nCmd); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: test-commands dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** List all commands whose name begins with "test" dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void cmd_test_cmd_list(void){ dbda8d6ce9 2007-07-21 drh: int i, nCmd; dbda8d6ce9 2007-07-21 drh: const char *aCmd[count(aCommand)]; dbda8d6ce9 2007-07-21 drh: for(i=nCmd=0; i<count(aCommand); i++){ dbda8d6ce9 2007-07-21 drh: if( strncmp(aCommand[i].zName,"test",4)!=0 ) continue; dbda8d6ce9 2007-07-21 drh: if( strcmp(aCommand[i].zName, g.argv[1])==0 ) continue; dbda8d6ce9 2007-07-21 drh: aCmd[nCmd++] = aCommand[i].zName; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: multi_column_list(aCmd, nCmd); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** RSS feeds need to reference absolute URLs so we need to calculate dbda8d6ce9 2007-07-21 drh: ** the base URL onto which we add components. This is basically dbda8d6ce9 2007-07-21 drh: ** cgi_redirect() stripped down and always returning an absolute URL. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static char *get_base_url(void){ dbda8d6ce9 2007-07-21 drh: int i; dbda8d6ce9 2007-07-21 drh: const char *zHost = PD("HTTP_HOST",""); dbda8d6ce9 2007-07-21 drh: const char *zMode = PD("HTTPS","off"); dbda8d6ce9 2007-07-21 drh: const char *zCur = PD("REQUEST_URI","/"); dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: for(i=0; zCur[i] && zCur[i]!='?' && zCur[i]!='#'; i++){} dbda8d6ce9 2007-07-21 drh: if( g.zExtra ){ dbda8d6ce9 2007-07-21 drh: /* Skip to start of extra stuff, then pass over any /'s that might dbda8d6ce9 2007-07-21 drh: ** have separated the document root from the extra stuff. This dbda8d6ce9 2007-07-21 drh: ** ensures that the redirection actually redirects the root, not dbda8d6ce9 2007-07-21 drh: ** something deep down at the bottom of a URL. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: i -= strlen(g.zExtra); dbda8d6ce9 2007-07-21 drh: while( i>0 && zCur[i-1]=='/' ){ i--; } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: while( i>0 && zCur[i-1]!='/' ){ i--; } dbda8d6ce9 2007-07-21 drh: while( i>0 && zCur[i-1]=='/' ){ i--; } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: if( strcmp(zMode,"on")==0 ){ dbda8d6ce9 2007-07-21 drh: return mprintf("https://%s%.*s", zHost, i, zCur); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: return mprintf("http://%s%.*s", zHost, i, zCur); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** Preconditions: dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** * Environment various are set up according to the CGI standard. dbda8d6ce9 2007-07-21 drh: ** * The respository database has been located and opened. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Process the webpage specified by the PATH_INFO or REQUEST_URI dbda8d6ce9 2007-07-21 drh: ** environment variable. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: static void process_one_web_page(void){ dbda8d6ce9 2007-07-21 drh: const char *zPathInfo; dbda8d6ce9 2007-07-21 drh: char *zPath; dbda8d6ce9 2007-07-21 drh: void (*xFunc)(void); dbda8d6ce9 2007-07-21 drh: int i, j; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* Find the page that the user has requested, construct and deliver that dbda8d6ce9 2007-07-21 drh: ** page. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: zPathInfo = P("PATH_INFO"); dbda8d6ce9 2007-07-21 drh: if( zPathInfo==0 || zPathInfo[0]==0 ){ dbda8d6ce9 2007-07-21 drh: const char *zUri; dbda8d6ce9 2007-07-21 drh: char *zBase; dbda8d6ce9 2007-07-21 drh: zUri = PD("REQUEST_URI","/"); dbda8d6ce9 2007-07-21 drh: for(i=0; zUri[i] && zUri[i]!='?' && zUri[i]!='#'; i++){} dbda8d6ce9 2007-07-21 drh: for(j=i; j>0 && zUri[j-1]!='/'; j--){} dbda8d6ce9 2007-07-21 drh: zBase = mprintf("%.*s/index", i-j, &zUri[j]); dbda8d6ce9 2007-07-21 drh: cgi_redirect(zBase); dbda8d6ce9 2007-07-21 drh: cgi_reply(); dbda8d6ce9 2007-07-21 drh: return; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: zPath = mprintf("%s", zPathInfo); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* Remove the leading "/" at the beginning of the path. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: g.zPath = &zPath[1]; dbda8d6ce9 2007-07-21 drh: for(i=1; zPath[i] && zPath[i]!='/'; i++){} dbda8d6ce9 2007-07-21 drh: if( zPath[i]=='/' ){ dbda8d6ce9 2007-07-21 drh: zPath[i] = 0; dbda8d6ce9 2007-07-21 drh: g.zExtra = &zPath[i+1]; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* CGI parameters get this treatment elsewhere, but places like getfile dbda8d6ce9 2007-07-21 drh: ** will use g.zExtra directly. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: dehttpize(g.zExtra); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: g.zExtra = 0; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: g.zBaseURL = get_base_url(); dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* Prevent robots from indexing this site. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: if( strcmp(g.zPath, "robots.txt")==0 ){ dbda8d6ce9 2007-07-21 drh: cgi_set_content_type("text/plain"); dbda8d6ce9 2007-07-21 drh: @ User-agent: * dbda8d6ce9 2007-07-21 drh: @ Disallow: / dbda8d6ce9 2007-07-21 drh: cgi_reply(); dbda8d6ce9 2007-07-21 drh: exit(0); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* Locate the method specified by the path and execute the function dbda8d6ce9 2007-07-21 drh: ** that implements that method. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: if( name_search(g.zPath, aWebpage, count(aWebpage), &xFunc) && dbda8d6ce9 2007-07-21 drh: name_search("not_found", aWebpage, count(aWebpage), &xFunc) ){ dbda8d6ce9 2007-07-21 drh: cgi_set_status(404,"Not Found"); dbda8d6ce9 2007-07-21 drh: @ <h1>Not Found</h1> dbda8d6ce9 2007-07-21 drh: @ <p>Page not found: %h(g.zPath)</p> dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: xFunc(); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* Return the result. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: cgi_reply(); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: cgi dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** The single argument is the name of a file that is the CGI script dbda8d6ce9 2007-07-21 drh: ** that is being run. This file should look something like this: dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** #!/usr/bin/fossil dbda8d6ce9 2007-07-21 drh: ** repository: /home/somebody/project.db dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** We are interested in the line that defines the name of the repository. dbda8d6ce9 2007-07-21 drh: ** Read the file, find the repository line. Then open the respository dbda8d6ce9 2007-07-21 drh: ** database. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Also do the usual CGI initialization stuff in the cgi.c module. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** After all of the above setup, call process_one_web_page() to do the dbda8d6ce9 2007-07-21 drh: ** web page processing and return the result. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void cmd_cgi(void){ dbda8d6ce9 2007-07-21 drh: const char *zFile; dbda8d6ce9 2007-07-21 drh: Blob config, line, key, value; dbda8d6ce9 2007-07-21 drh: if( g.argc==3 && strcmp(g.argv[1],"cgi")==0 ){ dbda8d6ce9 2007-07-21 drh: zFile = g.argv[2]; dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: zFile = g.argv[1]; dbda8d6ce9 2007-07-21 drh: } 396cc2a4eb 2007-07-30 drh: g.cgiPanic = 1; dbda8d6ce9 2007-07-21 drh: blob_read_from_file(&config, zFile); dbda8d6ce9 2007-07-21 drh: while( blob_line(&config, &line) ){ dbda8d6ce9 2007-07-21 drh: if( !blob_token(&line, &key) ) continue; dbda8d6ce9 2007-07-21 drh: if( blob_buffer(&key)[0]=='#' ) continue; dbda8d6ce9 2007-07-21 drh: if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){ dbda8d6ce9 2007-07-21 drh: g.fDebug = fopen(blob_str(&value), "a"); dbda8d6ce9 2007-07-21 drh: blob_reset(&value); dbda8d6ce9 2007-07-21 drh: continue; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){ dbda8d6ce9 2007-07-21 drh: cgi_setenv("HOME", blob_str(&value)); dbda8d6ce9 2007-07-21 drh: blob_reset(&value); dbda8d6ce9 2007-07-21 drh: continue; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( blob_eq(&key, "repository:") && blob_token(&line, &value) ){ dbda8d6ce9 2007-07-21 drh: db_open_repository(blob_str(&value)); dbda8d6ce9 2007-07-21 drh: blob_reset(&value); dbda8d6ce9 2007-07-21 drh: blob_reset(&config); dbda8d6ce9 2007-07-21 drh: break; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( g.db==0 ){ dbda8d6ce9 2007-07-21 drh: cgi_panic("Unable to find or open the project repository"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: cgi_init(); dbda8d6ce9 2007-07-21 drh: process_one_web_page(); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: http dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Handle a single HTTP request appearing on standard input. This dbda8d6ce9 2007-07-21 drh: ** method is used to launch an HTTP request handler from INETD, for dbda8d6ce9 2007-07-21 drh: ** example. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** The argument is the name of the repository. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void cmd_http(void){ dbda8d6ce9 2007-07-21 drh: if( g.argc!=2 && g.argc!=3 ){ dbda8d6ce9 2007-07-21 drh: cgi_panic("no repository specified"); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( g.argc==3 ){ dbda8d6ce9 2007-07-21 drh: db_open_repository(g.argv[2]); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: db_must_be_within_tree(); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: cgi_handle_http_request(); dbda8d6ce9 2007-07-21 drh: process_one_web_page(); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: /* dbda8d6ce9 2007-07-21 drh: ** COMMAND: server dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** Open a socket and begin listening for incoming HTTP requests. dbda8d6ce9 2007-07-21 drh: ** As each connection is received, fork a new child process to handle dbda8d6ce9 2007-07-21 drh: ** the request. dbda8d6ce9 2007-07-21 drh: ** dbda8d6ce9 2007-07-21 drh: ** The argument is the name of the repository. dbda8d6ce9 2007-07-21 drh: */ dbda8d6ce9 2007-07-21 drh: void cmd_webserver(void){ dbda8d6ce9 2007-07-21 drh: int iPort; dbda8d6ce9 2007-07-21 drh: const char *zPort; dbda8d6ce9 2007-07-21 drh: dbda8d6ce9 2007-07-21 drh: zPort = find_option("port", "P", 1); dbda8d6ce9 2007-07-21 drh: if( zPort ){ dbda8d6ce9 2007-07-21 drh: iPort = atoi(zPort); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: iPort = 8080; dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?"); dbda8d6ce9 2007-07-21 drh: cgi_http_server(iPort); dbda8d6ce9 2007-07-21 drh: if( g.fHttpTrace ){ dbda8d6ce9 2007-07-21 drh: fprintf(stderr, "====== SERVER pid %d =======\n", getpid()); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: if( g.argc==2 ){ dbda8d6ce9 2007-07-21 drh: db_must_be_within_tree(); dbda8d6ce9 2007-07-21 drh: }else{ dbda8d6ce9 2007-07-21 drh: db_open_repository(g.argv[2]); dbda8d6ce9 2007-07-21 drh: } dbda8d6ce9 2007-07-21 drh: cgi_handle_http_request(); dbda8d6ce9 2007-07-21 drh: process_one_web_page(); dbda8d6ce9 2007-07-21 drh: }