Fossil

Check-in [35b9edba]
Login

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

Overview
Comment:more cleanups in the json arg/path handling.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1:35b9edba835d0adf255af2069f99cba414bf62c1
User & Date: stephan 2011-09-16 18:29:34
Context
2011-09-16
18:39
Refactored the special-case CLI-mode arg/path handling in the CGI/server-mode mechanism. Fixed: server-mode response timestamp was always the time the server process started. check-in: 0bb82303 user: stephan tags: json
18:29
more cleanups in the json arg/path handling. check-in: 35b9edba user: stephan tags: json
17:53
minor cleanups to the json "command path" handling. check-in: 2dcc2397 user: stephan tags: json
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to src/json.c.

217
218
219
220
221
222
223
224
225

226
227
228
229
230
231
232
...
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
...
365
366
367
368
369
370
371
372
373
374
375


376
377
378
379
380
381
382
*/
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 "gpc"
         here.

      */;
    if( 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 login hijacking window here. We need to change the
         JSON auth token to be in the form: login_cookie_name()=...
................................................................................
    }
  }
  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.




*/
static void json_mode_bootstrap(){

  char const * zPath = P("PATH_INFO");
  cson_value * pathSplit =
    cson_cgi_getenv(&g.json.cgiCx,"e","PATH_INFO_SPLIT");



  g.json.isJsonMode = 1;
  g.json.resultCode = 0;
  g.json.cmdOffset = -1;
  if( !g.isCGI && g.fullHttpReply ){

    g.isCGI = 1;
  }
  /*json_err( 1000, zPath, 1 ); exit(0);*/
#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 into an
................................................................................
    internal list so that we can simplify command dispatching later
    on.
  */
  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.
    */

    cson_cgi_setenv( &g.json.cgiCx,
                     FossilJsonKeys.commandPath,
                     pathSplit );








  }else 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 */
    cson_value * arV = cson_value_new_array(); /* value to store path in */
    cson_array * ar = cson_value_get_array(arV); /* the real array object */
    cson_cgi_setenv( &g.json.cgiCx,
                     FossilJsonKeys.commandPath,
                     arV );

    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;













    }
  }
  /* 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. */;
  if( g.isCGI ){
    login_check_credentials();
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
  }

}

/*
** Returns the ndx'th item in the "command path", where index 0 is the
** position of the "json" part of the path. Returns NULL if ndx is out
** of bounds or there is no "json" path element.
**
** In CLI mode the "path" is the list of arguments (skipping argv[0]).
** In server/CGI modes the path is the PATH_INFO.
*/
static char const * json_path_part(unsigned char ndx){
  cson_array * ar = g.isCGI
    ? cson_value_get_array(cson_cgi_getenv(&g.json.cgiCx,
                                           "a",
                                           FossilJsonKeys.commandPath))
    : NULL;
  if( g.isCGI ){
    assert((NULL!=ar) && "Internal error.");
  }
  if( g.json.cmdOffset < 0 ){
    /* first-time setup. */
    short i = g.isCGI ? 0 : 1;
#define PARTAT cson_string_cstr( \
                 cson_value_get_string(                     \
                   cson_array_get(ar,i)  \
                   ))
#define NEXT (g.isCGI                               \
              ? PARTAT    \
              : ((g.argc > i) ? g.argv[i] : NULL))
................................................................................
    while( tok ){
      if( 0==strncmp("json",tok,4) ){
        g.json.cmdOffset = i;
        break;
      }
      ++i;
      tok = NEXT;
#undef NEXT
#undef PARTAT
    }
  }


  if( g.json.cmdOffset < 0 ){
    return NULL;
  }else{
    ndx = g.json.cmdOffset + ndx;
    return g.isCGI
      ? cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmdOffset + ndx )))
      : ((ndx < g.argc) ? g.argv[ndx] : NULL)







|
|
>







 







|
|
|
|
>
>
>
>


>



>
>
>




>


<







 







>



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







 







|




<








|












|







 







<
<


>
>







217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
...
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
...
391
392
393
394
395
396
397


398
399
400
401
402
403
404
405
406
407
408
*/
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.
      */;
    if( 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 login hijacking window here. We need to change the
         JSON auth token to be in the form: login_cookie_name()=...
................................................................................
    }
  }
  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 into an
................................................................................
    internal list so that we can simplify command dispatching later
    on.
  */
  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; 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. */;
  if( g.isCGI ){
    login_check_credentials()/* populates g.perm */;
  }
  else{
    db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
  }

}

/*
** Returns the ndx'th item in the "command path", where index 0 is the
** position of the "json" part of the path. Returns NULL if ndx is out
** of bounds or there is no "json" path element.
**
** In CLI mode the "path" is the list of arguments (skipping argv[0]).
** In server/CGI modes the path is taken from PATH_INFO.
*/
static char const * json_path_part(unsigned char ndx){
  cson_array * ar = g.isCGI
    ? cson_value_get_array(cson_cgi_getenv(&g.json.cgiCx,
                                           "a",
                                           FossilJsonKeys.commandPath))
    : NULL;
  if( g.isCGI ){
    assert((NULL!=ar) && "Internal error.");
  }
  if( g.json.cmdOffset < 0 ){
    /* first-time setup. */
    short i = g.isCGI ? 0 : 1/*skip argv[0] in CLI mode*/;
#define PARTAT cson_string_cstr( \
                 cson_value_get_string(                     \
                   cson_array_get(ar,i)  \
                   ))
#define NEXT (g.isCGI                               \
              ? PARTAT    \
              : ((g.argc > i) ? g.argv[i] : NULL))
................................................................................
    while( tok ){
      if( 0==strncmp("json",tok,4) ){
        g.json.cmdOffset = i;
        break;
      }
      ++i;
      tok = NEXT;


    }
  }
#undef NEXT
#undef PARTAT
  if( g.json.cmdOffset < 0 ){
    return NULL;
  }else{
    ndx = g.json.cmdOffset + ndx;
    return g.isCGI
      ? cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmdOffset + ndx )))
      : ((ndx < g.argc) ? g.argv[ndx] : NULL)