Fossil

Check-in [e7f13b82b6]
Login

Check-in [e7f13b82b6]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Corrected [b2ac2183] to work with CGI directory-serving mode. Renamed the two JSON bootstrap routines to be more descriptive and made it a harmless no-op to call json_bootstrap_early() (formerly json_main_bootstrap()) multiple times in order to simplify some code. Several minor code style fixes in related code.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256: e7f13b82b63a4f54171e253b12de17d0de776576db0c6d7c632073e025fb697c
User & Date: stephan 2020-07-21 02:47:01.849
Context
2020-07-21
12:52
Trivial makeheaders patch from [https://fossil-scm.org/forum/forumpost/0fc3f6b8d8], plus a small typo fix. ... (check-in: 00ad7ef3f2 user: stephan tags: trunk)
02:47
Corrected [b2ac2183] to work with CGI directory-serving mode. Renamed the two JSON bootstrap routines to be more descriptive and made it a harmless no-op to call json_bootstrap_early() (formerly json_main_bootstrap()) multiple times in order to simplify some code. Several minor code style fixes in related code. ... (check-in: e7f13b82b6 user: stephan tags: trunk)
2020-07-20
15:18
Fixed several "Ok" -> "OK". ... (check-in: b92e460fd7 user: wyoung tags: trunk)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
*/
void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
  cson_value * jv = NULL;
  int rc;
  CgiPostReadState state;
  cson_parse_opt popt = cson_parse_opt_empty;
  cson_parse_info pinfo = cson_parse_info_empty;
  assert(g.json.gc.a && "json_main_bootstrap() was not called!");
  popt.maxDepth = 15;
  state.fh = zIn;
  state.len = contentLen;
  state.pos = 0;
  rc = cson_parse( &jv,
                   contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
                   contentLen ? (void *)&state : (void *)zIn, &popt, &pinfo );







|







956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
*/
void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
  cson_value * jv = NULL;
  int rc;
  CgiPostReadState state;
  cson_parse_opt popt = cson_parse_opt_empty;
  cson_parse_info pinfo = cson_parse_info_empty;
  assert(g.json.gc.a && "json_bootstrap_early() was not called!");
  popt.maxDepth = 15;
  state.fh = zIn;
  state.len = contentLen;
  state.pos = 0;
  rc = cson_parse( &jv,
                   contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
                   contentLen ? (void *)&state : (void *)zIn, &popt, &pinfo );
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
  const char *zScriptName = cgi_parameter("SCRIPT_NAME",0);
  const char *zPathInfo = cgi_parameter("PATH_INFO",0);
#ifdef _WIN32
  const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0);
#endif

#ifdef FOSSIL_ENABLE_JSON
  int noJson = P("no_json")!=0;
  if( noJson==0 ){ json_main_bootstrap(); }
#endif
  g.isHTTP = 1;
  cgi_destination(CGI_BODY);
  if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME");
#ifdef _WIN32
  /* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
  ** PATH_INFO for virtually the same purpose.  Define REQUEST_URI the same as







|
<







1062
1063
1064
1065
1066
1067
1068
1069

1070
1071
1072
1073
1074
1075
1076
  const char *zScriptName = cgi_parameter("SCRIPT_NAME",0);
  const char *zPathInfo = cgi_parameter("PATH_INFO",0);
#ifdef _WIN32
  const char *zServerSoftware = cgi_parameter("SERVER_SOFTWARE",0);
#endif

#ifdef FOSSIL_ENABLE_JSON
  const int noJson = P("no_json")!=0;

#endif
  g.isHTTP = 1;
  cgi_destination(CGI_BODY);
  if( zScriptName==0 ) malformed_request("missing SCRIPT_NAME");
#ifdef _WIN32
  /* The Microsoft IIS web server does not define REQUEST_URI, instead it uses
  ** PATH_INFO for virtually the same purpose.  Define REQUEST_URI the same as
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112

1113
1114
1115
1116
1117
1118
1119
    int i, j;
    for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){}
    for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){}
    zPathInfo = mprintf("%.*s", j-i, zRequestUri+i);
    cgi_set_parameter("PATH_INFO", zPathInfo);
  }
#ifdef FOSSIL_ENABLE_JSON
  if(json_request_is_json_api(zPathInfo)){
    /* We need to change some following behaviour depending on whether
    ** we are operating in JSON mode or not. We cannot, however, be
    ** certain whether we should/need to be in JSON mode until the
    ** PATH_INFO is set up.
    */
    g.json.isJsonMode = 1;

  }else{
    assert(!g.json.isJsonMode &&
           "Internal misconfiguration of g.json.isJsonMode");
  }
#endif
  z = (char*)P("HTTP_COOKIE");
  if( z ){







|






>







1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
    int i, j;
    for(i=0; zRequestUri[i]==zScriptName[i] && zRequestUri[i]; i++){}
    for(j=i; zRequestUri[j] && zRequestUri[j]!='?'; j++){}
    zPathInfo = mprintf("%.*s", j-i, zRequestUri+i);
    cgi_set_parameter("PATH_INFO", zPathInfo);
  }
#ifdef FOSSIL_ENABLE_JSON
  if(noJson==0 && json_request_is_json_api(zPathInfo)){
    /* We need to change some following behaviour depending on whether
    ** we are operating in JSON mode or not. We cannot, however, be
    ** certain whether we should/need to be in JSON mode until the
    ** PATH_INFO is set up.
    */
    g.json.isJsonMode = 1;
    json_bootstrap_early();
  }else{
    assert(!g.json.isJsonMode &&
           "Internal misconfiguration of g.json.isJsonMode");
  }
#endif
  z = (char*)P("HTTP_COOKIE");
  if( z ){
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
  static char *zCmd = 0;
  char *z, *zToken;
  const char *zType = 0;
  int i, content_length = 0;
  char zLine[2000];     /* A single line of input. */

#ifdef FOSSIL_ENABLE_JSON
  if( nCycles==0 ){ json_main_bootstrap(); }
#endif
  if( zIpAddr ){
    if( nCycles==0 ){
      cgi_setenv("REMOTE_ADDR", zIpAddr);
      g.zIpAddr = mprintf("%s", zIpAddr);
    }
  }else{







|







1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
  static char *zCmd = 0;
  char *z, *zToken;
  const char *zType = 0;
  int i, content_length = 0;
  char zLine[2000];     /* A single line of input. */

#ifdef FOSSIL_ENABLE_JSON
  if( nCycles==0 ){ json_bootstrap_early(); }
#endif
  if( zIpAddr ){
    if( nCycles==0 ){
      cgi_setenv("REMOTE_ADDR", zIpAddr);
      g.zIpAddr = mprintf("%s", zIpAddr);
    }
  }else{
Changes to src/db.c.
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
static void db_err(const char *zFormat, ...){
  va_list ap;
  char *z;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
#ifdef FOSSIL_ENABLE_JSON
  if( g.json.isJsonMode ){
    /*
    ** Avoid calling into the JSON support subsystem if it
    ** has not yet been initialized, e.g. early SQLite log
    ** messages, etc.
    */
    if( !json_is_main_boostrapped() ) json_main_bootstrap();
    json_err( 0, z, 1 );
  }
  else
#endif /* FOSSIL_ENABLE_JSON */
  if( g.xferPanic && g.cgiOutput==1 ){
    cgi_reset_content();
    @ error Database\serror:\s%F(z)







|





|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
static void db_err(const char *zFormat, ...){
  va_list ap;
  char *z;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
#ifdef FOSSIL_ENABLE_JSON
  if( g.json.isJsonMode!=0 ){
    /*
    ** Avoid calling into the JSON support subsystem if it
    ** has not yet been initialized, e.g. early SQLite log
    ** messages, etc.
    */
    json_bootstrap_early();
    json_err( 0, z, 1 );
  }
  else
#endif /* FOSSIL_ENABLE_JSON */
  if( g.xferPanic && g.cgiOutput==1 ){
    cgi_reset_content();
    @ error Database\serror:\s%F(z)
Changes to src/json.c.
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71

72
73
74
75
76
77
78
79
80
81
82
  "resultText" /*resultText*/,
  "timestamp" /*timestamp*/
};

/*
** Given the current request path string, this function returns true
** if it refers to a JSON API path. i.e. if (1) it starts with /json
** or (2) g.zCmdName is "server" and the path starts with
** /somereponame/json. Specifically, it returns 1 in the former case
** and 2 for the latter.
*/
int json_request_is_json_api(const char * zPathInfo){
  int rc = 0;
  if(zPathInfo==0){
    rc = 0;
  }else if(0==strncmp("/json",zPathInfo,5)
           && (zPathInfo[5]==0 || zPathInfo[5]=='/')){
    rc = 1;
  }else if(g.zCmdName!=0 && 0==strcmp("server",g.zCmdName)){

    /* When running in server "directory" mode, zPathInfo is
    ** prefixed with the repository's name, so in order to determine
    ** whether or not we're really running in json mode we have to
    ** try a bit harder. Problem reported here:
    ** https://fossil-scm.org/forum/forumpost/e4953666d6
    */
    ReCompiled * pReg = 0;
    const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
    assert(zErr==0 && "Regex compilation failed?");
    if(zErr==0 &&
         re_match(pReg, (const unsigned char *)zPathInfo, -1)){







|










|
>
|

|
|







53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
  "resultText" /*resultText*/,
  "timestamp" /*timestamp*/
};

/*
** Given the current request path string, this function returns true
** if it refers to a JSON API path. i.e. if (1) it starts with /json
** or (2) g.zCmdName is "server" or "cgi" and the path starts with
** /somereponame/json. Specifically, it returns 1 in the former case
** and 2 for the latter.
*/
int json_request_is_json_api(const char * zPathInfo){
  int rc = 0;
  if(zPathInfo==0){
    rc = 0;
  }else if(0==strncmp("/json",zPathInfo,5)
           && (zPathInfo[5]==0 || zPathInfo[5]=='/')){
    rc = 1;
  }else if(g.zCmdName!=0 && (0==strcmp("server",g.zCmdName)
                             || 0==strcmp("cgi",g.zCmdName)) ){
    /* When running in server/cgi "directory" mode, zPathInfo is
    ** prefixed with the repository's name, so in order to determine
    ** whether or not we're really running in json mode we have to try
    ** a bit harder. Problem reported here:
    ** https://fossil-scm.org/forum/forumpost/e4953666d6
    */
    ReCompiled * pReg = 0;
    const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
    assert(zErr==0 && "Regex compilation failed?");
    if(zErr==0 &&
         re_match(pReg, (const unsigned char *)zPathInfo, -1)){
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
** Special case: if g.useLocalauth is true (i.e. the --localauth flag
** was used to start the fossil server instance) and the current
** connection is "local", any authToken provided by the user is
** ignored and no new token is created: localauth mode trumps the
** authToken.
*/
cson_value * json_auth_token(){
  assert(g.json.gc.a && "json_main_bootstrap() was not called!");
  if( g.json.authToken==0 && g.noPswd==0
      /* g.noPswd!=0 means the user was logged in via a local
         connection using --localauth. */
      ){
    /* Try to get an authorization token from GET parameter, POSTed
       JSON, or fossil cookie (in that order). */
    g.json.authToken = json_getenv(FossilJsonKeys.authToken);







|







686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
** Special case: if g.useLocalauth is true (i.e. the --localauth flag
** was used to start the fossil server instance) and the current
** connection is "local", any authToken provided by the user is
** ignored and no new token is created: localauth mode trumps the
** authToken.
*/
cson_value * json_auth_token(){
  assert(g.json.gc.a && "json_bootstrap_early() was not called!");
  if( g.json.authToken==0 && g.noPswd==0
      /* g.noPswd!=0 means the user was logged in via a local
         connection using --localauth. */
      ){
    /* Try to get an authorization token from GET parameter, POSTed
       JSON, or fossil cookie (in that order). */
    g.json.authToken = json_getenv(FossilJsonKeys.authToken);
738
739
740
741
742
743
744
745

746
747
748
749
750
751
752
753
754
755
756
757
758
759



760
761
762
763
764
765


766
767
768

769
770

771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
** caller.
*/
cson_value * json_req_payload_get(char const *pKey){
  return g.json.reqPayload.o
    ? cson_object_get(g.json.reqPayload.o,pKey)
    : NULL;
}


/*
** Returns non-zero if the json_main_bootstrap() function has already
** been called.  In general, this function should be used sparingly,
** e.g. from low-level support functions like fossil_warning() where
** there is genuine uncertainty about whether (or not) the JSON setup
** has already been called.
*/
int json_is_main_boostrapped(){
  return ((g.json.gc.v != NULL) && (g.json.gc.a != NULL));
}

/*
** Initializes some JSON bits which need to be initialized relatively
** early on. It should only be called from cgi_init() or



** json_cmd_top() (early on in those functions).
**
** Initializes g.json.gc and g.json.param. This code does not (and
** must not) rely on any of the fossil environment having been set
** up. e.g. it must not use cgi_parameter() and friends because this
** must be called before those data are initialized.


*/
void json_main_bootstrap(){
  cson_value * v;

  assert( (NULL == g.json.gc.v) &&
          "json_main_bootstrap() was called twice!" );


  g.json.timerId = fossil_timer_start();

  /* g.json.gc is our "garbage collector" - where we put JSON values
     which need a long lifetime but don't have a logical parent to put
     them in.
  */
  v = cson_value_new_array();
  g.json.gc.v = v;
  assert(0 != g.json.gc.v);
  g.json.gc.a = cson_value_get_array(v);
  assert(0 != g.json.gc.a);
  cson_value_add_reference(v)
    /* Needed to allow us to include this value in other JSON
       containers without transferring ownership to those containers.
       All other persistent g.json.XXX.v values get appended to
       g.json.gc.a, and therefore already have a live reference
       for this purpose.
    */
    ;

  /*
    g.json.param holds the JSONized counterpart of fossil's
    cgi_parameter_xxx() family of data. We store them as JSON, as
    opposed to using fossil's data directly, because we can retain
    full type information for data this way (as opposed to it always








>

|





|





|
>
>
>
|





>
>

|

>
|
|
>
|

<


|
<










|
<







739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781

782
783
784

785
786
787
788
789
790
791
792
793
794
795

796
797
798
799
800
801
802
** caller.
*/
cson_value * json_req_payload_get(char const *pKey){
  return g.json.reqPayload.o
    ? cson_object_get(g.json.reqPayload.o,pKey)
    : NULL;
}


/*
** Returns non-zero if the json_bootstrap_early() function has already
** been called.  In general, this function should be used sparingly,
** e.g. from low-level support functions like fossil_warning() where
** there is genuine uncertainty about whether (or not) the JSON setup
** has already been called.
*/
int json_is_bootstrapped_early(){
  return ((g.json.gc.v != NULL) && (g.json.gc.a != NULL));
}

/*
** Initializes some JSON bits which need to be initialized relatively
** early on. It should be called by any routine which might need to
** call into JSON relatively early on in the init process.
** Specifically, early on in cgi_init() and json_cmd_top(), but also
** from any error reporting routines which might be triggered (early
** on in those functions).
**
** Initializes g.json.gc and g.json.param. This code does not (and
** must not) rely on any of the fossil environment having been set
** up. e.g. it must not use cgi_parameter() and friends because this
** must be called before those data are initialized.
**
** If called multiple times, calls after the first are a no-op.
*/
void json_bootstrap_early(){
  cson_value * v;

  if(g.json.gc.v!=NULL){
    /* Avoid multiple bootstrappings. */
    return;
  }
  g.json.timerId = fossil_timer_start();

  /* g.json.gc is our "garbage collector" - where we put JSON values
     which need a long lifetime but don't have a logical parent to put
     them in. */

  v = cson_value_new_array();
  g.json.gc.v = v;
  assert(0 != g.json.gc.v);
  g.json.gc.a = cson_value_get_array(v);
  assert(0 != g.json.gc.a);
  cson_value_add_reference(v)
    /* Needed to allow us to include this value in other JSON
       containers without transferring ownership to those containers.
       All other persistent g.json.XXX.v values get appended to
       g.json.gc.a, and therefore already have a live reference
       for this purpose. */

    ;

  /*
    g.json.param holds the JSONized counterpart of fossil's
    cgi_parameter_xxx() family of data. We store them as JSON, as
    opposed to using fossil's data directly, because we can retain
    full type information for data this way (as opposed to it always
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
** for consistency with how json_err() works.
*/
void json_warn( int code, char const * fmt, ... ){
  cson_object * obj = NULL;
  assert( (code>FSL_JSON_W_START)
          && (code<FSL_JSON_W_END)
          && "Invalid warning code.");
  assert(g.json.gc.a && "json_main_bootstrap() was not called!");
  if(!g.json.warnings){
    g.json.warnings = cson_new_array();
    assert((NULL != g.json.warnings) && "Alloc error.");
    json_gc_add("$WARNINGS",cson_array_value(g.json.warnings));
  }
  obj = cson_new_object();
  cson_array_append(g.json.warnings, cson_object_value(obj));







|







832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
** for consistency with how json_err() works.
*/
void json_warn( int code, char const * fmt, ... ){
  cson_object * obj = NULL;
  assert( (code>FSL_JSON_W_START)
          && (code<FSL_JSON_W_END)
          && "Invalid warning code.");
  assert(g.json.gc.a && "json_bootstrap_early() was not called!");
  if(!g.json.warnings){
    g.json.warnings = cson_new_array();
    assert((NULL != g.json.warnings) && "Alloc error.");
    json_gc_add("$WARNINGS",cson_array_value(g.json.warnings));
  }
  obj = cson_new_object();
  cson_array_append(g.json.warnings, cson_object_value(obj));
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
** tested this) die with an error if an auth cookie is malformed.
**
** This must be called by the top-level JSON command dispatching code
** before they do any work.
**
** This must only be called once, or an assertion may be triggered.
*/
void json_mode_bootstrap(){
  static char once = 0  /* guard against multiple runs */;
  char const * zPath = P("PATH_INFO");
  assert(g.json.gc.a && "json_main_bootstrap() was not called!");
  assert( (0==once) && "json_mode_bootstrap() called too many times!");
  if( once ){
    return;
  }else{
    once = 1;
  }
  assert(g.json.isJsonMode
         && "g.json.isJsonMode should have been set up by now.");







|


|
|







979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
** tested this) die with an error if an auth cookie is malformed.
**
** This must be called by the top-level JSON command dispatching code
** before they do any work.
**
** This must only be called once, or an assertion may be triggered.
*/
void json_bootstrap_late(){
  static char once = 0  /* guard against multiple runs */;
  char const * zPath = P("PATH_INFO");
  assert(g.json.gc.a && "json_bootstrap_early() was not called!");
  assert( (0==once) && "json_bootstrap_late() called too many times!");
  if( once ){
    return;
  }else{
    once = 1;
  }
  assert(g.json.isJsonMode
         && "g.json.isJsonMode should have been set up by now.");
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
**
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
  cson_array * ar = g.json.cmd.a;
  assert((NULL!=ar) && "Internal error. Was json_mode_bootstrap() called?");
  assert((g.argc>1) && "Internal error - we never should have gotten this far.");
  if( g.json.cmd.offset < 0 ){
    /* first-time setup. */
    short i = 0;
#define NEXT cson_string_cstr(          \
                 cson_value_get_string( \
                   cson_array_get(ar,i) \







|







1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
**
** Note that CLI options are not included in the command path. Use
** find_option() to get those.
**
*/
char const * json_command_arg(unsigned short ndx){
  cson_array * ar = g.json.cmd.a;
  assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
  assert((g.argc>1) && "Internal error - we never should have gotten this far.");
  if( g.json.cmd.offset < 0 ){
    /* first-time setup. */
    short i = 0;
#define NEXT cson_string_cstr(          \
                 cson_value_get_string( \
                   cson_array_get(ar,i) \
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
**
** Pages under /json/... must be entered into JsonPageDefs.
** This function dispatches them, and is the HTTP equivalent of
** json_cmd_top().
*/
void json_page_top(void){
  char const * zCommand;
  assert(g.json.gc.a && "json_main_bootstrap() was not called!");
  assert(g.json.cmd.a && "json_mode_bootstrap() was not called!");
  zCommand = json_command_arg(1);
  if(!zCommand || !*zCommand){
    json_dispatch_missing_args_err( JsonPageDefs,
                                    "No command (sub-path) specified."
                                    " Try one of: ");
    return;
  }







|
|







2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
**
** Pages under /json/... must be entered into JsonPageDefs.
** This function dispatches them, and is the HTTP equivalent of
** json_cmd_top().
*/
void json_page_top(void){
  char const * zCommand;
  assert(g.json.gc.a && "json_bootstrap_early() was not called!");
  assert(g.json.cmd.a && "json_bootstrap_late() was not called!");
  zCommand = json_command_arg(1);
  if(!zCommand || !*zCommand){
    json_dispatch_missing_args_err( JsonPageDefs,
                                    "No command (sub-path) specified."
                                    " Try one of: ");
    return;
  }
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
       and they all default to false. We enable them
       here because (A) fossil doesn't use them in local
       mode but (B) having them set gives us one less
       difference in the CLI/CGI/Server-mode JSON
       handling.
    */
    ;
  json_main_bootstrap();
  json_mode_bootstrap();
  if( 2 > cson_array_length_get(g.json.cmd.a) ){
    goto usage;
  }
#if 0
  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing.");
  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again.");
#endif







|
|







2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
       and they all default to false. We enable them
       here because (A) fossil doesn't use them in local
       mode but (B) having them set gives us one less
       difference in the CLI/CGI/Server-mode JSON
       handling.
    */
    ;
  json_bootstrap_early();
  json_bootstrap_late();
  if( 2 > cson_array_length_get(g.json.cmd.a) ){
    goto usage;
  }
#if 0
  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing.");
  json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again.");
#endif
Changes to src/json_login.c.
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(){
  cson_value const *token = g.json.authToken;
    /* Remember that json_mode_bootstrap() replaces the login cookie
       with the JSON auth token if the request contains it. If the
       request is missing the auth token then this will fetch fossil's
       original cookie. Either way, it's what we want :).

       We require the auth token to avoid someone maliciously
       trying to log someone else out (not 100% sure if that
       would be possible, given fossil's hardened cookie, but







|







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(){
  cson_value const *token = g.json.authToken;
    /* Remember that json_bootstrap_late() replaces the login cookie
       with the JSON auth token if the request contains it. If the
       request is missing the auth token then this will fetch fossil's
       original cookie. Either way, it's what we want :).

       We require the auth token to avoid someone maliciously
       trying to log someone else out (not 100% sure if that
       would be possible, given fossil's hardened cookie, but
Changes to src/main.c.
1577
1578
1579
1580
1581
1582
1583
1584
1585

1586
1587
1588
1589
1590
1591
1592
  ** Ensure that JSON mode is set up if we're visiting /json, to allow
  ** us to customize some following behaviour (error handling and only
  ** process JSON-mode POST data if we're actually in a /json
  ** page). This is normally set up before this routine is called, but
  ** it looks like the ssh_request_loop() approach to dispatching
  ** might bypass that.
  */
  if( g.json.isJsonMode==0 && json_request_is_json_api(zPathInfo) ){
    g.json.isJsonMode = 1;

  }
#endif
  /* If the repository has not been opened already, then find the
  ** repository based on the first element of PATH_INFO and open it.
  */
  if( !g.repositoryOpen ){
    char *zRepo;               /* Candidate repository name */







|

>







1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
  ** Ensure that JSON mode is set up if we're visiting /json, to allow
  ** us to customize some following behaviour (error handling and only
  ** process JSON-mode POST data if we're actually in a /json
  ** page). This is normally set up before this routine is called, but
  ** it looks like the ssh_request_loop() approach to dispatching
  ** might bypass that.
  */
  if( g.json.isJsonMode==0 && json_request_is_json_api(zPathInfo)!=0 ){
    g.json.isJsonMode = 1;
    json_bootstrap_early();
  }
#endif
  /* If the repository has not been opened already, then find the
  ** repository based on the first element of PATH_INFO and open it.
  */
  if( !g.repositoryOpen ){
    char *zRepo;               /* Candidate repository name */
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
    ** Disabled by stephan when running in JSON mode because this
    ** particular parameter name is very common and i have had no end
    ** of grief with this handling. The JSON API never relies on the
    ** handling below, and by disabling it in JSON mode I can remove
    ** lots of special-case handling in several JSON handlers.
    */
#ifdef FOSSIL_ENABLE_JSON
    if(!g.json.isJsonMode){
#endif
      dehttpize(g.zExtra);
      cgi_set_parameter_nocopy("name", g.zExtra, 1);
#ifdef FOSSIL_ENABLE_JSON
    }
#endif
  }

  /* Locate the method specified by the path and execute the function
  ** that implements that method.
  */
  if( dispatch_name_search(g.zPath-1, CMDFLAG_WEBPAGE, &pCmd)
   && dispatch_alias(g.zPath-1, &pCmd)
  ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
      json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
    }else
#endif
    {
#ifdef FOSSIL_ENABLE_TH1_HOOKS
      int rc;
      if( !g.fNoThHook ){







|















|







1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
1883
1884
1885
1886
    ** Disabled by stephan when running in JSON mode because this
    ** particular parameter name is very common and i have had no end
    ** of grief with this handling. The JSON API never relies on the
    ** handling below, and by disabling it in JSON mode I can remove
    ** lots of special-case handling in several JSON handlers.
    */
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode==0){
#endif
      dehttpize(g.zExtra);
      cgi_set_parameter_nocopy("name", g.zExtra, 1);
#ifdef FOSSIL_ENABLE_JSON
    }
#endif
  }

  /* Locate the method specified by the path and execute the function
  ** that implements that method.
  */
  if( dispatch_name_search(g.zPath-1, CMDFLAG_WEBPAGE, &pCmd)
   && dispatch_alias(g.zPath-1, &pCmd)
  ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode!=0){
      json_err(FSL_JSON_E_RESOURCE_NOT_FOUND,NULL,0);
    }else
#endif
    {
#ifdef FOSSIL_ENABLE_TH1_HOOKS
      int rc;
      if( !g.fNoThHook ){
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918

1919
1920
1921
1922
1923
1924
1925
1926
          Th_WebpageNotify(g.zPath, 0);
        }
      }
#endif
    }
  }else if( pCmd->xFunc!=page_xfer && db_schema_is_outofdate() ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode){
      json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
    }else
#endif
    {
      @ <h1>Server Configuration Error</h1>
      @ <p>The database schema on the server is out-of-date.  Please ask
      @ the administrator to run <b>fossil rebuild</b>.</p>
    }
  }else{
#ifdef FOSSIL_ENABLE_JSON
    static int jsonOnce = 0;
    if( !jsonOnce && g.json.isJsonMode ){

      json_mode_bootstrap();
      jsonOnce = 1;
    }
#endif
    if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
      cgi_decode_post_parameters();
    }
    if( g.fCgiTrace ){







|











|
>
|







1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
1917
1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
          Th_WebpageNotify(g.zPath, 0);
        }
      }
#endif
    }
  }else if( pCmd->xFunc!=page_xfer && db_schema_is_outofdate() ){
#ifdef FOSSIL_ENABLE_JSON
    if(g.json.isJsonMode!=0){
      json_err(FSL_JSON_E_DB_NEEDS_REBUILD,NULL,0);
    }else
#endif
    {
      @ <h1>Server Configuration Error</h1>
      @ <p>The database schema on the server is out-of-date.  Please ask
      @ the administrator to run <b>fossil rebuild</b>.</p>
    }
  }else{
#ifdef FOSSIL_ENABLE_JSON
    static int jsonOnce = 0;
    if( jsonOnce==0 && g.json.isJsonMode!=0 ){
      assert(json_is_bootstrapped_early());
      json_bootstrap_late();
      jsonOnce = 1;
    }
#endif
    if( (pCmd->eCmdFlags & CMDFLAG_RAWCONTENT)==0 ){
      cgi_decode_post_parameters();
    }
    if( g.fCgiTrace ){
Changes to src/printf.c.
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
static int mainInFatalError = 0;

/*
** Write error message output
*/
static int fossil_print_error(int rc, const char *z){
#ifdef FOSSIL_ENABLE_JSON
  if( g.json.isJsonMode ){
    /*
    ** Avoid calling into the JSON support subsystem if it
    ** has not yet been initialized, e.g. early SQLite log
    ** messages, etc.
    */
    if( !json_is_main_boostrapped() ) json_main_bootstrap();
    json_err( 0, z, 1 );
    if( g.isHTTP && !g.json.preserveRc ){
      rc = 0 /* avoid HTTP 500 */;
    }
    if( g.cgiOutput==1 ){
      g.cgiOutput = 2;
      cgi_reply();







|





|







1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
static int mainInFatalError = 0;

/*
** Write error message output
*/
static int fossil_print_error(int rc, const char *z){
#ifdef FOSSIL_ENABLE_JSON
  if( g.json.isJsonMode!=0 ){
    /*
    ** Avoid calling into the JSON support subsystem if it
    ** has not yet been initialized, e.g. early SQLite log
    ** messages, etc.
    */
    assert(json_is_bootstrapped_early());
    json_err( 0, z, 1 );
    if( g.isHTTP && !g.json.preserveRc ){
      rc = 0 /* avoid HTTP 500 */;
    }
    if( g.cgiOutput==1 ){
      g.cgiOutput = 2;
      cgi_reply();
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
  char *z;
  va_list ap;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  fossil_errorlog("warning: %s", z);
#ifdef FOSSIL_ENABLE_JSON
  if(g.json.isJsonMode){
    /*
    ** Avoid calling into the JSON support subsystem if it
    ** has not yet been initialized, e.g. early SQLite log
    ** messages, etc.
    */
    if( !json_is_main_boostrapped() ) json_main_bootstrap();
    json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
  }else
#endif
  {
    if( g.cgiOutput==1 ){
      etag_cancel();
      cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);







|





|







1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
  char *z;
  va_list ap;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  fossil_errorlog("warning: %s", z);
#ifdef FOSSIL_ENABLE_JSON
  if( g.json.isJsonMode!=0 ){
    /*
    ** Avoid calling into the JSON support subsystem if it
    ** has not yet been initialized, e.g. early SQLite log
    ** messages, etc.
    */
    assert(json_is_bootstrapped_early());
    json_warn( FSL_JSON_W_UNKNOWN, "%s", z );
  }else
#endif
  {
    if( g.cgiOutput==1 ){
      etag_cancel();
      cgi_printf("<p class=\"generalError\">\n%h\n</p>\n", z);