Fossil

Check-in [4cf96814]
Login

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

Overview
Comment:Factored out cson_cgi bits - now using fossil's CGI bits. Removed cson_cgi from cson_amalgamation (cuts its size considerably). Seems to still work, and this removes some discrepancies in how CGI/server modes are handled.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1: 4cf968144093bb856a42e49911f0d04a277abc9c
User & Date: stephan 2011-09-18 04:31:49.037
Context
2011-09-18
05:45
More cleanups to the cson_cgi removal refactoring. Added common "indent" parameter to control indentation of JSON (uses cson_output_opt.indentation semantics). ... (check-in: b3653265 user: stephan tags: json)
04:31
Factored out cson_cgi bits - now using fossil's CGI bits. Removed cson_cgi from cson_amalgamation (cuts its size considerably). Seems to still work, and this removes some discrepancies in how CGI/server modes are handled. ... (check-in: 4cf96814 user: stephan tags: json)
2011-09-17
23:14
Doc changes only ... (check-in: 5a9e5679 user: stephan tags: json)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cgi.c.
506
507
508
509
510
511
512

513
514
515
516
517
518
519
    }else{
      if( *z ){ *z++ = 0; }
      zValue = "";
    }
    if( fossil_islower(zName[0]) ){
      cgi_set_parameter_nocopy(zName, zValue);
    }

  }
}

/*
** *pz is a string that consists of multiple lines of text.  This
** routine finds the end of the current line of text and converts
** the "\n" or "\r\n" that ends that line into a "\000".  It then







>







506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
    }else{
      if( *z ){ *z++ = 0; }
      zValue = "";
    }
    if( fossil_islower(zName[0]) ){
      cgi_set_parameter_nocopy(zName, zValue);
    }
    json_setenv( zName, cson_value_new_string(zValue,strlen(zValue)) );
  }
}

/*
** *pz is a string that consists of multiple lines of text.  This
** routine finds the end of the current line of text and converts
** the "\n" or "\r\n" that ends that line into a "\000".  It then
678
679
680
681
682
683
684








































































685
686
687
688
689
690
691
692
693
694

695







696
697
698
699
700
701
702

703

704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727



728



729

730
731
732
733
734
735
736
737
738
739
740
741
742
743
            cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z);
          }
        }
      }
    }
  }        
}









































































/*
** Initialize the query parameter database.  Information is pulled from
** the QUERY_STRING environment variable (if it exists), from standard
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;

  cgi_destination(CGI_BODY);







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

  z = (char*)P("REMOTE_ADDR");

  if( z ) g.zIpAddr = mprintf("%s", z);


  len = atoi(PD("CONTENT_LENGTH", "0"));
  g.zContentType = zType = P("CONTENT_TYPE");
  if( !g.json.isJsonMode && (len>0 && zType) ){/* in JSON mode this is delegated to the cson_cgi API.*/
    blob_zero(&g.cgiIn);
    if( fossil_strcmp(zType,"application/x-www-form-urlencoded")==0 
         || strncmp(zType,"multipart/form-data",19)==0 ){
      z = fossil_malloc( len+1 );
      len = fread(z, 1, len, g.httpIn);
      z[len] = 0;
      if( zType[0]=='a' ){
        add_param_list(z, '&');
      }else{
        process_multipart_form_data(z, len);
      }
    }else if( fossil_strcmp(zType, "application/x-fossil")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
      blob_uncompress(&g.cgiIn, &g.cgiIn);
    }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
    }
    /* FIXME: treat application/json and text/plain as unencoded



       JSON data.



    */

  }

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

/*
** This is the comparison function used to sort the aParamQP[] array of
** query parameters and cookies.
*/
static int qparam_compare(const void *a, const void *b){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>

>
>
>
>
>
>
>







>
|
>



|


















<
|
>
>
>
|
>
>
>
|
>
|
|
<
<
<
<
|







679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
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
797
798
799
800
801
802
803
804
805
806
807
808

809
810
811
812
813
814
815
816
817
818
819
820




821
822
823
824
825
826
827
828
            cgi_set_parameter_nocopy(mprintf("%s:mimetype",zName), z);
          }
        }
      }
    }
  }        
}


/*
** Internal helper for cson_data_source_FILE_n().
*/
typedef struct CgiPostReadState_ {
    FILE * fh;
    unsigned int len;
    unsigned int pos;
} CgiPostReadState;

/*
** cson_data_source_f() impl which reads only up to
** a specified amount of data from its input FILE.
** state MUST be a full populated (CgiPostReadState*).
*/
static int cson_data_source_FILE_n( void * state,
                                    void * dest,
                                    unsigned int * n ){
    if( ! state || !dest || !n ) return cson_rc.ArgError;
    else {
      CgiPostReadState * st = (CgiPostReadState *)state;
      if( st->pos >= st->len ){
        *n = 0;
        return 0;
      } else if( !*n || ((st->pos + *n) > st->len) ){
        return cson_rc.RangeError;
      }else{
        unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
        if( ! rsz ){
          *n = rsz;
          return feof(st->fh) ? 0 : cson_rc.IOError;
        }else{
          *n = rsz;
          st->pos += *n;
          return 0;
        }
      }
    }
}

/*
** Reads a JSON object from the first contentLen bytes of zIn.
** On success 0 is returned and g.json.post is updated to hold
** the content. On error non-0 is returned.
*/
static int cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
  cson_value * jv = NULL;
  int rc;
  CgiPostReadState state;
  cson_parse_info pinfo = cson_parse_info_empty;
  assert( 0 != contentLen );
  state.fh = zIn;
  state.len = contentLen;
  state.pos = 0;
  rc = cson_parse( &jv, cson_data_source_FILE_n, &state, NULL, &pinfo );
  if( rc ){
#if 0
    fprintf(stderr, "%s: Parsing POST as JSON failed: code=%d (%s) line=%u, col=%u\n",
            __FILE__, rc, cson_rc_string(rc), pinfo.line, pinfo.col );
#endif
    return rc;
  }
  rc = json_gc_add( "$POST", jv, 1 );
  if( 0 == rc ){
    g.json.post.v = jv;
    g.json.post.o = cson_value_get_object( jv );
    assert( g.json.post.o && "FIXME: also support an Array as POST data node." ); 
  }
  return rc;
}


/*
** Initialize the query parameter database.  Information is pulled from
** the QUERY_STRING environment variable (if it exists), from standard
** input if there is POST data, and from HTTP_COOKIE.
*/
void cgi_init(void){
  char *z;
  const char *zType;
  int len;
  json_main_bootstrap();
  cgi_destination(CGI_BODY);

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

  z = (char*)P("REMOTE_ADDR");
  if( z ){
    g.zIpAddr = mprintf("%s", z);
  }

  len = atoi(PD("CONTENT_LENGTH", "0"));
  g.zContentType = zType = P("CONTENT_TYPE");
  if( len>0 && zType ){
    blob_zero(&g.cgiIn);
    if( fossil_strcmp(zType,"application/x-www-form-urlencoded")==0 
         || strncmp(zType,"multipart/form-data",19)==0 ){
      z = fossil_malloc( len+1 );
      len = fread(z, 1, len, g.httpIn);
      z[len] = 0;
      if( zType[0]=='a' ){
        add_param_list(z, '&');
      }else{
        process_multipart_form_data(z, len);
      }
    }else if( fossil_strcmp(zType, "application/x-fossil")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
      blob_uncompress(&g.cgiIn, &g.cgiIn);
    }else if( fossil_strcmp(zType, "application/x-fossil-debug")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);
    }else if( fossil_strcmp(zType, "application/x-fossil-uncompressed")==0 ){
      blob_read_from_channel(&g.cgiIn, g.httpIn, len);

    }else if( fossil_strcmp(zType, "application/json")
              || fossil_strcmp(zType,"text/plain")/*assume this MIGHT be JSON*/
              || fossil_strcmp(zType,"application/javascript")){
      g.json.isJsonMode = 1;
      cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
      cgi_set_content_type("application/json")
        /* FIXME: guess a proper content type value based on the
           Accept header.  We have code for this in cson_cgi.
        */
        ;
    }
  }





}

/*
** This is the comparison function used to sort the aParamQP[] array of
** query parameters and cookies.
*/
static int qparam_compare(const void *a, const void *b){
Changes to src/json.c.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
** Code for the JSON API.
**
** For notes regarding the public JSON interface, please see:
**
** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit
**
**
** Notable FIXMEs:
**
** - The overlap between cson_cgi and fossil needs to be gotten rid
** of because cson_cgi cannot get all the environment info it needs
** when fossil is running in server mode. The goal is to remove all
** of the cson_cgi bits.
*/
#include "config.h"
#include "VERSION.h"
#include "json.h"
#include <assert.h>
#include <time.h>








<
<
<
<
<
<







18
19
20
21
22
23
24






25
26
27
28
29
30
31
** Code for the JSON API.
**
** For notes regarding the public JSON interface, please see:
**
** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit
**
**






*/
#include "config.h"
#include "VERSION.h"
#include "json.h"
#include <assert.h>
#include <time.h>

154
155
156
157
158
159
160























161
162
163
164
165
166
167
168
169
170

171











































172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200

201
202
203
204
205
206
207
208
209
210
211
212
213
214
215



216
217
218
219
220
221
222
223
224
225
226
227
228
229





















230



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300

301





302

303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
char const * json_rc_cstr( int code ){
  enum { BufSize = 12 };
  static char buf[BufSize] = {'F','O','S','S','I','L','-',0};
  assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
  sprintf(buf+7,"%04d", code);
  return buf;
}
























/*
** Returns the value of json_rc_cstr(code) as a new JSON
** string, which is owned by the caller and must eventually
** be cson_value_free()d or transfered to a JSON container.
*/
cson_value * json_rc_string( int code ){
  return cson_value_new_string( json_rc_cstr(code), 11 );
}


/*











































** Returns the current request's JSON authentication token, or NULL if
** none is found. The token's memory is owned by (or shared with)
** g.json.cgiCx.
**
** If an auth token is found in the GET/POST JSON request data then
** fossil is given that data for use in authentication for this
** session.
**
** Must be called once before login_check_credentials() is called or
** we will not be able to replace fossil's internal idea of the auth
** info in time (and future changes to that state may cause unexpected
** results).
**
** The result of this call are cached for future calls.
*/
static cson_value * json_auth_token(){
  if( !g.json.authToken ){
    /* Try to get an authorization token from GET parameter, POSTed
       JSON, or fossil cookie (in that order). */
    g.json.authToken = cson_cgi_getenv(&g.json.cgiCx, "gp", FossilJsonKeys.authToken)
      /* reminder to self: cson_cgi does not have access to the cookies
         because fossil's core consumes them. Thus we cannot use "agpc"
         here. We use "a" (App-specific) as a place to store fossil's
         cookie value. Reminder #2: in server mode cson_cgi also doesn't
         have access to the GET parameters because of how the QUERY_STRING
         is set. That's on my to-fix list for cson_cgi (feeding it our own
         query string).
      */;
    if(g.json.authToken && cson_value_is_string(g.json.authToken)){

      /* tell fossil to use this login info.
         
         FIXME: because the JSON bits don't carry around
         login_cookie_name(), there is a potential login hijacking
         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. */



      char const * zCookie = P(login_cookie_name());
      if( zCookie && *zCookie ){
        /* Transfer fossil's cookie to JSON for downstream convenience... */
        cson_value * v = cson_value_new_string(zCookie, strlen(zCookie));
        if( 0 == cson_cgi_gc_add( &g.json.cgiCx, FossilJsonKeys.authToken, v, 1 ) ){
          g.json.authToken = v;
        }
      }
    }
  }
  return g.json.authToken;
}

/*





















** Performs some common initialization of JSON-related state.



** Implicitly sets up the login information state in CGI mode, but
** does not perform any authentication here. It _might_ (haven't
** 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.
*/
static void json_mode_bootstrap(){
  static char once = 0  /* guard against multiple runs */;
  char const * zPath = P("PATH_INFO");
  cson_value * pathSplit =
    cson_cgi_getenv(&g.json.cgiCx,"e","PATH_INFO_SPLIT");
  assert( (0==once) && "json_mode_bootstrap() called too many times!");
  if( once ){
    return;
  }else{
    once = 1;
  }
  g.json.isJsonMode = 1;
  g.json.resultCode = 0;
  g.json.cmdOffset = -1;
  if( !g.isCGI && g.fullHttpReply ){
    /* workaround for server mode, so we see it as CGI mode. */
    g.isCGI = 1;
  }
#if defined(NDEBUG)
  /* avoids debug messages on stderr in JSON mode */
  sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0);
#endif




  /*
    The following if/else block translates the PATH_INFO path (in
    CLI/server modes) or g.argv (CLI mode) into an internal list so
    that we can simplify command dispatching later on.

    Note that translating g.argv this way is overkill but allows us to
    avoid CLI-only special-case handling in other code, e.g.
    json_command_arg().
  */
  if( pathSplit ){
    /* cson_cgi already did this, so let's just re-use it. This does
       not happen in "plain server" mode, but does in CGI mode.
    */
    assert( g.isCGI && "g.isCGI should have been set by now." );
    cson_cgi_setenv( &g.json.cgiCx,
                     FossilJsonKeys.commandPath,
                     pathSplit );
  }else{ /* either CLI or server mode... */
    cson_value * arV       /* value to store path in */;
    cson_array * ar        /* the "real" array object */;
    arV = cson_value_new_array();
    ar = cson_value_get_array(arV);
    cson_cgi_setenv( &g.json.cgiCx,
                     FossilJsonKeys.commandPath,
                     arV );
    if( zPath ){
      /* Translate fossil's PATH_INFO into cson_cgi for later
         convenience, to help consolidate how we handle CGI/server
         modes. This block is hit when running in plain server mode.
      */
      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!='?'; ++p){
        if( !*p || ('/' == *p) ){
          if( len ){
            cson_value * part;

            assert( head != p );





            part = cson_value_new_string(head, len);

            cson_array_append( ar, part );
            len = 0;
          }
          if( !*p ){
            break;
          }
          head = p+1;
          continue;
        }
        ++len;
      }
    }else{
      /* assume CLI mode */
      int i;
      char const * arg;
      cson_value * part;
      assert( (!g.isCGI) && "g.isCGI set and we do not expect that to be the case here." );
      for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
        arg = g.argv[i];
        if( !arg || !*arg ){
          continue;
        }
        part = cson_value_new_string(arg,strlen(arg));
        cson_array_append(ar, part);
      }
    }
  }
  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc. */
  g.json.reqPayload.v = cson_cgi_getenv( &g.json.cgiCx, "p", "payload" );
  if( g.json.reqPayload.v ){
    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.
        */;
  }
  json_auth_token()/* will copy our auth token, if any, to fossil's core. */;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
















|
|
<
<
<
<
<
<
<
|
>

|
|
|
|
|

|
|





|
>
>
>




|
|
<







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>

|










|
|








|









>
>
>









<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
|
|
|
|
|
|
|
|
|
|
|
|
>
|
>
>
>
>
>
|
>
|
|
|
|
|
|
|
|
|
|
|
<
|
|
|
|
<
|
|
|
|
|
|
|
|
|
|



|







148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253







254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279

280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355








356








357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388

389
390
391
392

393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
char const * json_rc_cstr( int code ){
  enum { BufSize = 12 };
  static char buf[BufSize] = {'F','O','S','S','I','L','-',0};
  assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
  sprintf(buf+7,"%04d", code);
  return buf;
}

/*
** Adds v to the API-internal cleanup mechanism. key must be a unique
** key for the given element. Adding another item with that key may
** free the previous one. If freeOnError is true then v is passed to
** cson_value_free() if the key cannot be inserted, otherweise
** ownership of v is not changed on error.
**
** Returns 0 on success.
**
*** On success, ownership of v is transfered to (or shared with)
*** g.json.gc, and v will be valid until that object is cleaned up or
*** its key is replaced via another call to this function.
*/
int json_gc_add( char const * key, cson_value * v, char freeOnError ){
  int const rc = cson_object_set( g.json.gc.o, key, v );
  assert( NULL != g.json.gc.o );
  if( (0 != rc) && freeOnError ){
    cson_value_free( v );
  }
  return rc;
}


/*
** Returns the value of json_rc_cstr(code) as a new JSON
** string, which is owned by the caller and must eventually
** be cson_value_free()d or transfered to a JSON container.
*/
cson_value * json_rc_string( int code ){
  return cson_value_new_string( json_rc_cstr(code), 11 );
}


/*
** Gets a POST/GET/COOKIE value. The returned memory is owned by the
** g.json object (one of its sub-objects). Returns NULL if no match is
** found.
**
** Precedence: GET, COOKIE, POST. COOKIE _should_ be last
** but currently is not for internal order-of-init reasons.
** Since fossil only uses one cookie, this is not a high-prio
** problem.
*/
cson_value * json_getenv( char const * zKey ){
  cson_value * rc;
  rc = cson_object_get( g.json.param.o, zKey );
  if( rc ){
    return rc;
  }else{
    rc = cson_object_get( g.json.post.o, zKey );
    if(rc){
      return rc;
    }else{
      char const * cv = PD(zKey,NULL);
      if(cv){/*transform it to JSON for later use.*/
        rc = cson_value_new_string(cv,strlen(cv));
        cson_object_set( g.json.param.o, zKey, rc );
        return rc;
      }
    }
  }
  return NULL;
}

/*
** Adds v to g.json.param.o using the given key. May cause
** any prior item with that key to be destroyed (depends on
** current reference count for that value).
** On succes, transfers ownership of v to g.json.param.o.
** On error ownership of v is not modified.
*/
int json_setenv( char const * zKey, cson_value * v ){
  return cson_object_set( g.json.param.o, zKey, v );
}


/*
** Returns the current request's JSON authentication token, or NULL if
** none is found. The token's memory is owned by (or shared with)
** g.json.
**
** If an auth token is found in the GET/POST JSON request data then
** fossil is given that data for use in authentication for this
** session.
**
** Must be called once before login_check_credentials() is called or
** we will not be able to replace fossil's internal idea of the auth
** info in time (and future changes to that state may cause unexpected
** results).
**
** The result of this call are cached for future calls.
*/
static cson_value * json_auth_token(){
  if( !g.json.authToken ){
    /* Try to get an authorization token from GET parameter, POSTed
       JSON, or fossil cookie (in that order). */
    g.json.authToken = json_getenv(FossilJsonKeys.authToken);
    if(g.json.authToken







       && cson_value_is_string(g.json.authToken)
       && !PD(login_cookie_name(),NULL)){
      /* tell fossil to use this login info.

      FIXME?: because the JSON bits don't carry around
      login_cookie_name(), there is a potential login hijacking
      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());
      if( zCookie && *zCookie ){
        /* Transfer fossil's cookie to JSON for downstream convenience... */
        cson_value * v = cson_value_new_string(zCookie, strlen(zCookie));
        json_setenv( FossilJsonKeys.authToken, v );
        g.json.authToken = v;

      }
    }
  }
  return g.json.authToken;
}

/*
** 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.
*/
void json_main_bootstrap(){
  cson_value * v;
  assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" );
  v = cson_value_new_object();
  g.json.gc.v = v;
  g.json.gc.o = cson_value_get_object(v);

  v = cson_value_new_object();
  g.json.param.v = v;
  g.json.param.o = cson_value_get_object(v);
  json_gc_add("$PARAMS", v, 1);
}


/*
** Performs some common initialization of JSON-related state.  Must be
** called by the json_page_top() and json_cmd_top() dispatching
** functions to set up the JSON stat used by the dispatched functions.
**
** Implicitly sets up the login information state in CGI mode, but
** does not perform any permissions checking. It _might_ (haven't
** 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.
*/
static void json_mode_bootstrap(){
  static char once = 0  /* guard against multiple runs */;
  char const * zPath = P("PATH_INFO");
  cson_value * pathSplit = NULL;
  cson_array * ar = NULL;
  assert( (0==once) && "json_mode_bootstrap() called too many times!");
  if( once ){
    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 defined(NDEBUG)
  /* avoids debug messages on stderr in JSON mode */
  sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0);
#endif

  g.json.cmd.v = cson_value_new_array();
  ar = g.json.cmd.a = cson_value_get_array(g.json.cmd.v);
  json_gc_add( FossilJsonKeys.commandPath, g.json.cmd.v, 1 );
  /*
    The following if/else block translates the PATH_INFO path (in
    CLI/server modes) or g.argv (CLI mode) into an internal list so
    that we can simplify command dispatching later on.

    Note that translating g.argv this way is overkill but allows us to
    avoid CLI-only special-case handling in other code, e.g.
    json_command_arg().
  */








  if( zPath ){/* either CLI or server mode... */








    /* Translate fossil's PATH_INFO into cson_cgi for later
       convenience, to help consolidate how we handle CGI/server
       modes. This block is hit when running in plain server mode.
    */
    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!='?'; ++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( ar, part );
          len = 0;
        }
        if( !*p ){
          break;
        }
        head = p+1;
        continue;
      }
      ++len;
    }

  }else{/* assume CLI mode */
    int i;
    char const * arg;
    cson_value * part;

    for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
      arg = g.argv[i];
      if( !arg || !*arg ){
        continue;
      }
      part = cson_value_new_string(arg,strlen(arg));
      cson_array_append(ar, part);
    }
  }
  
  /* g.json.reqPayload exists only to simplify some of our access to
     the request payload. We currently only use this in the context of
     Object payloads, not Arrays, strings, etc. */
  g.json.reqPayload.v = cson_object_get( g.json.post.o, "payload" );
  if( g.json.reqPayload.v ){
    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.
        */;
  }
  json_auth_token()/* will copy our auth token, if any, to fossil's core. */;
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
**
** Reminder to self: this breaks in CLI mode when called with an
** abbreviated name because we rely on the full name "json" here. The
** g object probably has the short form which we can use for our
** purposes, but i haven't yet looked for it.
*/
static char const * json_command_arg(unsigned char ndx){
  cson_array * ar = cson_value_get_array(
                      cson_cgi_getenv(&g.json.cgiCx,
                                      "a",
                                      FossilJsonKeys.commandPath));
  assert((NULL!=ar) && "Internal error. Was json_mode_bootstrap() called?");
  if( g.json.cmdOffset < 0 ){
    /* first-time setup. */
    short i = 0;
#define NEXT cson_string_cstr( \
                 cson_value_get_string(                     \
                   cson_array_get(ar,i)  \
                   ))
    char const * tok = NEXT;
    while( tok ){
      if( 0==strncmp("json",tok,4) ){
        g.json.cmdOffset = i;
        break;
      }
      ++i;
      tok = NEXT;
    }
  }
#undef NEXT
  if( g.json.cmdOffset < 0 ){
    return NULL;
  }else{
    ndx = g.json.cmdOffset + ndx;
    return cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmdOffset + ndx )));
  }
}

/*
** If g.json.reqPayload.o is NULL then NULL is returned, else the
** given property is searched for in the request payload.  If found it
** is returned. The returned value is owned by (or shares ownership
** with) g.json.cgiCx, and must NOT be cson_value_free()'d by the
** caller.
*/
static cson_value * json_payload_property( char const * key ){
  return g.json.reqPayload.o ?
    cson_object_get( g.json.reqPayload.o, key )
    : NULL;
}







|
<
<
<

|


|
|
|




|







|


|
|







|







430
431
432
433
434
435
436
437



438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
**
** Reminder to self: this breaks in CLI mode when called with an
** abbreviated name because we rely on the full name "json" here. The
** g object probably has the short form which we can use for our
** purposes, but i haven't yet looked for it.
*/
static char const * json_command_arg(unsigned char ndx){
  cson_array * ar = g.json.cmd.a;



  assert((NULL!=ar) && "Internal error. Was json_mode_bootstrap() called?");
  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) \
                   ))
    char const * tok = NEXT;
    while( tok ){
      if( 0==strncmp("json",tok,4) ){
        g.json.cmd.offset = i;
        break;
      }
      ++i;
      tok = NEXT;
    }
  }
#undef NEXT
  if(g.json.cmd.offset < 0){
    return NULL;
  }else{
    ndx = g.json.cmd.offset + ndx;
    return cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmd.offset + ndx )));
  }
}

/*
** If g.json.reqPayload.o is NULL then NULL is returned, else the
** given property is searched for in the request payload.  If found it
** is returned. The returned value is owned by (or shares ownership
** with) g.json, and must NOT be cson_value_free()'d by the
** caller.
*/
static cson_value * json_payload_property( char const * key ){
  return g.json.reqPayload.o ?
    cson_object_get( g.json.reqPayload.o, key )
    : NULL;
}
424
425
426
427
428
429
430
431
432


433
434
435
436
437
438
439
  ** Reminder to self: we cannot use sub-paths with commands this way
  ** without additional string-splitting downstream. e.g. foo/bar.
  ** Alternately, we can create different JsonPageDef arrays for each
  ** subset.
  */
  char const * name;
  /*
  ** Returns a payload object for the response.
  ** If it returns a non-NULL value, the caller owns it.


  */
  cson_value * (*func)();
  /*
  ** Which mode(s) of execution does func() support:
  **
  ** <0 = CLI only, >0 = HTTP only, 0==both
  */







|
|
>
>







494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
  ** Reminder to self: we cannot use sub-paths with commands this way
  ** without additional string-splitting downstream. e.g. foo/bar.
  ** Alternately, we can create different JsonPageDef arrays for each
  ** subset.
  */
  char const * name;
  /*
  ** Returns a payload object for the response.  If it returns a
  ** non-NULL value, the caller owns it.  To trigger an error this
  ** function should set g.json.resultCode to a value from the
  ** FossilJsonCodes enum.
  */
  cson_value * (*func)();
  /*
  ** Which mode(s) of execution does func() support:
  **
  ** <0 = CLI only, >0 = HTTP only, 0==both
  */
484
485
486
487
488
489
490

491
492
493
494
495
496
497
}

#if 0
static unsigned int json_timestamp(){

}
#endif


/*
** Creates a new Fossil/JSON response envelope skeleton.  It is owned
** by the caller, who must eventually free it using cson_value_free(),
** or add it to a cson container to transfer ownership. Returns NULL
** on error.
**







>







556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
}

#if 0
static unsigned int json_timestamp(){

}
#endif


/*
** Creates a new Fossil/JSON response envelope skeleton.  It is owned
** by the caller, who must eventually free it using cson_value_free(),
** or add it to a cson container to transfer ownership. Returns NULL
** on error.
**
566
567
568
569
570
571
572
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
    tmp = json_rc_string(resultCode);
    SET("resultCode");
  }
  if( pMsg && *pMsg ){
    tmp = cson_value_new_string(pMsg,strlen(pMsg));
    SET("resultText");
  }
  tmp = cson_cgi_getenv(&g.json.cgiCx, "gp", "requestId");
  if( tmp ) cson_object_set( o, "requestId", tmp );
  if( NULL != payload ){
    if( resultCode ){
      cson_value_free(payload);
      payload = NULL;
    }else{
      tmp = payload;
      SET("payload");
    }
  }
#undef SET

  if(0){/*Only for debuggering, add some info to the response.*/
    tmp = cson_cgi_env_get_val(&g.json.cgiCx,'a', 0);
    if(tmp){
      cson_object_set( o, "$APP", tmp );
    }
    tmp = cson_value_new_integer( g.json.cmdOffset );
    cson_object_set( o, "cmdOffset", tmp );
    cson_object_set( o, "isCGI", cson_value_new_bool( g.isCGI ) );
  }

  goto ok;
  cleanup:
  cson_value_free(v);
  v = NULL;







|













<
<
<
<
|
|







639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659




660
661
662
663
664
665
666
667
668
    tmp = json_rc_string(resultCode);
    SET("resultCode");
  }
  if( pMsg && *pMsg ){
    tmp = cson_value_new_string(pMsg,strlen(pMsg));
    SET("resultText");
  }
  tmp = json_getenv("requestId");
  if( tmp ) cson_object_set( o, "requestId", tmp );
  if( NULL != payload ){
    if( resultCode ){
      cson_value_free(payload);
      payload = NULL;
    }else{
      tmp = payload;
      SET("payload");
    }
  }
#undef SET

  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 ) );
  }

  goto ok;
  cleanup:
  cson_value_free(v);
  v = NULL;
663
664
665
666
667
668
669
670

671


672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
  cson_object_set( jobj, "releaseVersionNumber",
                   cson_value_new_integer(RELEASE_VERSION_NUMBER) );
  cson_object_set( jobj, "resultCodeParanoiaLevel",
                   cson_value_new_integer(g.json.errorDetailParanoia) );
  return jval;
}

#if 0

/* we have a disconnect here between fossil's server-mode QUERY_STRING


   handling and cson_cgi's.
*/
static cson_value * json_getenv( char const *zWhichEnv, char const * zKey ){
  return cson_cgi_getenv(&g.json.cgiCx, zWhichEnv, zKey);
}
static char const * json_getenv_cstr( char const *zWhichEnv, char const * zKey ){
  char const * rc = cson_value_get_cstr( json_getenv(zWhichEnv, zKey) );
  if( !rc && zWhichEnv && (NULL!=strstr(zWhichEnv,"g")) ){
    rc = PD(zKey,NULL);
  }
  return rc;
}
#endif

/*
** Implementation for /json/cap
**
** Returned object contains details about the "capabilities" of the
** current user (what he may/may not do).
**







<
>
|
>
>
|

<
<
<
|
|
<
<
|
<
|
<







732
733
734
735
736
737
738

739
740
741
742
743
744



745
746


747

748

749
750
751
752
753
754
755
  cson_object_set( jobj, "releaseVersionNumber",
                   cson_value_new_integer(RELEASE_VERSION_NUMBER) );
  cson_object_set( jobj, "resultCodeParanoiaLevel",
                   cson_value_new_integer(g.json.errorDetailParanoia) );
  return jval;
}


/*
** Returns the string form of a json_getenv() value, but ONLY
** If that value is-a String. Non-strings are not converted
** to strings for this purpose. Returned memory is owned by
** g.json or fossil..
*/



static char const * json_getenv_cstr( char const * zKey ){
  return cson_value_get_cstr( json_getenv(zKey) );


}




/*
** Implementation for /json/cap
**
** Returned object contains details about the "capabilities" of the
** current user (what he may/may not do).
**
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
       i'll assume it would be for the time being).
    */
    ;
  if(!token){
    g.json.resultCode = FSL_JSON_E_MISSING_AUTH;
  }else{
    login_clear_login_data();
    g.json.authToken = NULL /* memory is owned by g.json.cgiCx, but
                               now lives only in the cson_cgi garbage
                               collector.*/;
  }
  return NULL;
}

/*
** Implementation of the /json/stat page/command.
**







|
<
<







915
916
917
918
919
920
921
922


923
924
925
926
927
928
929
       i'll assume it would be for the time being).
    */
    ;
  if(!token){
    g.json.resultCode = FSL_JSON_E_MISSING_AUTH;
  }else{
    login_clear_login_data();
    g.json.authToken = NULL /* memory is owned elsewhere.*/;


  }
  return NULL;
}

/*
** Implementation of the /json/stat page/command.
**
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
void json_page_top(void){
  int rc = FSL_JSON_E_UNKNOWN_COMMAND;
  Blob buf = empty_blob;
  char const * cmd;
  cson_value * payload = NULL;
  cson_value * root = NULL;
  JsonPageDef const * pageDef = NULL;
  cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) );
  json_mode_bootstrap();
  cmd = json_command_arg(1);
  /*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/
  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, cmd, 0 );
    return;







<







1066
1067
1068
1069
1070
1071
1072

1073
1074
1075
1076
1077
1078
1079
void json_page_top(void){
  int rc = FSL_JSON_E_UNKNOWN_COMMAND;
  Blob buf = empty_blob;
  char const * cmd;
  cson_value * payload = NULL;
  cson_value * root = NULL;
  JsonPageDef const * pageDef = NULL;

  json_mode_bootstrap();
  cmd = json_command_arg(1);
  /*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/
  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, cmd, 0 );
    return;
1053
1054
1055
1056
1057
1058
1059

1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = 1002;
  cson_value * payload = NULL;
  JsonPageDef const * pageDef;

  json_mode_bootstrap();
  if( g.argc<3 ){
    goto usage;
  }
  db_find_and_open_repository(0, 0);
  cmd = json_command_arg(1);
  if( !cmd || !*cmd ){
    goto usage;
  }
  cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) );
  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 );
    return;
  }else if( pageDef->runMode > 0 /*HTTP only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{







>









<







1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130

1131
1132
1133
1134
1135
1136
1137
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = 1002;
  cson_value * payload = NULL;
  JsonPageDef const * pageDef;
  json_main_bootstrap();
  json_mode_bootstrap();
  if( g.argc<3 ){
    goto usage;
  }
  db_find_and_open_repository(0, 0);
  cmd = json_command_arg(1);
  if( !cmd || !*cmd ){
    goto usage;
  }

  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 );
    return;
  }else if( pageDef->runMode > 0 /*HTTP only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{
Changes to src/main.c.
167
168
169
170
171
172
173
174

175
176


177











178
179
180
181
182
183
184
185
186

187


188


189
190
191
192
193
194
195
  const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
  const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
  int anAuxCols[MX_AUX];         /* Number of columns for option() values */
  
  int allowSymlinks;             /* Cached "allow-symlinks" option */

  struct FossilJsonBits {
    int isJsonMode;            /* True if running in JSON mode, else false. This changes

                                  how errors are reported. In JSON mode we try to always
                                  output JSON-form error responses.


                               */











    int cmdOffset;             /* Tells us which PATH_INFO/CLI args
                                  part holds the "json" command, so
                                  that we can account for sub-repos
                                  and path prefixes.  This is handled
                                  differently for CLI and CGI modes.
                               */
    int resultCode;            /* used for passing back specific codes from /json callbacks. */
    int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
    cson_cgi_cx cgiCx;         /* cson_cgi context */

    cson_output_opt outOpt;    /* formatting options for JSON mode. */


    cson_value * authToken;    /* authentication token */


    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */
  } json;
};








|
>
|
|
>
>

>
>
>
>
>
>
>
>
>
>
>
|





|
<
|
>
|
>
>
|
>
>







167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198

199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
  const char *azAuxVal[MX_AUX];  /* Value of each aux() or option() value */
  const char **azAuxOpt[MX_AUX]; /* Options of each option() value */
  int anAuxCols[MX_AUX];         /* Number of columns for option() values */
  
  int allowSymlinks;             /* Cached "allow-symlinks" option */

  struct FossilJsonBits {
    int isJsonMode;            /* True if running in JSON mode, else
                                  false. This changes how errors are
                                  reported. In JSON mode we try to
                                  always output JSON-form error
                                  responses and always exit() with
                                  code 0 to avoid an HTTP 500 error.
                               */
    int resultCode;            /* used for passing back specific codes from /json callbacks. */
    int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
    cson_output_opt outOpt;    /* formatting options for JSON mode. */
    cson_value * authToken;    /* authentication token */
    struct {                   /* "garbage collector" */
      cson_value * v;
      cson_object * o;
    } gc;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_array * a;
      int offset;              /* Tells us which PATH_INFO/CLI args
                                  part holds the "json" command, so
                                  that we can account for sub-repos
                                  and path prefixes.  This is handled
                                  differently for CLI and CGI modes.
                               */
    } cmd;

    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_object * o;
    } post;
    struct {                   /* GET/COOKIE params in JSON form. */
      cson_value * v;
      cson_object * o;
    } param;
    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */
  } json;
};

257
258
259
260
261
262
263
264


265
266
267
268
269
270
271
}

/*
** atexit() handler which frees up "some" of the resources
** used by fossil.
*/
void fossil_atexit() {
  cson_cgi_cx_clean(&g.json.cgiCx);


  if(g.db){
    db_close(0);
  }
}

/*
** This procedure runs first.







|
>
>







275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
}

/*
** atexit() handler which frees up "some" of the resources
** used by fossil.
*/
void fossil_atexit() {

  cson_value_free(g.json.gc.v);
  memset(&g.json, 0, sizeof(g.json));
  if(g.db){
    db_close(0);
  }
}

/*
** This procedure runs first.
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
  g.json.errorDetailParanoia = 2 /* FIXME: make configurable
                                    One problem we have here is that this
                                    code is needed before the db is opened,
                                    so we can't sql for it.*/;
#else
  g.json.errorDetailParanoia = 0;
#endif
  g.json.cgiCx = cson_cgi_cx_empty;
  g.json.outOpt = cson_output_opt_empty;
  g.json.outOpt.addNewline = 1;
  g.json.outOpt.indentation = 1 /* FIXME: make configurable */;
  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;







<







305
306
307
308
309
310
311

312
313
314
315
316
317
318
  g.json.errorDetailParanoia = 2 /* FIXME: make configurable
                                    One problem we have here is that this
                                    code is needed before the db is opened,
                                    so we can't sql for it.*/;
#else
  g.json.errorDetailParanoia = 0;
#endif

  g.json.outOpt = cson_output_opt_empty;
  g.json.outOpt.addNewline = 1;
  g.json.outOpt.indentation = 1 /* FIXME: make configurable */;
  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;
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
      }
    }
    fossil_fatal("%s: ambiguous command prefix: %s\n"
                 "%s: could be any of:%s\n"
                 "%s: use \"help\" for more information\n",
                 argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
  }
  rc = cson_cgi_init(&g.json.cgiCx, g.argc, (char const * const *)g.argv, NULL)
    /* Reminder: cson_cgi_init() may process the POST data before
       fossil gets to, but it is configured to only read
       application/[json/javascript] and text/plain. form-urlencoded
       and x-fossil-* data will be consumed by fossil's cgi_init().

       Note that we set up the CGI bits even when not running in CGI
       mode because some of cson_cgi's facilities are useful in
       non-CGI contexts and we use those in the CLI variants of the
       JSON commands.

       FIXME: do some analysis of the request path (HTTP mode) or
       CLI args (CLI mode) and only call this if the command is
       a JSON-mode command. We can only do that easily from here
       if we use e.g. /json/foo instead of /foo.json, since we
       have a common prefix.
     */
    ;
  if(rc){
    fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: "
                 "cson error code #%d (%s)\n",
                 argv[0], rc, cson_rc_string(rc));
  }else{
    if( NULL != cson_cgi_env_get_obj( &g.json.cgiCx, 'p', 0 ) ){
      /* if cson_cgi read the POST data then we're certainly in JSON
         mode. If it didn't then we have to delay this decision until
         the JSON family of callbacks is called.
      */
      g.json.isJsonMode = 1;
    }
    atexit( fossil_atexit );
  }
  aCommand[idx].xFunc();
  fossil_exit(0);
  /*NOT_REACHED*/
  return 0;
}

/*







<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<




<
<
<
<
<
<
<
|
|
<







362
363
364
365
366
367
368


















369
370
371
372







373
374

375
376
377
378
379
380
381
      }
    }
    fossil_fatal("%s: ambiguous command prefix: %s\n"
                 "%s: could be any of:%s\n"
                 "%s: use \"help\" for more information\n",
                 argv[0], zCmdName, argv[0], blob_str(&couldbe), argv[0]);
  }


















  if(rc){
    fossil_fatal("%s: unrecoverable error while initializing JSON CGI bits: "
                 "cson error code #%d (%s)\n",
                 argv[0], rc, cson_rc_string(rc));







  }
  atexit( fossil_atexit );

  aCommand[idx].xFunc();
  fossil_exit(0);
  /*NOT_REACHED*/
  return 0;
}

/*