Fossil

Artifact Content
Login

Artifact 9a52a07b5277f5cf39e8b300281027f9f07d079bee8eb7f0bb00079a939984b6:


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