File Annotation
Not logged in
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: */
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 isAnon;             /* True if logged in anoymously */
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 */
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: }