Fossil SCM
fixed an inconsistency in the CLI/CGI args/path handling. Non-CGI server mode is still broken b/c we do not yet have the PATH_INFO (or equivalent) data.
Commit
73591cc746f12189145481aa653a3a20b7d2bb7d
Parent
afd36e987ca1f28…
2 files changed
+58
-12
+6
+58
-12
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -256,29 +256,67 @@ | ||
| 256 | 256 | ** is malformed. |
| 257 | 257 | */ |
| 258 | 258 | static void json_mode_bootstrap(){ |
| 259 | 259 | g.json.isJsonMode = 1; |
| 260 | 260 | g.json.resultCode = 0; |
| 261 | + g.json.cmdOffset = -1; | |
| 261 | 262 | #if defined(NDEBUG) |
| 262 | 263 | /* avoids debug messages on stderr in JSON mode */ |
| 263 | 264 | sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0); |
| 264 | 265 | #endif |
| 266 | + /* g.json.reqPayload exists only to simplify some of our access to | |
| 267 | + the request payload. We only use this in the context of Object | |
| 268 | + payloads, not Arrays, strings, etc. */ | |
| 265 | 269 | g.json.reqPayload.v = cson_cgi_getenv( &g.json.cgiCx, "p", "payload" ); |
| 266 | 270 | if( g.json.reqPayload.v ){ |
| 267 | - g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v ); | |
| 268 | - if( ! g.json.reqPayload.o ){/* POST data is an array, which we can't use. */ | |
| 269 | - assert( cson_value_is_array( g.json.reqPayload.v ) ); | |
| 270 | - g.json.reqPayload.v = NULL; | |
| 271 | - } | |
| 271 | + g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v ) | |
| 272 | + /* g.json.reqPayload.o may legally be NULL, which means only that | |
| 273 | + g.json.reqPayload.v is-not-a Object. | |
| 274 | + */; | |
| 272 | 275 | } |
| 273 | 276 | json_auth_token()/* will copy our auth token, if any, to fossil's core. */; |
| 274 | 277 | if( g.isCGI ){ |
| 275 | 278 | login_check_credentials(); |
| 276 | 279 | } |
| 277 | 280 | else{ |
| 278 | 281 | db_find_and_open_repository(OPEN_ANY_SCHEMA,0); |
| 279 | 282 | } |
| 283 | + | |
| 284 | +} | |
| 285 | + | |
| 286 | +/* | |
| 287 | +** Returns the ndx'th item in the PATH_INFO path, where index 0 is | |
| 288 | +** the position of the "json" part of the path. Returns NULL if ndx | |
| 289 | +** is out of bounds or there is no "json" path element. | |
| 290 | +*/ | |
| 291 | +static char const * json_path_part(unsigned char ndx){ | |
| 292 | + if( g.json.cmdOffset < 0 ){ | |
| 293 | + /* first-time setup. */ | |
| 294 | + short i = g.isCGI ? 0 : 1; | |
| 295 | +#define NEXT (g.isCGI \ | |
| 296 | + ? cson_cgi_path_part_cstr( &g.json.cgiCx, i ) \ | |
| 297 | + : ((g.argc > i) ? g.argv[i] : NULL)) | |
| 298 | + char const * tok = NEXT; | |
| 299 | + while( tok ){ | |
| 300 | + if( 0==strncmp("json",tok,4) ){ | |
| 301 | + g.json.cmdOffset = i; | |
| 302 | + break; | |
| 303 | + } | |
| 304 | + ++i; | |
| 305 | + tok = NEXT; | |
| 306 | +#undef NEXT | |
| 307 | + } | |
| 308 | + } | |
| 309 | + if( g.json.cmdOffset < 0 ){ | |
| 310 | + return NULL; | |
| 311 | + }else{ | |
| 312 | + ndx = g.json.cmdOffset + ndx; | |
| 313 | + return g.isCGI | |
| 314 | + ? cson_cgi_path_part_cstr( &g.json.cgiCx, g.json.cmdOffset + ndx ) | |
| 315 | + : ((ndx < g.argc) ? g.argv[ndx] : NULL) | |
| 316 | + ; | |
| 317 | + } | |
| 280 | 318 | } |
| 281 | 319 | |
| 282 | 320 | /* |
| 283 | 321 | ** If g.json.reqPayload.o is NULL then NULL is returned, else the |
| 284 | 322 | ** given property is searched for in the request payload. If found it |
| @@ -439,10 +477,19 @@ | ||
| 439 | 477 | tmp = payload; |
| 440 | 478 | SET("payload"); |
| 441 | 479 | } |
| 442 | 480 | } |
| 443 | 481 | #undef SET |
| 482 | + if(0){/*only for my own debuggering*/ | |
| 483 | + tmp = cson_cgi_env_get_val(&g.json.cgiCx,'e', 0); | |
| 484 | + if(tmp){ | |
| 485 | + cson_object_set( o, "$ENV", tmp ); | |
| 486 | + } | |
| 487 | + tmp = cson_value_new_integer( g.json.cmdOffset ); | |
| 488 | + cson_object_set( o, "cmdOffset", tmp ); | |
| 489 | + } | |
| 490 | + | |
| 444 | 491 | goto ok; |
| 445 | 492 | cleanup: |
| 446 | 493 | cson_value_free(v); |
| 447 | 494 | v = NULL; |
| 448 | 495 | ok: |
| @@ -747,11 +794,11 @@ | ||
| 747 | 794 | /* Last entry MUST have a NULL name. */ |
| 748 | 795 | {NULL,NULL} |
| 749 | 796 | }; |
| 750 | 797 | |
| 751 | 798 | /* |
| 752 | -** WEBPAGE: /json | |
| 799 | +** WEBPAGE: json | |
| 753 | 800 | ** |
| 754 | 801 | ** Pages under /json/... must be entered into JsonPageDefs. |
| 755 | 802 | */ |
| 756 | 803 | void json_page_top(void){ |
| 757 | 804 | int rc = FSL_JSON_E_UNKNOWN_COMMAND; |
| @@ -758,24 +805,23 @@ | ||
| 758 | 805 | Blob buf = empty_blob; |
| 759 | 806 | char const * cmd; |
| 760 | 807 | cson_value * payload = NULL; |
| 761 | 808 | cson_value * root = NULL; |
| 762 | 809 | JsonPageDef const * pageDef = NULL; |
| 810 | + cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) ); | |
| 763 | 811 | json_mode_bootstrap(); |
| 764 | - cmd = cson_cgi_path_part_cstr(&g.json.cgiCx,1); | |
| 812 | + cmd = json_path_part(1); | |
| 765 | 813 | pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); |
| 766 | - cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) ); | |
| 767 | 814 | if( ! pageDef ){ |
| 768 | - json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 ); | |
| 815 | + json_err( FSL_JSON_E_UNKNOWN_COMMAND, cmd, 0 ); | |
| 769 | 816 | return; |
| 770 | 817 | }else if( pageDef->runMode < 0 /*CLI only*/) { |
| 771 | 818 | rc = FSL_JSON_E_WRONG_MODE; |
| 772 | 819 | }else{ |
| 773 | 820 | rc = 0; |
| 774 | 821 | payload = (*pageDef->func)(); |
| 775 | 822 | } |
| 776 | - /* FIXME: we need a way of channeling resultCode values back here. */ | |
| 777 | 823 | if( g.json.resultCode ){ |
| 778 | 824 | json_err(g.json.resultCode, NULL, 0); |
| 779 | 825 | }else{ |
| 780 | 826 | blob_zero(&buf); |
| 781 | 827 | root = json_response_skeleton(rc, payload, NULL); |
| @@ -813,19 +859,19 @@ | ||
| 813 | 859 | json_mode_bootstrap(); |
| 814 | 860 | db_find_and_open_repository(0, 0); |
| 815 | 861 | if( g.argc<3 ){ |
| 816 | 862 | goto usage; |
| 817 | 863 | } |
| 818 | - cmd = g.argv[2]; | |
| 864 | + cmd = json_path_part(1); | |
| 819 | 865 | n = cmd ? strlen(cmd) : 0; |
| 820 | 866 | if( n==0 ){ |
| 821 | 867 | goto usage; |
| 822 | 868 | } |
| 823 | 869 | cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) ); |
| 824 | 870 | pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); |
| 825 | 871 | if( ! pageDef ){ |
| 826 | - json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 ); | |
| 872 | + json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 ); | |
| 827 | 873 | return; |
| 828 | 874 | }else if( pageDef->runMode > 0 /*HTTP only*/) { |
| 829 | 875 | rc = FSL_JSON_E_WRONG_MODE; |
| 830 | 876 | }else{ |
| 831 | 877 | rc = 0; |
| 832 | 878 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -256,29 +256,67 @@ | |
| 256 | ** is malformed. |
| 257 | */ |
| 258 | static void json_mode_bootstrap(){ |
| 259 | g.json.isJsonMode = 1; |
| 260 | g.json.resultCode = 0; |
| 261 | #if defined(NDEBUG) |
| 262 | /* avoids debug messages on stderr in JSON mode */ |
| 263 | sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0); |
| 264 | #endif |
| 265 | g.json.reqPayload.v = cson_cgi_getenv( &g.json.cgiCx, "p", "payload" ); |
| 266 | if( g.json.reqPayload.v ){ |
| 267 | g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v ); |
| 268 | if( ! g.json.reqPayload.o ){/* POST data is an array, which we can't use. */ |
| 269 | assert( cson_value_is_array( g.json.reqPayload.v ) ); |
| 270 | g.json.reqPayload.v = NULL; |
| 271 | } |
| 272 | } |
| 273 | json_auth_token()/* will copy our auth token, if any, to fossil's core. */; |
| 274 | if( g.isCGI ){ |
| 275 | login_check_credentials(); |
| 276 | } |
| 277 | else{ |
| 278 | db_find_and_open_repository(OPEN_ANY_SCHEMA,0); |
| 279 | } |
| 280 | } |
| 281 | |
| 282 | /* |
| 283 | ** If g.json.reqPayload.o is NULL then NULL is returned, else the |
| 284 | ** given property is searched for in the request payload. If found it |
| @@ -439,10 +477,19 @@ | |
| 439 | tmp = payload; |
| 440 | SET("payload"); |
| 441 | } |
| 442 | } |
| 443 | #undef SET |
| 444 | goto ok; |
| 445 | cleanup: |
| 446 | cson_value_free(v); |
| 447 | v = NULL; |
| 448 | ok: |
| @@ -747,11 +794,11 @@ | |
| 747 | /* Last entry MUST have a NULL name. */ |
| 748 | {NULL,NULL} |
| 749 | }; |
| 750 | |
| 751 | /* |
| 752 | ** WEBPAGE: /json |
| 753 | ** |
| 754 | ** Pages under /json/... must be entered into JsonPageDefs. |
| 755 | */ |
| 756 | void json_page_top(void){ |
| 757 | int rc = FSL_JSON_E_UNKNOWN_COMMAND; |
| @@ -758,24 +805,23 @@ | |
| 758 | Blob buf = empty_blob; |
| 759 | char const * cmd; |
| 760 | cson_value * payload = NULL; |
| 761 | cson_value * root = NULL; |
| 762 | JsonPageDef const * pageDef = NULL; |
| 763 | json_mode_bootstrap(); |
| 764 | cmd = cson_cgi_path_part_cstr(&g.json.cgiCx,1); |
| 765 | pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); |
| 766 | cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) ); |
| 767 | if( ! pageDef ){ |
| 768 | json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 ); |
| 769 | return; |
| 770 | }else if( pageDef->runMode < 0 /*CLI only*/) { |
| 771 | rc = FSL_JSON_E_WRONG_MODE; |
| 772 | }else{ |
| 773 | rc = 0; |
| 774 | payload = (*pageDef->func)(); |
| 775 | } |
| 776 | /* FIXME: we need a way of channeling resultCode values back here. */ |
| 777 | if( g.json.resultCode ){ |
| 778 | json_err(g.json.resultCode, NULL, 0); |
| 779 | }else{ |
| 780 | blob_zero(&buf); |
| 781 | root = json_response_skeleton(rc, payload, NULL); |
| @@ -813,19 +859,19 @@ | |
| 813 | json_mode_bootstrap(); |
| 814 | db_find_and_open_repository(0, 0); |
| 815 | if( g.argc<3 ){ |
| 816 | goto usage; |
| 817 | } |
| 818 | cmd = g.argv[2]; |
| 819 | n = cmd ? strlen(cmd) : 0; |
| 820 | if( n==0 ){ |
| 821 | goto usage; |
| 822 | } |
| 823 | cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) ); |
| 824 | pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); |
| 825 | if( ! pageDef ){ |
| 826 | json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 ); |
| 827 | return; |
| 828 | }else if( pageDef->runMode > 0 /*HTTP only*/) { |
| 829 | rc = FSL_JSON_E_WRONG_MODE; |
| 830 | }else{ |
| 831 | rc = 0; |
| 832 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -256,29 +256,67 @@ | |
| 256 | ** is malformed. |
| 257 | */ |
| 258 | static void json_mode_bootstrap(){ |
| 259 | g.json.isJsonMode = 1; |
| 260 | g.json.resultCode = 0; |
| 261 | g.json.cmdOffset = -1; |
| 262 | #if defined(NDEBUG) |
| 263 | /* avoids debug messages on stderr in JSON mode */ |
| 264 | sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0); |
| 265 | #endif |
| 266 | /* g.json.reqPayload exists only to simplify some of our access to |
| 267 | the request payload. We only use this in the context of Object |
| 268 | payloads, not Arrays, strings, etc. */ |
| 269 | g.json.reqPayload.v = cson_cgi_getenv( &g.json.cgiCx, "p", "payload" ); |
| 270 | if( g.json.reqPayload.v ){ |
| 271 | g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v ) |
| 272 | /* g.json.reqPayload.o may legally be NULL, which means only that |
| 273 | g.json.reqPayload.v is-not-a Object. |
| 274 | */; |
| 275 | } |
| 276 | json_auth_token()/* will copy our auth token, if any, to fossil's core. */; |
| 277 | if( g.isCGI ){ |
| 278 | login_check_credentials(); |
| 279 | } |
| 280 | else{ |
| 281 | db_find_and_open_repository(OPEN_ANY_SCHEMA,0); |
| 282 | } |
| 283 | |
| 284 | } |
| 285 | |
| 286 | /* |
| 287 | ** Returns the ndx'th item in the PATH_INFO path, where index 0 is |
| 288 | ** the position of the "json" part of the path. Returns NULL if ndx |
| 289 | ** is out of bounds or there is no "json" path element. |
| 290 | */ |
| 291 | static char const * json_path_part(unsigned char ndx){ |
| 292 | if( g.json.cmdOffset < 0 ){ |
| 293 | /* first-time setup. */ |
| 294 | short i = g.isCGI ? 0 : 1; |
| 295 | #define NEXT (g.isCGI \ |
| 296 | ? cson_cgi_path_part_cstr( &g.json.cgiCx, i ) \ |
| 297 | : ((g.argc > i) ? g.argv[i] : NULL)) |
| 298 | char const * tok = NEXT; |
| 299 | while( tok ){ |
| 300 | if( 0==strncmp("json",tok,4) ){ |
| 301 | g.json.cmdOffset = i; |
| 302 | break; |
| 303 | } |
| 304 | ++i; |
| 305 | tok = NEXT; |
| 306 | #undef NEXT |
| 307 | } |
| 308 | } |
| 309 | if( g.json.cmdOffset < 0 ){ |
| 310 | return NULL; |
| 311 | }else{ |
| 312 | ndx = g.json.cmdOffset + ndx; |
| 313 | return g.isCGI |
| 314 | ? cson_cgi_path_part_cstr( &g.json.cgiCx, g.json.cmdOffset + ndx ) |
| 315 | : ((ndx < g.argc) ? g.argv[ndx] : NULL) |
| 316 | ; |
| 317 | } |
| 318 | } |
| 319 | |
| 320 | /* |
| 321 | ** If g.json.reqPayload.o is NULL then NULL is returned, else the |
| 322 | ** given property is searched for in the request payload. If found it |
| @@ -439,10 +477,19 @@ | |
| 477 | tmp = payload; |
| 478 | SET("payload"); |
| 479 | } |
| 480 | } |
| 481 | #undef SET |
| 482 | if(0){/*only for my own debuggering*/ |
| 483 | tmp = cson_cgi_env_get_val(&g.json.cgiCx,'e', 0); |
| 484 | if(tmp){ |
| 485 | cson_object_set( o, "$ENV", tmp ); |
| 486 | } |
| 487 | tmp = cson_value_new_integer( g.json.cmdOffset ); |
| 488 | cson_object_set( o, "cmdOffset", tmp ); |
| 489 | } |
| 490 | |
| 491 | goto ok; |
| 492 | cleanup: |
| 493 | cson_value_free(v); |
| 494 | v = NULL; |
| 495 | ok: |
| @@ -747,11 +794,11 @@ | |
| 794 | /* Last entry MUST have a NULL name. */ |
| 795 | {NULL,NULL} |
| 796 | }; |
| 797 | |
| 798 | /* |
| 799 | ** WEBPAGE: json |
| 800 | ** |
| 801 | ** Pages under /json/... must be entered into JsonPageDefs. |
| 802 | */ |
| 803 | void json_page_top(void){ |
| 804 | int rc = FSL_JSON_E_UNKNOWN_COMMAND; |
| @@ -758,24 +805,23 @@ | |
| 805 | Blob buf = empty_blob; |
| 806 | char const * cmd; |
| 807 | cson_value * payload = NULL; |
| 808 | cson_value * root = NULL; |
| 809 | JsonPageDef const * pageDef = NULL; |
| 810 | cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) ); |
| 811 | json_mode_bootstrap(); |
| 812 | cmd = json_path_part(1); |
| 813 | pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); |
| 814 | if( ! pageDef ){ |
| 815 | json_err( FSL_JSON_E_UNKNOWN_COMMAND, cmd, 0 ); |
| 816 | return; |
| 817 | }else if( pageDef->runMode < 0 /*CLI only*/) { |
| 818 | rc = FSL_JSON_E_WRONG_MODE; |
| 819 | }else{ |
| 820 | rc = 0; |
| 821 | payload = (*pageDef->func)(); |
| 822 | } |
| 823 | if( g.json.resultCode ){ |
| 824 | json_err(g.json.resultCode, NULL, 0); |
| 825 | }else{ |
| 826 | blob_zero(&buf); |
| 827 | root = json_response_skeleton(rc, payload, NULL); |
| @@ -813,19 +859,19 @@ | |
| 859 | json_mode_bootstrap(); |
| 860 | db_find_and_open_repository(0, 0); |
| 861 | if( g.argc<3 ){ |
| 862 | goto usage; |
| 863 | } |
| 864 | cmd = json_path_part(1); |
| 865 | n = cmd ? strlen(cmd) : 0; |
| 866 | if( n==0 ){ |
| 867 | goto usage; |
| 868 | } |
| 869 | cgi_set_content_type( cson_cgi_guess_content_type(&g.json.cgiCx) ); |
| 870 | pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]); |
| 871 | if( ! pageDef ){ |
| 872 | json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 ); |
| 873 | return; |
| 874 | }else if( pageDef->runMode > 0 /*HTTP only*/) { |
| 875 | rc = FSL_JSON_E_WRONG_MODE; |
| 876 | }else{ |
| 877 | rc = 0; |
| 878 |
+6
| --- src/main.c | ||
| +++ src/main.c | ||
| @@ -173,10 +173,16 @@ | ||
| 173 | 173 | struct FossilJsonBits { |
| 174 | 174 | int isJsonMode; /* True if running in JSON mode, else false. This changes |
| 175 | 175 | how errors are reported. In JSON mode we try to always |
| 176 | 176 | output JSON-form error responses. |
| 177 | 177 | */ |
| 178 | + int cmdOffset; /* Tells us which PATH_INFO/CLI args | |
| 179 | + part holds the "json" command, so | |
| 180 | + that we can account for sub-repos | |
| 181 | + and path prefixes. This is handled | |
| 182 | + differently for CLI and CGI modes. | |
| 183 | + */ | |
| 178 | 184 | int resultCode; /* used for passing back specific codes from /json callbacks. */ |
| 179 | 185 | int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ |
| 180 | 186 | cson_cgi_cx cgiCx; /* cson_cgi context */ |
| 181 | 187 | cson_output_opt outOpt; /* formatting options for JSON mode. */ |
| 182 | 188 | cson_value * authToken; /* authentication token */ |
| 183 | 189 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -173,10 +173,16 @@ | |
| 173 | struct FossilJsonBits { |
| 174 | int isJsonMode; /* True if running in JSON mode, else false. This changes |
| 175 | how errors are reported. In JSON mode we try to always |
| 176 | output JSON-form error responses. |
| 177 | */ |
| 178 | int resultCode; /* used for passing back specific codes from /json callbacks. */ |
| 179 | int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ |
| 180 | cson_cgi_cx cgiCx; /* cson_cgi context */ |
| 181 | cson_output_opt outOpt; /* formatting options for JSON mode. */ |
| 182 | cson_value * authToken; /* authentication token */ |
| 183 |
| --- src/main.c | |
| +++ src/main.c | |
| @@ -173,10 +173,16 @@ | |
| 173 | struct FossilJsonBits { |
| 174 | int isJsonMode; /* True if running in JSON mode, else false. This changes |
| 175 | how errors are reported. In JSON mode we try to always |
| 176 | output JSON-form error responses. |
| 177 | */ |
| 178 | int cmdOffset; /* Tells us which PATH_INFO/CLI args |
| 179 | part holds the "json" command, so |
| 180 | that we can account for sub-repos |
| 181 | and path prefixes. This is handled |
| 182 | differently for CLI and CGI modes. |
| 183 | */ |
| 184 | int resultCode; /* used for passing back specific codes from /json callbacks. */ |
| 185 | int errorDetailParanoia; /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */ |
| 186 | cson_cgi_cx cgiCx; /* cson_cgi context */ |
| 187 | cson_output_opt outOpt; /* formatting options for JSON mode. */ |
| 188 | cson_value * authToken; /* authentication token */ |
| 189 |