Fossil

Check-in [9adc95c4]
Login

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

Overview
Comment:more minor internal cleanups. s/g.isCGI/g.isHTTP/ to avoid confusion later on.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1: 9adc95c47600c561e307fe2f9d7ff3a2107120c6
User & Date: stephan 2011-09-20 16:45:57.274
Context
2011-09-20
16:48
merged trunk [1f498a6ef2]. ... (check-in: c2b1bc68 user: stephan tags: json)
16:45
more minor internal cleanups. s/g.isCGI/g.isHTTP/ to avoid confusion later on. ... (check-in: 9adc95c4 user: stephan tags: json)
16:27
minor internal cleanups and doc additions. ... (check-in: 2f3e4385 user: stephan tags: json)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;
  json_main_bootstrap();
  g.isCGI = 1;
  cgi_destination(CGI_BODY);

  z = (char*)P("HTTP_COOKIE");
  if( z ){
    z = mprintf("%s",z);
    add_param_list(z, ';');
  }







|







763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;
  json_main_bootstrap();
  g.isHTTP = 1;
  cgi_destination(CGI_BODY);

  z = (char*)P("HTTP_COOKIE");
  if( z ){
    z = mprintf("%s",z);
    add_param_list(z, ';');
  }
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
  if( g.json.isJsonMode ){
    char * zMsg;
    va_start(ap, zFormat);
    zMsg = vmprintf(zFormat,ap);
    va_end(ap);
    json_err( FSL_JSON_E_PANIC, zMsg, 1 );
    free(zMsg);
    fossil_exit( g.isCGI ? 0 : 1 );
  }else{
    cgi_set_status(500, "Internal Server Error");
    cgi_printf(
      "<html><body><h1>Internal Server Error</h1>\n"
      "<plaintext>"
    );
    va_start(ap, zFormat);







|







1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
  if( g.json.isJsonMode ){
    char * zMsg;
    va_start(ap, zFormat);
    zMsg = vmprintf(zFormat,ap);
    va_end(ap);
    json_err( FSL_JSON_E_PANIC, zMsg, 1 );
    free(zMsg);
    fossil_exit( g.isHTTP ? 0 : 1 );
  }else{
    cgi_set_status(500, "Internal Server Error");
    cgi_printf(
      "<html><body><h1>Internal Server Error</h1>\n"
      "<plaintext>"
    );
    va_start(ap, zFormat);
Changes to src/db.c.
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
      "need to run \"fossil all rebuild\" to bring the repository\n"
      "schemas up to date.\n";
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.json.isJsonMode ){
    json_err( 0, z, 1 );
    if( g.isCGI ){
      rc = 0 /* avoid HTTP 500 */;
    }
  }else{
    if( g.xferPanic ){
      cgi_reset_content();
      @ error Database\serror:\s%F(z)
      cgi_reply();







|







64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
      "need to run \"fossil all rebuild\" to bring the repository\n"
      "schemas up to date.\n";
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.json.isJsonMode ){
    json_err( 0, z, 1 );
    if( g.isHTTP ){
      rc = 0 /* avoid HTTP 500 */;
    }
  }else{
    if( g.xferPanic ){
      cgi_reset_content();
      @ error Database\serror:\s%F(z)
      cgi_reply();
Changes to src/json.c.
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
      window here. We may need to change the JSON auth token to be
      in the form: login_cookie_name()=...

      Then again, the hardened cookie value helps ensure that
      only a proper key/value match is valid.
      */
      cgi_replace_parameter( login_cookie_name(), cson_value_get_cstr(g.json.authToken) );
    }else if( g.isCGI ){
      /* try fossil's conventional cookie. */
      /* Reminder: chicken/egg scenario regarding db access in CLI
         mode because login_cookie_name() needs the db. CLI
         mode does not use any authentication, so we don't need
         to support it here.
      */
      char const * zCookie = P(login_cookie_name());







|







405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
      window here. We may need to change the JSON auth token to be
      in the form: login_cookie_name()=...

      Then again, the hardened cookie value helps ensure that
      only a proper key/value match is valid.
      */
      cgi_replace_parameter( login_cookie_name(), cson_value_get_cstr(g.json.authToken) );
    }else if( g.isHTTP ){
      /* try fossil's conventional cookie. */
      /* Reminder: chicken/egg scenario regarding db access in CLI
         mode because login_cookie_name() needs the db. CLI
         mode does not use any authentication, so we don't need
         to support it here.
      */
      char const * zCookie = P(login_cookie_name());
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
    return;
  }else{
    once = 1;
  }
  g.json.isJsonMode = 1;
  g.json.resultCode = 0;
  g.json.cmd.offset = -1;
  if( !g.isCGI && g.fullHttpReply ){
    /* workaround for server mode, so we see it as CGI mode. */
    g.isCGI = 1;
  }
  if(! g.json.post.v ){
    /* If cgi_init() reads POSTed JSON then it sets the content type.
       If it did not then we need to set it.
    */
    cgi_set_content_type(json_guess_content_type());
  }







|

|







488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
    return;
  }else{
    once = 1;
  }
  g.json.isJsonMode = 1;
  g.json.resultCode = 0;
  g.json.cmd.offset = -1;
  if( !g.isHTTP && g.fullHttpReply ){
    /* workaround for server mode, so we see it as CGI mode. */
    g.isHTTP = 1;
  }
  if(! g.json.post.v ){
    /* If cgi_init() reads POSTed JSON then it sets the content type.
       If it did not then we need to set it.
    */
    cgi_set_content_type(json_guess_content_type());
  }
520
521
522
523
524
525
526
527
528
529
530
531

532
533
534
535
536
537
538
539

540
541
542




543
544
545
546
547
548
549
    avoid CLI-only special-case handling in other code, e.g.
    json_command_arg().
  */
  if( zPath ){/* Either CGI or server mode... */
    /* Translate PATH_INFO into JSON for later convenience. */
    char const * p = zPath /* current byte */;
    char const * head = p  /* current start-of-token */;
    unsigned int len = 0   /* current token's lengh */;
    assert( g.isCGI && "g.isCGI should have been set by now." );
    for( ; ; ++p){
      if( !*p || ('/' == *p) ){
        if( len ){

          cson_value * part;
          char * zPart;
          assert( head != p );
          zPart = (char*)malloc(len+1);
          assert( zPart != NULL );
          memcpy(zPart, head, len);
          zPart[len] = 0;
          dehttpize(zPart);

          part = cson_value_new_string(zPart, strlen(zPart));
          free(zPart);
          cson_array_append( g.json.cmd.a, part );




          len = 0;
        }
        if( !*p ){
          break;
        }
        head = p+1;
        continue;







|
|


|
>
|
|


|



>
|
<
|
>
>
>
>







520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542

543
544
545
546
547
548
549
550
551
552
553
554
    avoid CLI-only special-case handling in other code, e.g.
    json_command_arg().
  */
  if( zPath ){/* Either CGI or server mode... */
    /* Translate PATH_INFO into JSON for later convenience. */
    char const * p = zPath /* current byte */;
    char const * head = p  /* current start-of-token */;
    unsigned int len = 0   /* current token's length */;
    assert( g.isHTTP && "g.isHTTP should have been set by now." );
    for( ; ; ++p){
      if( !*p || ('/' == *p) ){
        if( len ){/* append head..(head+len) as next array
                     element. */
          cson_value * part = NULL;
          char * zPart = NULL;
          assert( head != p );
          zPart = (char*)malloc(len+1);
          assert( (zPart != NULL) && "malloc failure" );
          memcpy(zPart, head, len);
          zPart[len] = 0;
          dehttpize(zPart);
          if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
            part = cson_value_new_string(zPart, strlen(zPart));

            cson_array_append( g.json.cmd.a, part );
          }else{
            assert(0 && "i didn't think this was possible!");
          }
          free(zPart);
          len = 0;
        }
        if( !*p ){
          break;
        }
        head = p+1;
        continue;
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }

  {/* set up JSON output formatting options. */
    unsigned char indent = g.isCGI ? 0 : 1;
    cson_value const * indentV = json_getenv("indent");
    if(indentV){
      if(cson_value_is_string(indentV)){
        int const n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
        indent = (n>0)
          ? (unsigned char)n
          : 0;
      }else if(cson_value_is_number(indentV)){
        cson_int_t const n = cson_value_get_integer(indentV);
        indent = (n>0) ? (unsigned char)n : 0;
      }
    }
    g.json.outOpt.indentation = indent;
    g.json.outOpt.addNewline = g.isCGI ? 0 : 1;
  }

  if( g.isCGI ){
    json_auth_token()/* will copy our auth token, if any, to fossil's
                        core, which we need before we call
                        login_check_credentials(). */;
    login_check_credentials()/* populates g.perm */;
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);







|













|


|







578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
    g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
        /* g.json.reqPayload.o may legally be NULL, which means only that
           g.json.reqPayload.v is-not-a Object.
        */;
  }

  {/* set up JSON output formatting options. */
    unsigned char indent = g.isHTTP ? 0 : 1;
    cson_value const * indentV = json_getenv("indent");
    if(indentV){
      if(cson_value_is_string(indentV)){
        int const n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
        indent = (n>0)
          ? (unsigned char)n
          : 0;
      }else if(cson_value_is_number(indentV)){
        cson_int_t const n = cson_value_get_integer(indentV);
        indent = (n>0) ? (unsigned char)n : 0;
      }
    }
    g.json.outOpt.indentation = indent;
    g.json.outOpt.addNewline = g.isHTTP ? 0 : 1;
  }

  if( g.isHTTP ){
    json_auth_token()/* will copy our auth token, if any, to fossil's
                        core, which we need before we call
                        login_check_credentials(). */;
    login_check_credentials()/* populates g.perm */;
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
    short i = 0;
#define NEXT cson_string_cstr(          \
                 cson_value_get_string( \
                   cson_array_get(ar,i) \
                   ))
    char const * tok = NEXT;
    while( tok ){
      if( !g.isCGI/*workaround for "abbreviated name" in CLI mode*/
          ? (0==strcmp(g.argv[1],tok))
          : (0==strncmp("json",tok,4))
          ){
        g.json.cmd.offset = i;
        break;
      }
      ++i;







|







628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
    short i = 0;
#define NEXT cson_string_cstr(          \
                 cson_value_get_string( \
                   cson_array_get(ar,i) \
                   ))
    char const * tok = NEXT;
    while( tok ){
      if( !g.isHTTP/*workaround for "abbreviated name" in CLI mode*/
          ? (0==strcmp(g.argv[1],tok))
          : (0==strncmp("json",tok,4))
          ){
        g.json.cmd.offset = i;
        break;
      }
      ++i;
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
    if(g.json.param.v){
      tmp = g.json.param.v;
      SET("$params");
    }
    if(0){/*Only for debuggering, add some info to the response.*/
      tmp = cson_value_new_integer( g.json.cmd.offset );
      cson_object_set( o, "cmd.offset", tmp );
      cson_object_set( o, "isCGI", cson_value_new_bool( g.isCGI ) );
    }
  }

  /* Only add the payload to SUCCESS responses. Else delete it. */
  if( NULL != payload ){
    if( resultCode ){
      cson_value_free(payload);







|







838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
    if(g.json.param.v){
      tmp = g.json.param.v;
      SET("$params");
    }
    if(0){/*Only for debuggering, add some info to the response.*/
      tmp = cson_value_new_integer( g.json.cmd.offset );
      cson_object_set( o, "cmd.offset", tmp );
      cson_object_set( o, "isCGI", cson_value_new_bool( g.isHTTP ) );
    }
  }

  /* Only add the payload to SUCCESS responses. Else delete it. */
  if( NULL != payload ){
    if( resultCode ){
      cson_value_free(payload);
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886

/*
** Outputs a JSON error response to either the cgi_xxx() family of
** buffers (in CGI/server mode) or stdout (in CLI mode). If rc is 0
** then g.json.resultCode is used. If that is also 0 then the "Unknown
** Error" code is used.
**
** If g.isCGI then the generated JSON error response object replaces
** any currently buffered page output. Because the output goes via
** the cgi_xxx() family of functions, this function inherits any
** compression which fossil does for its output.
**
** If alsoOutput is true AND g.isCGI then cgi_reply() is called to
** flush the output (and headers). Generally only do this if you are
** about to call exit().
**
** !g.isCGI then alsoOutput is ignored and all output is sent to
** stdout immediately.
**
*/
void json_err( int code, char const * msg, char alsoOutput ){
  int rc = code ? code : (g.json.resultCode
                          ? g.json.resultCode
                          : FSL_JSON_E_UNKNOWN);







|




|



|







868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891

/*
** Outputs a JSON error response to either the cgi_xxx() family of
** buffers (in CGI/server mode) or stdout (in CLI mode). If rc is 0
** then g.json.resultCode is used. If that is also 0 then the "Unknown
** Error" code is used.
**
** If g.isHTTP then the generated JSON error response object replaces
** any currently buffered page output. Because the output goes via
** the cgi_xxx() family of functions, this function inherits any
** compression which fossil does for its output.
**
** If alsoOutput is true AND g.isHTTP then cgi_reply() is called to
** flush the output (and headers). Generally only do this if you are
** about to call exit().
**
** !g.isHTTP then alsoOutput is ignored and all output is sent to
** stdout immediately.
**
*/
void json_err( int code, char const * msg, char alsoOutput ){
  int rc = code ? code : (g.json.resultCode
                          ? g.json.resultCode
                          : FSL_JSON_E_UNKNOWN);
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
    /* about the only error case here is out-of-memory. DO NOT
       call fossil_panic() here because that calls this function.
    */
    fprintf(stderr, "%s: Fatal error: could not allocate "
            "response object.\n", fossil_nameofexe());
    fossil_exit(1);
  }
  if( g.isCGI ){
    Blob buf = empty_blob;
    cgi_reset_content();
    cson_output_Blob( resp, &buf, &g.json.outOpt );
    cgi_set_content(&buf);
    if( alsoOutput ){
      cgi_reply();
    }







|







899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
    /* about the only error case here is out-of-memory. DO NOT
       call fossil_panic() here because that calls this function.
    */
    fprintf(stderr, "%s: Fatal error: could not allocate "
            "response object.\n", fossil_nameofexe());
    fossil_exit(1);
  }
  if( g.isHTTP ){
    Blob buf = empty_blob;
    cgi_reset_content();
    cson_output_Blob( resp, &buf, &g.json.outOpt );
    cgi_set_content(&buf);
    if( alsoOutput ){
      cgi_reply();
    }
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147

/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(unsigned int depth){
  cson_value const *token = g.json.authToken;
    /* Remember that json_bootstrap() replaces the login cookie with
       the JSON auth token if the request contains it. If the reqest
       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
       i'll assume it would be for the time being).
    */







|
|
|







1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152

/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(unsigned int depth){
  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
       reqest 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
       i'll assume it would be for the time being).
    */
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 1);
  }else{
    payload = json_create_response(rc, payload, NULL);
    cson_output_FILE( payload, stdout, &g.json.outOpt );
    cson_value_free( payload );
    if((0 != rc) && !g.isCGI){
      /* FIXME: we need a way of passing this error back
         up to the routine which called this callback.
         e.g. add g.errCode.
      */
      fossil_exit(1);
    }
  }
  return;
  usage:
  usage("subcommand");
}







|











1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 1);
  }else{
    payload = json_create_response(rc, payload, NULL);
    cson_output_FILE( payload, stdout, &g.json.outOpt );
    cson_value_free( payload );
    if((0 != rc) && !g.isHTTP){
      /* FIXME: we need a way of passing this error back
         up to the routine which called this callback.
         e.g. add g.errCode.
      */
      fossil_exit(1);
    }
  }
  return;
  usage:
  usage("subcommand");
}
Changes to src/main.c.
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */
  int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
  int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
  int *aCommitFile;       /* Array of files to be committed */
  int markPrivate;        /* All new artifacts are private if true */
  int clockSkewSeen;      /* True if clocks on client and server out of sync */
  int isCGI;              /* True if running in server/CGI modes, else assume CLI. */

  int urlIsFile;          /* True if a "file:" url */
  int urlIsHttps;         /* True if a "https:" url */
  int urlIsSsh;           /* True if an "ssh:" url */
  char *urlName;          /* Hostname for http: or filename for file: */
  char *urlHostname;      /* The HOST: parameter on http headers */
  char *urlProtocol;      /* "http" or "https" */







|







114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
  FILE *httpIn;           /* Accept HTTP input from here */
  FILE *httpOut;          /* Send HTTP output here */
  int xlinkClusterOnly;   /* Set when cloning.  Only process clusters */
  int fTimeFormat;        /* 1 for UTC.  2 for localtime.  0 not yet selected */
  int *aCommitFile;       /* Array of files to be committed */
  int markPrivate;        /* All new artifacts are private if true */
  int clockSkewSeen;      /* True if clocks on client and server out of sync */
  int isHTTP;             /* True if running in server/CGI modes, else assume CLI. */

  int urlIsFile;          /* True if a "file:" url */
  int urlIsHttps;         /* True if a "https:" url */
  int urlIsSsh;           /* True if an "ssh:" url */
  char *urlName;          /* Hostname for http: or filename for file: */
  char *urlHostname;      /* The HOST: parameter on http headers */
  char *urlProtocol;      /* "http" or "https" */
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#endif
  g.json.outOpt = cson_output_opt_empty;
  g.json.outOpt.addNewline = 1;
  g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
  for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
  if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
    zCmdName = "cgi";
    g.isCGI = 1;
  }else if( argc<2 ){
    fossil_fatal("Usage: %s COMMAND ...\n"
                 "\"%s help\" for a list of available commands\n"
                 "\"%s help COMMAND\" for specific details\n",
                 argv[0], argv[0], argv[0]);
  }else{
    g.isCGI = 0;
    g.fQuiet = find_option("quiet", 0, 0)!=0;
    g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
    g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
    g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
    if( g.fSqlTrace ) g.fSqlStats = 1;
    g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
    g.fHttpTrace = find_option("httptrace", 0, 0)!=0;







|






|







312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
#endif
  g.json.outOpt = cson_output_opt_empty;
  g.json.outOpt.addNewline = 1;
  g.json.outOpt.indentation = 1 /* in CGI/server mode this can be configured */;
  for(i=0; i<argc; i++) g.argv[i] = fossil_mbcs_to_utf8(argv[i]);
  if( getenv("GATEWAY_INTERFACE")!=0 && !find_option("nocgi", 0, 0)){
    zCmdName = "cgi";
    g.isHTTP = 1;
  }else if( argc<2 ){
    fossil_fatal("Usage: %s COMMAND ...\n"
                 "\"%s help\" for a list of available commands\n"
                 "\"%s help COMMAND\" for specific details\n",
                 argv[0], argv[0], argv[0]);
  }else{
    g.isHTTP = 0;
    g.fQuiet = find_option("quiet", 0, 0)!=0;
    g.fSqlTrace = find_option("sqltrace", 0, 0)!=0;
    g.fSqlStats = find_option("sqlstats", 0, 0)!=0;
    g.fSystemTrace = find_option("systemtrace", 0, 0)!=0;
    if( g.fSqlTrace ) g.fSqlStats = 1;
    g.fSqlPrint = find_option("sqlprint", 0, 0)!=0;
    g.fHttpTrace = find_option("httptrace", 0, 0)!=0;
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  static int once = 1;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.json.isJsonMode ){
    json_err( 0, z, 1 );
    if( g.isCGI ){
      rc = 0 /* avoid HTTP 500 */;
    }
  }else if( g.cgiOutput && once ){
    once = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();
  }else{







|







416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
  static int once = 1;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.json.isJsonMode ){
    json_err( 0, z, 1 );
    if( g.isHTTP ){
      rc = 0 /* avoid HTTP 500 */;
    }
  }else if( g.cgiOutput && once ){
    once = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();
  }else{
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  va_list ap;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.json.isJsonMode ){
    json_err( g.json.resultCode, z, 1 );
    if( g.isCGI ){
      rc = 0 /* avoid HTTP 500 */;
    }
  }
  else if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();







|







442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
  va_list ap;
  mainInFatalError = 1;
  va_start(ap, zFormat);
  z = vmprintf(zFormat, ap);
  va_end(ap);
  if( g.json.isJsonMode ){
    json_err( g.json.resultCode, z, 1 );
    if( g.isHTTP ){
      rc = 0 /* avoid HTTP 500 */;
    }
  }
  else if( g.cgiOutput ){
    g.cgiOutput = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);
    cgi_reply();