Fossil SCM
Refactored page/command callback to take on argument to simplify certain dispatching ops. json_getenv() now falls back to getenv() if neither the POST data nor cgi_parameter() contains the requested value, but this is basically a workaround for my current inability to add --opt support in CLI mode (due to how HTTP/CLI command handling is consolidated).
Commit
206908faee4c76cb980d07242e2df7c8716d247d
Parent
afc1cec6a308d04…
1 file changed
+43
-25
+43
-25
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -53,26 +53,37 @@ | ||
| 53 | 53 | ** success value - it simply means the response will contain no |
| 54 | 54 | ** payload. If g.json.resultCode is non-zero when this function |
| 55 | 55 | ** returns then the top-level dispatcher will destroy any payload |
| 56 | 56 | ** returned by this function and will output a JSON error response |
| 57 | 57 | ** instead. |
| 58 | +** | |
| 59 | +** The depth parameter comes from the dispatcher and tells the | |
| 60 | +** callback what index in json_command_arg() its command/path starts. | |
| 61 | +** e.g. when dispatching /json/foo/bar, the foo callback will get a | |
| 62 | +** depth of 1 and the bar callback will get a depth of 2. This is only | |
| 63 | +** useful for callbacks which use json_command_arg(), and the only | |
| 64 | +** callbacks which do that are ones which dispatch to sub-pages | |
| 65 | +** (e.g. /json/wiki/...). This is a parameter, as opposed to simply | |
| 66 | +** hard-coding the offset in each callback impl, so that refactoring | |
| 67 | +** will (or should) be easier if we later reimplement the path/command | |
| 68 | +** handling and arguments/path parts get moved around. | |
| 58 | 69 | ** |
| 59 | 70 | ** All of the setup/response code is handled by the top dispatcher |
| 60 | 71 | ** functions and the callbacks concern themselves only with generating |
| 61 | 72 | ** the payload. |
| 62 | 73 | ** |
| 63 | 74 | ** It is imperitive that NO callback functions EVER output ANYTHING to |
| 64 | 75 | ** stdout, as that will effectively corrupt any HTTP output. |
| 65 | 76 | */ |
| 66 | -typedef cson_value * (*fossil_json_f)(); | |
| 77 | +typedef cson_value * (*fossil_json_f)(unsigned int depth); | |
| 67 | 78 | |
| 68 | 79 | |
| 69 | 80 | /* |
| 70 | 81 | ** Placeholder /json/XXX page impl for NYI (Not Yet Implemented) |
| 71 | 82 | ** (but planned) pages/commands. |
| 72 | 83 | */ |
| 73 | -static cson_value * json_page_nyi(void){ | |
| 84 | +static cson_value * json_page_nyi(unsigned int depth){ | |
| 74 | 85 | g.json.resultCode = FSL_JSON_E_NYI; |
| 75 | 86 | return NULL; |
| 76 | 87 | } |
| 77 | 88 | |
| 78 | 89 | /* |
| @@ -236,17 +247,19 @@ | ||
| 236 | 247 | return cson_value_new_string( json_rc_cstr(code), 11 ); |
| 237 | 248 | } |
| 238 | 249 | |
| 239 | 250 | |
| 240 | 251 | /* |
| 241 | -** Gets a POST/GET/COOKIE value. The returned memory is owned by the | |
| 252 | +** Gets a POST/GET/COOKIE/ENV value. The returned memory is owned by the | |
| 242 | 253 | ** g.json object (one of its sub-objects). Returns NULL if no match is |
| 243 | 254 | ** found. |
| 244 | 255 | ** |
| 245 | -** Precedence: GET, COOKIE, POST. COOKIE _should_ be last | |
| 246 | -** but currently is not for internal order-of-init reasons. | |
| 247 | -** Since fossil only uses one cookie, this is not a high-prio | |
| 256 | +** ENV means the system environment (getenv()). | |
| 257 | +** | |
| 258 | +** Precedence: GET, COOKIE, POST, ENV. COOKIE _should_ be after POST | |
| 259 | +** but currently is not for internal order-of-init reasons. Since | |
| 260 | +** fossil only uses one cookie, this is not a real/high-priority | |
| 248 | 261 | ** problem. |
| 249 | 262 | */ |
| 250 | 263 | cson_value * json_getenv( char const * zKey ){ |
| 251 | 264 | cson_value * rc; |
| 252 | 265 | rc = cson_object_get( g.json.param.o, zKey ); |
| @@ -256,10 +269,13 @@ | ||
| 256 | 269 | rc = cson_object_get( g.json.post.o, zKey ); |
| 257 | 270 | if(rc){ |
| 258 | 271 | return rc; |
| 259 | 272 | }else{ |
| 260 | 273 | char const * cv = PD(zKey,NULL); |
| 274 | + if(!cv){ | |
| 275 | + cv = getenv(zKey); | |
| 276 | + } | |
| 261 | 277 | if(cv){/*transform it to JSON for later use.*/ |
| 262 | 278 | rc = cson_value_new_string(cv,strlen(cv)); |
| 263 | 279 | cson_object_set( g.json.param.o, zKey, rc ); |
| 264 | 280 | return rc; |
| 265 | 281 | } |
| @@ -299,11 +315,10 @@ | ||
| 299 | 315 | ** It will try to figure out if the client can support |
| 300 | 316 | ** application/json or application/javascript, and will fall back to |
| 301 | 317 | ** text/plain if it cannot figure out anything more specific. |
| 302 | 318 | ** |
| 303 | 319 | ** Returned memory is static and immutable. |
| 304 | -** | |
| 305 | 320 | */ |
| 306 | 321 | char const * json_guess_content_type(){ |
| 307 | 322 | char const * cset; |
| 308 | 323 | char doUtf8; |
| 309 | 324 | cset = PD("HTTP_ACCEPT_CHARSET",NULL); |
| @@ -399,11 +414,14 @@ | ||
| 399 | 414 | /* |
| 400 | 415 | ** Initializes some JSON bits which need to be initialized relatively |
| 401 | 416 | ** early on. It should only be called from cgi_init() or |
| 402 | 417 | ** json_cmd_top() (early on in those functions). |
| 403 | 418 | ** |
| 404 | -** Initializes g.json.gc and g.json.param. | |
| 419 | +** Initializes g.json.gc and g.json.param. This code does not (and | |
| 420 | +** must not) rely on any of the fossil environment having been set | |
| 421 | +** up. e.g. it must not use cgi_parameter() and friends because this | |
| 422 | +** must be called before those data are initialized. | |
| 405 | 423 | */ |
| 406 | 424 | void json_main_bootstrap(){ |
| 407 | 425 | cson_value * v; |
| 408 | 426 | assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" ); |
| 409 | 427 | |
| @@ -538,11 +556,11 @@ | ||
| 538 | 556 | /* g.json.reqPayload.o may legally be NULL, which means only that |
| 539 | 557 | g.json.reqPayload.v is-not-a Object. |
| 540 | 558 | */; |
| 541 | 559 | } |
| 542 | 560 | |
| 543 | - do{/* set up JSON output formatting options. */ | |
| 561 | + {/* set up JSON output formatting options. */ | |
| 544 | 562 | unsigned char indent = g.isCGI ? 0 : 1; |
| 545 | 563 | cson_value const * indentV = json_getenv("indent"); |
| 546 | 564 | if(indentV){ |
| 547 | 565 | if(cson_value_is_string(indentV)){ |
| 548 | 566 | int const n = atoi(cson_string_cstr(cson_value_get_string(indentV))); |
| @@ -554,11 +572,11 @@ | ||
| 554 | 572 | indent = (n>0) ? (unsigned char)n : 0; |
| 555 | 573 | } |
| 556 | 574 | } |
| 557 | 575 | g.json.outOpt.indentation = indent; |
| 558 | 576 | g.json.outOpt.addNewline = g.isCGI ? 0 : 1; |
| 559 | - }while(0); | |
| 577 | + } | |
| 560 | 578 | |
| 561 | 579 | if( g.isCGI ){ |
| 562 | 580 | json_auth_token()/* will copy our auth token, if any, to fossil's |
| 563 | 581 | core, which we need before we call |
| 564 | 582 | login_check_credentials(). */; |
| @@ -727,12 +745,12 @@ | ||
| 727 | 745 | ** provide his own or may use an empty string to suppress the |
| 728 | 746 | ** resultText property. |
| 729 | 747 | ** |
| 730 | 748 | */ |
| 731 | 749 | cson_value * json_create_response( int resultCode, |
| 732 | - cson_value * payload, | |
| 733 | - char const * pMsg ){ | |
| 750 | + cson_value * payload, | |
| 751 | + char const * pMsg ){ | |
| 734 | 752 | cson_value * v = NULL; |
| 735 | 753 | cson_value * tmp = NULL; |
| 736 | 754 | cson_object * o = NULL; |
| 737 | 755 | int rc; |
| 738 | 756 | resultCode = json_dumbdown_rc(resultCode); |
| @@ -880,11 +898,11 @@ | ||
| 880 | 898 | /* |
| 881 | 899 | ** /json/version implementation. |
| 882 | 900 | ** |
| 883 | 901 | ** Returns the payload object (owned by the caller). |
| 884 | 902 | */ |
| 885 | -cson_value * json_page_version(void){ | |
| 903 | +cson_value * json_page_version(unsigned int depth){ | |
| 886 | 904 | cson_value * jval = NULL; |
| 887 | 905 | cson_object * jobj = NULL; |
| 888 | 906 | jval = cson_value_new_object(); |
| 889 | 907 | jobj = cson_value_get_object(jval); |
| 890 | 908 | #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) |
| @@ -909,11 +927,11 @@ | ||
| 909 | 927 | ** current user (what he may/may not do). |
| 910 | 928 | ** |
| 911 | 929 | ** This is primarily intended for debuggering, but may have |
| 912 | 930 | ** a use in client code. (?) |
| 913 | 931 | */ |
| 914 | -cson_value * json_page_cap(void){ | |
| 932 | +cson_value * json_page_cap(unsigned int depth){ | |
| 915 | 933 | cson_value * payload = cson_value_new_object(); |
| 916 | 934 | cson_value * sub = cson_value_new_object(); |
| 917 | 935 | char * zCap; |
| 918 | 936 | Stmt q; |
| 919 | 937 | cson_object * obj = cson_value_get_object(payload); |
| @@ -966,11 +984,11 @@ | ||
| 966 | 984 | |
| 967 | 985 | /* |
| 968 | 986 | ** Implementation of the /json/login page. |
| 969 | 987 | ** |
| 970 | 988 | */ |
| 971 | -cson_value * json_page_login(void){ | |
| 989 | +cson_value * json_page_login(unsigned int depth){ | |
| 972 | 990 | static char preciseErrors = /* if true, "complete" JSON error codes are used, |
| 973 | 991 | else they are "dumbed down" to a generic login |
| 974 | 992 | error code. |
| 975 | 993 | */ |
| 976 | 994 | #if 0 |
| @@ -1096,11 +1114,11 @@ | ||
| 1096 | 1114 | |
| 1097 | 1115 | /* |
| 1098 | 1116 | ** Impl of /json/logout. |
| 1099 | 1117 | ** |
| 1100 | 1118 | */ |
| 1101 | -cson_value * json_page_logout(void){ | |
| 1119 | +cson_value * json_page_logout(unsigned int depth){ | |
| 1102 | 1120 | cson_value const *token = g.json.authToken; |
| 1103 | 1121 | /* Remember that json_bootstrap() replaces the login cookie with |
| 1104 | 1122 | the JSON auth token if the request contains it. If the reqest |
| 1105 | 1123 | is missing the auth token then this will fetch fossil's |
| 1106 | 1124 | original cookie. Either way, it's what we want :). |
| @@ -1121,11 +1139,11 @@ | ||
| 1121 | 1139 | } |
| 1122 | 1140 | |
| 1123 | 1141 | /* |
| 1124 | 1142 | ** Implementation of the /json/anonymousPassword page. |
| 1125 | 1143 | */ |
| 1126 | -cson_value * json_page_anon_password(void){ | |
| 1144 | +cson_value * json_page_anon_password(unsigned int depth){ | |
| 1127 | 1145 | cson_value * v = cson_value_new_object(); |
| 1128 | 1146 | cson_object * o = cson_value_get_object(v); |
| 1129 | 1147 | unsigned const int seed = captcha_seed(); |
| 1130 | 1148 | char const * zCaptcha = captcha_decode(seed); |
| 1131 | 1149 | cson_object_set(o, "seed", |
| @@ -1140,11 +1158,11 @@ | ||
| 1140 | 1158 | |
| 1141 | 1159 | /* |
| 1142 | 1160 | ** Implementation of the /json/stat page/command. |
| 1143 | 1161 | ** |
| 1144 | 1162 | */ |
| 1145 | -cson_value * json_page_stat(void){ | |
| 1163 | +cson_value * json_page_stat(unsigned int depth){ | |
| 1146 | 1164 | i64 t, fsize; |
| 1147 | 1165 | int n, m; |
| 1148 | 1166 | const char *zDb; |
| 1149 | 1167 | enum { BufLen = 1000 }; |
| 1150 | 1168 | char zBuf[BufLen]; |
| @@ -1235,11 +1253,11 @@ | ||
| 1235 | 1253 | return jv; |
| 1236 | 1254 | #undef SETBUF |
| 1237 | 1255 | } |
| 1238 | 1256 | |
| 1239 | 1257 | |
| 1240 | -static cson_value * json_wiki_list(void); | |
| 1258 | +static cson_value * json_wiki_list(unsigned int depth); | |
| 1241 | 1259 | |
| 1242 | 1260 | /* |
| 1243 | 1261 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 1244 | 1262 | */ |
| 1245 | 1263 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| @@ -1253,13 +1271,13 @@ | ||
| 1253 | 1271 | /* |
| 1254 | 1272 | ** Implements the /json/wiki family of pages/commands. Far from |
| 1255 | 1273 | ** complete. |
| 1256 | 1274 | ** |
| 1257 | 1275 | */ |
| 1258 | -static cson_value * json_page_wiki(void){ | |
| 1276 | +static cson_value * json_page_wiki(unsigned int depth){ | |
| 1259 | 1277 | JsonPageDef const * def; |
| 1260 | - char const * cmd = json_command_arg(2); | |
| 1278 | + char const * cmd = json_command_arg(1+depth); | |
| 1261 | 1279 | if( ! cmd ){ |
| 1262 | 1280 | g.json.resultCode = FSL_JSON_E_MISSING_AUTH; |
| 1263 | 1281 | return NULL; |
| 1264 | 1282 | } |
| 1265 | 1283 | def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] ); |
| @@ -1266,18 +1284,18 @@ | ||
| 1266 | 1284 | if(!def){ |
| 1267 | 1285 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1268 | 1286 | return NULL; |
| 1269 | 1287 | } |
| 1270 | 1288 | else{ |
| 1271 | - return (*def->func)(); | |
| 1289 | + return (*def->func)(1+depth); | |
| 1272 | 1290 | } |
| 1273 | 1291 | } |
| 1274 | 1292 | |
| 1275 | 1293 | /* |
| 1276 | 1294 | ** INTERIM implementation of /json/wiki/list |
| 1277 | 1295 | */ |
| 1278 | -static cson_value * json_wiki_list(void){ | |
| 1296 | +static cson_value * json_wiki_list(unsigned int depth){ | |
| 1279 | 1297 | cson_value * listV = NULL; |
| 1280 | 1298 | cson_array * list = NULL; |
| 1281 | 1299 | Stmt q; |
| 1282 | 1300 | db_prepare(&q,"SELECT" |
| 1283 | 1301 | " substr(tagname,6) as name" |
| @@ -1395,11 +1413,11 @@ | ||
| 1395 | 1413 | return; |
| 1396 | 1414 | }else if( pageDef->runMode < 0 /*CLI only*/){ |
| 1397 | 1415 | rc = FSL_JSON_E_WRONG_MODE; |
| 1398 | 1416 | }else{ |
| 1399 | 1417 | rc = 0; |
| 1400 | - payload = (*pageDef->func)(); | |
| 1418 | + payload = (*pageDef->func)(1); | |
| 1401 | 1419 | } |
| 1402 | 1420 | if( g.json.resultCode ){ |
| 1403 | 1421 | json_err(g.json.resultCode, NULL, 0); |
| 1404 | 1422 | }else{ |
| 1405 | 1423 | blob_zero(&buf); |
| @@ -1465,11 +1483,11 @@ | ||
| 1465 | 1483 | return; |
| 1466 | 1484 | }else if( pageDef->runMode > 0 /*HTTP only*/){ |
| 1467 | 1485 | rc = FSL_JSON_E_WRONG_MODE; |
| 1468 | 1486 | }else{ |
| 1469 | 1487 | rc = 0; |
| 1470 | - payload = (*pageDef->func)(); | |
| 1488 | + payload = (*pageDef->func)(1); | |
| 1471 | 1489 | } |
| 1472 | 1490 | if( g.json.resultCode ){ |
| 1473 | 1491 | json_err(g.json.resultCode, NULL, 1); |
| 1474 | 1492 | }else{ |
| 1475 | 1493 | payload = json_create_response(rc, payload, NULL); |
| 1476 | 1494 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -53,26 +53,37 @@ | |
| 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 | ** Placeholder /json/XXX page impl for NYI (Not Yet Implemented) |
| 71 | ** (but planned) pages/commands. |
| 72 | */ |
| 73 | static cson_value * json_page_nyi(void){ |
| 74 | g.json.resultCode = FSL_JSON_E_NYI; |
| 75 | return NULL; |
| 76 | } |
| 77 | |
| 78 | /* |
| @@ -236,17 +247,19 @@ | |
| 236 | return cson_value_new_string( json_rc_cstr(code), 11 ); |
| 237 | } |
| 238 | |
| 239 | |
| 240 | /* |
| 241 | ** Gets a POST/GET/COOKIE value. The returned memory is owned by the |
| 242 | ** g.json object (one of its sub-objects). Returns NULL if no match is |
| 243 | ** found. |
| 244 | ** |
| 245 | ** Precedence: GET, COOKIE, POST. COOKIE _should_ be last |
| 246 | ** but currently is not for internal order-of-init reasons. |
| 247 | ** Since fossil only uses one cookie, this is not a high-prio |
| 248 | ** problem. |
| 249 | */ |
| 250 | cson_value * json_getenv( char const * zKey ){ |
| 251 | cson_value * rc; |
| 252 | rc = cson_object_get( g.json.param.o, zKey ); |
| @@ -256,10 +269,13 @@ | |
| 256 | rc = cson_object_get( g.json.post.o, zKey ); |
| 257 | if(rc){ |
| 258 | return rc; |
| 259 | }else{ |
| 260 | char const * cv = PD(zKey,NULL); |
| 261 | if(cv){/*transform it to JSON for later use.*/ |
| 262 | rc = cson_value_new_string(cv,strlen(cv)); |
| 263 | cson_object_set( g.json.param.o, zKey, rc ); |
| 264 | return rc; |
| 265 | } |
| @@ -299,11 +315,10 @@ | |
| 299 | ** It will try to figure out if the client can support |
| 300 | ** application/json or application/javascript, and will fall back to |
| 301 | ** text/plain if it cannot figure out anything more specific. |
| 302 | ** |
| 303 | ** Returned memory is static and immutable. |
| 304 | ** |
| 305 | */ |
| 306 | char const * json_guess_content_type(){ |
| 307 | char const * cset; |
| 308 | char doUtf8; |
| 309 | cset = PD("HTTP_ACCEPT_CHARSET",NULL); |
| @@ -399,11 +414,14 @@ | |
| 399 | /* |
| 400 | ** Initializes some JSON bits which need to be initialized relatively |
| 401 | ** early on. It should only be called from cgi_init() or |
| 402 | ** json_cmd_top() (early on in those functions). |
| 403 | ** |
| 404 | ** Initializes g.json.gc and g.json.param. |
| 405 | */ |
| 406 | void json_main_bootstrap(){ |
| 407 | cson_value * v; |
| 408 | assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" ); |
| 409 | |
| @@ -538,11 +556,11 @@ | |
| 538 | /* g.json.reqPayload.o may legally be NULL, which means only that |
| 539 | g.json.reqPayload.v is-not-a Object. |
| 540 | */; |
| 541 | } |
| 542 | |
| 543 | do{/* set up JSON output formatting options. */ |
| 544 | unsigned char indent = g.isCGI ? 0 : 1; |
| 545 | cson_value const * indentV = json_getenv("indent"); |
| 546 | if(indentV){ |
| 547 | if(cson_value_is_string(indentV)){ |
| 548 | int const n = atoi(cson_string_cstr(cson_value_get_string(indentV))); |
| @@ -554,11 +572,11 @@ | |
| 554 | indent = (n>0) ? (unsigned char)n : 0; |
| 555 | } |
| 556 | } |
| 557 | g.json.outOpt.indentation = indent; |
| 558 | g.json.outOpt.addNewline = g.isCGI ? 0 : 1; |
| 559 | }while(0); |
| 560 | |
| 561 | if( g.isCGI ){ |
| 562 | json_auth_token()/* will copy our auth token, if any, to fossil's |
| 563 | core, which we need before we call |
| 564 | login_check_credentials(). */; |
| @@ -727,12 +745,12 @@ | |
| 727 | ** provide his own or may use an empty string to suppress the |
| 728 | ** resultText property. |
| 729 | ** |
| 730 | */ |
| 731 | cson_value * json_create_response( int resultCode, |
| 732 | cson_value * payload, |
| 733 | char const * pMsg ){ |
| 734 | cson_value * v = NULL; |
| 735 | cson_value * tmp = NULL; |
| 736 | cson_object * o = NULL; |
| 737 | int rc; |
| 738 | resultCode = json_dumbdown_rc(resultCode); |
| @@ -880,11 +898,11 @@ | |
| 880 | /* |
| 881 | ** /json/version implementation. |
| 882 | ** |
| 883 | ** Returns the payload object (owned by the caller). |
| 884 | */ |
| 885 | cson_value * json_page_version(void){ |
| 886 | cson_value * jval = NULL; |
| 887 | cson_object * jobj = NULL; |
| 888 | jval = cson_value_new_object(); |
| 889 | jobj = cson_value_get_object(jval); |
| 890 | #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) |
| @@ -909,11 +927,11 @@ | |
| 909 | ** current user (what he may/may not do). |
| 910 | ** |
| 911 | ** This is primarily intended for debuggering, but may have |
| 912 | ** a use in client code. (?) |
| 913 | */ |
| 914 | cson_value * json_page_cap(void){ |
| 915 | cson_value * payload = cson_value_new_object(); |
| 916 | cson_value * sub = cson_value_new_object(); |
| 917 | char * zCap; |
| 918 | Stmt q; |
| 919 | cson_object * obj = cson_value_get_object(payload); |
| @@ -966,11 +984,11 @@ | |
| 966 | |
| 967 | /* |
| 968 | ** Implementation of the /json/login page. |
| 969 | ** |
| 970 | */ |
| 971 | cson_value * json_page_login(void){ |
| 972 | static char preciseErrors = /* if true, "complete" JSON error codes are used, |
| 973 | else they are "dumbed down" to a generic login |
| 974 | error code. |
| 975 | */ |
| 976 | #if 0 |
| @@ -1096,11 +1114,11 @@ | |
| 1096 | |
| 1097 | /* |
| 1098 | ** Impl of /json/logout. |
| 1099 | ** |
| 1100 | */ |
| 1101 | cson_value * json_page_logout(void){ |
| 1102 | cson_value const *token = g.json.authToken; |
| 1103 | /* Remember that json_bootstrap() replaces the login cookie with |
| 1104 | the JSON auth token if the request contains it. If the reqest |
| 1105 | is missing the auth token then this will fetch fossil's |
| 1106 | original cookie. Either way, it's what we want :). |
| @@ -1121,11 +1139,11 @@ | |
| 1121 | } |
| 1122 | |
| 1123 | /* |
| 1124 | ** Implementation of the /json/anonymousPassword page. |
| 1125 | */ |
| 1126 | cson_value * json_page_anon_password(void){ |
| 1127 | cson_value * v = cson_value_new_object(); |
| 1128 | cson_object * o = cson_value_get_object(v); |
| 1129 | unsigned const int seed = captcha_seed(); |
| 1130 | char const * zCaptcha = captcha_decode(seed); |
| 1131 | cson_object_set(o, "seed", |
| @@ -1140,11 +1158,11 @@ | |
| 1140 | |
| 1141 | /* |
| 1142 | ** Implementation of the /json/stat page/command. |
| 1143 | ** |
| 1144 | */ |
| 1145 | cson_value * json_page_stat(void){ |
| 1146 | i64 t, fsize; |
| 1147 | int n, m; |
| 1148 | const char *zDb; |
| 1149 | enum { BufLen = 1000 }; |
| 1150 | char zBuf[BufLen]; |
| @@ -1235,11 +1253,11 @@ | |
| 1235 | return jv; |
| 1236 | #undef SETBUF |
| 1237 | } |
| 1238 | |
| 1239 | |
| 1240 | static cson_value * json_wiki_list(void); |
| 1241 | |
| 1242 | /* |
| 1243 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 1244 | */ |
| 1245 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| @@ -1253,13 +1271,13 @@ | |
| 1253 | /* |
| 1254 | ** Implements the /json/wiki family of pages/commands. Far from |
| 1255 | ** complete. |
| 1256 | ** |
| 1257 | */ |
| 1258 | static cson_value * json_page_wiki(void){ |
| 1259 | JsonPageDef const * def; |
| 1260 | char const * cmd = json_command_arg(2); |
| 1261 | if( ! cmd ){ |
| 1262 | g.json.resultCode = FSL_JSON_E_MISSING_AUTH; |
| 1263 | return NULL; |
| 1264 | } |
| 1265 | def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] ); |
| @@ -1266,18 +1284,18 @@ | |
| 1266 | if(!def){ |
| 1267 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1268 | return NULL; |
| 1269 | } |
| 1270 | else{ |
| 1271 | return (*def->func)(); |
| 1272 | } |
| 1273 | } |
| 1274 | |
| 1275 | /* |
| 1276 | ** INTERIM implementation of /json/wiki/list |
| 1277 | */ |
| 1278 | static cson_value * json_wiki_list(void){ |
| 1279 | cson_value * listV = NULL; |
| 1280 | cson_array * list = NULL; |
| 1281 | Stmt q; |
| 1282 | db_prepare(&q,"SELECT" |
| 1283 | " substr(tagname,6) as name" |
| @@ -1395,11 +1413,11 @@ | |
| 1395 | return; |
| 1396 | }else if( pageDef->runMode < 0 /*CLI only*/){ |
| 1397 | rc = FSL_JSON_E_WRONG_MODE; |
| 1398 | }else{ |
| 1399 | rc = 0; |
| 1400 | payload = (*pageDef->func)(); |
| 1401 | } |
| 1402 | if( g.json.resultCode ){ |
| 1403 | json_err(g.json.resultCode, NULL, 0); |
| 1404 | }else{ |
| 1405 | blob_zero(&buf); |
| @@ -1465,11 +1483,11 @@ | |
| 1465 | return; |
| 1466 | }else if( pageDef->runMode > 0 /*HTTP only*/){ |
| 1467 | rc = FSL_JSON_E_WRONG_MODE; |
| 1468 | }else{ |
| 1469 | rc = 0; |
| 1470 | payload = (*pageDef->func)(); |
| 1471 | } |
| 1472 | if( g.json.resultCode ){ |
| 1473 | json_err(g.json.resultCode, NULL, 1); |
| 1474 | }else{ |
| 1475 | payload = json_create_response(rc, payload, NULL); |
| 1476 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -53,26 +53,37 @@ | |
| 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 | ** The depth parameter comes from the dispatcher and tells the |
| 60 | ** callback what index in json_command_arg() its command/path starts. |
| 61 | ** e.g. when dispatching /json/foo/bar, the foo callback will get a |
| 62 | ** depth of 1 and the bar callback will get a depth of 2. This is only |
| 63 | ** useful for callbacks which use json_command_arg(), and the only |
| 64 | ** callbacks which do that are ones which dispatch to sub-pages |
| 65 | ** (e.g. /json/wiki/...). This is a parameter, as opposed to simply |
| 66 | ** hard-coding the offset in each callback impl, so that refactoring |
| 67 | ** will (or should) be easier if we later reimplement the path/command |
| 68 | ** handling and arguments/path parts get moved around. |
| 69 | ** |
| 70 | ** All of the setup/response code is handled by the top dispatcher |
| 71 | ** functions and the callbacks concern themselves only with generating |
| 72 | ** the payload. |
| 73 | ** |
| 74 | ** It is imperitive that NO callback functions EVER output ANYTHING to |
| 75 | ** stdout, as that will effectively corrupt any HTTP output. |
| 76 | */ |
| 77 | typedef cson_value * (*fossil_json_f)(unsigned int depth); |
| 78 | |
| 79 | |
| 80 | /* |
| 81 | ** Placeholder /json/XXX page impl for NYI (Not Yet Implemented) |
| 82 | ** (but planned) pages/commands. |
| 83 | */ |
| 84 | static cson_value * json_page_nyi(unsigned int depth){ |
| 85 | g.json.resultCode = FSL_JSON_E_NYI; |
| 86 | return NULL; |
| 87 | } |
| 88 | |
| 89 | /* |
| @@ -236,17 +247,19 @@ | |
| 247 | return cson_value_new_string( json_rc_cstr(code), 11 ); |
| 248 | } |
| 249 | |
| 250 | |
| 251 | /* |
| 252 | ** Gets a POST/GET/COOKIE/ENV value. The returned memory is owned by the |
| 253 | ** g.json object (one of its sub-objects). Returns NULL if no match is |
| 254 | ** found. |
| 255 | ** |
| 256 | ** ENV means the system environment (getenv()). |
| 257 | ** |
| 258 | ** Precedence: GET, COOKIE, POST, ENV. COOKIE _should_ be after POST |
| 259 | ** but currently is not for internal order-of-init reasons. Since |
| 260 | ** fossil only uses one cookie, this is not a real/high-priority |
| 261 | ** problem. |
| 262 | */ |
| 263 | cson_value * json_getenv( char const * zKey ){ |
| 264 | cson_value * rc; |
| 265 | rc = cson_object_get( g.json.param.o, zKey ); |
| @@ -256,10 +269,13 @@ | |
| 269 | rc = cson_object_get( g.json.post.o, zKey ); |
| 270 | if(rc){ |
| 271 | return rc; |
| 272 | }else{ |
| 273 | char const * cv = PD(zKey,NULL); |
| 274 | if(!cv){ |
| 275 | cv = getenv(zKey); |
| 276 | } |
| 277 | if(cv){/*transform it to JSON for later use.*/ |
| 278 | rc = cson_value_new_string(cv,strlen(cv)); |
| 279 | cson_object_set( g.json.param.o, zKey, rc ); |
| 280 | return rc; |
| 281 | } |
| @@ -299,11 +315,10 @@ | |
| 315 | ** It will try to figure out if the client can support |
| 316 | ** application/json or application/javascript, and will fall back to |
| 317 | ** text/plain if it cannot figure out anything more specific. |
| 318 | ** |
| 319 | ** Returned memory is static and immutable. |
| 320 | */ |
| 321 | char const * json_guess_content_type(){ |
| 322 | char const * cset; |
| 323 | char doUtf8; |
| 324 | cset = PD("HTTP_ACCEPT_CHARSET",NULL); |
| @@ -399,11 +414,14 @@ | |
| 414 | /* |
| 415 | ** Initializes some JSON bits which need to be initialized relatively |
| 416 | ** early on. It should only be called from cgi_init() or |
| 417 | ** json_cmd_top() (early on in those functions). |
| 418 | ** |
| 419 | ** Initializes g.json.gc and g.json.param. This code does not (and |
| 420 | ** must not) rely on any of the fossil environment having been set |
| 421 | ** up. e.g. it must not use cgi_parameter() and friends because this |
| 422 | ** must be called before those data are initialized. |
| 423 | */ |
| 424 | void json_main_bootstrap(){ |
| 425 | cson_value * v; |
| 426 | assert( (NULL == g.json.gc.v) && "cgi_json_bootstrap() was called twice!" ); |
| 427 | |
| @@ -538,11 +556,11 @@ | |
| 556 | /* g.json.reqPayload.o may legally be NULL, which means only that |
| 557 | g.json.reqPayload.v is-not-a Object. |
| 558 | */; |
| 559 | } |
| 560 | |
| 561 | {/* set up JSON output formatting options. */ |
| 562 | unsigned char indent = g.isCGI ? 0 : 1; |
| 563 | cson_value const * indentV = json_getenv("indent"); |
| 564 | if(indentV){ |
| 565 | if(cson_value_is_string(indentV)){ |
| 566 | int const n = atoi(cson_string_cstr(cson_value_get_string(indentV))); |
| @@ -554,11 +572,11 @@ | |
| 572 | indent = (n>0) ? (unsigned char)n : 0; |
| 573 | } |
| 574 | } |
| 575 | g.json.outOpt.indentation = indent; |
| 576 | g.json.outOpt.addNewline = g.isCGI ? 0 : 1; |
| 577 | } |
| 578 | |
| 579 | if( g.isCGI ){ |
| 580 | json_auth_token()/* will copy our auth token, if any, to fossil's |
| 581 | core, which we need before we call |
| 582 | login_check_credentials(). */; |
| @@ -727,12 +745,12 @@ | |
| 745 | ** provide his own or may use an empty string to suppress the |
| 746 | ** resultText property. |
| 747 | ** |
| 748 | */ |
| 749 | cson_value * json_create_response( int resultCode, |
| 750 | cson_value * payload, |
| 751 | char const * pMsg ){ |
| 752 | cson_value * v = NULL; |
| 753 | cson_value * tmp = NULL; |
| 754 | cson_object * o = NULL; |
| 755 | int rc; |
| 756 | resultCode = json_dumbdown_rc(resultCode); |
| @@ -880,11 +898,11 @@ | |
| 898 | /* |
| 899 | ** /json/version implementation. |
| 900 | ** |
| 901 | ** Returns the payload object (owned by the caller). |
| 902 | */ |
| 903 | cson_value * json_page_version(unsigned int depth){ |
| 904 | cson_value * jval = NULL; |
| 905 | cson_object * jobj = NULL; |
| 906 | jval = cson_value_new_object(); |
| 907 | jobj = cson_value_get_object(jval); |
| 908 | #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X))) |
| @@ -909,11 +927,11 @@ | |
| 927 | ** current user (what he may/may not do). |
| 928 | ** |
| 929 | ** This is primarily intended for debuggering, but may have |
| 930 | ** a use in client code. (?) |
| 931 | */ |
| 932 | cson_value * json_page_cap(unsigned int depth){ |
| 933 | cson_value * payload = cson_value_new_object(); |
| 934 | cson_value * sub = cson_value_new_object(); |
| 935 | char * zCap; |
| 936 | Stmt q; |
| 937 | cson_object * obj = cson_value_get_object(payload); |
| @@ -966,11 +984,11 @@ | |
| 984 | |
| 985 | /* |
| 986 | ** Implementation of the /json/login page. |
| 987 | ** |
| 988 | */ |
| 989 | cson_value * json_page_login(unsigned int depth){ |
| 990 | static char preciseErrors = /* if true, "complete" JSON error codes are used, |
| 991 | else they are "dumbed down" to a generic login |
| 992 | error code. |
| 993 | */ |
| 994 | #if 0 |
| @@ -1096,11 +1114,11 @@ | |
| 1114 | |
| 1115 | /* |
| 1116 | ** Impl of /json/logout. |
| 1117 | ** |
| 1118 | */ |
| 1119 | cson_value * json_page_logout(unsigned int depth){ |
| 1120 | cson_value const *token = g.json.authToken; |
| 1121 | /* Remember that json_bootstrap() replaces the login cookie with |
| 1122 | the JSON auth token if the request contains it. If the reqest |
| 1123 | is missing the auth token then this will fetch fossil's |
| 1124 | original cookie. Either way, it's what we want :). |
| @@ -1121,11 +1139,11 @@ | |
| 1139 | } |
| 1140 | |
| 1141 | /* |
| 1142 | ** Implementation of the /json/anonymousPassword page. |
| 1143 | */ |
| 1144 | cson_value * json_page_anon_password(unsigned int depth){ |
| 1145 | cson_value * v = cson_value_new_object(); |
| 1146 | cson_object * o = cson_value_get_object(v); |
| 1147 | unsigned const int seed = captcha_seed(); |
| 1148 | char const * zCaptcha = captcha_decode(seed); |
| 1149 | cson_object_set(o, "seed", |
| @@ -1140,11 +1158,11 @@ | |
| 1158 | |
| 1159 | /* |
| 1160 | ** Implementation of the /json/stat page/command. |
| 1161 | ** |
| 1162 | */ |
| 1163 | cson_value * json_page_stat(unsigned int depth){ |
| 1164 | i64 t, fsize; |
| 1165 | int n, m; |
| 1166 | const char *zDb; |
| 1167 | enum { BufLen = 1000 }; |
| 1168 | char zBuf[BufLen]; |
| @@ -1235,11 +1253,11 @@ | |
| 1253 | return jv; |
| 1254 | #undef SETBUF |
| 1255 | } |
| 1256 | |
| 1257 | |
| 1258 | static cson_value * json_wiki_list(unsigned int depth); |
| 1259 | |
| 1260 | /* |
| 1261 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 1262 | */ |
| 1263 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| @@ -1253,13 +1271,13 @@ | |
| 1271 | /* |
| 1272 | ** Implements the /json/wiki family of pages/commands. Far from |
| 1273 | ** complete. |
| 1274 | ** |
| 1275 | */ |
| 1276 | static cson_value * json_page_wiki(unsigned int depth){ |
| 1277 | JsonPageDef const * def; |
| 1278 | char const * cmd = json_command_arg(1+depth); |
| 1279 | if( ! cmd ){ |
| 1280 | g.json.resultCode = FSL_JSON_E_MISSING_AUTH; |
| 1281 | return NULL; |
| 1282 | } |
| 1283 | def = json_handler_for_name( cmd, &JsonPageDefs_Wiki[0] ); |
| @@ -1266,18 +1284,18 @@ | |
| 1284 | if(!def){ |
| 1285 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| 1286 | return NULL; |
| 1287 | } |
| 1288 | else{ |
| 1289 | return (*def->func)(1+depth); |
| 1290 | } |
| 1291 | } |
| 1292 | |
| 1293 | /* |
| 1294 | ** INTERIM implementation of /json/wiki/list |
| 1295 | */ |
| 1296 | static cson_value * json_wiki_list(unsigned int depth){ |
| 1297 | cson_value * listV = NULL; |
| 1298 | cson_array * list = NULL; |
| 1299 | Stmt q; |
| 1300 | db_prepare(&q,"SELECT" |
| 1301 | " substr(tagname,6) as name" |
| @@ -1395,11 +1413,11 @@ | |
| 1413 | return; |
| 1414 | }else if( pageDef->runMode < 0 /*CLI only*/){ |
| 1415 | rc = FSL_JSON_E_WRONG_MODE; |
| 1416 | }else{ |
| 1417 | rc = 0; |
| 1418 | payload = (*pageDef->func)(1); |
| 1419 | } |
| 1420 | if( g.json.resultCode ){ |
| 1421 | json_err(g.json.resultCode, NULL, 0); |
| 1422 | }else{ |
| 1423 | blob_zero(&buf); |
| @@ -1465,11 +1483,11 @@ | |
| 1483 | return; |
| 1484 | }else if( pageDef->runMode > 0 /*HTTP only*/){ |
| 1485 | rc = FSL_JSON_E_WRONG_MODE; |
| 1486 | }else{ |
| 1487 | rc = 0; |
| 1488 | payload = (*pageDef->func)(1); |
| 1489 | } |
| 1490 | if( g.json.resultCode ){ |
| 1491 | json_err(g.json.resultCode, NULL, 1); |
| 1492 | }else{ |
| 1493 | payload = json_create_response(rc, payload, NULL); |
| 1494 |