Fossil

Check-in [87e20659]
Login

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

Overview
Comment:minor cleanups in prep for the "larger" JSON APIs.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1: 87e20659c6e4f75fcc8f463d2b30ff6fa80399fa
User & Date: stephan 2011-09-19 17:11:18.811
Context
2011-09-19
18:48
Implemented /json/wiki/list (first draft, may change). Pulled in latest cson_sqlite3 additions to simplify the impl. ... (check-in: 5cc88946 user: stephan tags: json)
17:11
minor cleanups in prep for the "larger" JSON APIs. ... (check-in: 87e20659 user: stephan tags: json)
2011-09-18
19:16
terribly minor internal cleanups. ... (check-in: 507a4582 user: stephan tags: json)
Changes
Unified Diff Ignore Whitespace Patch
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
38
39
40
41
42
** 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>

#if INTERFACE
#include "cson_amalgamation.h"
#include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */
#endif

























/*
** Holds keys used for various JSON API properties.
*/
static const struct FossilJsonKeys_{
  char const * authToken;
  char const * commandPath;







>
>
>
>
>
>
>
>











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







18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
** Code for the JSON API.
**
** For notes regarding the public JSON interface, please see:
**
** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit
**
**
** Notes for hackers...
**
** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or
** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then
** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f().
** See the API docs for that typedef (below) for the semantics of the callbacks.
**
**
*/
#include "config.h"
#include "VERSION.h"
#include "json.h"
#include <assert.h>
#include <time.h>

#if INTERFACE
#include "cson_amalgamation.h"
#include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */
#endif

/*
** Signature for JSON page/command callbacks. By the time the callback
** is called, json_page_top() or json_cmd_top() will have set up the
** JSON-related environment. Implementations may generate a "result
** payload" of any JSON type by returning its value from this function
** (ownership is tranferred to the caller). On error they should set
** g.json.resultCode to one of the FossilJsonCodes values and return
** either their payload object or NULL. Note that NULL is a legal
** success value - it simply means the response will contain no
** payload. If g.json.resultCode is non-zero when this function
** returns then the top-level dispatcher will destroy any payload
** returned by this function and will output a JSON error response
** instead.
**
** All of the setup/response code is handled by the top dispatcher
** functions and the callbacks concern themselves only with generating
** the payload.
**
** It is imperitive that NO callback functions EVER output ANYTHING to
** stdout, as that will effectively corrupt any HTTP output.
*/
typedef cson_value * (*fossil_json_f)();


/*
** Holds keys used for various JSON API properties.
*/
static const struct FossilJsonKeys_{
  char const * authToken;
  char const * commandPath;
215
216
217
218
219
220
221
222

223











224
225
226
227
228
229
230
231
232
233
234
235
        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 );
}

/*
** Guesses a RESPONSE Content-Type value based (primarily) on the








>

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







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
        cson_object_set( g.json.param.o, zKey, rc );
        return rc;
      }
    }
  }
  return NULL;
}


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


/*
** 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 success, transfers (or shares) ownership
** of v to (or with) 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 );
}

/*
** Guesses a RESPONSE Content-Type value based (primarily) on the
320
321
322
323
324
325
326
327
328

329
330
331
332
333
334
335
         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;
}

/*







|
|
>







364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
         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));
        if(0 == json_gc_add( FossilJsonKeys.authToken, v, 1 )){
          g.json.authToken = v;
        }
      }
    }
  }
  return g.json.authToken;
}

/*
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
  }

  do{/* set up JSON out 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 n = atoi(cson_string_cstr(cson_value_get_string(indentV)));
        indent = (n>0)
          ? (unsigned char)n
          : 0;
      }else if(cson_value_is_number(indentV)){
        double 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;
  }while(0);

  json_auth_token()/* will copy our auth token, if any, to fossil's core. */;







|




|
|
<
<







509
510
511
512
513
514
515
516
517
518
519
520
521
522


523
524
525
526
527
528
529
  }

  do{/* set up JSON out 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;
  }while(0);

  json_auth_token()/* will copy our auth token, if any, to fossil's core. */;
551
552
553
554
555
556
557

558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577


578
579
580
581
582
583
584
585
586

/* Returns the C-string form of json_auth_token(), or NULL
** if json_auth_token() returns NULL.
*/
char const * json_auth_token_cstr(){
  return cson_value_get_cstr( json_auth_token() );
}


/*
** Holds name-to-function mappings for JSON page/command dispatching.
**
*/
typedef struct JsonPageDef{
  /*
  ** The commmand/page's name (path, not including leading /json/).
  **
  ** 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
  */
  char runMode;
} JsonPageDef;







>



















|
>
>

|







594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632

/* Returns the C-string form of json_auth_token(), or NULL
** if json_auth_token() returns NULL.
*/
char const * json_auth_token_cstr(){
  return cson_value_get_cstr( json_auth_token() );
}


/*
** Holds name-to-function mappings for JSON page/command dispatching.
**
*/
typedef struct JsonPageDef{
  /*
  ** The commmand/page's name (path, not including leading /json/).
  **
  ** 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. If it sets an error value and returns
  ** a payload, the payload will be destroyed (not sent with the
  ** response).
  */
  fossil_json_f func;
  /*
  ** Which mode(s) of execution does func() support:
  **
  ** <0 = CLI only, >0 = HTTP only, 0==both
  */
  char runMode;
} JsonPageDef;
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
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
      case 3: modulo = 1000; break;
      default: break;
    }
    if( modulo ) code = code - (code % modulo);
    return code;
  }
}

#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.
**
** If payload is not NULL and resultCode is 0 then it is set as the
** "payload" property of the returned object. If resultCode is non-0
** then this function will destroy payload if it is not NULL. i.e.

** onwership of payload is transfered to this function.

**
** pMsg is an optional message string (resultText) property of the
** response. If resultCode is non-0 and pMsg is NULL then
** json_err_str() is used to get the error string. The caller may
** provide his own or may use an empty string to suppress the
** resultText property.
**
** If resultCode is non-zero and payload is not NULL then this
** function calls cson_value_free(payload) and does not insert the
** payload into the response.
**
*/
cson_value * json_create_response( int resultCode,
                                     cson_value * payload,
                                     char const * pMsg ){
  cson_value * v = NULL;
  cson_value * tmp = NULL;
  cson_object * o = NULL;







<
<
<
<
<
<
<








|
|
>
|
>

|





<
<
<
<







669
670
671
672
673
674
675







676
677
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
      case 3: modulo = 1000; break;
      default: break;
    }
    if( modulo ) code = code - (code % modulo);
    return code;
  }
}








/*
** 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.
**
** If payload is not NULL and resultCode is 0 then it is set as the
** "payload" property of the returned object.  If resultCode is
** non-zero and payload is not NULL then this function calls
** cson_value_free(payload) and does not insert the payload into the
** response. In either case, onwership of payload is transfered to
** this function.
**
** pMsg is an optional message string property (resultText) of the
** response. If resultCode is non-0 and pMsg is NULL then
** json_err_str() is used to get the error string. The caller may
** provide his own or may use an empty string to suppress the
** resultText property.
**




*/
cson_value * json_create_response( int resultCode,
                                     cson_value * payload,
                                     char const * pMsg ){
  cson_value * v = NULL;
  cson_value * tmp = NULL;
  cson_object * o = NULL;
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
  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(0){
    if(g.json.cmd.v){/* this is only intended for my own testing...*/
      tmp = g.json.cmd.v;
      SET("$commandPath");
    }
    if(g.json.param.v){/* this is only intended for my own testing...*/
      tmp = g.json.param.v;
      SET("$params");
    }





  }

  /* Only add the payload to SUCCESS responses. Else delete it. */
  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;
  ok:
  return v;
}

/*
** Outputs a JSON error response to g.httpOut.  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 error object replaces any currently
** buffered page output.


**
** If alsoOutput is true AND g.isCGI then the 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.
**
** This clears any previously buffered CGI content, replacing it with
** JSON.
*/
void json_err( int code, char const * msg, char alsoOutput ){
  int rc = code ? code : (g.json.resultCode
                          ? g.json.resultCode
                          : FSL_JSON_E_UNKNOWN);
  cson_value * resp = NULL;
  rc = json_dumbdown_rc(rc);
  if( rc && !msg ){
    msg = json_err_str(rc);
  }
  resp = json_create_response(rc, NULL, msg);








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







|
|



|



>
>
>
>
>














<
<
<
<
<
<
<









|
>
|


|
|
>
>

|






<
<











>
>
>
>
>
>
>
>







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
829
830
831
832
833
834
835
836
837
838
  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(0){/* these are only intended for my own testing...*/
    if(g.json.cmd.v){
      tmp = g.json.cmd.v;
      SET("$commandPath");
    }
    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);
      payload = NULL;
    }else{
      tmp = payload;
      SET("payload");
    }
  }

#undef SET







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

/*
** 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);
  cson_value * resp = NULL;
  rc = json_dumbdown_rc(rc);
  if( rc && !msg ){
    msg = json_err_str(rc);
  }
  resp = json_create_response(rc, NULL, msg);
  if(!resp){
    /* 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();
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
  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).
**







<
<
<
<
<
<
<
<
<
<







863
864
865
866
867
868
869










870
871
872
873
874
875
876
  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;
}












/*
** Implementation for /json/cap
**
** Returned object contains details about the "capabilities" of the
** current user (what he may/may not do).
**
1205
1206
1207
1208
1209
1210
1211

1212
1213
1214
1215
1216
1217
1218
1219






















































1220
1221
1222


1223
1224
1225
1226
1227
1228
1229
{"cap", json_page_cap, 0},
{"HAI",json_page_version,0},
{"login",json_page_login,1},
{"logout",json_page_logout,1},
{"stat",json_page_stat,0},
{"tag", json_page_nyi,0},
{"ticket", json_page_nyi,0},

{"user", json_page_nyi,0},
{"version",json_page_version,0},
{"wiki",json_page_wiki,0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*






















































** WEBPAGE: json
**
** Pages under /json/... must be entered into JsonPageDefs.


*/
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;







>








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



>
>







1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
{"cap", json_page_cap, 0},
{"HAI",json_page_version,0},
{"login",json_page_login,1},
{"logout",json_page_logout,1},
{"stat",json_page_stat,0},
{"tag", json_page_nyi,0},
{"ticket", json_page_nyi,0},
{"timeline", json_page_nyi,0},
{"user", json_page_nyi,0},
{"version",json_page_version,0},
{"wiki",json_page_wiki,0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Wiki[] = {
{"get", json_page_nyi, 0},
{"list", json_page_nyi, 0},
{"save", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Mapping of /json/ticket/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Ticket[] = {
{"get", json_page_nyi, 0},
{"list", json_page_nyi, 0},
{"save", json_page_nyi, 1},
{"create", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Mapping of /json/artifact/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Artifact[] = {
{"vinfo", json_page_nyi, 0},
{"finfo", json_page_nyi, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Mapping of /json/branch/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Branch[] = {
{"list", json_page_nyi, 0},
{"create", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Mapping of /json/tag/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Tag[] = {
{"list", json_page_nyi, 0},
{"create", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};


/*
** WEBPAGE: json
**
** 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){
  int rc = FSL_JSON_E_UNKNOWN_COMMAND;
  Blob buf = empty_blob;
  char const * cmd;
  cson_value * payload = NULL;
  cson_value * root = NULL;
1249
1250
1251
1252
1253
1254
1255



1256
1257
1258
1259
1260
1261
1262
    cson_output_Blob( root, &buf, NULL );
    cson_value_free(root);
    cgi_set_content(&buf)/*takes ownership of the buf memory*/;
  }
}

/*



** COMMAND: json
**
** Usage: %fossil json SUBCOMMAND
**
** The commands include:
**
**   cap







>
>
>







1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
    cson_output_Blob( root, &buf, NULL );
    cson_value_free(root);
    cgi_set_content(&buf)/*takes ownership of the buf memory*/;
  }
}

/*
** This function dispatches json commands and is the CLI equivalent of
** json_page_top().
**
** COMMAND: json
**
** Usage: %fossil json SUBCOMMAND
**
** The commands include:
**
**   cap
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
**   timeline
**   wiki
**   ...
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = 1002;
  cson_value * payload = NULL;
  JsonPageDef const * pageDef;
  memset( &g.perm, 0xff, sizeof(g.perm) )
    /* In CLI mode fossil does not use permissions
       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







|







1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
**   timeline
**   wiki
**   ...
**
*/
void json_cmd_top(void){
  char const * cmd = NULL;
  int rc = FSL_JSON_E_UNKNOWN_COMMAND;
  cson_value * payload = NULL;
  JsonPageDef const * pageDef;
  memset( &g.perm, 0xff, sizeof(g.perm) )
    /* In CLI mode fossil does not use permissions
       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
Changes to src/main.c.
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
  int rc = 1;
  va_list ap;
  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 ){
    g.cgiOutput = 0;
    cgi_printf("<p class=\"generalError\">%h</p>", z);







|







441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
  int rc = 1;
  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);