1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
|
}
}
}
#ifdef FOSSIL_ENABLE_JSON
/*
** Internal helper for cson_data_source_FILE_n().
*/
typedef struct CgiPostReadState_ {
FILE * fh;
unsigned int len;
unsigned int pos;
} CgiPostReadState;
/*
** cson_data_source_f() impl which reads only up to
** a specified amount of data from its input FILE.
** state MUST be a full populated (CgiPostReadState*).
*/
static int cson_data_source_FILE_n( void * state,
void * dest,
unsigned int * n ){
if( ! state || !dest || !n ) return cson_rc.ArgError;
else {
CgiPostReadState * st = (CgiPostReadState *)state;
if( st->pos >= st->len ){
*n = 0;
return 0;
}else if( !*n || ((st->pos + *n) > st->len) ){
return cson_rc.RangeError;
}else{
unsigned int rsz = (unsigned int)fread( dest, 1, *n, st->fh );
if( ! rsz ){
*n = rsz;
return feof(st->fh) ? 0 : cson_rc.IOError;
}else{
*n = rsz;
st->pos += *n;
return 0;
}
}
}
}
/*
** Reads a JSON object from the first contentLen bytes of zIn. On
** g.json.post is updated to hold the content. On error a
** FSL_JSON_E_INVALID_REQUEST response is output and fossil_exit() is
** called (in HTTP mode exit code 0 is used).
**
** If contentLen is 0 then the whole file is read.
*/
void cgi_parse_POST_JSON( FILE * zIn, unsigned int contentLen ){
cson_value * jv = NULL;
int rc;
CgiPostReadState state;
cson_parse_opt popt = cson_parse_opt_empty;
cson_parse_info pinfo = cson_parse_info_empty;
assert(g.json.gc.a && "json_bootstrap_early() was not called!");
popt.maxDepth = 15;
state.fh = zIn;
state.len = contentLen;
state.pos = 0;
rc = cson_parse( &jv,
contentLen ? cson_data_source_FILE_n : cson_data_source_FILE,
contentLen ? (void *)&state : (void *)zIn, &popt, &pinfo );
if(rc){
goto invalidRequest;
}else{
json_gc_add( "POST.JSON", jv );
g.json.post.v = jv;
g.json.post.o = cson_value_get_object( jv );
if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
goto invalidRequest;
}
}
return;
invalidRequest:
cgi_set_content_type(json_guess_content_type());
if(0 != pinfo.errorCode){ /* fancy error message */
char * msg = mprintf("JSON parse error at line %u, column %u, "
"byte offset %u: %s",
pinfo.line, pinfo.col, pinfo.length,
cson_rc_string(pinfo.errorCode));
json_err( FSL_JSON_E_INVALID_REQUEST, msg, 1 );
free(msg);
}else if(jv && !g.json.post.o){
json_err( FSL_JSON_E_INVALID_REQUEST,
"Request envelope must be a JSON Object (not array).", 1 );
}else{ /* generic error message */
json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
}
fossil_exit( g.isHTTP ? 0 : 1);
|
<
<
<
<
<
<
<
|
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
|
|
|
<
<
|
<
<
<
<
<
|
<
<
|
|
|
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
|
}
}
}
#ifdef FOSSIL_ENABLE_JSON
/*
** Reads a JSON object from the given blob, which is assumed to have
** been populated by the caller from stdin, the SSL API, or a file, as
** appropriate for the particular use case. On success g.json.post is
** updated to hold the content. On error a FSL_JSON_E_INVALID_REQUEST
** response is output and fossil_exit() is called (in HTTP mode exit
** code 0 is used).
*/
void cgi_parse_POST_JSON( Blob * pIn ){
cson_value * jv = NULL;
cson_parse_opt popt = cson_parse_opt_empty;
cson_parse_info pinfo = cson_parse_info_empty;
assert(g.json.gc.a && "json_bootstrap_early() was not called!");
popt.maxDepth = 15;
jv = cson_parse_Blob(pIn, &pinfo);
if( jv==NULL ){
goto invalidRequest;
}else{
json_gc_add( "POST.JSON", jv );
g.json.post.v = jv;
g.json.post.o = cson_value_get_object( jv );
if( !g.json.post.o ){ /* we don't support non-Object (Array) requests */
goto invalidRequest;
}
}
return;
invalidRequest:
cgi_set_content_type(json_guess_content_type());
if(0 != pinfo.errorCode){ /* fancy error message */
char * msg = mprintf("JSON parse error at line %u, column %u, "
"byte offset %u: %s",
pinfo.line, pinfo.col, pinfo.length,
cson_rc_string(pinfo.errorCode));
json_err( FSL_JSON_E_INVALID_REQUEST, msg, 1 );
fossil_free(msg);
}else if(jv && !g.json.post.o){
json_err( FSL_JSON_E_INVALID_REQUEST,
"Request envelope must be a JSON Object (not array).", 1 );
}else{ /* generic error message */
json_err( FSL_JSON_E_INVALID_REQUEST, NULL, 1 );
}
fossil_exit( g.isHTTP ? 0 : 1);
|
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
|
if( len>0 && zType ){
if( fossil_strcmp(zType, "application/x-fossil")==0 ){
if( blob_read_from_cgi(&g.cgiIn, len)!=len ){
malformed_request("CGI content-length mismatch");
}
blob_uncompress(&g.cgiIn, &g.cgiIn);
}
#ifdef FOSSIL_ENABLE_JSON
else if( noJson==0 && g.json.isJsonMode!=0
&& json_can_consume_content_type(zType)!=0 ){
assert( !g.httpUseSSL );
cgi_parse_POST_JSON(g.httpIn, (unsigned int)len);
/*
Potential TODOs:
1) If parsing fails, immediately return an error response
without dispatching the ostensibly-upcoming JSON API.
*/
cgi_set_content_type(json_guess_content_type());
}
#endif /* FOSSIL_ENABLE_JSON */
else{
if( blob_read_from_cgi(&g.cgiIn, len)!=len ){
malformed_request("CGI content-length mismatch");
}
}
}
}
/*
** Decode POST parameter information in the cgiIn content, if any.
*/
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
>
>
>
>
>
>
|
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
|
if( len>0 && zType ){
if( fossil_strcmp(zType, "application/x-fossil")==0 ){
if( blob_read_from_cgi(&g.cgiIn, len)!=len ){
malformed_request("CGI content-length mismatch");
}
blob_uncompress(&g.cgiIn, &g.cgiIn);
}
else{
if( blob_read_from_cgi(&g.cgiIn, len)!=len ){
malformed_request("CGI content-length mismatch");
}
#ifdef FOSSIL_ENABLE_JSON
if( noJson==0 && g.json.isJsonMode!=0
&& json_can_consume_content_type(zType)!=0 ){
cgi_parse_POST_JSON(&g.cgiIn);
cgi_set_content_type(json_guess_content_type());
}
#endif /* FOSSIL_ENABLE_JSON */
}
}
}
/*
** Decode POST parameter information in the cgiIn content, if any.
*/
|