Fossil SCM
minor cleanups in prep for the "larger" JSON APIs.
Commit
87e20659c6e4f75fcc8f463d2b30ff6fa80399fa
Parent
507a4582777c6bd…
2 files changed
+151
-57
+1
-1
+151
-57
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -20,10 +20,18 @@ | ||
| 20 | 20 | ** For notes regarding the public JSON interface, please see: |
| 21 | 21 | ** |
| 22 | 22 | ** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit |
| 23 | 23 | ** |
| 24 | 24 | ** |
| 25 | +** Notes for hackers... | |
| 26 | +** | |
| 27 | +** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or | |
| 28 | +** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then | |
| 29 | +** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f(). | |
| 30 | +** See the API docs for that typedef (below) for the semantics of the callbacks. | |
| 31 | +** | |
| 32 | +** | |
| 25 | 33 | */ |
| 26 | 34 | #include "config.h" |
| 27 | 35 | #include "VERSION.h" |
| 28 | 36 | #include "json.h" |
| 29 | 37 | #include <assert.h> |
| @@ -31,10 +39,34 @@ | ||
| 31 | 39 | |
| 32 | 40 | #if INTERFACE |
| 33 | 41 | #include "cson_amalgamation.h" |
| 34 | 42 | #include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */ |
| 35 | 43 | #endif |
| 44 | + | |
| 45 | +/* | |
| 46 | +** Signature for JSON page/command callbacks. By the time the callback | |
| 47 | +** is called, json_page_top() or json_cmd_top() will have set up the | |
| 48 | +** JSON-related environment. Implementations may generate a "result | |
| 49 | +** payload" of any JSON type by returning its value from this function | |
| 50 | +** (ownership is tranferred to the caller). On error they should set | |
| 51 | +** g.json.resultCode to one of the FossilJsonCodes values and return | |
| 52 | +** either their payload object or NULL. Note that NULL is a legal | |
| 53 | +** success value - it simply means the response will contain no | |
| 54 | +** payload. If g.json.resultCode is non-zero when this function | |
| 55 | +** returns then the top-level dispatcher will destroy any payload | |
| 56 | +** returned by this function and will output a JSON error response | |
| 57 | +** instead. | |
| 58 | +** | |
| 59 | +** All of the setup/response code is handled by the top dispatcher | |
| 60 | +** functions and the callbacks concern themselves only with generating | |
| 61 | +** the payload. | |
| 62 | +** | |
| 63 | +** It is imperitive that NO callback functions EVER output ANYTHING to | |
| 64 | +** stdout, as that will effectively corrupt any HTTP output. | |
| 65 | +*/ | |
| 66 | +typedef cson_value * (*fossil_json_f)(); | |
| 67 | + | |
| 36 | 68 | |
| 37 | 69 | /* |
| 38 | 70 | ** Holds keys used for various JSON API properties. |
| 39 | 71 | */ |
| 40 | 72 | static const struct FossilJsonKeys_{ |
| @@ -217,17 +249,29 @@ | ||
| 217 | 249 | } |
| 218 | 250 | } |
| 219 | 251 | } |
| 220 | 252 | return NULL; |
| 221 | 253 | } |
| 254 | + | |
| 255 | + | |
| 256 | +/* | |
| 257 | +** Returns the string form of a json_getenv() value, but ONLY | |
| 258 | +** If that value is-a String. Non-strings are not converted | |
| 259 | +** to strings for this purpose. Returned memory is owned by | |
| 260 | +** g.json or fossil.. | |
| 261 | +*/ | |
| 262 | +static char const * json_getenv_cstr( char const * zKey ){ | |
| 263 | + return cson_value_get_cstr( json_getenv(zKey) ); | |
| 264 | +} | |
| 265 | + | |
| 222 | 266 | |
| 223 | 267 | /* |
| 224 | -** Adds v to g.json.param.o using the given key. May cause | |
| 225 | -** any prior item with that key to be destroyed (depends on | |
| 226 | -** current reference count for that value). | |
| 227 | -** On succes, transfers ownership of v to g.json.param.o. | |
| 228 | -** On error ownership of v is not modified. | |
| 268 | +** Adds v to g.json.param.o using the given key. May cause any prior | |
| 269 | +** item with that key to be destroyed (depends on current reference | |
| 270 | +** count for that value). On success, transfers (or shares) ownership | |
| 271 | +** of v to (or with) g.json.param.o. On error ownership of v is not | |
| 272 | +** modified. | |
| 229 | 273 | */ |
| 230 | 274 | int json_setenv( char const * zKey, cson_value * v ){ |
| 231 | 275 | return cson_object_set( g.json.param.o, zKey, v ); |
| 232 | 276 | } |
| 233 | 277 | |
| @@ -322,12 +366,13 @@ | ||
| 322 | 366 | */ |
| 323 | 367 | char const * zCookie = P(login_cookie_name()); |
| 324 | 368 | if( zCookie && *zCookie ){ |
| 325 | 369 | /* Transfer fossil's cookie to JSON for downstream convenience... */ |
| 326 | 370 | cson_value * v = cson_value_new_string(zCookie, strlen(zCookie)); |
| 327 | - json_setenv( FossilJsonKeys.authToken, v ); | |
| 328 | - g.json.authToken = v; | |
| 371 | + if(0 == json_gc_add( FossilJsonKeys.authToken, v, 1 )){ | |
| 372 | + g.json.authToken = v; | |
| 373 | + } | |
| 329 | 374 | } |
| 330 | 375 | } |
| 331 | 376 | } |
| 332 | 377 | return g.json.authToken; |
| 333 | 378 | } |
| @@ -466,19 +511,17 @@ | ||
| 466 | 511 | do{/* set up JSON out formatting options. */ |
| 467 | 512 | unsigned char indent = g.isCGI ? 0 : 1; |
| 468 | 513 | cson_value const * indentV = json_getenv("indent"); |
| 469 | 514 | if(indentV){ |
| 470 | 515 | if(cson_value_is_string(indentV)){ |
| 471 | - int n = atoi(cson_string_cstr(cson_value_get_string(indentV))); | |
| 516 | + int const n = atoi(cson_string_cstr(cson_value_get_string(indentV))); | |
| 472 | 517 | indent = (n>0) |
| 473 | 518 | ? (unsigned char)n |
| 474 | 519 | : 0; |
| 475 | 520 | }else if(cson_value_is_number(indentV)){ |
| 476 | - double n = cson_value_get_integer(indentV); | |
| 477 | - indent = (n>0) | |
| 478 | - ? (unsigned char)n | |
| 479 | - : 0; | |
| 521 | + cson_int_t const n = cson_value_get_integer(indentV); | |
| 522 | + indent = (n>0) ? (unsigned char)n : 0; | |
| 480 | 523 | } |
| 481 | 524 | } |
| 482 | 525 | g.json.outOpt.indentation = indent; |
| 483 | 526 | g.json.outOpt.addNewline = g.isCGI ? 0 : 1; |
| 484 | 527 | }while(0); |
| @@ -553,10 +596,11 @@ | ||
| 553 | 596 | ** if json_auth_token() returns NULL. |
| 554 | 597 | */ |
| 555 | 598 | char const * json_auth_token_cstr(){ |
| 556 | 599 | return cson_value_get_cstr( json_auth_token() ); |
| 557 | 600 | } |
| 601 | + | |
| 558 | 602 | |
| 559 | 603 | /* |
| 560 | 604 | ** Holds name-to-function mappings for JSON page/command dispatching. |
| 561 | 605 | ** |
| 562 | 606 | */ |
| @@ -572,13 +616,15 @@ | ||
| 572 | 616 | char const * name; |
| 573 | 617 | /* |
| 574 | 618 | ** Returns a payload object for the response. If it returns a |
| 575 | 619 | ** non-NULL value, the caller owns it. To trigger an error this |
| 576 | 620 | ** function should set g.json.resultCode to a value from the |
| 577 | - ** FossilJsonCodes enum. | |
| 621 | + ** FossilJsonCodes enum. If it sets an error value and returns | |
| 622 | + ** a payload, the payload will be destroyed (not sent with the | |
| 623 | + ** response). | |
| 578 | 624 | */ |
| 579 | - cson_value * (*func)(); | |
| 625 | + fossil_json_f func; | |
| 580 | 626 | /* |
| 581 | 627 | ** Which mode(s) of execution does func() support: |
| 582 | 628 | ** |
| 583 | 629 | ** <0 = CLI only, >0 = HTTP only, 0==both |
| 584 | 630 | */ |
| @@ -625,39 +671,30 @@ | ||
| 625 | 671 | } |
| 626 | 672 | if( modulo ) code = code - (code % modulo); |
| 627 | 673 | return code; |
| 628 | 674 | } |
| 629 | 675 | } |
| 630 | - | |
| 631 | -#if 0 | |
| 632 | -static unsigned int json_timestamp(){ | |
| 633 | - | |
| 634 | -} | |
| 635 | -#endif | |
| 636 | - | |
| 637 | 676 | |
| 638 | 677 | /* |
| 639 | 678 | ** Creates a new Fossil/JSON response envelope skeleton. It is owned |
| 640 | 679 | ** by the caller, who must eventually free it using cson_value_free(), |
| 641 | 680 | ** or add it to a cson container to transfer ownership. Returns NULL |
| 642 | 681 | ** on error. |
| 643 | 682 | ** |
| 644 | 683 | ** If payload is not NULL and resultCode is 0 then it is set as the |
| 645 | -** "payload" property of the returned object. If resultCode is non-0 | |
| 646 | -** then this function will destroy payload if it is not NULL. i.e. | |
| 647 | -** onwership of payload is transfered to this function. | |
| 684 | +** "payload" property of the returned object. If resultCode is | |
| 685 | +** non-zero and payload is not NULL then this function calls | |
| 686 | +** cson_value_free(payload) and does not insert the payload into the | |
| 687 | +** response. In either case, onwership of payload is transfered to | |
| 688 | +** this function. | |
| 648 | 689 | ** |
| 649 | -** pMsg is an optional message string (resultText) property of the | |
| 690 | +** pMsg is an optional message string property (resultText) of the | |
| 650 | 691 | ** response. If resultCode is non-0 and pMsg is NULL then |
| 651 | 692 | ** json_err_str() is used to get the error string. The caller may |
| 652 | 693 | ** provide his own or may use an empty string to suppress the |
| 653 | 694 | ** resultText property. |
| 654 | 695 | ** |
| 655 | -** If resultCode is non-zero and payload is not NULL then this | |
| 656 | -** function calls cson_value_free(payload) and does not insert the | |
| 657 | -** payload into the response. | |
| 658 | -** | |
| 659 | 696 | */ |
| 660 | 697 | cson_value * json_create_response( int resultCode, |
| 661 | 698 | cson_value * payload, |
| 662 | 699 | char const * pMsg ){ |
| 663 | 700 | cson_value * v = NULL; |
| @@ -717,19 +754,24 @@ | ||
| 717 | 754 | SET("resultText"); |
| 718 | 755 | } |
| 719 | 756 | tmp = json_getenv("requestId"); |
| 720 | 757 | if( tmp ) cson_object_set( o, "requestId", tmp ); |
| 721 | 758 | |
| 722 | - if(0){ | |
| 723 | - if(g.json.cmd.v){/* this is only intended for my own testing...*/ | |
| 759 | + if(0){/* these are only intended for my own testing...*/ | |
| 760 | + if(g.json.cmd.v){ | |
| 724 | 761 | tmp = g.json.cmd.v; |
| 725 | 762 | SET("$commandPath"); |
| 726 | 763 | } |
| 727 | - if(g.json.param.v){/* this is only intended for my own testing...*/ | |
| 764 | + if(g.json.param.v){ | |
| 728 | 765 | tmp = g.json.param.v; |
| 729 | 766 | SET("$params"); |
| 730 | 767 | } |
| 768 | + if(0){/*Only for debuggering, add some info to the response.*/ | |
| 769 | + tmp = cson_value_new_integer( g.json.cmd.offset ); | |
| 770 | + cson_object_set( o, "cmd.offset", tmp ); | |
| 771 | + cson_object_set( o, "isCGI", cson_value_new_bool( g.isCGI ) ); | |
| 772 | + } | |
| 731 | 773 | } |
| 732 | 774 | |
| 733 | 775 | /* Only add the payload to SUCCESS responses. Else delete it. */ |
| 734 | 776 | if( NULL != payload ){ |
| 735 | 777 | if( resultCode ){ |
| @@ -740,42 +782,36 @@ | ||
| 740 | 782 | SET("payload"); |
| 741 | 783 | } |
| 742 | 784 | } |
| 743 | 785 | |
| 744 | 786 | #undef SET |
| 745 | - | |
| 746 | - if(0){/*Only for debuggering, add some info to the response.*/ | |
| 747 | - tmp = cson_value_new_integer( g.json.cmd.offset ); | |
| 748 | - cson_object_set( o, "cmd.offset", tmp ); | |
| 749 | - cson_object_set( o, "isCGI", cson_value_new_bool( g.isCGI ) ); | |
| 750 | - } | |
| 751 | - | |
| 752 | 787 | goto ok; |
| 753 | 788 | cleanup: |
| 754 | 789 | cson_value_free(v); |
| 755 | 790 | v = NULL; |
| 756 | 791 | ok: |
| 757 | 792 | return v; |
| 758 | 793 | } |
| 759 | 794 | |
| 760 | 795 | /* |
| 761 | -** Outputs a JSON error response to g.httpOut. If rc is 0 then | |
| 762 | -** g.json.resultCode is used. If that is also 0 then the "Unknown | |
| 796 | +** Outputs a JSON error response to either the cgi_xxx() family of | |
| 797 | +** buffers (in CGI/server mode) or stdout (in CLI mode). If rc is 0 | |
| 798 | +** then g.json.resultCode is used. If that is also 0 then the "Unknown | |
| 763 | 799 | ** Error" code is used. |
| 764 | 800 | ** |
| 765 | -** If g.isCGI then the generated error object replaces any currently | |
| 766 | -** buffered page output. | |
| 801 | +** If g.isCGI then the generated JSON error response object replaces | |
| 802 | +** any currently buffered page output. Because the output goes via | |
| 803 | +** the cgi_xxx() family of functions, this function inherits any | |
| 804 | +** compression which fossil does for its output. | |
| 767 | 805 | ** |
| 768 | -** If alsoOutput is true AND g.isCGI then the cgi_reply() is called to | |
| 806 | +** If alsoOutput is true AND g.isCGI then cgi_reply() is called to | |
| 769 | 807 | ** flush the output (and headers). Generally only do this if you are |
| 770 | 808 | ** about to call exit(). |
| 771 | 809 | ** |
| 772 | 810 | ** !g.isCGI then alsoOutput is ignored and all output is sent to |
| 773 | 811 | ** stdout immediately. |
| 774 | 812 | ** |
| 775 | -** This clears any previously buffered CGI content, replacing it with | |
| 776 | -** JSON. | |
| 777 | 813 | */ |
| 778 | 814 | void json_err( int code, char const * msg, char alsoOutput ){ |
| 779 | 815 | int rc = code ? code : (g.json.resultCode |
| 780 | 816 | ? g.json.resultCode |
| 781 | 817 | : FSL_JSON_E_UNKNOWN); |
| @@ -783,10 +819,18 @@ | ||
| 783 | 819 | rc = json_dumbdown_rc(rc); |
| 784 | 820 | if( rc && !msg ){ |
| 785 | 821 | msg = json_err_str(rc); |
| 786 | 822 | } |
| 787 | 823 | resp = json_create_response(rc, NULL, msg); |
| 824 | + if(!resp){ | |
| 825 | + /* about the only error case here is out-of-memory. DO NOT | |
| 826 | + call fossil_panic() here because that calls this function. | |
| 827 | + */ | |
| 828 | + fprintf(stderr, "%s: Fatal error: could not allocate " | |
| 829 | + "response object.\n", fossil_nameofexe()); | |
| 830 | + fossil_exit(1); | |
| 831 | + } | |
| 788 | 832 | if( g.isCGI ){ |
| 789 | 833 | Blob buf = empty_blob; |
| 790 | 834 | cgi_reset_content(); |
| 791 | 835 | cson_output_Blob( resp, &buf, &g.json.outOpt ); |
| 792 | 836 | cgi_set_content(&buf); |
| @@ -821,20 +865,10 @@ | ||
| 821 | 865 | cson_object_set( jobj, "resultCodeParanoiaLevel", |
| 822 | 866 | cson_value_new_integer(g.json.errorDetailParanoia) ); |
| 823 | 867 | return jval; |
| 824 | 868 | } |
| 825 | 869 | |
| 826 | -/* | |
| 827 | -** Returns the string form of a json_getenv() value, but ONLY | |
| 828 | -** If that value is-a String. Non-strings are not converted | |
| 829 | -** to strings for this purpose. Returned memory is owned by | |
| 830 | -** g.json or fossil.. | |
| 831 | -*/ | |
| 832 | -static char const * json_getenv_cstr( char const * zKey ){ | |
| 833 | - return cson_value_get_cstr( json_getenv(zKey) ); | |
| 834 | -} | |
| 835 | - | |
| 836 | 870 | |
| 837 | 871 | /* |
| 838 | 872 | ** Implementation for /json/cap |
| 839 | 873 | ** |
| 840 | 874 | ** Returned object contains details about the "capabilities" of the |
| @@ -1207,21 +1241,78 @@ | ||
| 1207 | 1241 | {"login",json_page_login,1}, |
| 1208 | 1242 | {"logout",json_page_logout,1}, |
| 1209 | 1243 | {"stat",json_page_stat,0}, |
| 1210 | 1244 | {"tag", json_page_nyi,0}, |
| 1211 | 1245 | {"ticket", json_page_nyi,0}, |
| 1246 | +{"timeline", json_page_nyi,0}, | |
| 1212 | 1247 | {"user", json_page_nyi,0}, |
| 1213 | 1248 | {"version",json_page_version,0}, |
| 1214 | 1249 | {"wiki",json_page_wiki,0}, |
| 1215 | 1250 | /* Last entry MUST have a NULL name. */ |
| 1216 | 1251 | {NULL,NULL,0} |
| 1217 | 1252 | }; |
| 1253 | + | |
| 1254 | +/* | |
| 1255 | +** Mapping of /json/wiki/XXX commands/paths to callbacks. | |
| 1256 | +*/ | |
| 1257 | +static const JsonPageDef JsonPageDefs_Wiki[] = { | |
| 1258 | +{"get", json_page_nyi, 0}, | |
| 1259 | +{"list", json_page_nyi, 0}, | |
| 1260 | +{"save", json_page_nyi, 1}, | |
| 1261 | +/* Last entry MUST have a NULL name. */ | |
| 1262 | +{NULL,NULL,0} | |
| 1263 | +}; | |
| 1264 | + | |
| 1265 | +/* | |
| 1266 | +** Mapping of /json/ticket/XXX commands/paths to callbacks. | |
| 1267 | +*/ | |
| 1268 | +static const JsonPageDef JsonPageDefs_Ticket[] = { | |
| 1269 | +{"get", json_page_nyi, 0}, | |
| 1270 | +{"list", json_page_nyi, 0}, | |
| 1271 | +{"save", json_page_nyi, 1}, | |
| 1272 | +{"create", json_page_nyi, 1}, | |
| 1273 | +/* Last entry MUST have a NULL name. */ | |
| 1274 | +{NULL,NULL,0} | |
| 1275 | +}; | |
| 1276 | + | |
| 1277 | +/* | |
| 1278 | +** Mapping of /json/artifact/XXX commands/paths to callbacks. | |
| 1279 | +*/ | |
| 1280 | +static const JsonPageDef JsonPageDefs_Artifact[] = { | |
| 1281 | +{"vinfo", json_page_nyi, 0}, | |
| 1282 | +{"finfo", json_page_nyi, 0}, | |
| 1283 | +/* Last entry MUST have a NULL name. */ | |
| 1284 | +{NULL,NULL,0} | |
| 1285 | +}; | |
| 1286 | + | |
| 1287 | +/* | |
| 1288 | +** Mapping of /json/branch/XXX commands/paths to callbacks. | |
| 1289 | +*/ | |
| 1290 | +static const JsonPageDef JsonPageDefs_Branch[] = { | |
| 1291 | +{"list", json_page_nyi, 0}, | |
| 1292 | +{"create", json_page_nyi, 1}, | |
| 1293 | +/* Last entry MUST have a NULL name. */ | |
| 1294 | +{NULL,NULL,0} | |
| 1295 | +}; | |
| 1296 | + | |
| 1297 | +/* | |
| 1298 | +** Mapping of /json/tag/XXX commands/paths to callbacks. | |
| 1299 | +*/ | |
| 1300 | +static const JsonPageDef JsonPageDefs_Tag[] = { | |
| 1301 | +{"list", json_page_nyi, 0}, | |
| 1302 | +{"create", json_page_nyi, 1}, | |
| 1303 | +/* Last entry MUST have a NULL name. */ | |
| 1304 | +{NULL,NULL,0} | |
| 1305 | +}; | |
| 1306 | + | |
| 1218 | 1307 | |
| 1219 | 1308 | /* |
| 1220 | 1309 | ** WEBPAGE: json |
| 1221 | 1310 | ** |
| 1222 | 1311 | ** Pages under /json/... must be entered into JsonPageDefs. |
| 1312 | +** This function dispatches them, and is the HTTP equivalent of | |
| 1313 | +** json_cmd_top(). | |
| 1223 | 1314 | */ |
| 1224 | 1315 | void json_page_top(void){ |
| 1225 | 1316 | int rc = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1226 | 1317 | Blob buf = empty_blob; |
| 1227 | 1318 | char const * cmd; |
| @@ -1251,10 +1342,13 @@ | ||
| 1251 | 1342 | cgi_set_content(&buf)/*takes ownership of the buf memory*/; |
| 1252 | 1343 | } |
| 1253 | 1344 | } |
| 1254 | 1345 | |
| 1255 | 1346 | /* |
| 1347 | +** This function dispatches json commands and is the CLI equivalent of | |
| 1348 | +** json_page_top(). | |
| 1349 | +** | |
| 1256 | 1350 | ** COMMAND: json |
| 1257 | 1351 | ** |
| 1258 | 1352 | ** Usage: %fossil json SUBCOMMAND |
| 1259 | 1353 | ** |
| 1260 | 1354 | ** The commands include: |
| @@ -1274,11 +1368,11 @@ | ||
| 1274 | 1368 | ** ... |
| 1275 | 1369 | ** |
| 1276 | 1370 | */ |
| 1277 | 1371 | void json_cmd_top(void){ |
| 1278 | 1372 | char const * cmd = NULL; |
| 1279 | - int rc = 1002; | |
| 1373 | + int rc = FSL_JSON_E_UNKNOWN_COMMAND; | |
| 1280 | 1374 | cson_value * payload = NULL; |
| 1281 | 1375 | JsonPageDef const * pageDef; |
| 1282 | 1376 | memset( &g.perm, 0xff, sizeof(g.perm) ) |
| 1283 | 1377 | /* In CLI mode fossil does not use permissions |
| 1284 | 1378 | and they all default to false. We enable them |
| 1285 | 1379 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -20,10 +20,18 @@ | |
| 20 | ** For notes regarding the public JSON interface, please see: |
| 21 | ** |
| 22 | ** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit |
| 23 | ** |
| 24 | ** |
| 25 | */ |
| 26 | #include "config.h" |
| 27 | #include "VERSION.h" |
| 28 | #include "json.h" |
| 29 | #include <assert.h> |
| @@ -31,10 +39,34 @@ | |
| 31 | |
| 32 | #if INTERFACE |
| 33 | #include "cson_amalgamation.h" |
| 34 | #include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */ |
| 35 | #endif |
| 36 | |
| 37 | /* |
| 38 | ** Holds keys used for various JSON API properties. |
| 39 | */ |
| 40 | static const struct FossilJsonKeys_{ |
| @@ -217,17 +249,29 @@ | |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | return NULL; |
| 221 | } |
| 222 | |
| 223 | /* |
| 224 | ** Adds v to g.json.param.o using the given key. May cause |
| 225 | ** any prior item with that key to be destroyed (depends on |
| 226 | ** current reference count for that value). |
| 227 | ** On succes, transfers ownership of v to g.json.param.o. |
| 228 | ** On error ownership of v is not modified. |
| 229 | */ |
| 230 | int json_setenv( char const * zKey, cson_value * v ){ |
| 231 | return cson_object_set( g.json.param.o, zKey, v ); |
| 232 | } |
| 233 | |
| @@ -322,12 +366,13 @@ | |
| 322 | */ |
| 323 | char const * zCookie = P(login_cookie_name()); |
| 324 | if( zCookie && *zCookie ){ |
| 325 | /* Transfer fossil's cookie to JSON for downstream convenience... */ |
| 326 | cson_value * v = cson_value_new_string(zCookie, strlen(zCookie)); |
| 327 | json_setenv( FossilJsonKeys.authToken, v ); |
| 328 | g.json.authToken = v; |
| 329 | } |
| 330 | } |
| 331 | } |
| 332 | return g.json.authToken; |
| 333 | } |
| @@ -466,19 +511,17 @@ | |
| 466 | do{/* set up JSON out formatting options. */ |
| 467 | unsigned char indent = g.isCGI ? 0 : 1; |
| 468 | cson_value const * indentV = json_getenv("indent"); |
| 469 | if(indentV){ |
| 470 | if(cson_value_is_string(indentV)){ |
| 471 | int n = atoi(cson_string_cstr(cson_value_get_string(indentV))); |
| 472 | indent = (n>0) |
| 473 | ? (unsigned char)n |
| 474 | : 0; |
| 475 | }else if(cson_value_is_number(indentV)){ |
| 476 | double n = cson_value_get_integer(indentV); |
| 477 | indent = (n>0) |
| 478 | ? (unsigned char)n |
| 479 | : 0; |
| 480 | } |
| 481 | } |
| 482 | g.json.outOpt.indentation = indent; |
| 483 | g.json.outOpt.addNewline = g.isCGI ? 0 : 1; |
| 484 | }while(0); |
| @@ -553,10 +596,11 @@ | |
| 553 | ** if json_auth_token() returns NULL. |
| 554 | */ |
| 555 | char const * json_auth_token_cstr(){ |
| 556 | return cson_value_get_cstr( json_auth_token() ); |
| 557 | } |
| 558 | |
| 559 | /* |
| 560 | ** Holds name-to-function mappings for JSON page/command dispatching. |
| 561 | ** |
| 562 | */ |
| @@ -572,13 +616,15 @@ | |
| 572 | char const * name; |
| 573 | /* |
| 574 | ** Returns a payload object for the response. If it returns a |
| 575 | ** non-NULL value, the caller owns it. To trigger an error this |
| 576 | ** function should set g.json.resultCode to a value from the |
| 577 | ** FossilJsonCodes enum. |
| 578 | */ |
| 579 | cson_value * (*func)(); |
| 580 | /* |
| 581 | ** Which mode(s) of execution does func() support: |
| 582 | ** |
| 583 | ** <0 = CLI only, >0 = HTTP only, 0==both |
| 584 | */ |
| @@ -625,39 +671,30 @@ | |
| 625 | } |
| 626 | if( modulo ) code = code - (code % modulo); |
| 627 | return code; |
| 628 | } |
| 629 | } |
| 630 | |
| 631 | #if 0 |
| 632 | static unsigned int json_timestamp(){ |
| 633 | |
| 634 | } |
| 635 | #endif |
| 636 | |
| 637 | |
| 638 | /* |
| 639 | ** Creates a new Fossil/JSON response envelope skeleton. It is owned |
| 640 | ** by the caller, who must eventually free it using cson_value_free(), |
| 641 | ** or add it to a cson container to transfer ownership. Returns NULL |
| 642 | ** on error. |
| 643 | ** |
| 644 | ** If payload is not NULL and resultCode is 0 then it is set as the |
| 645 | ** "payload" property of the returned object. If resultCode is non-0 |
| 646 | ** then this function will destroy payload if it is not NULL. i.e. |
| 647 | ** onwership of payload is transfered to this function. |
| 648 | ** |
| 649 | ** pMsg is an optional message string (resultText) property of the |
| 650 | ** response. If resultCode is non-0 and pMsg is NULL then |
| 651 | ** json_err_str() is used to get the error string. The caller may |
| 652 | ** provide his own or may use an empty string to suppress the |
| 653 | ** resultText property. |
| 654 | ** |
| 655 | ** If resultCode is non-zero and payload is not NULL then this |
| 656 | ** function calls cson_value_free(payload) and does not insert the |
| 657 | ** payload into the response. |
| 658 | ** |
| 659 | */ |
| 660 | cson_value * json_create_response( int resultCode, |
| 661 | cson_value * payload, |
| 662 | char const * pMsg ){ |
| 663 | cson_value * v = NULL; |
| @@ -717,19 +754,24 @@ | |
| 717 | SET("resultText"); |
| 718 | } |
| 719 | tmp = json_getenv("requestId"); |
| 720 | if( tmp ) cson_object_set( o, "requestId", tmp ); |
| 721 | |
| 722 | if(0){ |
| 723 | if(g.json.cmd.v){/* this is only intended for my own testing...*/ |
| 724 | tmp = g.json.cmd.v; |
| 725 | SET("$commandPath"); |
| 726 | } |
| 727 | if(g.json.param.v){/* this is only intended for my own testing...*/ |
| 728 | tmp = g.json.param.v; |
| 729 | SET("$params"); |
| 730 | } |
| 731 | } |
| 732 | |
| 733 | /* Only add the payload to SUCCESS responses. Else delete it. */ |
| 734 | if( NULL != payload ){ |
| 735 | if( resultCode ){ |
| @@ -740,42 +782,36 @@ | |
| 740 | SET("payload"); |
| 741 | } |
| 742 | } |
| 743 | |
| 744 | #undef SET |
| 745 | |
| 746 | if(0){/*Only for debuggering, add some info to the response.*/ |
| 747 | tmp = cson_value_new_integer( g.json.cmd.offset ); |
| 748 | cson_object_set( o, "cmd.offset", tmp ); |
| 749 | cson_object_set( o, "isCGI", cson_value_new_bool( g.isCGI ) ); |
| 750 | } |
| 751 | |
| 752 | goto ok; |
| 753 | cleanup: |
| 754 | cson_value_free(v); |
| 755 | v = NULL; |
| 756 | ok: |
| 757 | return v; |
| 758 | } |
| 759 | |
| 760 | /* |
| 761 | ** Outputs a JSON error response to g.httpOut. If rc is 0 then |
| 762 | ** g.json.resultCode is used. If that is also 0 then the "Unknown |
| 763 | ** Error" code is used. |
| 764 | ** |
| 765 | ** If g.isCGI then the generated error object replaces any currently |
| 766 | ** buffered page output. |
| 767 | ** |
| 768 | ** If alsoOutput is true AND g.isCGI then the cgi_reply() is called to |
| 769 | ** flush the output (and headers). Generally only do this if you are |
| 770 | ** about to call exit(). |
| 771 | ** |
| 772 | ** !g.isCGI then alsoOutput is ignored and all output is sent to |
| 773 | ** stdout immediately. |
| 774 | ** |
| 775 | ** This clears any previously buffered CGI content, replacing it with |
| 776 | ** JSON. |
| 777 | */ |
| 778 | void json_err( int code, char const * msg, char alsoOutput ){ |
| 779 | int rc = code ? code : (g.json.resultCode |
| 780 | ? g.json.resultCode |
| 781 | : FSL_JSON_E_UNKNOWN); |
| @@ -783,10 +819,18 @@ | |
| 783 | rc = json_dumbdown_rc(rc); |
| 784 | if( rc && !msg ){ |
| 785 | msg = json_err_str(rc); |
| 786 | } |
| 787 | resp = json_create_response(rc, NULL, msg); |
| 788 | if( g.isCGI ){ |
| 789 | Blob buf = empty_blob; |
| 790 | cgi_reset_content(); |
| 791 | cson_output_Blob( resp, &buf, &g.json.outOpt ); |
| 792 | cgi_set_content(&buf); |
| @@ -821,20 +865,10 @@ | |
| 821 | cson_object_set( jobj, "resultCodeParanoiaLevel", |
| 822 | cson_value_new_integer(g.json.errorDetailParanoia) ); |
| 823 | return jval; |
| 824 | } |
| 825 | |
| 826 | /* |
| 827 | ** Returns the string form of a json_getenv() value, but ONLY |
| 828 | ** If that value is-a String. Non-strings are not converted |
| 829 | ** to strings for this purpose. Returned memory is owned by |
| 830 | ** g.json or fossil.. |
| 831 | */ |
| 832 | static char const * json_getenv_cstr( char const * zKey ){ |
| 833 | return cson_value_get_cstr( json_getenv(zKey) ); |
| 834 | } |
| 835 | |
| 836 | |
| 837 | /* |
| 838 | ** Implementation for /json/cap |
| 839 | ** |
| 840 | ** Returned object contains details about the "capabilities" of the |
| @@ -1207,21 +1241,78 @@ | |
| 1207 | {"login",json_page_login,1}, |
| 1208 | {"logout",json_page_logout,1}, |
| 1209 | {"stat",json_page_stat,0}, |
| 1210 | {"tag", json_page_nyi,0}, |
| 1211 | {"ticket", json_page_nyi,0}, |
| 1212 | {"user", json_page_nyi,0}, |
| 1213 | {"version",json_page_version,0}, |
| 1214 | {"wiki",json_page_wiki,0}, |
| 1215 | /* Last entry MUST have a NULL name. */ |
| 1216 | {NULL,NULL,0} |
| 1217 | }; |
| 1218 | |
| 1219 | /* |
| 1220 | ** WEBPAGE: json |
| 1221 | ** |
| 1222 | ** Pages under /json/... must be entered into JsonPageDefs. |
| 1223 | */ |
| 1224 | void json_page_top(void){ |
| 1225 | int rc = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1226 | Blob buf = empty_blob; |
| 1227 | char const * cmd; |
| @@ -1251,10 +1342,13 @@ | |
| 1251 | cgi_set_content(&buf)/*takes ownership of the buf memory*/; |
| 1252 | } |
| 1253 | } |
| 1254 | |
| 1255 | /* |
| 1256 | ** COMMAND: json |
| 1257 | ** |
| 1258 | ** Usage: %fossil json SUBCOMMAND |
| 1259 | ** |
| 1260 | ** The commands include: |
| @@ -1274,11 +1368,11 @@ | |
| 1274 | ** ... |
| 1275 | ** |
| 1276 | */ |
| 1277 | void json_cmd_top(void){ |
| 1278 | char const * cmd = NULL; |
| 1279 | int rc = 1002; |
| 1280 | cson_value * payload = NULL; |
| 1281 | JsonPageDef const * pageDef; |
| 1282 | memset( &g.perm, 0xff, sizeof(g.perm) ) |
| 1283 | /* In CLI mode fossil does not use permissions |
| 1284 | and they all default to false. We enable them |
| 1285 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -20,10 +20,18 @@ | |
| 20 | ** For notes regarding the public JSON interface, please see: |
| 21 | ** |
| 22 | ** https://docs.google.com/document/d/1fXViveNhDbiXgCuE7QDXQOKeFzf2qNUkBEgiUvoqFN4/edit |
| 23 | ** |
| 24 | ** |
| 25 | ** Notes for hackers... |
| 26 | ** |
| 27 | ** Here's how command/page dispatching works: json_page_top() (in HTTP mode) or |
| 28 | ** json_cmd_top() (in CLI mode) catch the "json" path/command. Those functions then |
| 29 | ** dispatch to a JSON-mode-specific command/page handler with the type fossil_json_f(). |
| 30 | ** See the API docs for that typedef (below) for the semantics of the callbacks. |
| 31 | ** |
| 32 | ** |
| 33 | */ |
| 34 | #include "config.h" |
| 35 | #include "VERSION.h" |
| 36 | #include "json.h" |
| 37 | #include <assert.h> |
| @@ -31,10 +39,34 @@ | |
| 39 | |
| 40 | #if INTERFACE |
| 41 | #include "cson_amalgamation.h" |
| 42 | #include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */ |
| 43 | #endif |
| 44 | |
| 45 | /* |
| 46 | ** Signature for JSON page/command callbacks. By the time the callback |
| 47 | ** is called, json_page_top() or json_cmd_top() will have set up the |
| 48 | ** JSON-related environment. Implementations may generate a "result |
| 49 | ** payload" of any JSON type by returning its value from this function |
| 50 | ** (ownership is tranferred to the caller). On error they should set |
| 51 | ** g.json.resultCode to one of the FossilJsonCodes values and return |
| 52 | ** either their payload object or NULL. Note that NULL is a legal |
| 53 | ** success value - it simply means the response will contain no |
| 54 | ** payload. If g.json.resultCode is non-zero when this function |
| 55 | ** returns then the top-level dispatcher will destroy any payload |
| 56 | ** returned by this function and will output a JSON error response |
| 57 | ** instead. |
| 58 | ** |
| 59 | ** All of the setup/response code is handled by the top dispatcher |
| 60 | ** functions and the callbacks concern themselves only with generating |
| 61 | ** the payload. |
| 62 | ** |
| 63 | ** It is imperitive that NO callback functions EVER output ANYTHING to |
| 64 | ** stdout, as that will effectively corrupt any HTTP output. |
| 65 | */ |
| 66 | typedef cson_value * (*fossil_json_f)(); |
| 67 | |
| 68 | |
| 69 | /* |
| 70 | ** Holds keys used for various JSON API properties. |
| 71 | */ |
| 72 | static const struct FossilJsonKeys_{ |
| @@ -217,17 +249,29 @@ | |
| 249 | } |
| 250 | } |
| 251 | } |
| 252 | return NULL; |
| 253 | } |
| 254 | |
| 255 | |
| 256 | /* |
| 257 | ** Returns the string form of a json_getenv() value, but ONLY |
| 258 | ** If that value is-a String. Non-strings are not converted |
| 259 | ** to strings for this purpose. Returned memory is owned by |
| 260 | ** g.json or fossil.. |
| 261 | */ |
| 262 | static char const * json_getenv_cstr( char const * zKey ){ |
| 263 | return cson_value_get_cstr( json_getenv(zKey) ); |
| 264 | } |
| 265 | |
| 266 | |
| 267 | /* |
| 268 | ** Adds v to g.json.param.o using the given key. May cause any prior |
| 269 | ** item with that key to be destroyed (depends on current reference |
| 270 | ** count for that value). On success, transfers (or shares) ownership |
| 271 | ** of v to (or with) g.json.param.o. On error ownership of v is not |
| 272 | ** modified. |
| 273 | */ |
| 274 | int json_setenv( char const * zKey, cson_value * v ){ |
| 275 | return cson_object_set( g.json.param.o, zKey, v ); |
| 276 | } |
| 277 | |
| @@ -322,12 +366,13 @@ | |
| 366 | */ |
| 367 | char const * zCookie = P(login_cookie_name()); |
| 368 | if( zCookie && *zCookie ){ |
| 369 | /* Transfer fossil's cookie to JSON for downstream convenience... */ |
| 370 | cson_value * v = cson_value_new_string(zCookie, strlen(zCookie)); |
| 371 | if(0 == json_gc_add( FossilJsonKeys.authToken, v, 1 )){ |
| 372 | g.json.authToken = v; |
| 373 | } |
| 374 | } |
| 375 | } |
| 376 | } |
| 377 | return g.json.authToken; |
| 378 | } |
| @@ -466,19 +511,17 @@ | |
| 511 | do{/* set up JSON out formatting options. */ |
| 512 | unsigned char indent = g.isCGI ? 0 : 1; |
| 513 | cson_value const * indentV = json_getenv("indent"); |
| 514 | if(indentV){ |
| 515 | if(cson_value_is_string(indentV)){ |
| 516 | int const n = atoi(cson_string_cstr(cson_value_get_string(indentV))); |
| 517 | indent = (n>0) |
| 518 | ? (unsigned char)n |
| 519 | : 0; |
| 520 | }else if(cson_value_is_number(indentV)){ |
| 521 | cson_int_t const n = cson_value_get_integer(indentV); |
| 522 | indent = (n>0) ? (unsigned char)n : 0; |
| 523 | } |
| 524 | } |
| 525 | g.json.outOpt.indentation = indent; |
| 526 | g.json.outOpt.addNewline = g.isCGI ? 0 : 1; |
| 527 | }while(0); |
| @@ -553,10 +596,11 @@ | |
| 596 | ** if json_auth_token() returns NULL. |
| 597 | */ |
| 598 | char const * json_auth_token_cstr(){ |
| 599 | return cson_value_get_cstr( json_auth_token() ); |
| 600 | } |
| 601 | |
| 602 | |
| 603 | /* |
| 604 | ** Holds name-to-function mappings for JSON page/command dispatching. |
| 605 | ** |
| 606 | */ |
| @@ -572,13 +616,15 @@ | |
| 616 | char const * name; |
| 617 | /* |
| 618 | ** Returns a payload object for the response. If it returns a |
| 619 | ** non-NULL value, the caller owns it. To trigger an error this |
| 620 | ** function should set g.json.resultCode to a value from the |
| 621 | ** FossilJsonCodes enum. If it sets an error value and returns |
| 622 | ** a payload, the payload will be destroyed (not sent with the |
| 623 | ** response). |
| 624 | */ |
| 625 | fossil_json_f func; |
| 626 | /* |
| 627 | ** Which mode(s) of execution does func() support: |
| 628 | ** |
| 629 | ** <0 = CLI only, >0 = HTTP only, 0==both |
| 630 | */ |
| @@ -625,39 +671,30 @@ | |
| 671 | } |
| 672 | if( modulo ) code = code - (code % modulo); |
| 673 | return code; |
| 674 | } |
| 675 | } |
| 676 | |
| 677 | /* |
| 678 | ** Creates a new Fossil/JSON response envelope skeleton. It is owned |
| 679 | ** by the caller, who must eventually free it using cson_value_free(), |
| 680 | ** or add it to a cson container to transfer ownership. Returns NULL |
| 681 | ** on error. |
| 682 | ** |
| 683 | ** If payload is not NULL and resultCode is 0 then it is set as the |
| 684 | ** "payload" property of the returned object. If resultCode is |
| 685 | ** non-zero and payload is not NULL then this function calls |
| 686 | ** cson_value_free(payload) and does not insert the payload into the |
| 687 | ** response. In either case, onwership of payload is transfered to |
| 688 | ** this function. |
| 689 | ** |
| 690 | ** pMsg is an optional message string property (resultText) of the |
| 691 | ** response. If resultCode is non-0 and pMsg is NULL then |
| 692 | ** json_err_str() is used to get the error string. The caller may |
| 693 | ** provide his own or may use an empty string to suppress the |
| 694 | ** resultText property. |
| 695 | ** |
| 696 | */ |
| 697 | cson_value * json_create_response( int resultCode, |
| 698 | cson_value * payload, |
| 699 | char const * pMsg ){ |
| 700 | cson_value * v = NULL; |
| @@ -717,19 +754,24 @@ | |
| 754 | SET("resultText"); |
| 755 | } |
| 756 | tmp = json_getenv("requestId"); |
| 757 | if( tmp ) cson_object_set( o, "requestId", tmp ); |
| 758 | |
| 759 | if(0){/* these are only intended for my own testing...*/ |
| 760 | if(g.json.cmd.v){ |
| 761 | tmp = g.json.cmd.v; |
| 762 | SET("$commandPath"); |
| 763 | } |
| 764 | if(g.json.param.v){ |
| 765 | tmp = g.json.param.v; |
| 766 | SET("$params"); |
| 767 | } |
| 768 | if(0){/*Only for debuggering, add some info to the response.*/ |
| 769 | tmp = cson_value_new_integer( g.json.cmd.offset ); |
| 770 | cson_object_set( o, "cmd.offset", tmp ); |
| 771 | cson_object_set( o, "isCGI", cson_value_new_bool( g.isCGI ) ); |
| 772 | } |
| 773 | } |
| 774 | |
| 775 | /* Only add the payload to SUCCESS responses. Else delete it. */ |
| 776 | if( NULL != payload ){ |
| 777 | if( resultCode ){ |
| @@ -740,42 +782,36 @@ | |
| 782 | SET("payload"); |
| 783 | } |
| 784 | } |
| 785 | |
| 786 | #undef SET |
| 787 | goto ok; |
| 788 | cleanup: |
| 789 | cson_value_free(v); |
| 790 | v = NULL; |
| 791 | ok: |
| 792 | return v; |
| 793 | } |
| 794 | |
| 795 | /* |
| 796 | ** Outputs a JSON error response to either the cgi_xxx() family of |
| 797 | ** buffers (in CGI/server mode) or stdout (in CLI mode). If rc is 0 |
| 798 | ** then g.json.resultCode is used. If that is also 0 then the "Unknown |
| 799 | ** Error" code is used. |
| 800 | ** |
| 801 | ** If g.isCGI then the generated JSON error response object replaces |
| 802 | ** any currently buffered page output. Because the output goes via |
| 803 | ** the cgi_xxx() family of functions, this function inherits any |
| 804 | ** compression which fossil does for its output. |
| 805 | ** |
| 806 | ** If alsoOutput is true AND g.isCGI then cgi_reply() is called to |
| 807 | ** flush the output (and headers). Generally only do this if you are |
| 808 | ** about to call exit(). |
| 809 | ** |
| 810 | ** !g.isCGI then alsoOutput is ignored and all output is sent to |
| 811 | ** stdout immediately. |
| 812 | ** |
| 813 | */ |
| 814 | void json_err( int code, char const * msg, char alsoOutput ){ |
| 815 | int rc = code ? code : (g.json.resultCode |
| 816 | ? g.json.resultCode |
| 817 | : FSL_JSON_E_UNKNOWN); |
| @@ -783,10 +819,18 @@ | |
| 819 | rc = json_dumbdown_rc(rc); |
| 820 | if( rc && !msg ){ |
| 821 | msg = json_err_str(rc); |
| 822 | } |
| 823 | resp = json_create_response(rc, NULL, msg); |
| 824 | if(!resp){ |
| 825 | /* about the only error case here is out-of-memory. DO NOT |
| 826 | call fossil_panic() here because that calls this function. |
| 827 | */ |
| 828 | fprintf(stderr, "%s: Fatal error: could not allocate " |
| 829 | "response object.\n", fossil_nameofexe()); |
| 830 | fossil_exit(1); |
| 831 | } |
| 832 | if( g.isCGI ){ |
| 833 | Blob buf = empty_blob; |
| 834 | cgi_reset_content(); |
| 835 | cson_output_Blob( resp, &buf, &g.json.outOpt ); |
| 836 | cgi_set_content(&buf); |
| @@ -821,20 +865,10 @@ | |
| 865 | cson_object_set( jobj, "resultCodeParanoiaLevel", |
| 866 | cson_value_new_integer(g.json.errorDetailParanoia) ); |
| 867 | return jval; |
| 868 | } |
| 869 | |
| 870 | |
| 871 | /* |
| 872 | ** Implementation for /json/cap |
| 873 | ** |
| 874 | ** Returned object contains details about the "capabilities" of the |
| @@ -1207,21 +1241,78 @@ | |
| 1241 | {"login",json_page_login,1}, |
| 1242 | {"logout",json_page_logout,1}, |
| 1243 | {"stat",json_page_stat,0}, |
| 1244 | {"tag", json_page_nyi,0}, |
| 1245 | {"ticket", json_page_nyi,0}, |
| 1246 | {"timeline", json_page_nyi,0}, |
| 1247 | {"user", json_page_nyi,0}, |
| 1248 | {"version",json_page_version,0}, |
| 1249 | {"wiki",json_page_wiki,0}, |
| 1250 | /* Last entry MUST have a NULL name. */ |
| 1251 | {NULL,NULL,0} |
| 1252 | }; |
| 1253 | |
| 1254 | /* |
| 1255 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 1256 | */ |
| 1257 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| 1258 | {"get", json_page_nyi, 0}, |
| 1259 | {"list", json_page_nyi, 0}, |
| 1260 | {"save", json_page_nyi, 1}, |
| 1261 | /* Last entry MUST have a NULL name. */ |
| 1262 | {NULL,NULL,0} |
| 1263 | }; |
| 1264 | |
| 1265 | /* |
| 1266 | ** Mapping of /json/ticket/XXX commands/paths to callbacks. |
| 1267 | */ |
| 1268 | static const JsonPageDef JsonPageDefs_Ticket[] = { |
| 1269 | {"get", json_page_nyi, 0}, |
| 1270 | {"list", json_page_nyi, 0}, |
| 1271 | {"save", json_page_nyi, 1}, |
| 1272 | {"create", json_page_nyi, 1}, |
| 1273 | /* Last entry MUST have a NULL name. */ |
| 1274 | {NULL,NULL,0} |
| 1275 | }; |
| 1276 | |
| 1277 | /* |
| 1278 | ** Mapping of /json/artifact/XXX commands/paths to callbacks. |
| 1279 | */ |
| 1280 | static const JsonPageDef JsonPageDefs_Artifact[] = { |
| 1281 | {"vinfo", json_page_nyi, 0}, |
| 1282 | {"finfo", json_page_nyi, 0}, |
| 1283 | /* Last entry MUST have a NULL name. */ |
| 1284 | {NULL,NULL,0} |
| 1285 | }; |
| 1286 | |
| 1287 | /* |
| 1288 | ** Mapping of /json/branch/XXX commands/paths to callbacks. |
| 1289 | */ |
| 1290 | static const JsonPageDef JsonPageDefs_Branch[] = { |
| 1291 | {"list", json_page_nyi, 0}, |
| 1292 | {"create", json_page_nyi, 1}, |
| 1293 | /* Last entry MUST have a NULL name. */ |
| 1294 | {NULL,NULL,0} |
| 1295 | }; |
| 1296 | |
| 1297 | /* |
| 1298 | ** Mapping of /json/tag/XXX commands/paths to callbacks. |
| 1299 | */ |
| 1300 | static const JsonPageDef JsonPageDefs_Tag[] = { |
| 1301 | {"list", json_page_nyi, 0}, |
| 1302 | {"create", json_page_nyi, 1}, |
| 1303 | /* Last entry MUST have a NULL name. */ |
| 1304 | {NULL,NULL,0} |
| 1305 | }; |
| 1306 | |
| 1307 | |
| 1308 | /* |
| 1309 | ** WEBPAGE: json |
| 1310 | ** |
| 1311 | ** Pages under /json/... must be entered into JsonPageDefs. |
| 1312 | ** This function dispatches them, and is the HTTP equivalent of |
| 1313 | ** json_cmd_top(). |
| 1314 | */ |
| 1315 | void json_page_top(void){ |
| 1316 | int rc = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1317 | Blob buf = empty_blob; |
| 1318 | char const * cmd; |
| @@ -1251,10 +1342,13 @@ | |
| 1342 | cgi_set_content(&buf)/*takes ownership of the buf memory*/; |
| 1343 | } |
| 1344 | } |
| 1345 | |
| 1346 | /* |
| 1347 | ** This function dispatches json commands and is the CLI equivalent of |
| 1348 | ** json_page_top(). |
| 1349 | ** |
| 1350 | ** COMMAND: json |
| 1351 | ** |
| 1352 | ** Usage: %fossil json SUBCOMMAND |
| 1353 | ** |
| 1354 | ** The commands include: |
| @@ -1274,11 +1368,11 @@ | |
| 1368 | ** ... |
| 1369 | ** |
| 1370 | */ |
| 1371 | void json_cmd_top(void){ |
| 1372 | char const * cmd = NULL; |
| 1373 | int rc = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1374 | cson_value * payload = NULL; |
| 1375 | JsonPageDef const * pageDef; |
| 1376 | memset( &g.perm, 0xff, sizeof(g.perm) ) |
| 1377 | /* In CLI mode fossil does not use permissions |
| 1378 | and they all default to false. We enable them |
| 1379 |
+1
-1
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -443,11 +443,11 @@ | ||
| 443 | 443 | mainInFatalError = 1; |
| 444 | 444 | va_start(ap, zFormat); |
| 445 | 445 | z = vmprintf(zFormat, ap); |
| 446 | 446 | va_end(ap); |
| 447 | 447 | if( g.json.isJsonMode ){ |
| 448 | - json_err( 0, z, 1 ); | |
| 448 | + json_err( g.json.resultCode, z, 1 ); | |
| 449 | 449 | if( g.isCGI ){ |
| 450 | 450 | rc = 0 /* avoid HTTP 500 */; |
| 451 | 451 | } |
| 452 | 452 | } |
| 453 | 453 | else if( g.cgiOutput ){ |
| 454 | 454 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | mainInFatalError = 1; |
| 444 | va_start(ap, zFormat); |
| 445 | z = vmprintf(zFormat, ap); |
| 446 | va_end(ap); |
| 447 | if( g.json.isJsonMode ){ |
| 448 | json_err( 0, z, 1 ); |
| 449 | if( g.isCGI ){ |
| 450 | rc = 0 /* avoid HTTP 500 */; |
| 451 | } |
| 452 | } |
| 453 | else if( g.cgiOutput ){ |
| 454 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -443,11 +443,11 @@ | |
| 443 | mainInFatalError = 1; |
| 444 | va_start(ap, zFormat); |
| 445 | z = vmprintf(zFormat, ap); |
| 446 | va_end(ap); |
| 447 | if( g.json.isJsonMode ){ |
| 448 | json_err( g.json.resultCode, z, 1 ); |
| 449 | if( g.isCGI ){ |
| 450 | rc = 0 /* avoid HTTP 500 */; |
| 451 | } |
| 452 | } |
| 453 | else if( g.cgiOutput ){ |
| 454 |