Fossil

Artifact [87d67e74]
Login

Artifact 87d67e7452c9d4095bfd1950f551599cb2644502ad77fb74a9509215d21e631e:


     1  /*
     2  ** Copyright (c) 2006 D. Richard Hipp
     3  **
     4  ** This program is free software; you can redistribute it and/or
     5  ** modify it under the terms of the Simplified BSD License (also
     6  ** known as the "2-Clause License" or "FreeBSD License".)
     7  **
     8  ** This program is distributed in the hope that it will be useful,
     9  ** but without any warranty; without even the implied warranty of
    10  ** merchantability or fitness for a particular purpose.
    11  **
    12  ** Author contact information:
    13  **   drh@hwaci.com
    14  **   http://www.hwaci.com/drh/
    15  **
    16  *******************************************************************************
    17  **
    18  ** This module codes the main() procedure that runs first when the
    19  ** program is invoked.
    20  */
    21  #include "VERSION.h"
    22  #include "config.h"
    23  #if defined(_WIN32)
    24  #  include <windows.h>
    25  #  include <io.h>
    26  #  define isatty(h) _isatty(h)
    27  #  define GETPID (int)GetCurrentProcessId
    28  #endif
    29  #include "main.h"
    30  #include <string.h>
    31  #include <time.h>
    32  #include <fcntl.h>
    33  #include <sys/types.h>
    34  #include <sys/stat.h>
    35  #include <stdlib.h> /* atexit() */
    36  #if !defined(_WIN32)
    37  #  include <errno.h> /* errno global */
    38  #  include <unistd.h>
    39  #  include <signal.h>
    40  #  define GETPID getpid
    41  #endif
    42  #ifdef FOSSIL_ENABLE_SSL
    43  #  include "openssl/crypto.h"
    44  #endif
    45  #if defined(FOSSIL_ENABLE_MINIZ)
    46  #  define MINIZ_HEADER_FILE_ONLY
    47  #  include "miniz.c"
    48  #else
    49  #  include <zlib.h>
    50  #endif
    51  #if INTERFACE
    52  #ifdef FOSSIL_ENABLE_TCL
    53  #  include "tcl.h"
    54  #endif
    55  #ifdef FOSSIL_ENABLE_JSON
    56  #  include "cson_amalgamation.h" /* JSON API. */
    57  #  include "json_detail.h"
    58  #endif
    59  #ifdef HAVE_BACKTRACE
    60  # include <execinfo.h>
    61  #endif
    62  
    63  /*
    64  ** Default length of a timeout for serving an HTTP request.  Changable
    65  ** using the "--timeout N" command-line option or via "timeout: N" in the
    66  ** CGI script.
    67  */
    68  #ifndef FOSSIL_DEFAULT_TIMEOUT
    69  # define FOSSIL_DEFAULT_TIMEOUT 600  /* 10 minutes */
    70  #endif
    71  
    72  /*
    73  ** Maximum number of auxiliary parameters on reports
    74  */
    75  #define MX_AUX  5
    76  
    77  /*
    78  ** Holds flags for fossil user permissions.
    79  */
    80  struct FossilUserPerms {
    81    char Setup;            /* s: use Setup screens on web interface */
    82    char Admin;            /* a: administrative permission */
    83    char Delete;           /* d: delete wiki or tickets */
    84    char Password;         /* p: change password */
    85    char Query;            /* q: create new reports */
    86    char Write;            /* i: xfer inbound. check-in */
    87    char Read;             /* o: xfer outbound. check-out */
    88    char Hyperlink;        /* h: enable the display of hyperlinks */
    89    char Clone;            /* g: clone */
    90    char RdWiki;           /* j: view wiki via web */
    91    char NewWiki;          /* f: create new wiki via web */
    92    char ApndWiki;         /* m: append to wiki via web */
    93    char WrWiki;           /* k: edit wiki via web */
    94    char ModWiki;          /* l: approve and publish wiki content (Moderator) */
    95    char RdTkt;            /* r: view tickets via web */
    96    char NewTkt;           /* n: create new tickets */
    97    char ApndTkt;          /* c: append to tickets via the web */
    98    char WrTkt;            /* w: make changes to tickets via web */
    99    char ModTkt;           /* q: approve and publish ticket changes (Moderator) */
   100    char Attach;           /* b: add attachments */
   101    char TktFmt;           /* t: create new ticket report formats */
   102    char RdAddr;           /* e: read email addresses or other private data */
   103    char Zip;              /* z: download zipped artifact via /zip URL */
   104    char Private;          /* x: can send and receive private content */
   105    char WrUnver;          /* y: can push unversioned content */
   106    char RdForum;          /* 2: Read forum posts */
   107    char WrForum;          /* 3: Create new forum posts */
   108    char WrTForum;         /* 4: Post to forums not subject to moderation */
   109    char ModForum;         /* 5: Moderate (approve or reject) forum posts */
   110    char AdminForum;       /* 6: Grant capability 4 to other users */
   111    char EmailAlert;       /* 7: Sign up for email notifications */
   112    char Announce;         /* A: Send announcements */
   113    char Debug;            /* D: show extra Fossil debugging features */
   114    /* These last two are included to block infinite recursion */
   115    char XReader;          /* u: Inherit all privileges of "reader" */
   116    char XDeveloper;       /* v: Inherit all privileges of "developer" */
   117  };
   118  
   119  #ifdef FOSSIL_ENABLE_TCL
   120  /*
   121  ** All Tcl related context information is in this structure.  This structure
   122  ** definition has been copied from and should be kept in sync with the one in
   123  ** "th_tcl.c".
   124  */
   125  struct TclContext {
   126    int argc;              /* Number of original (expanded) arguments. */
   127    char **argv;           /* Full copy of the original (expanded) arguments. */
   128    void *hLibrary;        /* The Tcl library module handle. */
   129    void *xFindExecutable; /* See tcl_FindExecutableProc in th_tcl.c. */
   130    void *xCreateInterp;   /* See tcl_CreateInterpProc in th_tcl.c. */
   131    void *xDeleteInterp;   /* See tcl_DeleteInterpProc in th_tcl.c. */
   132    void *xFinalize;       /* See tcl_FinalizeProc in th_tcl.c. */
   133    Tcl_Interp *interp;    /* The on-demand created Tcl interpreter. */
   134    int useObjProc;        /* Non-zero if an objProc can be called directly. */
   135    int useTip285;         /* Non-zero if TIP #285 is available. */
   136    char *setup;           /* The optional Tcl setup script. */
   137    void *xPreEval;        /* Optional, called before Tcl_Eval*(). */
   138    void *pPreContext;     /* Optional, provided to xPreEval(). */
   139    void *xPostEval;       /* Optional, called after Tcl_Eval*(). */
   140    void *pPostContext;    /* Optional, provided to xPostEval(). */
   141  };
   142  #endif
   143  
   144  struct Global {
   145    int argc; char **argv;  /* Command-line arguments to the program */
   146    int argDashDashIndex;   /* Index of the "--" flag in g.argv, if provided
   147                            ** (else 0). Not valid until verify_all_options()
   148                            ** or verify_all_options2() is called. */;
   149    char *nameOfExe;        /* Full path of executable. */
   150    const char *zErrlog;    /* Log errors to this file, if not NULL */
   151    int isConst;            /* True if the output is unchanging & cacheable */
   152    const char *zVfsName;   /* The VFS to use for database connections */
   153    sqlite3 *db;            /* The connection to the databases */
   154    sqlite3 *dbConfig;      /* Separate connection for global_config table */
   155    char *zAuxSchema;       /* Main repository aux-schema */
   156    int dbIgnoreErrors;     /* Ignore database errors if true */
   157    char *zConfigDbName;    /* Path of the config database. NULL if not open */
   158    sqlite3_int64 now;      /* Seconds since 1970 */
   159    int repositoryOpen;     /* True if the main repository database is open */
   160    unsigned iRepoDataVers;  /* Initial data version for repository database */
   161    char *zRepositoryOption; /* Most recent cached repository option value */
   162    char *zRepositoryName;  /* Name of the repository database file */
   163    char *zLocalDbName;     /* Name of the local database file */
   164    char *zOpenRevision;    /* Check-in version to use during database open */
   165    const char *zCmdName;   /* Name of the Fossil command currently running */
   166    int localOpen;          /* True if the local database is open */
   167    char *zLocalRoot;       /* The directory holding the  local database */
   168    int minPrefix;          /* Number of digits needed for a distinct UUID */
   169    int eHashPolicy;        /* Current hash policy.  One of HPOLICY_* */
   170    int fSqlTrace;          /* True if --sqltrace flag is present */
   171    int fSqlStats;          /* True if --sqltrace or --sqlstats are present */
   172    int fSqlPrint;          /* True if --sqlprint flag is present */
   173    int fCgiTrace;          /* True if --cgitrace is enabled */
   174    int fQuiet;             /* True if -quiet flag is present */
   175    int fJail;              /* True if running with a chroot jail */
   176    int fHttpTrace;         /* Trace outbound HTTP requests */
   177    int fAnyTrace;          /* Any kind of tracing */
   178    char *zHttpAuth;        /* HTTP Authorization user:pass information */
   179    int fSystemTrace;       /* Trace calls to fossil_system(), --systemtrace */
   180    int fSshTrace;          /* Trace the SSH setup traffic */
   181    int fSshClient;         /* HTTP client flags for SSH client */
   182    int fNoHttpCompress;    /* Do not compress HTTP traffic (for debugging) */
   183    char *zSshCmd;          /* SSH command string */
   184    int fNoSync;            /* Do not do an autosync ever.  --nosync */
   185    int fIPv4;              /* Use only IPv4, not IPv6. --ipv4 */
   186    char *zPath;            /* Name of webpage being served */
   187    char *zExtra;           /* Extra path information past the webpage name */
   188    char *zBaseURL;         /* Full text of the URL being served */
   189    char *zHttpsURL;        /* zBaseURL translated to https: */
   190    char *zTop;             /* Parent directory of zPath */
   191    const char *zExtRoot;   /* Document root for the /ext sub-website */
   192    const char *zContentType;  /* The content type of the input HTTP request */
   193    int iErrPriority;       /* Priority of current error message */
   194    char *zErrMsg;          /* Text of an error message */
   195    int sslNotAvailable;    /* SSL is not available.  Do not redirect to https: */
   196    Blob cgiIn;             /* Input to an xfer www method */
   197    int cgiOutput;          /* 0: command-line 1: CGI. 2: after CGI */
   198    int xferPanic;          /* Write error messages in XFER protocol */
   199    int fullHttpReply;      /* True for full HTTP reply.  False for CGI reply */
   200    Th_Interp *interp;      /* The TH1 interpreter */
   201    char *th1Setup;         /* The TH1 post-creation setup script, if any */
   202    int th1Flags;           /* The TH1 integration state flags */
   203    FILE *httpIn;           /* Accept HTTP input from here */
   204    FILE *httpOut;          /* Send HTTP output here */
   205    int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
   206    int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
   207    int *aCommitFile;       /* Array of files to be committed */
   208    int markPrivate;        /* All new artifacts are private if true */
   209    char *ckinLockFail;     /* Check-in lock failure received from server */
   210    int clockSkewSeen;      /* True if clocks on client and server out of sync */
   211    int wikiFlags;          /* Wiki conversion flags applied to %W */
   212    char isHTTP;            /* True if server/CGI modes, else assume CLI. */
   213    char javascriptHyperlink; /* If true, set href= using script, not HTML */
   214    Blob httpHeader;        /* Complete text of the HTTP request header */
   215    UrlData url;            /* Information about current URL */
   216    const char *zLogin;     /* Login name.  NULL or "" if not logged in. */
   217    const char *zSSLIdentity;  /* Value of --ssl-identity option, filename of
   218                               ** SSL client identity */
   219    int useLocalauth;       /* No login required if from 127.0.0.1 */
   220    int noPswd;             /* Logged in without password (on 127.0.0.1) */
   221    int userUid;            /* Integer user id */
   222    int isHuman;            /* True if access by a human, not a spider or bot */
   223    int comFmtFlags;        /* Zero or more "COMMENT_PRINT_*" bit flags, should be
   224                            ** accessed through get_comment_format(). */
   225  
   226    /* Information used to populate the RCVFROM table */
   227    int rcvid;              /* The rcvid.  0 if not yet defined. */
   228    char *zIpAddr;          /* The remote IP address */
   229    char *zNonce;           /* The nonce used for login */
   230  
   231    /* permissions available to current user */
   232    struct FossilUserPerms perm;
   233  
   234    /* permissions available to current user or to "anonymous".
   235    ** This is the logical union of perm permissions above with
   236    ** the value that perm would take if g.zLogin were "anonymous". */
   237    struct FossilUserPerms anon;
   238  
   239  #ifdef FOSSIL_ENABLE_TCL
   240    /* all Tcl related context necessary for integration */
   241    struct TclContext tcl;
   242  #endif
   243  
   244    /* For defense against Cross-site Request Forgery attacks */
   245    char zCsrfToken[12];    /* Value of the anti-CSRF token */
   246    int okCsrf;             /* Anti-CSRF token is present and valid */
   247  
   248    int parseCnt[10];       /* Counts of artifacts parsed */
   249    FILE *fDebug;           /* Write debug information here, if the file exists */
   250  #ifdef FOSSIL_ENABLE_TH1_HOOKS
   251    int fNoThHook;          /* Disable all TH1 command/webpage hooks */
   252  #endif
   253    int thTrace;            /* True to enable TH1 debugging output */
   254    Blob thLog;             /* Text of the TH1 debugging output */
   255  
   256    int isHome;             /* True if rendering the "home" page */
   257  
   258    /* Storage for the aux() and/or option() SQL function arguments */
   259    int nAux;                    /* Number of distinct aux() or option() values */
   260    const char *azAuxName[MX_AUX]; /* Name of each aux() or option() value */
   261    char *azAuxParam[MX_AUX];      /* Param of each aux() or option() value */
   262    const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
   263    const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
   264    int anAuxCols[MX_AUX];         /* Number of columns for option() values */
   265    int allowSymlinks;             /* Cached "allow-symlinks" option */
   266    int mainTimerId;               /* Set to fossil_timer_start() */
   267    int nPendingRequest;           /* # of HTTP requests in "fossil server" */
   268    int nRequest;                  /* Total # of HTTP request */
   269  #ifdef FOSSIL_ENABLE_JSON
   270    struct FossilJsonBits {
   271      int isJsonMode;            /* True if running in JSON mode, else
   272                                    false. This changes how errors are
   273                                    reported. In JSON mode we try to
   274                                    always output JSON-form error
   275                                    responses and always exit() with
   276                                    code 0 to avoid an HTTP 500 error.
   277                                 */
   278      int resultCode;            /* used for passing back specific codes
   279                                 ** from /json callbacks. */
   280      int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
   281      cson_output_opt outOpt;    /* formatting options for JSON mode. */
   282      cson_value *authToken;     /* authentication token */
   283      const char *jsonp;         /* Name of JSONP function wrapper. */
   284      unsigned char dispatchDepth /* Tells JSON command dispatching
   285                                     which argument we are currently
   286                                     working on. For this purpose, arg#0
   287                                     is the "json" path/CLI arg.
   288                                  */;
   289      struct {                   /* "garbage collector" */
   290        cson_value *v;
   291        cson_array *a;
   292      } gc;
   293      struct {                   /* JSON POST data. */
   294        cson_value *v;
   295        cson_array *a;
   296        int offset;              /* Tells us which PATH_INFO/CLI args
   297                                    part holds the "json" command, so
   298                                    that we can account for sub-repos
   299                                    and path prefixes.  This is handled
   300                                    differently for CLI and CGI modes.
   301                                 */
   302        const char *commandStr   /*"command" request param.*/;
   303      } cmd;
   304      struct {                   /* JSON POST data. */
   305        cson_value *v;
   306        cson_object *o;
   307      } post;
   308      struct {                   /* GET/COOKIE params in JSON mode. */
   309        cson_value *v;
   310        cson_object *o;
   311      } param;
   312      struct {
   313        cson_value *v;
   314        cson_object *o;
   315      } reqPayload;              /* request payload object (if any) */
   316      cson_array *warnings;      /* response warnings */
   317      int timerId;               /* fetched from fossil_timer_start() */
   318    } json;
   319  #endif /* FOSSIL_ENABLE_JSON */
   320  };
   321  
   322  /*
   323  ** Macro for debugging:
   324  */
   325  #define CGIDEBUG(X)  if( g.fDebug ) cgi_debug X
   326  
   327  #endif
   328  
   329  Global g;
   330  
   331  /*
   332  ** atexit() handler which frees up "some" of the resources
   333  ** used by fossil.
   334  */
   335  static void fossil_atexit(void) {
   336    static int once = 0;
   337    if( once++ ) return; /* Ensure that this routine only runs once */
   338  #if USE_SEE
   339    /*
   340    ** Zero, unlock, and free the saved database encryption key now.
   341    */
   342    db_unsave_encryption_key();
   343  #endif
   344  #if defined(_WIN32) || defined(__BIONIC__)
   345    /*
   346    ** Free the secure getpass() buffer now.
   347    */
   348    freepass();
   349  #endif
   350  #if defined(_WIN32) && !defined(_WIN64) && defined(FOSSIL_ENABLE_TCL) && \
   351      defined(USE_TCL_STUBS)
   352    /*
   353    ** If Tcl is compiled on Windows using the latest MinGW, Fossil can crash
   354    ** when exiting while a stubs-enabled Tcl is still loaded.  This is due to
   355    ** a bug in MinGW, see:
   356    **
   357    **     http://comments.gmane.org/gmane.comp.gnu.mingw.user/41724
   358    **
   359    ** The workaround is to manually unload the loaded Tcl library prior to
   360    ** exiting the process.  This issue does not impact 64-bit Windows.
   361    */
   362    unloadTcl(g.interp, &g.tcl);
   363  #endif
   364  #ifdef FOSSIL_ENABLE_JSON
   365    cson_value_free(g.json.gc.v);
   366    memset(&g.json, 0, sizeof(g.json));
   367  #endif
   368    free(g.zErrMsg);
   369    if(g.db){
   370      db_close(0);
   371    }
   372    /*
   373    ** FIXME: The next two lines cannot always be enabled; however, they
   374    **        are very useful for tracking down TH1 memory leaks.
   375    */
   376    if( fossil_getenv("TH1_DELETE_INTERP")!=0 ){
   377      if( g.interp ){
   378        Th_DeleteInterp(g.interp); g.interp = 0;
   379      }
   380      assert( Th_GetOutstandingMalloc()==0 );
   381    }
   382  }
   383  
   384  /*
   385  ** Convert all arguments from mbcs (or unicode) to UTF-8. Then
   386  ** search g.argv for arguments "--args FILENAME". If found, then
   387  ** (1) remove the two arguments from g.argv
   388  ** (2) Read the file FILENAME
   389  ** (3) Use the contents of FILE to replace the two removed arguments:
   390  **     (a) Ignore blank lines in the file
   391  **     (b) Each non-empty line of the file is an argument, except
   392  **     (c) If the line begins with "-" and contains a space, it is broken
   393  **         into two arguments at the space.
   394  */
   395  void expand_args_option(int argc, void *argv){
   396    Blob file = empty_blob;   /* Content of the file */
   397    Blob line = empty_blob;   /* One line of the file */
   398    unsigned int nLine;       /* Number of lines in the file*/
   399    unsigned int i, j, k;     /* Loop counters */
   400    int n;                    /* Number of bytes in one line */
   401    unsigned int nArg;        /* Number of new arguments */
   402    char *z;                  /* General use string pointer */
   403    char **newArgv;           /* New expanded g.argv under construction */
   404    const char *zFileName;    /* input file name */
   405    FILE *inFile;             /* input FILE */
   406  #if defined(_WIN32)
   407    wchar_t buf[MAX_PATH];
   408  #endif
   409  
   410    g.argc = argc;
   411    g.argv = argv;
   412    sqlite3_initialize();
   413  #if defined(_WIN32) && defined(BROKEN_MINGW_CMDLINE)
   414    for(i=0; i<g.argc; i++) g.argv[i] = fossil_mbcs_to_utf8(g.argv[i]);
   415  #else
   416    for(i=0; i<g.argc; i++) g.argv[i] = fossil_path_to_utf8(g.argv[i]);
   417  #endif
   418  #if defined(_WIN32)
   419    GetModuleFileNameW(NULL, buf, MAX_PATH);
   420    g.nameOfExe = fossil_path_to_utf8(buf);
   421  #else
   422    g.nameOfExe = g.argv[0];
   423  #endif
   424    for(i=1; i<g.argc-1; i++){
   425      z = g.argv[i];
   426      if( z[0]!='-' ) continue;
   427      z++;
   428      if( z[0]=='-' ) z++;
   429      if( z[0]==0 ) return;   /* Stop searching at "--" */
   430      if( fossil_strcmp(z, "args")==0 ) break;
   431    }
   432    if( i>=g.argc-1 ) return;
   433  
   434    zFileName = g.argv[i+1];
   435    if( strcmp(zFileName,"-")==0 ){
   436      inFile = stdin;
   437    }else if( !file_isfile(zFileName, ExtFILE) ){
   438      fossil_fatal("Not an ordinary file: \"%s\"", zFileName);
   439    }else{
   440      inFile = fossil_fopen(zFileName,"rb");
   441      if( inFile==0 ){
   442        fossil_fatal("Cannot open -args file [%s]", zFileName);
   443      }
   444    }
   445    blob_read_from_channel(&file, inFile, -1);
   446    if(stdin != inFile){
   447      fclose(inFile);
   448    }
   449    inFile = NULL;
   450    blob_to_utf8_no_bom(&file, 1);
   451    z = blob_str(&file);
   452    for(k=0, nLine=1; z[k]; k++) if( z[k]=='\n' ) nLine++;
   453    if( nLine>100000000 ) fossil_fatal("too many command-line arguments");
   454    nArg = g.argc + nLine*2;
   455    newArgv = fossil_malloc( sizeof(char*)*nArg );
   456    for(j=0; j<i; j++) newArgv[j] = g.argv[j];
   457  
   458    blob_rewind(&file);
   459    while( (n = blob_line(&file, &line))>0 ){
   460      if( n<1 ){
   461        /* Reminder: corner-case: a line with 1 byte and no newline. */
   462        continue;
   463      }
   464      z = blob_buffer(&line);
   465      if('\n'==z[n-1]){
   466        z[n-1] = 0;
   467      }
   468  
   469      if((n>1) && ('\r'==z[n-2])){
   470        if(n==2) continue /*empty line*/;
   471        z[n-2] = 0;
   472      }
   473      if(!z[0]) continue;
   474      if( j>=nArg ){
   475        fossil_fatal("malformed command-line arguments");
   476      }
   477      newArgv[j++] = z;
   478      if( z[0]=='-' ){
   479        for(k=1; z[k] && !fossil_isspace(z[k]); k++){}
   480        if( z[k] ){
   481          z[k] = 0;
   482          k++;
   483          if( z[k] ) newArgv[j++] = &z[k];
   484        }
   485      }
   486    }
   487    i += 2;
   488    while( i<g.argc ) newArgv[j++] = g.argv[i++];
   489    newArgv[j] = 0;
   490    g.argc = j;
   491    g.argv = newArgv;
   492  }
   493  
   494  #ifdef FOSSIL_ENABLE_TCL
   495  /*
   496  ** Make a deep copy of the provided argument array and return it.
   497  */
   498  static char **copy_args(int argc, char **argv){
   499    char **zNewArgv;
   500    int i;
   501    zNewArgv = fossil_malloc( sizeof(char*)*(argc+1) );
   502    memset(zNewArgv, 0, sizeof(char*)*(argc+1));
   503    for(i=0; i<argc; i++){
   504      zNewArgv[i] = fossil_strdup(argv[i]);
   505    }
   506    return zNewArgv;
   507  }
   508  #endif
   509  
   510  /*
   511  ** Returns a name for a SQLite return code.
   512  */
   513  static const char *fossil_sqlite_return_code_name(int rc){
   514    static char zCode[30];
   515    switch( rc & 0xff ){
   516      case SQLITE_OK:         return "SQLITE_OK";
   517      case SQLITE_ERROR:      return "SQLITE_ERROR";
   518      case SQLITE_INTERNAL:   return "SQLITE_INTERNAL";
   519      case SQLITE_PERM:       return "SQLITE_PERM";
   520      case SQLITE_ABORT:      return "SQLITE_ABORT";
   521      case SQLITE_BUSY:       return "SQLITE_BUSY";
   522      case SQLITE_LOCKED:     return "SQLITE_LOCKED";
   523      case SQLITE_NOMEM:      return "SQLITE_NOMEM";
   524      case SQLITE_READONLY:   return "SQLITE_READONLY";
   525      case SQLITE_INTERRUPT:  return "SQLITE_INTERRUPT";
   526      case SQLITE_IOERR:      return "SQLITE_IOERR";
   527      case SQLITE_CORRUPT:    return "SQLITE_CORRUPT";
   528      case SQLITE_NOTFOUND:   return "SQLITE_NOTFOUND";
   529      case SQLITE_FULL:       return "SQLITE_FULL";
   530      case SQLITE_CANTOPEN:   return "SQLITE_CANTOPEN";
   531      case SQLITE_PROTOCOL:   return "SQLITE_PROTOCOL";
   532      case SQLITE_EMPTY:      return "SQLITE_EMPTY";
   533      case SQLITE_SCHEMA:     return "SQLITE_SCHEMA";
   534      case SQLITE_TOOBIG:     return "SQLITE_TOOBIG";
   535      case SQLITE_CONSTRAINT: return "SQLITE_CONSTRAINT";
   536      case SQLITE_MISMATCH:   return "SQLITE_MISMATCH";
   537      case SQLITE_MISUSE:     return "SQLITE_MISUSE";
   538      case SQLITE_NOLFS:      return "SQLITE_NOLFS";
   539      case SQLITE_AUTH:       return "SQLITE_AUTH";
   540      case SQLITE_FORMAT:     return "SQLITE_FORMAT";
   541      case SQLITE_RANGE:      return "SQLITE_RANGE";
   542      case SQLITE_NOTADB:     return "SQLITE_NOTADB";
   543      case SQLITE_NOTICE:     return "SQLITE_NOTICE";
   544      case SQLITE_WARNING:    return "SQLITE_WARNING";
   545      case SQLITE_ROW:        return "SQLITE_ROW";
   546      case SQLITE_DONE:       return "SQLITE_DONE";
   547      default: {
   548        sqlite3_snprintf(sizeof(zCode), zCode, "SQLite return code %d", rc);
   549      }
   550    }
   551    return zCode;
   552  }
   553  
   554  /* Error logs from SQLite */
   555  static void fossil_sqlite_log(void *notUsed, int iCode, const char *zErrmsg){
   556    sqlite3_stmt *p;
   557    Blob msg;
   558  #ifdef __APPLE__
   559    /* Disable the file alias warning on apple products because Time Machine
   560    ** creates lots of aliases and the warnings alarm people. */
   561    if( iCode==SQLITE_WARNING ) return;
   562  #endif
   563  #ifndef FOSSIL_DEBUG
   564    /* Disable the automatic index warning except in FOSSIL_DEBUG builds. */
   565    if( iCode==SQLITE_WARNING_AUTOINDEX ) return;
   566  #endif
   567    if( iCode==SQLITE_SCHEMA ) return;
   568    if( g.dbIgnoreErrors ) return;
   569  #ifdef SQLITE_READONLY_DIRECTORY
   570    if( iCode==SQLITE_READONLY_DIRECTORY ){
   571      zErrmsg = "database is in a read-only directory";
   572    }
   573  #endif
   574    blob_init(&msg, 0, 0);
   575    blob_appendf(&msg, "%s(%d): %s",
   576       fossil_sqlite_return_code_name(iCode), iCode, zErrmsg);
   577    if( g.db ){
   578      for(p=sqlite3_next_stmt(g.db, 0); p; p=sqlite3_next_stmt(g.db,p)){
   579        const char *zSql;
   580        if( !sqlite3_stmt_busy(p) ) continue;
   581        zSql = sqlite3_sql(p);
   582        if( zSql==0 ) continue;
   583        blob_appendf(&msg, "\nSQL: %s", zSql);
   584      }
   585    }
   586    fossil_warning("%s", blob_str(&msg));
   587    blob_reset(&msg);
   588  }
   589  
   590  /*
   591  ** This function attempts to find command line options known to contain
   592  ** bitwise flags and initializes the associated global variables.  After
   593  ** this function executes, all global variables (i.e. in the "g" struct)
   594  ** containing option-settable bitwise flag fields must be initialized.
   595  */
   596  static void fossil_init_flags_from_options(void){
   597    const char *zValue = find_option("comfmtflags", 0, 1);
   598    if( zValue==0 ){
   599      zValue = find_option("comment-format", 0, 1);
   600    }
   601    if( zValue ){
   602      g.comFmtFlags = atoi(zValue);
   603    }else{
   604      g.comFmtFlags = COMMENT_PRINT_UNSET;   /* Command-line option not found. */
   605    }
   606  }
   607  
   608  /*
   609  ** Check to see if the Fossil binary contains an appended repository
   610  ** file using the appendvfs extension.  If so, change command-line arguments
   611  ** to cause Fossil to launch with "fossil ui" on that repo.
   612  */
   613  static int fossilExeHasAppendedRepo(void){
   614    extern int deduceDatabaseType(const char*,int);
   615    if( 2==deduceDatabaseType(g.nameOfExe,0) ){
   616      static char *azAltArgv[] = { 0, "ui", 0, 0 };
   617      azAltArgv[0] = g.nameOfExe;
   618      azAltArgv[2] = g.nameOfExe;
   619      g.argv = azAltArgv;
   620      g.argc = 3;
   621      return 1;
   622    }else{
   623      return 0;
   624    }
   625  }
   626  
   627  /*
   628  ** This procedure runs first.
   629  */
   630  #if defined(FOSSIL_FUZZ)
   631    /* Do not include a main() procedure when building for fuzz testing.
   632    ** libFuzzer will supply main(). */
   633  #elif defined(_WIN32) && !defined(BROKEN_MINGW_CMDLINE)
   634    int _dowildcard = -1; /* This turns on command-line globbing in MinGW-w64 */
   635    int wmain(int argc, wchar_t **argv){ return fossil_main(argc, argv); }
   636  #elif defined(_WIN32)
   637    int _CRT_glob = 0x0001; /* See MinGW bug #2062 */
   638    int main(int argc, char **argv){ return fossil_main(argc, argv); }
   639  #else
   640    int main(int argc, char **argv){ return fossil_main(argc, argv); }
   641  #endif
   642  
   643  /* All the work of main() is done by a separate procedure "fossil_main()".
   644  ** We have to break this out, because fossil_main() is sometimes called
   645  ** separately (by the "shell" command) but we do not want atwait() handlers
   646  ** being called by separate invocations of fossil_main().
   647  */
   648  int fossil_main(int argc, char **argv){
   649    const char *zCmdName = "unknown";
   650    const CmdOrPage *pCmd = 0;
   651    int rc;
   652  
   653  #if !defined(_WIN32_WCE)
   654    if( fossil_getenv("FOSSIL_BREAK") ){
   655      if( isatty(0) && isatty(2) ){
   656        fprintf(stderr,
   657            "attach debugger to process %d and press any key to continue.\n",
   658            GETPID());
   659        fgetc(stdin);
   660      }else{
   661  #if defined(_WIN32) || defined(WIN32)
   662        DebugBreak();
   663  #elif defined(SIGTRAP)
   664        raise(SIGTRAP);
   665  #endif
   666      }
   667    }
   668  #endif
   669  
   670    fossil_limit_memory(1);
   671    if( sqlite3_libversion_number()<3014000 ){
   672      fossil_panic("Unsuitable SQLite version %s, must be at least 3.14.0",
   673                   sqlite3_libversion());
   674    }
   675    sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
   676    sqlite3_config(SQLITE_CONFIG_LOG, fossil_sqlite_log, 0);
   677    memset(&g, 0, sizeof(g));
   678    g.now = time(0);
   679    g.httpHeader = empty_blob;
   680  #ifdef FOSSIL_ENABLE_JSON
   681  #if defined(NDEBUG)
   682    g.json.errorDetailParanoia = 2 /* FIXME: make configurable
   683                                      One problem we have here is that this
   684                                      code is needed before the db is opened,
   685                                      so we can't sql for it.*/;
   686  #else
   687    g.json.errorDetailParanoia = 0;
   688  #endif
   689    g.json.outOpt = cson_output_opt_empty;
   690    g.json.outOpt.addNewline = 1;
   691    g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
   692  #endif /* FOSSIL_ENABLE_JSON */
   693    expand_args_option(argc, argv);
   694  #ifdef FOSSIL_ENABLE_TCL
   695    memset(&g.tcl, 0, sizeof(TclContext));
   696    g.tcl.argc = g.argc;
   697    g.tcl.argv = copy_args(g.argc, g.argv); /* save full arguments */
   698  #endif
   699    g.mainTimerId = fossil_timer_start();
   700    capture_case_sensitive_option();
   701    g.zVfsName = find_option("vfs",0,1);
   702    if( g.zVfsName==0 ){
   703      g.zVfsName = fossil_getenv("FOSSIL_VFS");
   704    }
   705    if( g.zVfsName ){
   706      sqlite3_vfs *pVfs = sqlite3_vfs_find(g.zVfsName);
   707      if( pVfs ){
   708        sqlite3_vfs_register(pVfs, 1);
   709      }else{
   710        fossil_fatal("no such VFS: \"%s\"", g.zVfsName);
   711      }
   712    }
   713    if( fossil_getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
   714      zCmdName = "cgi";
   715      g.isHTTP = 1;
   716    }else if( g.argc<2 && !fossilExeHasAppendedRepo() ){
   717      fossil_print(
   718         "Usage: %s COMMAND ...\n"
   719         "   or: %s help           -- for a list of common commands\n"
   720         "   or: %s help COMMAND   -- for help with the named command\n",
   721         g.argv[0], g.argv[0], g.argv[0]);
   722      fossil_print(
   723        "\nCommands and filenames may be passed on to fossil from a file\n"
   724        "by using:\n"
   725        "\n    %s --args FILENAME ...\n",
   726        g.argv[0]
   727      );
   728      fossil_print(
   729        "\nEach line of the file is assumed to be a filename unless it starts\n"
   730        "with '-' and contains a space, in which case it is assumed to be\n"
   731        "another flag and is treated as such. --args FILENAME may be used\n"
   732        "in conjunction with any other flags.\n");
   733      fossil_exit(1);
   734    }else{
   735      const char *zChdir = find_option("chdir",0,1);
   736      g.isHTTP = 0;
   737      g.rcvid = 0;
   738      g.fQuiet = find_option("quiet", 0, 0)!=0;
   739      g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
   740      g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
   741      g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
   742      g.fSshTrace = find_option("sshtrace", 0, 0)!=0;
   743      g.fCgiTrace = find_option("cgitrace", 0, 0)!=0;
   744      g.fSshClient = 0;
   745      g.zSshCmd = 0;
   746      if( g.fSqlTrace ) g.fSqlStats = 1;
   747      g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
   748  #ifdef FOSSIL_ENABLE_TH1_HOOKS
   749      g.fNoThHook = find_option("no-th-hook", 0, 0)!=0;
   750  #endif
   751      g.fAnyTrace = g.fSqlTrace|g.fSystemTrace|g.fSshTrace|
   752                    g.fHttpTrace|g.fCgiTrace;
   753      g.zHttpAuth = 0;
   754      g.zLogin = find_option("user", "U", 1);
   755      g.zSSLIdentity = find_option("ssl-identity", 0, 1);
   756      g.zErrlog = find_option("errorlog", 0, 1);
   757      fossil_init_flags_from_options();
   758      if( find_option("utc",0,0) ) g.fTimeFormat = 1;
   759      if( find_option("localtime",0,0) ) g.fTimeFormat = 2;
   760      if( zChdir && file_chdir(zChdir, 0) ){
   761        fossil_fatal("unable to change directories to %s", zChdir);
   762      }
   763      if( find_option("help",0,0)!=0 ){
   764        /* If --help is found anywhere on the command line, translate the command
   765         * to "fossil help cmdname" where "cmdname" is the first argument that
   766         * does not begin with a "-" character.  If all arguments start with "-",
   767         * translate to "fossil help argv[1] argv[2]...". */
   768        int i, nNewArgc;
   769        char **zNewArgv = fossil_malloc( sizeof(char*)*(g.argc+2) );
   770        zNewArgv[0] = g.argv[0];
   771        zNewArgv[1] = "help";
   772        for(i=1; i<g.argc; i++){
   773          if( g.argv[i][0]!='-' ){
   774            nNewArgc = 3;
   775            zNewArgv[2] = g.argv[i];
   776            zNewArgv[3] = 0;
   777            break;
   778          }
   779        }
   780        if( i==g.argc ){
   781          for(i=1; i<g.argc; i++) zNewArgv[i+1] = g.argv[i];
   782          nNewArgc = g.argc+1;
   783          zNewArgv[i+1] = 0;
   784        }
   785        g.argc = nNewArgc;
   786        g.argv = zNewArgv;
   787      }
   788      zCmdName = g.argv[1];
   789    }
   790  #ifndef _WIN32
   791    /* There is a bug in stunnel4 in which it sometimes starts up client
   792    ** processes without first opening file descriptor 2 (standard error).
   793    ** If this happens, and a subsequent open() of a database returns file
   794    ** descriptor 2, and then an assert() fires and writes on fd 2, that
   795    ** can corrupt the data file.  To avoid this problem, make sure open()
   796    ** will never return file descriptor 2 or less. */
   797    if( !is_valid_fd(2) ){
   798      int nTry = 0;
   799      int fd = 0;
   800      int x = 0;
   801      do{
   802        fd = open("/dev/null",O_WRONLY);
   803        if( fd>=2 ) break;
   804        if( fd<0 ) x = errno;
   805      }while( nTry++ < 2 );
   806      if( fd<2 ){
   807        g.cgiOutput = 1;
   808        g.httpOut = stdout;
   809        g.fullHttpReply = !g.isHTTP;
   810        fossil_panic("file descriptor 2 is not open. (fd=%d, errno=%d)",
   811                     fd, x);
   812      }
   813    }
   814  #endif
   815    g.zCmdName = zCmdName;
   816    rc = dispatch_name_search(zCmdName, CMDFLAG_COMMAND|CMDFLAG_PREFIX, &pCmd);
   817    if( rc==1 ){
   818  #ifdef FOSSIL_ENABLE_TH1_HOOKS
   819      if( !g.isHTTP && !g.fNoThHook ){
   820        rc = Th_CommandHook(zCmdName, 0);
   821      }else{
   822        rc = TH_OK;
   823      }
   824      if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
   825        if( rc==TH_OK || rc==TH_RETURN ){
   826  #endif
   827          fossil_fatal("%s: unknown command: %s\n"
   828                       "%s: use \"help\" for more information",
   829                       g.argv[0], zCmdName, g.argv[0]);
   830  #ifdef FOSSIL_ENABLE_TH1_HOOKS
   831        }
   832        if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
   833          Th_CommandNotify(zCmdName, 0);
   834        }
   835      }
   836      fossil_exit(0);
   837  #endif
   838    }else if( rc==2 ){
   839      Blob couldbe;
   840      blob_init(&couldbe,0,0);
   841      dispatch_matching_names(zCmdName, &couldbe);
   842      fossil_print("%s: ambiguous command prefix: %s\n"
   843                   "%s: could be any of:%s\n"
   844                   "%s: use \"help\" for more information\n",
   845                   g.argv[0], zCmdName, g.argv[0], blob_str(&couldbe), g.argv[0]);
   846      fossil_exit(1);
   847    }
   848    atexit( fossil_atexit );
   849  #ifdef FOSSIL_ENABLE_TH1_HOOKS
   850    /*
   851    ** The TH1 return codes from the hook will be handled as follows:
   852    **
   853    ** TH_OK: The xFunc() and the TH1 notification will both be executed.
   854    **
   855    ** TH_ERROR: The xFunc() will be skipped, the TH1 notification will be
   856    **           skipped.  If the xFunc() is being hooked, the error message
   857    **           will be emitted.
   858    **
   859    ** TH_BREAK: The xFunc() and the TH1 notification will both be skipped.
   860    **
   861    ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be
   862    **            skipped.
   863    **
   864    ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
   865    **              executed.
   866    */
   867    if( !g.isHTTP && !g.fNoThHook ){
   868      rc = Th_CommandHook(pCmd->zName, pCmd->eCmdFlags);
   869    }else{
   870      rc = TH_OK;
   871    }
   872    if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
   873      if( rc==TH_OK || rc==TH_RETURN ){
   874  #endif
   875        pCmd->xFunc();
   876  #ifdef FOSSIL_ENABLE_TH1_HOOKS
   877      }
   878      if( !g.isHTTP && !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
   879        Th_CommandNotify(pCmd->zName, pCmd->eCmdFlags);
   880      }
   881    }
   882  #endif
   883    fossil_exit(0);
   884    /*NOT_REACHED*/
   885    return 0;
   886  }
   887  
   888  /*
   889  ** Print a usage comment and quit
   890  */
   891  void usage(const char *zFormat){
   892    fossil_fatal("Usage: %s %s %s", g.argv[0], g.argv[1], zFormat);
   893  }
   894  
   895  /*
   896  ** Remove n elements from g.argv beginning with the i-th element.
   897  */
   898  static void remove_from_argv(int i, int n){
   899    int j;
   900    for(j=i+n; j<g.argc; i++, j++){
   901      g.argv[i] = g.argv[j];
   902    }
   903    g.argc = i;
   904  }
   905  
   906  
   907  /*
   908  ** Look for a command-line option.  If present, remove it from the
   909  ** argument list and return a pointer to either the flag's name (if
   910  ** hasArg==0), sans leading - or --, or its value (if hasArg==1).
   911  ** Return NULL if the flag is not found.
   912  **
   913  ** zLong is the "long" form of the flag and zShort is the
   914  ** short/abbreviated form (typically a single letter, but it may be
   915  ** longer). zLong must not be NULL, but zShort may be.
   916  **
   917  ** hasArg==0 means the option is a flag.  It is either present or not.
   918  ** hasArg==1 means the option has an argument, in which case a pointer
   919  ** to the argument's value is returned. For zLong, a flag value (if
   920  ** hasValue==1) may either be in the form (--flag=value) or (--flag
   921  ** value). For zShort, only the latter form is accepted.
   922  **
   923  ** If a standalone argument of "--" is encountered in the argument
   924  ** list while searching for the given flag(s), this routine stops
   925  ** searching and NULL is returned.
   926  */
   927  const char *find_option(const char *zLong, const char *zShort, int hasArg){
   928    int i;
   929    int nLong;
   930    const char *zReturn = 0;
   931    assert( hasArg==0 || hasArg==1 );
   932    nLong = strlen(zLong);
   933    for(i=1; i<g.argc; i++){
   934      char *z;
   935      if( i+hasArg >= g.argc ) break;
   936      z = g.argv[i];
   937      if( z[0]!='-' ) continue;
   938      z++;
   939      if( z[0]=='-' ){
   940        if( z[1]==0 ){
   941          /* Stop processing at "--" */
   942          break;
   943        }
   944        z++;
   945      }
   946      if( strncmp(z,zLong,nLong)==0 ){
   947        if( hasArg && z[nLong]=='=' ){
   948          zReturn = &z[nLong+1];
   949          remove_from_argv(i, 1);
   950          break;
   951        }else if( z[nLong]==0 ){
   952          zReturn = g.argv[i+hasArg];
   953          remove_from_argv(i, 1+hasArg);
   954          break;
   955        }
   956      }else if( fossil_strcmp(z,zShort)==0 ){
   957        zReturn = g.argv[i+hasArg];
   958        remove_from_argv(i, 1+hasArg);
   959        break;
   960      }
   961    }
   962    return zReturn;
   963  }
   964  
   965  /* Return true if zOption exists in the command-line arguments,
   966  ** but do not remove it from the list or otherwise process it.
   967  */
   968  int has_option(const char *zOption){
   969    int i;
   970    int n = (int)strlen(zOption);
   971    for(i=1; i<g.argc; i++){
   972      char *z = g.argv[i];
   973      if( z[0]!='-' ) continue;
   974      z++;
   975      if( z[0]=='-' ){
   976        z++;
   977        if( z[0]==0 ){
   978          /* Stop searching at "--" */
   979          break;
   980        }
   981      }
   982      if( strncmp(z,zOption,n)==0 && (z[n]==0 || z[n]=='=') ) return 1;
   983    }
   984    return 0;
   985  }
   986  
   987  /*
   988  ** Look for multiple occurrences of a command-line option with the
   989  ** corresponding argument.
   990  **
   991  ** Return a malloc allocated array of pointers to the arguments.
   992  **
   993  ** pnUsedArgs is used to store the number of matched arguments.
   994  **
   995  ** Caller is responsible for freeing allocated memory by passing the
   996  ** head of the array (not each entry) to fossil_free(). (The
   997  ** individual entries have the same lifetime as values returned from
   998  ** find_option().)
   999  */
  1000  const char **find_repeatable_option(
  1001    const char *zLong,
  1002    const char *zShort,
  1003    int *pnUsedArgs
  1004  ){
  1005    const char *zOption;
  1006    const char **pzArgs = 0;
  1007    int nAllocArgs = 0;
  1008    int nUsedArgs = 0;
  1009  
  1010    while( (zOption = find_option(zLong, zShort, 1))!=0 ){
  1011      if( pzArgs==0 && nAllocArgs==0 ){
  1012        nAllocArgs = 1;
  1013        pzArgs = fossil_malloc( nAllocArgs*sizeof(pzArgs[0]) );
  1014      }else if( nAllocArgs<=nUsedArgs ){
  1015        nAllocArgs = nAllocArgs*2;
  1016        pzArgs = fossil_realloc( (void *)pzArgs, nAllocArgs*sizeof(pzArgs[0]) );
  1017      }
  1018      pzArgs[nUsedArgs++] = zOption;
  1019    }
  1020    *pnUsedArgs = nUsedArgs;
  1021    return pzArgs;
  1022  }
  1023  
  1024  /*
  1025  ** Look for a repository command-line option.  If present, [re-]cache it in
  1026  ** the global state and return the new pointer, freeing any previous value.
  1027  ** If absent and there is no cached value, return NULL.
  1028  */
  1029  const char *find_repository_option(){
  1030    const char *zRepository = find_option("repository", "R", 1);
  1031    if( zRepository ){
  1032      if( g.zRepositoryOption ) fossil_free(g.zRepositoryOption);
  1033      g.zRepositoryOption = mprintf("%s", zRepository);
  1034    }
  1035    return g.zRepositoryOption;
  1036  }
  1037  
  1038  /*
  1039  ** Verify that there are no unprocessed command-line options.  If
  1040  ** Any remaining command-line argument begins with "-" print
  1041  ** an error message and quit.
  1042  **
  1043  ** If fAllowDoubleDash is true then if the flag "--" is found, it is
  1044  ** removed from the list and arguments after that flag are not
  1045  ** inspected by this function (they are assumed to be
  1046  ** file/wiki/branch/etc. names, even if they syntactically look like
  1047  ** flags). If fAllowDoubleDash is false then the "--" flag will
  1048  ** trigger a fatal error exactly as if an unprocessed flag were
  1049  ** encountered.
  1050  **
  1051  ** Returns false (0) if fAllowDoubleDash is false or if "--" is not
  1052  ** encountered. If fAllowDoubleDash is true and "--" is encountered,
  1053  ** the argument index (in g.argv) at which "--" was encountered (and
  1054  ** removed) is returned (that value will always be greater than 0).
  1055  **
  1056  ** Sidebar: the question of whether fAllowDoubleDash should be true or
  1057  ** false would seem to boil down to: does the calling routine
  1058  ** expect/allow arbitrary file/page/branch/whatever name arguments
  1059  ** after its required arguments?
  1060  */
  1061  static int verify_all_options_impl(int fAllowDoubleDash){
  1062    int i;
  1063    g.argDashDashIndex = 0;
  1064    for(i=1; i<g.argc; i++){
  1065      const char * arg = g.argv[i];
  1066      if( arg[0]=='-' ){
  1067        if( arg[1]=='-' && arg[2]==0 ){
  1068          if(fAllowDoubleDash){
  1069            /* Remove "--" from the list and assume any following
  1070            ** arguments are file names. */
  1071            remove_from_argv(i, 1);
  1072            return g.argDashDashIndex = i;
  1073          }else{
  1074            fossil_fatal("The -- flag is not allowed here.");
  1075          }
  1076        }else if( arg[1]!=0 ){
  1077          fossil_fatal(
  1078            "unrecognized command-line option, or missing argument: %s",
  1079            arg);
  1080        }
  1081      }
  1082    }
  1083    return 0;
  1084  }
  1085  
  1086  /*
  1087  ** Must be called by all commands which process CLI flags, after
  1088  ** consuming those flags (via find_option() and friends), to confirm
  1089  ** that no unconsumed flags are still in the arguments list.  If the
  1090  ** command should/can honor the "--" flag, call verify_all_options2()
  1091  ** instead.
  1092  */
  1093  void verify_all_options(void){
  1094    verify_all_options_impl(0);
  1095  }
  1096  
  1097  /*
  1098  ** Identical to verify_all_options() except that it honors the "--"
  1099  ** flag and returns true (non-0) if that flag was encountered/consumed.
  1100  */
  1101  int verify_all_options2(void){
  1102    return verify_all_options_impl(1);
  1103  }
  1104  
  1105  /*
  1106  ** Expects nArgPos to be a valid index of g.argv. If nArgPos is a
  1107  ** string with the value "-" and g.argDashDashIndex is greater than 0
  1108  ** and <= nArgPos then the value "-" gets transformed to "./-". In all
  1109  ** other cases, the value of g.argv[nArgPos] is returned as-is.
  1110  **
  1111  ** The intention is that this function be called by commands which
  1112  ** accept a filename argument for which "-" is interpreted as stdin or
  1113  ** stdout. If the "--" flag was found BEFORE that filename flag then
  1114  ** "-" is transformed such that it will be seen as a file named "./-"
  1115  ** rather than "-" (stdin or stdout, depending on the context). Returns
  1116  ** a string from g.argv or the static string "./-", so its lifetime is
  1117  ** effectively that of the app.
  1118  */
  1119  const char * get_dash_filename_arg(int nArgPos){
  1120    const char * zName;
  1121    assert(nArgPos < g.argc);
  1122    zName = g.argv[nArgPos];
  1123    if(zName!=0
  1124       && g.argDashDashIndex>0 && g.argDashDashIndex<=nArgPos
  1125       && fossil_strcmp("-",zName)==0){
  1126      zName = "./-";
  1127    }
  1128    return zName;
  1129  };
  1130  
  1131  /*
  1132   ** Only for debugging during "--"-related refactoring. To be deleted
  1133   ** before merging with trunk.
  1134   */
  1135  void dump_g_argv(){
  1136    int i;
  1137    for( i = 0; i < g.argc; ++i ){
  1138      fossil_print("\tg.argv[%d] = %s\n", i, g.argv[i]);
  1139    }
  1140  }
  1141  
  1142  /*
  1143  ** This function returns a human readable version string.
  1144  */
  1145  const char *get_version(){
  1146    static const char version[] = RELEASE_VERSION " " MANIFEST_VERSION " "
  1147                                  MANIFEST_DATE " UTC";
  1148    return version;
  1149  }
  1150  
  1151  /*
  1152  ** This function populates a blob with version information.  It is used by
  1153  ** the "version" command and "test-version" web page.  It assumes the blob
  1154  ** passed to it is uninitialized; otherwise, it will leak memory.
  1155  */
  1156  static void get_version_blob(
  1157    Blob *pOut,                 /* Write the manifest here */
  1158    int bVerbose                /* Non-zero for full information. */
  1159  ){
  1160  #if defined(FOSSIL_ENABLE_TCL)
  1161    int rc;
  1162    const char *zRc;
  1163  #endif
  1164    Stmt q;
  1165    size_t pageSize = 0;
  1166    blob_zero(pOut);
  1167    blob_appendf(pOut, "This is fossil version %s\n", get_version());
  1168    if( !bVerbose ) return;
  1169    blob_appendf(pOut, "Compiled on %s %s using %s (%d-bit)\n",
  1170                 __DATE__, __TIME__, COMPILER_NAME, sizeof(void*)*8);
  1171    blob_appendf(pOut, "Schema version %s\n", AUX_SCHEMA_MAX);
  1172    fossil_get_page_size(&pageSize);
  1173    blob_appendf(pOut, "Detected memory page size is %lu bytes\n",
  1174                 (unsigned long)pageSize);
  1175  #if defined(FOSSIL_ENABLE_MINIZ)
  1176    blob_appendf(pOut, "miniz %s, loaded %s\n", MZ_VERSION, mz_version());
  1177  #else
  1178    blob_appendf(pOut, "zlib %s, loaded %s\n", ZLIB_VERSION, zlibVersion());
  1179  #endif
  1180  #if FOSSIL_HARDENED_SHA1
  1181    blob_appendf(pOut, "hardened-SHA1 by Marc Stevens and Dan Shumow\n");
  1182  #endif
  1183  #if defined(FOSSIL_ENABLE_SSL)
  1184    blob_appendf(pOut, "SSL (%s)\n", SSLeay_version(SSLEAY_VERSION));
  1185  #endif
  1186  #if defined(FOSSIL_HAVE_FUSEFS)
  1187    blob_appendf(pOut, "libfuse %s, loaded %s\n", fusefs_inc_version(),
  1188                 fusefs_lib_version());
  1189  #endif
  1190  #if defined(FOSSIL_DEBUG)
  1191    blob_append(pOut, "FOSSIL_DEBUG\n", -1);
  1192  #endif
  1193  #if defined(FOSSIL_ENABLE_DELTA_CKSUM_TEST)
  1194    blob_append(pOut, "FOSSIL_ENABLE_DELTA_CKSUM_TEST\n", -1);
  1195  #endif
  1196  #if defined(FOSSIL_ENABLE_LEGACY_MV_RM)
  1197    blob_append(pOut, "FOSSIL_ENABLE_LEGACY_MV_RM\n", -1);
  1198  #endif
  1199  #if defined(FOSSIL_ENABLE_EXEC_REL_PATHS)
  1200    blob_append(pOut, "FOSSIL_ENABLE_EXEC_REL_PATHS\n", -1);
  1201  #endif
  1202  #if defined(FOSSIL_ENABLE_TH1_DOCS)
  1203    blob_append(pOut, "FOSSIL_ENABLE_TH1_DOCS\n", -1);
  1204  #endif
  1205  #if defined(FOSSIL_ENABLE_TH1_HOOKS)
  1206    blob_append(pOut, "FOSSIL_ENABLE_TH1_HOOKS\n", -1);
  1207  #endif
  1208  #if defined(FOSSIL_ENABLE_TCL)
  1209    Th_FossilInit(TH_INIT_DEFAULT | TH_INIT_FORCE_TCL);
  1210    rc = Th_Eval(g.interp, 0, "tclInvoke info patchlevel", -1);
  1211    zRc = Th_ReturnCodeName(rc, 0);
  1212    blob_appendf(pOut, "TCL (Tcl %s, loaded %s: %s)\n",
  1213      TCL_PATCH_LEVEL, zRc, Th_GetResult(g.interp, 0)
  1214    );
  1215  #endif
  1216  #if defined(USE_TCL_STUBS)
  1217    blob_append(pOut, "USE_TCL_STUBS\n", -1);
  1218  #endif
  1219  #if defined(FOSSIL_ENABLE_TCL_STUBS)
  1220    blob_append(pOut, "FOSSIL_TCL_STUBS\n", -1);
  1221  #endif
  1222  #if defined(FOSSIL_ENABLE_TCL_PRIVATE_STUBS)
  1223    blob_append(pOut, "FOSSIL_ENABLE_TCL_PRIVATE_STUBS\n", -1);
  1224  #endif
  1225  #if defined(FOSSIL_ENABLE_JSON)
  1226    blob_appendf(pOut, "JSON (API %s)\n", FOSSIL_JSON_API_VERSION);
  1227  #endif
  1228  #if defined(BROKEN_MINGW_CMDLINE)
  1229    blob_append(pOut, "MBCS_COMMAND_LINE\n", -1);
  1230  #else
  1231    blob_append(pOut, "UNICODE_COMMAND_LINE\n", -1);
  1232  #endif
  1233  #if defined(FOSSIL_DYNAMIC_BUILD)
  1234    blob_append(pOut, "FOSSIL_DYNAMIC_BUILD\n", -1);
  1235  #else
  1236    blob_append(pOut, "FOSSIL_STATIC_BUILD\n", -1);
  1237  #endif
  1238  #if defined(HAVE_PLEDGE)
  1239    blob_append(pOut, "HAVE_PLEDGE\n", -1);
  1240  #endif
  1241  #if defined(USE_MMAN_H)
  1242    blob_append(pOut, "USE_MMAN_H\n", -1);
  1243  #endif
  1244  #if defined(USE_SEE)
  1245    blob_append(pOut, "USE_SEE\n", -1);
  1246  #endif
  1247  #if defined(FOSSIL_ALLOW_OUT_OF_ORDER_DATES)
  1248    blob_append(pOut, "FOSSIL_ALLOW_OUT_OF_ORDER_DATES\n");
  1249  #endif
  1250    blob_appendf(pOut, "SQLite %s %.30s\n", sqlite3_libversion(),
  1251                 sqlite3_sourceid());
  1252    if( g.db==0 ) sqlite3_open(":memory:", &g.db);
  1253    db_prepare(&q,
  1254       "pragma compile_options");
  1255    while( db_step(&q)==SQLITE_ROW ){
  1256      const char *text = db_column_text(&q, 0);
  1257      if( strncmp(text, "COMPILER", 8) ){
  1258        blob_appendf(pOut, "SQLITE_%s\n", text);
  1259      }
  1260    }
  1261    db_finalize(&q);
  1262  }
  1263  
  1264  /*
  1265  ** This function returns the user-agent string for Fossil, for
  1266  ** use in HTTP(S) requests.
  1267  */
  1268  const char *get_user_agent(){
  1269    static const char version[] = "Fossil/" RELEASE_VERSION " (" MANIFEST_DATE
  1270                                  " " MANIFEST_VERSION ")";
  1271    return version;
  1272  }
  1273  
  1274  
  1275  /*
  1276  ** COMMAND: version
  1277  **
  1278  ** Usage: %fossil version ?-verbose|-v?
  1279  **
  1280  ** Print the source code version number for the fossil executable.
  1281  ** If the verbose option is specified, additional details will
  1282  ** be output about what optional features this binary was compiled
  1283  ** with
  1284  */
  1285  void version_cmd(void){
  1286    Blob versionInfo;
  1287    int verboseFlag = find_option("verbose","v",0)!=0;
  1288  
  1289    /* We should be done with options.. */
  1290    verify_all_options();
  1291    get_version_blob(&versionInfo, verboseFlag);
  1292    fossil_print("%s", blob_str(&versionInfo));
  1293  }
  1294  
  1295  
  1296  /*
  1297  ** WEBPAGE: version
  1298  **
  1299  ** Show the version information for Fossil.
  1300  **
  1301  ** Query parameters:
  1302  **
  1303  **    verbose       Show details
  1304  */
  1305  void test_version_page(void){
  1306    Blob versionInfo;
  1307    int verboseFlag;
  1308  
  1309    login_check_credentials();
  1310    if( !g.perm.Read ){ login_needed(g.anon.Read); return; }
  1311    verboseFlag = PD("verbose", 0) != 0;
  1312    style_header("Version Information");
  1313    style_submenu_element("Stat", "stat");
  1314    get_version_blob(&versionInfo, verboseFlag);
  1315    @ <pre>
  1316    @ %h(blob_str(&versionInfo))
  1317    @ </pre>
  1318    style_footer();
  1319  }
  1320  
  1321  
  1322  /*
  1323  ** Set the g.zBaseURL value to the full URL for the toplevel of
  1324  ** the fossil tree.  Set g.zTop to g.zBaseURL without the
  1325  ** leading "http://" and the host and port.
  1326  **
  1327  ** The g.zBaseURL is normally set based on HTTP_HOST and SCRIPT_NAME
  1328  ** environment variables.  However, if zAltBase is not NULL then it
  1329  ** is the argument to the --baseurl option command-line option and
  1330  ** g.zBaseURL and g.zTop is set from that instead.
  1331  */
  1332  void set_base_url(const char *zAltBase){
  1333    int i;
  1334    const char *zHost;
  1335    const char *zMode;
  1336    const char *zCur;
  1337  
  1338    if( g.zBaseURL!=0 ) return;
  1339    if( zAltBase ){
  1340      int i, n, c;
  1341      g.zTop = g.zBaseURL = mprintf("%s", zAltBase);
  1342      if( strncmp(g.zTop, "http://", 7)==0 ){
  1343        /* it is HTTP, replace prefix with HTTPS. */
  1344        g.zHttpsURL = mprintf("https://%s", &g.zTop[7]);
  1345      }else if( strncmp(g.zTop, "https://", 8)==0 ){
  1346        /* it is already HTTPS, use it. */
  1347        g.zHttpsURL = mprintf("%s", g.zTop);
  1348      }else{
  1349        fossil_fatal("argument to --baseurl should be 'http://host/path'"
  1350                     " or 'https://host/path'");
  1351      }
  1352      for(i=n=0; (c = g.zTop[i])!=0; i++){
  1353        if( c=='/' ){
  1354          n++;
  1355          if( n==3 ){
  1356            g.zTop += i;
  1357            break;
  1358          }
  1359        }
  1360      }
  1361      if( g.zTop==g.zBaseURL ){
  1362        fossil_fatal("argument to --baseurl should be 'http://host/path'"
  1363                     " or 'https://host/path'");
  1364      }
  1365      if( g.zTop[1]==0 ) g.zTop++;
  1366    }else{
  1367      zHost = PD("HTTP_HOST","");
  1368      zMode = PD("HTTPS","off");
  1369      zCur = PD("SCRIPT_NAME","/");
  1370      i = strlen(zCur);
  1371      while( i>0 && zCur[i-1]=='/' ) i--;
  1372      if( fossil_stricmp(zMode,"on")==0 ){
  1373        g.zBaseURL = mprintf("https://%s%.*s", zHost, i, zCur);
  1374        g.zTop = &g.zBaseURL[8+strlen(zHost)];
  1375        g.zHttpsURL = g.zBaseURL;
  1376      }else{
  1377        g.zBaseURL = mprintf("http://%s%.*s", zHost, i, zCur);
  1378        g.zTop = &g.zBaseURL[7+strlen(zHost)];
  1379        g.zHttpsURL = mprintf("https://%s%.*s", zHost, i, zCur);
  1380      }
  1381    }
  1382    if( db_is_writeable("repository") ){
  1383      if( !db_exists("SELECT 1 FROM config WHERE name='baseurl:%q'", g.zBaseURL)){
  1384        db_multi_exec("INSERT INTO config(name,value,mtime)"
  1385                      "VALUES('baseurl:%q',1,now())", g.zBaseURL);
  1386      }else{
  1387        db_optional_sql("repository",
  1388             "REPLACE INTO config(name,value,mtime)"
  1389             "VALUES('baseurl:%q',1,now())", g.zBaseURL
  1390        );
  1391      }
  1392    }
  1393  }
  1394  
  1395  /*
  1396  ** Send an HTTP redirect back to the designated Index Page.
  1397  */
  1398  NORETURN void fossil_redirect_home(void){
  1399    cgi_redirectf("%s%s", g.zTop, db_get("index-page", "/index"));
  1400  }
  1401  
  1402  /*
  1403  ** If running as root, chroot to the directory containing the
  1404  ** repository zRepo and then drop root privileges.  Return the
  1405  ** new repository name.
  1406  **
  1407  ** zRepo might be a directory itself.  In that case chroot into
  1408  ** the directory zRepo.
  1409  **
  1410  ** Assume the user-id and group-id of the repository, or if zRepo
  1411  ** is a directory, of that directory.
  1412  **
  1413  ** The noJail flag means that the chroot jail is not entered.  But
  1414  ** privileges are still lowered to that of the user-id and group-id
  1415  ** of the repository file.
  1416  */
  1417  char *enter_chroot_jail(char *zRepo, int noJail){
  1418  #if !defined(_WIN32)
  1419    if( getuid()==0 ){
  1420      int i;
  1421      struct stat sStat;
  1422      Blob dir;
  1423      char *zDir;
  1424      if( g.db!=0 ){
  1425        db_close(1);
  1426      }
  1427  
  1428      file_canonical_name(zRepo, &dir, 0);
  1429      zDir = blob_str(&dir);
  1430      if( !noJail ){
  1431        if( file_isdir(zDir, ExtFILE)==1 ){
  1432          if( file_chdir(zDir, 1) ){
  1433            fossil_panic("unable to chroot into %s", zDir);
  1434          }
  1435          g.fJail = 1;
  1436          zRepo = "/";
  1437        }else{
  1438          for(i=strlen(zDir)-1; i>0 && zDir[i]!='/'; i--){}
  1439          if( zDir[i]!='/' ) fossil_panic("bad repository name: %s", zRepo);
  1440          if( i>0 ){
  1441            zDir[i] = 0;
  1442            if( file_chdir(zDir, 1) ){
  1443              fossil_panic("unable to chroot into %s", zDir);
  1444            }
  1445            zDir[i] = '/';
  1446          }
  1447          zRepo = &zDir[i];
  1448        }
  1449      }
  1450      if( stat(zRepo, &sStat)!=0 ){
  1451        fossil_fatal("cannot stat() repository: %s", zRepo);
  1452      }
  1453      i = setgid(sStat.st_gid);
  1454      i = i || setuid(sStat.st_uid);
  1455      if(i){
  1456        fossil_fatal("setgid/uid() failed with errno %d", errno);
  1457      }
  1458      if( g.db==0 && file_isfile(zRepo, ExtFILE) ){
  1459        db_open_repository(zRepo);
  1460      }
  1461    }
  1462  #endif
  1463    return zRepo;
  1464  }
  1465  
  1466  /*
  1467  ** Called whenever a crash is encountered while processing a webpage.
  1468  */
  1469  void sigsegv_handler(int x){
  1470  #if HAVE_BACKTRACE
  1471    void *array[20];
  1472    size_t size;
  1473    char **strings;
  1474    size_t i;
  1475    Blob out;
  1476    size = backtrace(array, sizeof(array)/sizeof(array[0]));
  1477    strings = backtrace_symbols(array, size);
  1478    blob_init(&out, 0, 0);
  1479    blob_appendf(&out, "Segfault");
  1480    for(i=0; i<size; i++){
  1481      blob_appendf(&out, "\n(%d) %s", i, strings[i]);
  1482    }
  1483    fossil_panic("%s", blob_str(&out));
  1484  #else
  1485    fossil_panic("Segfault");
  1486  #endif
  1487    exit(1);
  1488  }
  1489  
  1490  /*
  1491  ** Called if a server gets a SIGPIPE.  This often happens when a client
  1492  ** webbrowser opens a connection but never sends the HTTP request
  1493  */
  1494  void sigpipe_handler(int x){
  1495  #ifndef _WIN32
  1496    if( g.fAnyTrace ){
  1497      fprintf(stderr,"/***** sigpipe received by subprocess %d ****\n", getpid());
  1498    }
  1499  #endif
  1500    db_panic_close();
  1501    exit(1);
  1502  }
  1503  
  1504  /*
  1505  ** Return true if it is appropriate to redirect requests to HTTPS.
  1506  **
  1507  ** Redirect to https is appropriate if all of the above are true:
  1508  **    (1) The redirect-to-https flag has a valud of iLevel or greater.
  1509  **    (2) The current connection is http, not https or ssh
  1510  **    (3) The sslNotAvailable flag is clear
  1511  */
  1512  int fossil_wants_https(int iLevel){
  1513    if( g.sslNotAvailable ) return 0;
  1514    if( db_get_int("redirect-to-https",0)<iLevel ) return 0;
  1515    if( P("HTTPS")!=0 ) return 0;
  1516    return 1;
  1517  }
  1518  
  1519  /*
  1520  ** Redirect to the equivalent HTTPS request if the current connection is
  1521  ** insecure and if the redirect-to-https flag greater than or equal to 
  1522  ** iLevel.  iLevel is 1 for /login pages and 2 for every other page.
  1523  */
  1524  int fossil_redirect_to_https_if_needed(int iLevel){
  1525    if( fossil_wants_https(iLevel) ){
  1526      const char *zQS = P("QUERY_STRING");
  1527      char *zURL;
  1528      if( zQS==0 || zQS[0]==0 ){
  1529        zURL = mprintf("%s%T", g.zHttpsURL, P("PATH_INFO"));
  1530      }else if( zQS[0]!=0 ){
  1531        zURL = mprintf("%s%T?%s", g.zHttpsURL, P("PATH_INFO"), zQS);
  1532      }
  1533      cgi_redirect_with_status(zURL, 301, "Moved Permanently");
  1534      return 1;
  1535    }
  1536    return 0;
  1537  }
  1538  
  1539  /*
  1540  ** Preconditions:
  1541  **
  1542  **  * Environment variables are set up according to the CGI standard.
  1543  **
  1544  ** If the repository is known, it has already been opened.  If unknown,
  1545  ** then g.zRepositoryName holds the directory that contains the repository
  1546  ** and the actual repository is taken from the first element of PATH_INFO.
  1547  **
  1548  ** Process the webpage specified by the PATH_INFO or REQUEST_URI
  1549  ** environment variable.
  1550  **
  1551  ** If the repository is not known, then a search is done through the
  1552  ** file hierarchy rooted at g.zRepositoryName for a suitable repository
  1553  ** with a name of $prefix.fossil, where $prefix is any prefix of PATH_INFO.
  1554  ** Or, if an ordinary file named $prefix is found, and $prefix matches
  1555  ** pFileGlob and $prefix does not match "*.fossil*" and the mimetype of
  1556  ** $prefix can be determined from its suffix, then the file $prefix is
  1557  ** returned as static text.
  1558  **
  1559  ** If no suitable webpage is found, try to redirect to zNotFound.
  1560  */
  1561  static void process_one_web_page(
  1562    const char *zNotFound,      /* Redirect here on a 404 if not NULL */
  1563    Glob *pFileGlob,            /* Deliver static files matching */
  1564    int allowRepoList           /* Send repo list for "/" URL */
  1565  ){
  1566    const char *zPathInfo = PD("PATH_INFO", "");
  1567    char *zPath = NULL;
  1568    int i;
  1569    const CmdOrPage *pCmd = 0;
  1570    const char *zBase = g.zRepositoryName;
  1571  
  1572  #if !defined(_WIN32)
  1573    signal(SIGSEGV, sigsegv_handler);
  1574  #endif
  1575  
  1576    /* Handle universal query parameters */
  1577    if( PB("utc") ){
  1578      g.fTimeFormat = 1;
  1579    }else if( PB("localtime") ){
  1580      g.fTimeFormat = 2;
  1581    }
  1582  
  1583    /* If the repository has not been opened already, then find the
  1584    ** repository based on the first element of PATH_INFO and open it.
  1585    */
  1586    if( !g.repositoryOpen ){
  1587      char *zRepo;               /* Candidate repository name */
  1588      char *zToFree = 0;         /* Malloced memory that needs to be freed */
  1589      const char *zCleanRepo;    /* zRepo with surplus leading "/" removed */
  1590      const char *zOldScript = PD("SCRIPT_NAME", "");  /* Original SCRIPT_NAME */
  1591      char *zNewScript;          /* Revised SCRIPT_NAME after processing */
  1592      int j, k;                  /* Loop variables */
  1593      i64 szFile;                /* File size of the candidate repository */
  1594  
  1595      i = zPathInfo[0]!=0;
  1596      if( fossil_strcmp(g.zRepositoryName, "/")==0 ){
  1597        zBase++;
  1598  #if defined(_WIN32) || defined(__CYGWIN__)
  1599        if( sqlite3_strglob("/[a-zA-Z]:/*", zPathInfo)==0 ) i = 4;
  1600  #endif
  1601      }
  1602      while( 1 ){
  1603        while( zPathInfo[i] && zPathInfo[i]!='/' ){ i++; }
  1604  
  1605        /* The candidate repository name is some prefix of the PATH_INFO
  1606        ** with ".fossil" appended */
  1607        zRepo = zToFree = mprintf("%s%.*s.fossil",zBase,i,zPathInfo);
  1608        if( g.fHttpTrace ){
  1609          @ <!-- Looking for repository named "%h(zRepo)" -->
  1610          fprintf(stderr, "# looking for repository named \"%s\"\n", zRepo);
  1611        }
  1612  
  1613  
  1614        /* For safety -- to prevent an attacker from accessing arbitrary disk
  1615        ** files by sending a maliciously crafted request URI to a public
  1616        ** server -- make sure the repository basename contains no
  1617        ** characters other than alphanumerics, "/", "_", "-", and ".", and
  1618        ** that "-" never occurs immediately after a "/" and that "." is always
  1619        ** surrounded by two alphanumerics.  Any character that does not
  1620        ** satisfy these constraints is converted into "_".
  1621        */
  1622        szFile = 0;
  1623        for(j=strlen(zBase)+1, k=0; zRepo[j] && k<i-1; j++, k++){
  1624          char c = zRepo[j];
  1625          if( fossil_isalnum(c) ) continue;
  1626  #if defined(_WIN32) || defined(__CYGWIN__)
  1627          /* Allow names to begin with "/X:/" on windows */
  1628          if( c==':' && j==2 && sqlite3_strglob("/[a-zA-Z]:/*", zRepo)==0 ){
  1629            continue;
  1630          }
  1631  #endif
  1632          if( c=='/' ) continue;
  1633          if( c=='_' ) continue;
  1634          if( c=='-' && zRepo[j-1]!='/' ) continue;
  1635          if( c=='.' && fossil_isalnum(zRepo[j-1]) && fossil_isalnum(zRepo[j+1])){
  1636            continue;
  1637          }
  1638          /* If we reach this point, it means that the request URI contains
  1639          ** an illegal character or character combination.  Provoke a
  1640          ** "Not Found" error. */
  1641          szFile = 1;
  1642          if( g.fHttpTrace ){
  1643            @ <!-- Unsafe pathname rejected: "%h(zRepo)" -->
  1644            fprintf(stderr, "# unsafe pathname rejected: %s\n", zRepo);
  1645          }
  1646          break;
  1647        }
  1648  
  1649        /* Check to see if a file name zRepo exists.  If a file named zRepo
  1650        ** does not exist, szFile will become -1.  If the file does exist,
  1651        ** then szFile will become zero (for an empty file) or positive.
  1652        ** Special case:  Assume any file with a basename of ".fossil" does
  1653        ** not exist.
  1654        */
  1655        zCleanRepo = file_cleanup_fullpath(zRepo);
  1656        if( szFile==0 && sqlite3_strglob("*/.fossil",zRepo)!=0 ){
  1657          szFile = file_size(zCleanRepo, ExtFILE);
  1658          if( g.fHttpTrace ){
  1659            char zBuf[24];
  1660            sqlite3_snprintf(sizeof(zBuf), zBuf, "%lld", szFile);
  1661            @ <!-- file_size(%h(zCleanRepo)) is %s(zBuf) -->
  1662            fprintf(stderr, "# file_size(%s) = %s\n", zCleanRepo, zBuf);
  1663          }
  1664        }
  1665  
  1666        /* If no file named by zRepo exists, remove the added ".fossil" suffix
  1667        ** and check to see if there is a file or directory with the same
  1668        ** name as the raw PATH_INFO text.
  1669        */
  1670        if( szFile<0 && i>0 ){
  1671          const char *zMimetype;
  1672          assert( fossil_strcmp(&zRepo[j], ".fossil")==0 );
  1673          zRepo[j] = 0;  /* Remove the ".fossil" suffix */
  1674  
  1675          /* The PATH_INFO prefix seen so far is a valid directory.
  1676          ** Continue the loop with the next element of the PATH_INFO */
  1677          if( zPathInfo[i]=='/' && file_isdir(zCleanRepo, ExtFILE)==1 ){
  1678            fossil_free(zToFree);
  1679            i++;
  1680            continue;
  1681          }
  1682  
  1683          /* If zRepo is the name of an ordinary file that matches the
  1684          ** "--file GLOB" pattern, then the CGI reply is the text of
  1685          ** of the file.
  1686          **
  1687          ** For safety, do not allow any file whose name contains ".fossil"
  1688          ** to be returned this way, to prevent complete repositories from
  1689          ** being delivered accidently.  This is not intended to be a
  1690          ** general-purpose web server.  The "--file GLOB" mechanism is
  1691          ** designed to allow the delivery of a few static images or HTML
  1692          ** pages.
  1693          */
  1694          if( pFileGlob!=0
  1695           && file_isfile(zCleanRepo, ExtFILE)
  1696           && glob_match(pFileGlob, file_cleanup_fullpath(zRepo))
  1697           && sqlite3_strglob("*.fossil*",zRepo)!=0
  1698           && (zMimetype = mimetype_from_name(zRepo))!=0
  1699           && strcmp(zMimetype, "application/x-fossil-artifact")!=0
  1700          ){
  1701            Blob content;
  1702            blob_read_from_file(&content, file_cleanup_fullpath(zRepo), ExtFILE);
  1703            cgi_set_content_type(zMimetype);
  1704            cgi_set_content(&content);
  1705            cgi_reply();
  1706            return;
  1707          }
  1708          zRepo[j] = '.';
  1709        }
  1710  
  1711        /* If we reach this point, it means that the search of the PATH_INFO
  1712        ** string is finished.  Either zRepo contains the name of the
  1713        ** repository to be used, or else no repository could be found an
  1714        ** some kind of error response is required.
  1715        */
  1716        if( szFile<1024 ){
  1717          set_base_url(0);
  1718          if( (zPathInfo[0]==0 || strcmp(zPathInfo,"/")==0)
  1719                    && allowRepoList
  1720                    && repo_list_page() ){
  1721            /* Will return a list of repositories */
  1722          }else if( zNotFound ){
  1723            cgi_redirect(zNotFound);
  1724          }else{
  1725  #ifdef FOSSIL_ENABLE_JSON
  1726            if(g.json.isJsonMode){
  1727              json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
  1728              return;
  1729            }
  1730  #endif
  1731            @ <html><head>
  1732            @ <meta name="viewport" \
  1733            @ content="width=device-width, initial-scale=1.0">
  1734            @ </head><body>
  1735            @ <h1>Not Found</h1>
  1736            @ </body>
  1737            cgi_set_status(404, "not found");
  1738            cgi_reply();
  1739          }
  1740          return;
  1741        }
  1742        break;
  1743      }
  1744  
  1745      /* Add the repository name (without the ".fossil" suffix) to the end
  1746      ** of SCRIPT_NAME and g.zTop and g.zBaseURL and remove the repository
  1747      ** name from the beginning of PATH_INFO.
  1748      */
  1749      zNewScript = mprintf("%s%.*s", zOldScript, i, zPathInfo);
  1750      if( g.zTop ) g.zTop = mprintf("%s%.*s", g.zTop, i, zPathInfo);
  1751      if( g.zBaseURL ) g.zBaseURL = mprintf("%s%.*s", g.zBaseURL, i, zPathInfo);
  1752      cgi_replace_parameter("PATH_INFO", &zPathInfo[i+1]);
  1753      zPathInfo += i;
  1754      cgi_replace_parameter("SCRIPT_NAME", zNewScript);
  1755      db_open_repository(file_cleanup_fullpath(zRepo));
  1756      if( g.fHttpTrace ){
  1757        @ <!-- repository: "%h(zRepo)" -->
  1758        @ <!-- translated PATH_INFO: "%h(zPathInfo)" -->
  1759        @ <!-- translated SCRIPT_NAME: "%h(zNewScript)" -->
  1760        fprintf(stderr,
  1761            "# repository: [%s]\n"
  1762            "# translated PATH_INFO = [%s]\n"
  1763            "# translated SCRIPT_NAME = [%s]\n",
  1764            zRepo, zPathInfo, zNewScript);
  1765        if( g.zTop ){
  1766          @ <!-- translated g.zTop: "%h(g.zTop)" -->
  1767          fprintf(stderr, "# translated g.zTop = [%s]\n", g.zTop);
  1768        }
  1769        if( g.zBaseURL ){
  1770          @ <!-- translated g.zBaseURL: "%h(g.zBaseURL)" -->
  1771          fprintf(stderr, "# translated g.zBaseURL = [%s]\n", g.zBaseURL);
  1772        }
  1773      }
  1774    }
  1775  
  1776    /* At this point, the appropriate repository database file will have
  1777    ** been opened.
  1778    **
  1779    ** Check to see if the the PATH_INFO begins with "draft[1-9]" and if
  1780    ** so activate the special handling for draft skins
  1781    */
  1782    if( zPathInfo && strncmp(zPathInfo,"/draft",6)==0
  1783     && zPathInfo[6]>='1' && zPathInfo[6]<='9'
  1784     && (zPathInfo[7]=='/' || zPathInfo[7]==0)
  1785    ){
  1786      int iSkin = zPathInfo[6] - '0';
  1787      char *zNewScript;
  1788      skin_use_draft(iSkin);
  1789      zNewScript = mprintf("%T/draft%d", P("SCRIPT_NAME"), iSkin);
  1790      if( g.zTop ) g.zTop = mprintf("%s/draft%d", g.zTop, iSkin);
  1791      if( g.zBaseURL ) g.zBaseURL = mprintf("%s/draft%d", g.zBaseURL, iSkin);
  1792      zPathInfo += 7;
  1793      cgi_replace_parameter("PATH_INFO", zPathInfo);
  1794      cgi_replace_parameter("SCRIPT_NAME", zNewScript);
  1795    }
  1796  
  1797    /* If the content type is application/x-fossil or 
  1798    ** application/x-fossil-debug, then a sync/push/pull/clone is
  1799    ** desired, so default the PATH_INFO to /xfer
  1800    */
  1801    if( g.zContentType &&
  1802        strncmp(g.zContentType, "application/x-fossil", 20)==0 ){
  1803      /* Special case:  If the content mimetype shows that it is "fossil sync"
  1804      ** payload, then pretend that the PATH_INFO is /xfer so that we always
  1805      ** invoke the sync page. */
  1806      zPathInfo = "/xfer";
  1807    }
  1808  
  1809    /* Use the first element of PATH_INFO as the page name
  1810    ** and deliver the appropriate page back to the user.
  1811    */
  1812    set_base_url(0);
  1813    if( fossil_redirect_to_https_if_needed(2) ) return;
  1814    if( zPathInfo==0 || zPathInfo[0]==0
  1815        || (zPathInfo[0]=='/' && zPathInfo[1]==0) ){
  1816      /* Second special case: If the PATH_INFO is blank, issue a redirect to
  1817      ** the home page identified by the "index-page" setting in the repository
  1818      ** CONFIG table, to "/index" if there no "index-page" setting. */
  1819  #ifdef FOSSIL_ENABLE_JSON
  1820      if(g.json.isJsonMode){
  1821        json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,1);
  1822        fossil_exit(0);
  1823      }
  1824  #endif
  1825      fossil_redirect_home() /*does not return*/;
  1826    }else{
  1827      zPath = mprintf("%s", zPathInfo);
  1828    }
  1829  
  1830    /* Make g.zPath point to the first element of the path.  Make
  1831    ** g.zExtra point to everything past that point.
  1832    */
  1833    while(1){
  1834      g.zPath = &zPath[1];
  1835      for(i=1; zPath[i] && zPath[i]!='/'; i++){}
  1836      if( zPath[i]=='/' ){
  1837        zPath[i] = 0;
  1838        g.zExtra = &zPath[i+1];
  1839      }else{
  1840        g.zExtra = 0;
  1841      }
  1842      break;
  1843    }
  1844  #ifdef FOSSIL_ENABLE_JSON
  1845    /*
  1846    ** Workaround to allow us to customize some following behaviour for
  1847    ** JSON mode.  The problem is, we don't always know if we're in JSON
  1848    ** mode at this point (namely, for GET mode we don't know but POST
  1849    ** we do), so we snoop g.zPath and cheat a bit.
  1850    */
  1851    if( !g.json.isJsonMode && g.zPath && (0==strncmp("json",g.zPath,4)) ){
  1852      g.json.isJsonMode = 1;
  1853    }
  1854  #endif
  1855    if( g.zExtra ){
  1856      /* CGI parameters get this treatment elsewhere, but places like getfile
  1857      ** will use g.zExtra directly.
  1858      ** Reminder: the login mechanism uses 'name' differently, and may
  1859      ** eventually have a problem/collision with this.
  1860      **
  1861      ** Disabled by stephan when running in JSON mode because this
  1862      ** particular parameter name is very common and i have had no end
  1863      ** of grief with this handling. The JSON API never relies on the
  1864      ** handling below, and by disabling it in JSON mode I can remove
  1865      ** lots of special-case handling in several JSON handlers.
  1866      */
  1867  #ifdef FOSSIL_ENABLE_JSON
  1868      if(!g.json.isJsonMode){
  1869  #endif
  1870        dehttpize(g.zExtra);
  1871        cgi_set_parameter_nocopy("name", g.zExtra, 1);
  1872  #ifdef FOSSIL_ENABLE_JSON
  1873      }
  1874  #endif
  1875    }
  1876  
  1877    /* Locate the method specified by the path and execute the function
  1878    ** that implements that method.
  1879    */
  1880    if( dispatch_name_search(g.zPath-1, CMDFLAG_WEBPAGE, &pCmd)
  1881     && dispatch_alias(g.zPath-1, &pCmd)
  1882    ){
  1883  #ifdef FOSSIL_ENABLE_JSON
  1884      if(g.json.isJsonMode){
  1885        json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
  1886      }else
  1887  #endif
  1888      {
  1889  #ifdef FOSSIL_ENABLE_TH1_HOOKS
  1890        int rc;
  1891        if( !g.fNoThHook ){
  1892          rc = Th_WebpageHook(g.zPath, 0);
  1893        }else{
  1894          rc = TH_OK;
  1895        }
  1896        if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
  1897          if( rc==TH_OK || rc==TH_RETURN ){
  1898  #endif
  1899            cgi_set_status(404,"Not Found");
  1900            @ <h1>Not Found</h1>
  1901            @ <p>Page not found: %h(g.zPath)</p>
  1902  #ifdef FOSSIL_ENABLE_TH1_HOOKS
  1903          }
  1904          if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
  1905            Th_WebpageNotify(g.zPath, 0);
  1906          }
  1907        }
  1908  #endif
  1909      }
  1910    }else if( pCmd->xFunc!=page_xfer && db_schema_is_outofdate() ){
  1911  #ifdef FOSSIL_ENABLE_JSON
  1912      if(g.json.isJsonMode){
  1913        json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
  1914      }else
  1915  #endif
  1916      {
  1917        @ <h1>Server Configuration Error</h1>
  1918        @ <p>The database schema on the server is out-of-date.  Please ask
  1919        @ the administrator to run <b>fossil rebuild</b>.</p>
  1920      }
  1921    }else{
  1922      if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
  1923        cgi_decode_post_parameters();
  1924      }
  1925      if( g.fCgiTrace ){
  1926        fossil_trace("######## Calling %s #########\n", pCmd->zName);
  1927        cgi_print_all(1, 1);
  1928      }
  1929  #ifdef FOSSIL_ENABLE_TH1_HOOKS
  1930      {
  1931        /*
  1932        ** The TH1 return codes from the hook will be handled as follows:
  1933        **
  1934        ** TH_OK: The xFunc() and the TH1 notification will both be executed.
  1935        **
  1936        ** TH_ERROR: The xFunc() will be skipped, the TH1 notification will be
  1937        **           skipped.  If the xFunc() is being hooked, the error message
  1938        **           will be emitted.
  1939        **
  1940        ** TH_BREAK: The xFunc() and the TH1 notification will both be skipped.
  1941        **
  1942        ** TH_RETURN: The xFunc() will be executed, the TH1 notification will be
  1943        **            skipped.
  1944        **
  1945        ** TH_CONTINUE: The xFunc() will be skipped, the TH1 notification will be
  1946        **              executed.
  1947        */
  1948        int rc;
  1949        if( !g.fNoThHook ){
  1950          rc = Th_WebpageHook(pCmd->zName+1, pCmd->eCmdFlags);
  1951        }else{
  1952          rc = TH_OK;
  1953        }
  1954        if( rc==TH_OK || rc==TH_RETURN || rc==TH_CONTINUE ){
  1955          if( rc==TH_OK || rc==TH_RETURN ){
  1956  #endif
  1957            pCmd->xFunc();
  1958  #ifdef FOSSIL_ENABLE_TH1_HOOKS
  1959          }
  1960          if( !g.fNoThHook && (rc==TH_OK || rc==TH_CONTINUE) ){
  1961            Th_WebpageNotify(pCmd->zName+1, pCmd->eCmdFlags);
  1962          }
  1963        }
  1964      }
  1965  #endif
  1966    }
  1967  
  1968    /* Return the result.
  1969    */
  1970    cgi_reply();
  1971  }
  1972  
  1973  /* If the CGI program contains one or more lines of the form
  1974  **
  1975  **    redirect:  repository-filename  http://hostname/path/%s
  1976  **
  1977  ** then control jumps here.  Search each repository for an artifact ID
  1978  ** or ticket ID that matches the "name" CGI parameter and for the
  1979  ** first match, redirect to the corresponding URL with the "name" CGI
  1980  ** parameter inserted.  Paint an error page if no match is found.
  1981  **
  1982  ** If there is a line of the form:
  1983  **
  1984  **    redirect: * URL
  1985  **
  1986  ** Then a redirect is made to URL if no match is found.  Otherwise a
  1987  ** very primitive error message is returned.
  1988  */
  1989  static void redirect_web_page(int nRedirect, char **azRedirect){
  1990    int i;                             /* Loop counter */
  1991    const char *zNotFound = 0;         /* Not found URL */
  1992    const char *zName = P("name");
  1993    set_base_url(0);
  1994    if( zName==0 ){
  1995      zName = P("SCRIPT_NAME");
  1996      if( zName && zName[0]=='/' ) zName++;
  1997    }
  1998    if( zName && validate16(zName, strlen(zName)) ){
  1999      for(i=0; i<nRedirect; i++){
  2000        if( fossil_strcmp(azRedirect[i*2],"*")==0 ){
  2001          zNotFound = azRedirect[i*2+1];
  2002          continue;
  2003        }
  2004        db_open_repository(azRedirect[i*2]);
  2005        if( db_exists("SELECT 1 FROM blob WHERE uuid GLOB '%q*'", zName) ||
  2006            db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
  2007          cgi_redirectf(azRedirect[i*2+1] /*works-like:"%s"*/, zName);
  2008          return;
  2009        }
  2010        db_close(1);
  2011      }
  2012    }
  2013    if( zNotFound ){
  2014      cgi_redirectf(zNotFound /*works-like:"%s"*/, zName);
  2015    }else{
  2016      @ <html>
  2017      @ <head><title>No Such Object</title></head>
  2018      @ <body>
  2019      @ <p>No such object: <b>%h(zName)</b></p>
  2020      @ </body>
  2021      cgi_reply();
  2022    }
  2023  }
  2024  
  2025  /*
  2026  ** COMMAND: cgi*
  2027  **
  2028  ** Usage: %fossil ?cgi? FILE
  2029  **
  2030  ** This command causes Fossil to generate reply to a CGI request.
  2031  **
  2032  ** The FILE argument is the name of a control file that provides Fossil
  2033  ** with important information such as where to find its repository.  In
  2034  ** a typical CGI deployment, FILE is the name of the CGI script and will
  2035  ** typically look something like this:
  2036  **
  2037  **      #!/usr/bin/fossil
  2038  **      repository: /home/somebody/project.db
  2039  **
  2040  ** The command name, "cgi", may be omitted if the GATEWAY_INTERFACE
  2041  ** environment variable is set to "CGI", which should always be the
  2042  ** case for CGI scripts run by a webserver.  Fossil ignores any lines
  2043  ** that begin with "#".
  2044  **
  2045  ** The following control lines are recognized:
  2046  **
  2047  **    repository: PATH         Name of the Fossil repository
  2048  **
  2049  **    directory:  PATH         Name of a directory containing many Fossil
  2050  **                             repositories whose names all end with ".fossil".
  2051  **                             There should only be one of "repository:"
  2052  **                             or "directory:"
  2053  **
  2054  **    notfound: URL            When in "directory:" mode, redirect to
  2055  **                             URL if no suitable repository is found.
  2056  **
  2057  **    repolist                 When in "directory:" mode, display a page
  2058  **                             showing a list of available repositories if
  2059  **                             the URL is "/".
  2060  **
  2061  **    localauth                Grant administrator privileges to connections
  2062  **                             from 127.0.0.1 or ::1.
  2063  **
  2064  **    skin: LABEL              Use the built-in skin called LABEL rather than
  2065  **                             the default.  If there are no skins called LABEL
  2066  **                             then this line is a no-op.
  2067  **
  2068  **    files: GLOBLIST          GLOBLIST is a comma-separated list of GLOB
  2069  **                             patterns that specify files that can be
  2070  **                             returned verbatim.  This feature allows Fossil
  2071  **                             to act as a web server returning static
  2072  **                             content.
  2073  **
  2074  **    setenv: NAME VALUE       Set environment variable NAME to VALUE.  Or
  2075  **                             if VALUE is omitted, unset NAME.
  2076  **
  2077  **    HOME: PATH               Shorthand for "setenv: HOME PATH"
  2078  **
  2079  **    debug: FILE              Causing debugging information to be written
  2080  **                             into FILE.
  2081  **
  2082  **    errorlog: FILE           Warnings, errors, and panics written to FILE.
  2083  **
  2084  **    timeout: SECONDS         Do not run for longer than SECONDS.  The default
  2085  **                             timeout is FOSSIL_DEFAULT_TIMEOUT (600) seconds.
  2086  **
  2087  **    extroot: DIR             Directory that is the root of the sub-CGI tree
  2088  **                             on the /ext page.
  2089  **
  2090  **    redirect: REPO URL       Extract the "name" query parameter and search
  2091  **                             REPO for a check-in or ticket that matches the
  2092  **                             value of "name", then redirect to URL.  There
  2093  **                             can be multiple "redirect:" lines that are
  2094  **                             processed in order.  If the REPO is "*", then
  2095  **                             an unconditional redirect to URL is taken.
  2096  **
  2097  ** Most CGI files contain only a "repository:" line.  It is uncommon to
  2098  ** use any other option.
  2099  **
  2100  ** See also: http, server, winsrv
  2101  */
  2102  void cmd_cgi(void){
  2103    const char *zFile;
  2104    const char *zNotFound = 0;
  2105    char **azRedirect = 0;             /* List of repositories to redirect to */
  2106    int nRedirect = 0;                 /* Number of entries in azRedirect */
  2107    Glob *pFileGlob = 0;               /* Pattern for files */
  2108    int allowRepoList = 0;             /* Allow lists of repository files */
  2109    Blob config, line, key, value, value2;
  2110    if( g.argc==3 && fossil_strcmp(g.argv[1],"cgi")==0 ){
  2111      zFile = g.argv[2];
  2112    }else{
  2113      zFile = g.argv[1];
  2114    }
  2115    g.httpOut = stdout;
  2116    g.httpIn = stdin;
  2117    fossil_binary_mode(g.httpOut);
  2118    fossil_binary_mode(g.httpIn);
  2119    g.cgiOutput = 1;
  2120    fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
  2121    blob_read_from_file(&config, zFile, ExtFILE);
  2122    while( blob_line(&config, &line) ){
  2123      if( !blob_token(&line, &key) ) continue;
  2124      if( blob_buffer(&key)[0]=='#' ) continue;
  2125      if( blob_eq(&key, "repository:") && blob_tail(&line, &value) ){
  2126        /* repository: FILENAME
  2127        **
  2128        ** The name of the Fossil repository to be served via CGI.  Most
  2129        ** fossil CGI scripts have a single non-comment line that contains
  2130        ** this one entry.
  2131        */
  2132        blob_trim(&value);
  2133        db_open_repository(blob_str(&value));
  2134        blob_reset(&value);
  2135        continue;
  2136      }
  2137      if( blob_eq(&key, "directory:") && blob_token(&line, &value) ){
  2138        /* directory: DIRECTORY
  2139        **
  2140        ** If repository: is omitted, then terms of the PATH_INFO cgi parameter
  2141        ** are appended to DIRECTORY looking for a repository (whose name ends
  2142        ** in ".fossil") or a file in "files:".
  2143        */
  2144        db_close(1);
  2145        g.zRepositoryName = mprintf("%s", blob_str(&value));
  2146        blob_reset(&value);
  2147        continue;
  2148      }
  2149      if( blob_eq(&key, "notfound:") && blob_token(&line, &value) ){
  2150        /* notfound: URL
  2151        **
  2152        ** If using directory: and no suitable repository or file is found,
  2153        ** then redirect to URL.
  2154        */
  2155        zNotFound = mprintf("%s", blob_str(&value));
  2156        blob_reset(&value);
  2157        continue;
  2158      }
  2159      if( blob_eq(&key, "localauth") ){
  2160        /* localauth
  2161        **
  2162        ** Grant "administrator" privileges to users connecting with HTTP
  2163        ** from IP address 127.0.0.1.  Do not bother checking credentials.
  2164        */
  2165        g.useLocalauth = 1;
  2166        continue;
  2167      }
  2168      if( blob_eq(&key, "repolist") ){
  2169        /* repolist
  2170        **
  2171        ** If using "directory:" and the URL is "/" then generate a page
  2172        ** showing a list of available repositories.
  2173        */
  2174        allowRepoList = 1;
  2175        continue;
  2176      }
  2177      if( blob_eq(&key, "redirect:") && blob_token(&line, &value)
  2178              && blob_token(&line, &value2) ){
  2179        /* See the header comment on the redirect_web_page() function
  2180        ** above for details. */
  2181        nRedirect++;
  2182        azRedirect = fossil_realloc(azRedirect, 2*nRedirect*sizeof(char*));
  2183        azRedirect[nRedirect*2-2] = mprintf("%s", blob_str(&value));
  2184        azRedirect[nRedirect*2-1] = mprintf("%s", blob_str(&value2));
  2185        blob_reset(&value);
  2186        blob_reset(&value2);
  2187        continue;
  2188      }
  2189      if( blob_eq(&key, "files:") && blob_token(&line, &value) ){
  2190        /* files: GLOBLIST
  2191        **
  2192        ** GLOBLIST is a comma-separated list of filename globs.  For
  2193        ** example:  *.html,*.css,*.js
  2194        **
  2195        ** If the repository: line is omitted and then PATH_INFO is searched
  2196        ** for files that match any of these GLOBs and if any such file is
  2197        ** found it is returned verbatim.  This feature allows "fossil server"
  2198        ** to function as a primitive web-server delivering arbitrary content.
  2199        */
  2200        pFileGlob = glob_create(blob_str(&value));
  2201        blob_reset(&value);
  2202        continue;
  2203      }
  2204      if( blob_eq(&key, "setenv:") && blob_token(&line, &value) ){
  2205        /* setenv: NAME VALUE
  2206        ** setenv: NAME
  2207        **
  2208        ** Sets environment variable NAME to VALUE.  If VALUE is omitted, then
  2209        ** the environment variable is unset.
  2210        */
  2211        blob_token(&line,&value2);
  2212        fossil_setenv(blob_str(&value), blob_str(&value2));
  2213        blob_reset(&value);
  2214        blob_reset(&value2);
  2215        continue;
  2216      }
  2217      if( blob_eq(&key, "debug:") && blob_token(&line, &value) ){
  2218        /* debug: FILENAME
  2219        **
  2220        ** Causes output from cgi_debug() and CGIDEBUG(()) calls to go
  2221        ** into FILENAME.
  2222        */
  2223        g.fDebug = fossil_fopen(blob_str(&value), "ab");
  2224        blob_reset(&value);
  2225        continue;
  2226      }
  2227      if( blob_eq(&key, "errorlog:") && blob_token(&line, &value) ){
  2228        /* errorlog: FILENAME
  2229        **
  2230        ** Causes messages from warnings, errors, and panics to be appended
  2231        ** to FILENAME.
  2232        */
  2233        g.zErrlog = mprintf("%s", blob_str(&value));
  2234        blob_reset(&value);
  2235        continue;
  2236      }
  2237      if( blob_eq(&key, "extroot:") && blob_token(&line, &value) ){
  2238        /* extroot: DIRECTORY
  2239        **
  2240        ** Enables the /ext webpage to use sub-cgi rooted at DIRECTORY
  2241        */
  2242        g.zExtRoot = mprintf("%s", blob_str(&value));
  2243        blob_reset(&value);
  2244        continue;
  2245      }
  2246      if( blob_eq(&key, "timeout:") && blob_token(&line, &value) ){
  2247        /* timeout: SECONDS
  2248        **
  2249        ** Set an alarm() that kills the process after SECONDS.  The
  2250        ** default value is FOSSIL_DEFAULT_TIMEOUT (600) seconds.
  2251        */
  2252        fossil_set_timeout(atoi(blob_str(&value)));
  2253        continue;
  2254      }
  2255      if( blob_eq(&key, "HOME:") && blob_token(&line, &value) ){
  2256        /* HOME: VALUE
  2257        **
  2258        ** Set CGI parameter "HOME" to VALUE.  This is legacy.  Use
  2259        ** setenv: instead.
  2260        */
  2261        cgi_setenv("HOME", blob_str(&value));
  2262        blob_reset(&value);
  2263        continue;
  2264      }
  2265      if( blob_eq(&key, "skin:") && blob_token(&line, &value) ){
  2266        /* skin: LABEL
  2267        **
  2268        ** Use one of the built-in skins defined by LABEL.  LABEL is the
  2269        ** name of the subdirectory under the skins/ directory that holds
  2270        ** the elements of the built-in skin.  If LABEL does not match,
  2271        ** this directive is a silent no-op.
  2272        */
  2273        skin_use_alternative(blob_str(&value));
  2274        blob_reset(&value);
  2275        continue;
  2276      }
  2277    }
  2278    blob_reset(&config);
  2279    if( g.db==0 && g.zRepositoryName==0 && nRedirect==0 ){
  2280      cgi_panic("Unable to find or open the project repository");
  2281    }
  2282    cgi_init();
  2283    if( nRedirect ){
  2284      redirect_web_page(nRedirect, azRedirect);
  2285    }else{
  2286      process_one_web_page(zNotFound, pFileGlob, allowRepoList);
  2287    }
  2288  }
  2289  
  2290  /*
  2291  ** If g.argv[arg] exists then it is either the name of a repository
  2292  ** that will be used by a server, or else it is a directory that
  2293  ** contains multiple repositories that can be served.  If g.argv[arg]
  2294  ** is a directory, the repositories it contains must be named
  2295  ** "*.fossil".  If g.argv[arg] does not exist, then we must be within
  2296  ** an open check-out and the repository to serve is the repository of
  2297  ** that check-out.
  2298  **
  2299  ** Open the repository to be served if it is known.  If g.argv[arg] is
  2300  ** a directory full of repositories, then set g.zRepositoryName to
  2301  ** the name of that directory and the specific repository will be
  2302  ** opened later by process_one_web_page() based on the content of
  2303  ** the PATH_INFO variable.
  2304  **
  2305  ** If the fCreate flag is set, then create the repository if it
  2306  ** does not already exist. Always use "auto" hash-policy in this case.
  2307  */
  2308  static void find_server_repository(int arg, int fCreate){
  2309    if( g.argc<=arg ){
  2310      db_must_be_within_tree();
  2311    }else{
  2312      const char *zRepo = g.argv[arg];
  2313      int isDir = file_isdir(zRepo, ExtFILE);
  2314      if( isDir==1 ){
  2315        g.zRepositoryName = mprintf("%s", zRepo);
  2316        file_simplify_name(g.zRepositoryName, -1, 0);
  2317      }else{
  2318        if( isDir==0 && fCreate ){
  2319          const char *zPassword;
  2320          db_create_repository(zRepo);
  2321          db_open_repository(zRepo);
  2322          db_begin_transaction();
  2323          g.eHashPolicy = HPOLICY_SHA3;
  2324          db_set_int("hash-policy", HPOLICY_SHA3, 0);
  2325          db_initial_setup(0, "now", g.zLogin);
  2326          db_end_transaction(0);
  2327          fossil_print("project-id: %s\n", db_get("project-code", 0));
  2328          fossil_print("server-id:  %s\n", db_get("server-code", 0));
  2329          zPassword = db_text(0, "SELECT pw FROM user WHERE login=%Q", g.zLogin);
  2330          fossil_print("admin-user: %s (initial password is \"%s\")\n",
  2331                       g.zLogin, zPassword);
  2332          cache_initialize();
  2333          g.zLogin = 0;
  2334          g.userUid = 0;
  2335        }else{
  2336          db_open_repository(zRepo);
  2337        }
  2338      }
  2339    }
  2340  }
  2341  
  2342  #if defined(_WIN32) && USE_SEE
  2343  /*
  2344  ** This function attempts to parse a string value in the following
  2345  ** format:
  2346  **
  2347  **     "%lu:%p:%u"
  2348  **
  2349  ** There are three parts, which must be delimited by colons.  The
  2350  ** first part is an unsigned long integer in base-10 (decimal) format.
  2351  ** The second part is a numerical representation of a native pointer,
  2352  ** in the appropriate implementation defined format.  The third part
  2353  ** is an unsigned integer in base-10 (decimal) format.
  2354  **
  2355  ** If the specified value cannot be parsed, for any reason, a fatal
  2356  ** error will be raised and the process will be terminated.
  2357  */
  2358  void parse_pid_key_value(
  2359    const char *zPidKey, /* The value to be parsed. */
  2360    DWORD *pProcessId,   /* The extracted process identifier. */
  2361    LPVOID *ppAddress,   /* The extracted pointer value. */
  2362    SIZE_T *pnSize       /* The extracted size value. */
  2363  ){
  2364    unsigned int nSize = 0;
  2365    if( sscanf(zPidKey, "%lu:%p:%u", pProcessId, ppAddress, &nSize)==3 ){
  2366      *pnSize = (SIZE_T)nSize;
  2367    }else{
  2368      fossil_fatal("failed to parse pid key");
  2369    }
  2370  }
  2371  #endif
  2372  
  2373  /*
  2374  ** COMMAND: http*
  2375  **
  2376  ** Usage: %fossil http ?REPOSITORY? ?OPTIONS?
  2377  **
  2378  ** Handle a single HTTP request appearing on stdin.  The resulting webpage
  2379  ** is delivered on stdout.  This method is used to launch an HTTP request
  2380  ** handler from inetd, for example.  The argument is the name of the
  2381  ** repository.
  2382  **
  2383  ** If REPOSITORY is a directory that contains one or more repositories,
  2384  ** either directly in REPOSITORY itself or in subdirectories, and
  2385  ** with names of the form "*.fossil" then a prefix of the URL pathname
  2386  ** selects from among the various repositories.  If the pathname does
  2387  ** not select a valid repository and the --notfound option is available,
  2388  ** then the server redirects (HTTP code 302) to the URL of --notfound.
  2389  ** When REPOSITORY is a directory, the pathname must contain only
  2390  ** alphanumerics, "_", "/", "-" and "." and no "-" may occur after a "/"
  2391  ** and every "." must be surrounded on both sides by alphanumerics or else
  2392  ** a 404 error is returned.  Static content files in the directory are
  2393  ** returned if they match comma-separate GLOB pattern specified by --files
  2394  ** and do not match "*.fossil*" and have a well-known suffix.
  2395  **
  2396  ** The --host option can be used to specify the hostname for the server.
  2397  ** The --https option indicates that the request came from HTTPS rather
  2398  ** than HTTP. If --nossl is given, then SSL connections will not be available,
  2399  ** thus also no redirecting from http: to https: will take place.
  2400  **
  2401  ** If the --localauth option is given, then automatic login is performed
  2402  ** for requests coming from localhost, if the "localauth" setting is not
  2403  ** enabled.
  2404  **
  2405  ** Options:
  2406  **   --baseurl URL    base URL (useful with reverse proxies)
  2407  **   --extroot DIR    document root for the /ext extension mechanism
  2408  **   --files GLOB     comma-separate glob patterns for static file to serve
  2409  **   --host NAME      specify hostname of the server
  2410  **   --https          signal a request coming in via https
  2411  **   --in FILE        Take input from FILE instead of standard input
  2412  **   --ipaddr ADDR    Assume the request comes from the given IP address
  2413  **   --localauth      enable automatic login for local connections
  2414  **   --nocompress     do not compress HTTP replies
  2415  **   --nodelay        omit backoffice processing if it would delay process exit
  2416  **   --nojail         drop root privilege but do not enter the chroot jail
  2417  **   --nossl          signal that no SSL connections are available
  2418  **   --notfound URL   use URL as "HTTP 404, object not found" page.
  2419  **   --out FILE       write results to FILE instead of to standard output
  2420  **   --repolist       If REPOSITORY is directory, URL "/" lists all repos
  2421  **   --scgi           Interpret input as SCGI rather than HTTP
  2422  **   --skin LABEL     Use override skin LABEL
  2423  **   --th-trace       trace TH1 execution (for debugging purposes)
  2424  **   --usepidkey      Use saved encryption key from parent process.  This is
  2425  **                    only necessary when using SEE on Windows.
  2426  **
  2427  ** See also: cgi, server, winsrv
  2428  */
  2429  void cmd_http(void){
  2430    const char *zIpAddr = 0;
  2431    const char *zNotFound;
  2432    const char *zHost;
  2433    const char *zAltBase;
  2434    const char *zFileGlob;
  2435    const char *zInFile;
  2436    const char *zOutFile;
  2437    int useSCGI;
  2438    int noJail;
  2439    int allowRepoList;
  2440  #if defined(_WIN32) && USE_SEE
  2441    const char *zPidKey;
  2442  #endif
  2443  
  2444    Th_InitTraceLog();
  2445  
  2446    /* The winhttp module passes the --files option as --files-urlenc with
  2447    ** the argument being URL encoded, to avoid wildcard expansion in the
  2448    ** shell.  This option is for internal use and is undocumented.
  2449    */
  2450    zFileGlob = find_option("files-urlenc",0,1);
  2451    if( zFileGlob ){
  2452      char *z = mprintf("%s", zFileGlob);
  2453      dehttpize(z);
  2454      zFileGlob = z;
  2455    }else{
  2456      zFileGlob = find_option("files",0,1);
  2457    }
  2458    skin_override();
  2459    zNotFound = find_option("notfound", 0, 1);
  2460    noJail = find_option("nojail",0,0)!=0;
  2461    allowRepoList = find_option("repolist",0,0)!=0;
  2462    g.useLocalauth = find_option("localauth", 0, 0)!=0;
  2463    g.sslNotAvailable = find_option("nossl", 0, 0)!=0;
  2464    g.fNoHttpCompress = find_option("nocompress",0,0)!=0;
  2465    g.zExtRoot = find_option("extroot",0,1);
  2466    zInFile = find_option("in",0,1);
  2467    if( zInFile ){
  2468      backoffice_disable();
  2469      g.httpIn = fossil_fopen(zInFile, "rb");
  2470      if( g.httpIn==0 ) fossil_fatal("cannot open \"%s\" for reading", zInFile);
  2471    }else{
  2472      g.httpIn = stdin;
  2473    }
  2474    zOutFile = find_option("out",0,1);
  2475    if( zOutFile ){
  2476      g.httpOut = fossil_fopen(zOutFile, "wb");
  2477      if( g.httpOut==0 ) fossil_fatal("cannot open \"%s\" for writing", zOutFile);
  2478    }else{
  2479      g.httpOut = stdout;
  2480    }
  2481    zIpAddr = find_option("ipaddr",0,1);
  2482    useSCGI = find_option("scgi", 0, 0)!=0;
  2483    zAltBase = find_option("baseurl", 0, 1);
  2484    if( find_option("nodelay",0,0)!=0 ) backoffice_no_delay();
  2485    if( zAltBase ) set_base_url(zAltBase);
  2486    if( find_option("https",0,0)!=0 ){
  2487      zIpAddr = fossil_getenv("REMOTE_HOST"); /* From stunnel */
  2488      cgi_replace_parameter("HTTPS","on");
  2489    }
  2490    zHost = find_option("host", 0, 1);
  2491    if( zHost ) cgi_replace_parameter("HTTP_HOST",zHost);
  2492  
  2493  #if defined(_WIN32) && USE_SEE
  2494    zPidKey = find_option("usepidkey", 0, 1);
  2495    if( zPidKey ){
  2496      DWORD processId = 0;
  2497      LPVOID pAddress = NULL;
  2498      SIZE_T nSize = 0;
  2499      parse_pid_key_value(zPidKey, &processId, &pAddress, &nSize);
  2500      db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
  2501    }
  2502  #endif
  2503  
  2504    /* We should be done with options.. */
  2505    verify_all_options();
  2506  
  2507    if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
  2508    g.cgiOutput = 1;
  2509    g.fullHttpReply = 1;
  2510    find_server_repository(2, 0);
  2511    if( zIpAddr==0 ){
  2512      zIpAddr = cgi_ssh_remote_addr(0);
  2513      if( zIpAddr && zIpAddr[0] ){
  2514        g.fSshClient |= CGI_SSH_CLIENT;
  2515      }
  2516    }
  2517    g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
  2518    if( useSCGI ){
  2519      cgi_handle_scgi_request();
  2520    }else if( g.fSshClient & CGI_SSH_CLIENT ){
  2521      ssh_request_loop(zIpAddr, glob_create(zFileGlob));
  2522    }else{
  2523      cgi_handle_http_request(zIpAddr);
  2524    }
  2525    process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
  2526  }
  2527  
  2528  /*
  2529  ** Process all requests in a single SSH connection if possible.
  2530  */
  2531  void ssh_request_loop(const char *zIpAddr, Glob *FileGlob){
  2532    blob_zero(&g.cgiIn);
  2533    do{
  2534      cgi_handle_ssh_http_request(zIpAddr);
  2535      process_one_web_page(0, FileGlob, 0);
  2536      blob_reset(&g.cgiIn);
  2537    } while ( g.fSshClient & CGI_SSH_FOSSIL ||
  2538            g.fSshClient & CGI_SSH_COMPAT );
  2539  }
  2540  
  2541  /*
  2542  ** Note that the following command is used by ssh:// processing.
  2543  **
  2544  ** COMMAND: test-http
  2545  **
  2546  ** Works like the http command but gives setup permission to all users.
  2547  **
  2548  ** Options:
  2549  **   --th-trace          trace TH1 execution (for debugging purposes)
  2550  **
  2551  */
  2552  void cmd_test_http(void){
  2553    const char *zIpAddr;    /* IP address of remote client */
  2554  
  2555    Th_InitTraceLog();
  2556    login_set_capabilities("sx", 0);
  2557    g.useLocalauth = 1;
  2558    g.httpIn = stdin;
  2559    g.httpOut = stdout;
  2560    fossil_binary_mode(g.httpOut);
  2561    fossil_binary_mode(g.httpIn);
  2562    g.zExtRoot = find_option("extroot",0,1);
  2563    find_server_repository(2, 0);
  2564    g.cgiOutput = 1;
  2565    g.fNoHttpCompress = 1;
  2566    g.fullHttpReply = 1;
  2567    zIpAddr = cgi_ssh_remote_addr(0);
  2568    if( zIpAddr && zIpAddr[0] ){
  2569      g.fSshClient |= CGI_SSH_CLIENT;
  2570      ssh_request_loop(zIpAddr, 0);
  2571    }else{
  2572      cgi_set_parameter("REMOTE_ADDR", "127.0.0.1");
  2573      cgi_handle_http_request(0);
  2574      process_one_web_page(0, 0, 1);
  2575    }
  2576  }
  2577  
  2578  #if !defined(_WIN32)
  2579  #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
  2580  /*
  2581  ** Search for an executable on the PATH environment variable.
  2582  ** Return true (1) if found and false (0) if not found.
  2583  */
  2584  static int binaryOnPath(const char *zBinary){
  2585    const char *zPath = fossil_getenv("PATH");
  2586    char *zFull;
  2587    int i;
  2588    int bExists;
  2589    while( zPath && zPath[0] ){
  2590      while( zPath[0]==':' ) zPath++;
  2591      for(i=0; zPath[i] && zPath[i]!=':'; i++){}
  2592      zFull = mprintf("%.*s/%s", i, zPath, zBinary);
  2593      bExists = file_access(zFull, X_OK);
  2594      fossil_free(zFull);
  2595      if( bExists==0 ) return 1;
  2596      zPath += i;
  2597    }
  2598    return 0;
  2599  }
  2600  #endif
  2601  #endif
  2602  
  2603  /*
  2604  ** Respond to a SIGALRM by writing a message to the error log (if there
  2605  ** is one) and exiting.
  2606  */
  2607  #ifndef _WIN32
  2608  static void sigalrm_handler(int x){
  2609    fossil_panic("TIMEOUT");
  2610  }
  2611  #endif
  2612  
  2613  /*
  2614  ** Arrange to timeout using SIGALRM after N seconds.  Or if N==0, cancel
  2615  ** any pending timeout.
  2616  **
  2617  ** Bugs:
  2618  ** (1) This only works on unix systems.
  2619  ** (2) Any call to sleep() or sqlite3_sleep() will cancel the alarm.
  2620  */
  2621  void fossil_set_timeout(int N){
  2622  #ifndef _WIN32
  2623    signal(SIGALRM, sigalrm_handler);
  2624    alarm(N);
  2625  #endif
  2626  }
  2627  
  2628  /*
  2629  ** COMMAND: server*
  2630  ** COMMAND: ui
  2631  **
  2632  ** Usage: %fossil server ?OPTIONS? ?REPOSITORY?
  2633  **    or: %fossil ui ?OPTIONS? ?REPOSITORY?
  2634  **
  2635  ** Open a socket and begin listening and responding to HTTP requests on
  2636  ** TCP port 8080, or on any other TCP port defined by the -P or
  2637  ** --port option.  The optional argument is the name of the repository.
  2638  ** The repository argument may be omitted if the working directory is
  2639  ** within an open checkout.
  2640  **
  2641  ** The "ui" command automatically starts a web browser after initializing
  2642  ** the web server.  The "ui" command also binds to 127.0.0.1 and so will
  2643  ** only process HTTP traffic from the local machine.
  2644  **
  2645  ** The REPOSITORY can be a directory (aka folder) that contains one or
  2646  ** more repositories with names ending in ".fossil".  In this case, a
  2647  ** prefix of the URL pathname is used to search the directory for an
  2648  ** appropriate repository.  To thwart mischief, the pathname in the URL must
  2649  ** contain only alphanumerics, "_", "/", "-", and ".", and no "-" may
  2650  ** occur after "/", and every "." must be surrounded on both sides by
  2651  ** alphanumerics.  Any pathname that does not satisfy these constraints
  2652  ** results in a 404 error.  Files in REPOSITORY that match the comma-separated
  2653  ** list of glob patterns given by --files and that have known suffixes
  2654  ** such as ".txt" or ".html" or ".jpeg" and do not match the pattern
  2655  ** "*.fossil*" will be served as static content.  With the "ui" command,
  2656  ** the REPOSITORY can only be a directory if the --notfound option is
  2657  ** also present.
  2658  **
  2659  ** For the special case REPOSITORY name of "/", the list global configuration
  2660  ** database is consulted for a list of all known repositories.  The --repolist
  2661  ** option is implied by this special case.  See also the "fossil all ui"
  2662  ** command.
  2663  **
  2664  ** By default, the "ui" command provides full administrative access without
  2665  ** having to log in.  This can be disabled by turning off the "localauth"
  2666  ** setting.  Automatic login for the "server" command is available if the
  2667  ** --localauth option is present and the "localauth" setting is off and the
  2668  ** connection is from localhost.  The "ui" command also enables --repolist
  2669  ** by default.
  2670  **
  2671  ** Options:
  2672  **   --baseurl URL       Use URL as the base (useful for reverse proxies)
  2673  **   --create            Create a new REPOSITORY if it does not already exist
  2674  **   --extroot DIR       Document root for the /ext extension mechanism
  2675  **   --files GLOBLIST    Comma-separated list of glob patterns for static files
  2676  **   --localauth         enable automatic login for requests from localhost
  2677  **   --localhost         listen on 127.0.0.1 only (always true for "ui")
  2678  **   --https             Indicates that the input is coming through a reverse
  2679  **                       proxy that has already translated HTTPS into HTTP.
  2680  **   --max-latency N     Do not let any single HTTP request run for more than N
  2681  **                       seconds (only works on unix)
  2682  **   --nocompress        Do not compress HTTP replies
  2683  **   --nojail            Drop root privileges but do not enter the chroot jail
  2684  **   --nossl             signal that no SSL connections are available (Always
  2685  **                       set by default for the "ui" command)
  2686  **   --notfound URL      Redirect
  2687  **   --page PAGE         Start "ui" on PAGE.  ex: --page "timeline?y=ci"
  2688  **   -P|--port TCPPORT   listen to request on port TCPPORT
  2689  **   --th-trace          trace TH1 execution (for debugging purposes)
  2690  **   --repolist          If REPOSITORY is dir, URL "/" lists repos.
  2691  **   --scgi              Accept SCGI rather than HTTP
  2692  **   --skin LABEL        Use override skin LABEL
  2693  **   --usepidkey         Use saved encryption key from parent process.  This is
  2694  **                       only necessary when using SEE on Windows.
  2695  **
  2696  ** See also: cgi, http, winsrv
  2697  */
  2698  void cmd_webserver(void){
  2699    int iPort, mxPort;        /* Range of TCP ports allowed */
  2700    const char *zPort;        /* Value of the --port option */
  2701    const char *zBrowser;     /* Name of web browser program */
  2702    char *zBrowserCmd = 0;    /* Command to launch the web browser */
  2703    int isUiCmd;              /* True if command is "ui", not "server' */
  2704    const char *zNotFound;    /* The --notfound option or NULL */
  2705    int flags = 0;            /* Server flags */
  2706  #if !defined(_WIN32)
  2707    int noJail;               /* Do not enter the chroot jail */
  2708    const char *zTimeout = 0; /* Max runtime of any single HTTP request */
  2709  #endif
  2710    int allowRepoList;         /* List repositories on URL "/" */
  2711    const char *zAltBase;      /* Argument to the --baseurl option */
  2712    const char *zFileGlob;     /* Static content must match this */
  2713    char *zIpAddr = 0;         /* Bind to this IP address */
  2714    int fCreate = 0;           /* The --create flag */
  2715    const char *zInitPage = 0; /* Start on this page.  --page option */
  2716  #if defined(_WIN32) && USE_SEE
  2717    const char *zPidKey;
  2718  #endif
  2719  
  2720  #if defined(_WIN32)
  2721    const char *zStopperFile;    /* Name of file used to terminate server */
  2722    zStopperFile = find_option("stopper", 0, 1);
  2723  #endif
  2724  
  2725    if( g.zErrlog==0 ){
  2726      g.zErrlog = "-";
  2727    }
  2728    g.zExtRoot = find_option("extroot",0,1);
  2729    zFileGlob = find_option("files-urlenc",0,1);
  2730    if( zFileGlob ){
  2731      char *z = mprintf("%s", zFileGlob);
  2732      dehttpize(z);
  2733      zFileGlob = z;
  2734    }else{
  2735      zFileGlob = find_option("files",0,1);
  2736    }
  2737    skin_override();
  2738  #if !defined(_WIN32)
  2739    noJail = find_option("nojail",0,0)!=0;
  2740    zTimeout = find_option("max-latency",0,1);
  2741  #endif
  2742    g.useLocalauth = find_option("localauth", 0, 0)!=0;
  2743    Th_InitTraceLog();
  2744    zPort = find_option("port", "P", 1);
  2745    isUiCmd = g.argv[1][0]=='u';
  2746    if( isUiCmd ){
  2747      zInitPage = find_option("page", 0, 1);
  2748    }
  2749    zNotFound = find_option("notfound", 0, 1);
  2750    allowRepoList = find_option("repolist",0,0)!=0;
  2751    if( find_option("nocompress",0,0)!=0 ) g.fNoHttpCompress = 1;
  2752    zAltBase = find_option("baseurl", 0, 1);
  2753    fCreate = find_option("create",0,0)!=0;
  2754    if( find_option("scgi", 0, 0)!=0 ) flags |= HTTP_SERVER_SCGI;
  2755    if( zAltBase ){
  2756      set_base_url(zAltBase);
  2757    }
  2758    g.sslNotAvailable = find_option("nossl", 0, 0)!=0 || isUiCmd;
  2759    if( find_option("https",0,0)!=0 ){
  2760      cgi_replace_parameter("HTTPS","on");
  2761    }
  2762    if( find_option("localhost", 0, 0)!=0 ){
  2763      flags |= HTTP_SERVER_LOCALHOST;
  2764    }
  2765  
  2766  #if defined(_WIN32) && USE_SEE
  2767    zPidKey = find_option("usepidkey", 0, 1);
  2768    if( zPidKey ){
  2769      DWORD processId = 0;
  2770      LPVOID pAddress = NULL;
  2771      SIZE_T nSize = 0;
  2772      parse_pid_key_value(zPidKey, &processId, &pAddress, &nSize);
  2773      db_read_saved_encryption_key_from_process(processId, pAddress, nSize);
  2774    }
  2775  #endif
  2776  
  2777    /* We should be done with options.. */
  2778    verify_all_options();
  2779  
  2780    if( g.argc!=2 && g.argc!=3 ) usage("?REPOSITORY?");
  2781    if( isUiCmd ){
  2782      flags |= HTTP_SERVER_LOCALHOST|HTTP_SERVER_REPOLIST;
  2783      g.useLocalauth = 1;
  2784      allowRepoList = 1;
  2785    }
  2786    find_server_repository(2, fCreate);
  2787    if( zInitPage==0 ){
  2788      if( isUiCmd && g.localOpen ){
  2789        zInitPage = "timeline?c=current";
  2790      }else{
  2791        zInitPage = "";
  2792      }
  2793    }
  2794    if( zPort ){
  2795      if( strchr(zPort,':') ){
  2796        int i;
  2797        for(i=strlen(zPort)-1; i>=0 && zPort[i]!=':'; i--){}
  2798        if( i>0 ){
  2799          if( zPort[0]=='[' && zPort[i-1]==']' ){
  2800            zIpAddr = mprintf("%.*s", i-2, zPort+1);
  2801          }else{
  2802            zIpAddr = mprintf("%.*s", i, zPort);
  2803          }
  2804          zPort += i+1;
  2805        }
  2806      }
  2807      iPort = mxPort = atoi(zPort);
  2808    }else{
  2809      iPort = db_get_int("http-port", 8080);
  2810      mxPort = iPort+100;
  2811    }
  2812  #if !defined(_WIN32)
  2813    /* Unix implementation */
  2814    if( isUiCmd ){
  2815  #if !defined(__DARWIN__) && !defined(__APPLE__) && !defined(__HAIKU__)
  2816      zBrowser = db_get("web-browser", 0);
  2817      if( zBrowser==0 ){
  2818        static const char *const azBrowserProg[] =
  2819            { "xdg-open", "gnome-open", "firefox", "google-chrome" };
  2820        int i;
  2821        zBrowser = "echo";
  2822        for(i=0; i<count(azBrowserProg); i++){
  2823          if( binaryOnPath(azBrowserProg[i]) ){
  2824            zBrowser = azBrowserProg[i];
  2825            break;
  2826          }
  2827        }
  2828      }
  2829  #else
  2830      zBrowser = db_get("web-browser", "open");
  2831  #endif
  2832      if( zIpAddr==0 ){
  2833        zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
  2834                              zBrowser, zInitPage);
  2835      }else if( strchr(zIpAddr,':') ){
  2836        zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
  2837                              zBrowser, zIpAddr, zInitPage);
  2838      }else{
  2839        zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
  2840                              zBrowser, zIpAddr, zInitPage);
  2841      }
  2842    }
  2843    if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  2844    if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  2845    db_close(1);
  2846    if( cgi_http_server(iPort, mxPort, zBrowserCmd, zIpAddr, flags) ){
  2847      fossil_fatal("unable to listen on TCP socket %d", iPort);
  2848    }
  2849    /* For the parent process, the cgi_http_server() command above never
  2850    ** returns (except in the case of an error).  Instead, for each incoming
  2851    ** client connection, a child process is created, file descriptors 0
  2852    ** and 1 are bound to that connection, and the child returns.
  2853    **
  2854    ** So, when control reaches this point, we are running as a
  2855    ** child process, the HTTP or SCGI request is pending on file
  2856    ** descriptor 0 and the reply should be written to file descriptor 1.
  2857    */
  2858    if( zTimeout ){
  2859      fossil_set_timeout(atoi(zTimeout));
  2860    }else{
  2861      fossil_set_timeout(FOSSIL_DEFAULT_TIMEOUT);
  2862    }
  2863    g.httpIn = stdin;
  2864    g.httpOut = stdout;
  2865  
  2866  #if !defined(_WIN32)
  2867    signal(SIGSEGV, sigsegv_handler);
  2868    signal(SIGPIPE, sigpipe_handler);
  2869  #endif
  2870  
  2871    if( g.fAnyTrace ){
  2872      fprintf(stderr, "/***** Subprocess %d *****/\n", getpid());
  2873    }
  2874    g.cgiOutput = 1;
  2875    find_server_repository(2, 0);
  2876    if( fossil_strcmp(g.zRepositoryName,"/")==0 ){
  2877      allowRepoList = 1;
  2878    }else{
  2879      g.zRepositoryName = enter_chroot_jail(g.zRepositoryName, noJail);
  2880    }
  2881    if( flags & HTTP_SERVER_SCGI ){
  2882      cgi_handle_scgi_request();
  2883    }else{
  2884      cgi_handle_http_request(0);
  2885    }
  2886    process_one_web_page(zNotFound, glob_create(zFileGlob), allowRepoList);
  2887    if( g.fAnyTrace ){
  2888      fprintf(stderr, "/***** Webpage finished in subprocess %d *****/\n",
  2889              getpid());
  2890    }
  2891  #else
  2892    /* Win32 implementation */
  2893    if( isUiCmd ){
  2894      zBrowser = db_get("web-browser", "start");
  2895      if( zIpAddr==0 ){
  2896        zBrowserCmd = mprintf("%s http://localhost:%%d/%s &",
  2897                              zBrowser, zInitPage);
  2898      }else if( strchr(zIpAddr,':') ){
  2899        zBrowserCmd = mprintf("%s http://[%s]:%%d/%s &",
  2900                              zBrowser, zIpAddr, zInitPage);
  2901      }else{
  2902        zBrowserCmd = mprintf("%s http://%s:%%d/%s &",
  2903                              zBrowser, zIpAddr, zInitPage);
  2904      }
  2905    }
  2906    if( g.repositoryOpen ) flags |= HTTP_SERVER_HAD_REPOSITORY;
  2907    if( g.localOpen ) flags |= HTTP_SERVER_HAD_CHECKOUT;
  2908    db_close(1);
  2909    if( allowRepoList ){
  2910      flags |= HTTP_SERVER_REPOLIST;
  2911    }
  2912    if( win32_http_service(iPort, zAltBase, zNotFound, zFileGlob, flags) ){
  2913      win32_http_server(iPort, mxPort, zBrowserCmd, zStopperFile,
  2914                        zAltBase, zNotFound, zFileGlob, zIpAddr, flags);
  2915    }
  2916  #endif
  2917  }
  2918  
  2919  /*
  2920  ** COMMAND: test-echo
  2921  **
  2922  ** Usage:  %fossil test-echo [--hex] ARGS...
  2923  **
  2924  ** Echo all command-line arguments (enclosed in [...]) to the screen so that
  2925  ** wildcard expansion behavior of the host shell can be investigated.
  2926  **
  2927  ** With the --hex option, show the output as hexadecimal.  This can be used
  2928  ** to verify the fossil_path_to_utf8() routine on Windows and Mac.
  2929  */
  2930  void test_echo_cmd(void){
  2931    int i, j;
  2932    if( find_option("hex",0,0)==0 ){
  2933      fossil_print("g.nameOfExe = [%s]\n", g.nameOfExe);
  2934      for(i=0; i<g.argc; i++){
  2935        fossil_print("argv[%d] = [%s]\n", i, g.argv[i]);
  2936      }
  2937    }else{
  2938      unsigned char *z, c;
  2939      for(i=0; i<g.argc; i++){
  2940        fossil_print("argv[%d] = [", i);
  2941        z = (unsigned char*)g.argv[i];
  2942        for(j=0; (c = z[j])!=0; j++){
  2943          fossil_print("%02x", c);
  2944        }
  2945        fossil_print("]\n");
  2946      }
  2947    }
  2948  }
  2949  
  2950  /*
  2951  ** WEBPAGE: test-warning
  2952  **
  2953  ** Test error and warning log operation.  This webpage is accessible to
  2954  ** the administrator only.
  2955  **
  2956  **     case=1           Issue a fossil_warning() while generating the page.
  2957  **     case=2           Extra db_begin_transaction()
  2958  **     case=3           Extra db_end_transaction()
  2959  **     case=4           Error during SQL processing
  2960  **     case=5           Call the segfault handler
  2961  **     case=6           Call webpage_assert()
  2962  **     case=7           Call webpage_error()
  2963  */
  2964  void test_warning_page(void){
  2965    int iCase = atoi(PD("case","0"));
  2966    int i;
  2967    login_check_credentials();
  2968    if( !g.perm.Admin ){
  2969      login_needed(0);
  2970      return;
  2971    }
  2972    style_header("Warning Test Page");
  2973    style_submenu_element("Error Log","%R/errorlog");
  2974    if( iCase<1 || iCase>4 ){
  2975      @ <p>Generate a message to the <a href="%R/errorlog">error log</a>
  2976      @ by clicking on one of the following cases:
  2977    }else{
  2978      @ <p>This is the test page for case=%d(iCase).  All possible cases:
  2979    }
  2980    for(i=1; i<=7; i++){
  2981      @ <a href='./test-warning?case=%d(i)'>[%d(i)]</a>
  2982    }
  2983    @ </p>
  2984    @ <p><ol>
  2985    @ <li value='1'> Call fossil_warning()
  2986    if( iCase==1 ){
  2987      fossil_warning("Test warning message from /test-warning");
  2988    }
  2989    @ <li value='2'> Call db_begin_transaction()
  2990    if( iCase==2 ){
  2991      db_begin_transaction();
  2992    }
  2993    @ <li value='3'> Call db_end_transaction()
  2994    if( iCase==3 ){
  2995      db_end_transaction(0);
  2996    }
  2997    @ <li value='4'> warning during SQL
  2998    if( iCase==4 ){
  2999      Stmt q;
  3000      db_prepare(&q, "SELECT uuid FROM blob LIMIT 5");
  3001      db_step(&q);
  3002      sqlite3_log(SQLITE_ERROR, "Test warning message during SQL");
  3003      db_finalize(&q);
  3004    }
  3005    @ <li value='5'> simulate segfault handling
  3006    if( iCase==5 ){
  3007      sigsegv_handler(0);
  3008    }
  3009    @ <li value='6'> call webpage_assert(0)
  3010    if( iCase==6 ){
  3011      webpage_assert( 5==7 );
  3012    }
  3013    @ <li value='7'> call webpage_error()"
  3014    if( iCase==7 ){
  3015      cgi_reset_content();
  3016      webpage_error("Case 7 from /test-warning");
  3017    }
  3018    @ </ol>
  3019    @ <p>End of test</p>
  3020    style_footer();
  3021  }