1 #ifdef FOSSIL_ENABLE_JSON
2 /*
3 ** Copyright (c) 2011 D. Richard Hipp
4 **
5 ** This program is free software; you can redistribute it and/or
6 ** modify it under the terms of the Simplified BSD License (also
7 ** known as the "2-Clause License" or "FreeBSD License".)
8
9 ** This program is distributed in the hope that it will be useful,
10 ** but without any warranty; without even the implied warranty of
11 ** merchantability or fitness for a particular purpose.
12 **
13 ** Author contact information:
14 ** drh@hwaci.com
15 ** http://www.hwaci.com/drh/
16 **
17 *******************************************************************************
18 **
19 ** Code for the JSON API.
20 **
21 ** The JSON API's public interface is documented at:
22 **
23 ** https://fossil-scm.org/fossil/doc/trunk/www/json-api/index.md
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 "VERSION.h"
35 #include "config.h"
36 #include "json.h"
37 #include <assert.h>
38 #include <time.h>
39
40 #if INTERFACE
41 #include "json_detail.h" /* workaround for apparent enum limitation in makeheaders */
42 #endif
43
44 const FossilJsonKeys_ FossilJsonKeys = {
45 "anonymousSeed" /*anonymousSeed*/,
46 "authToken" /*authToken*/,
47 "COMMAND_PATH" /*commandPath*/,
48 "mtime" /*mtime*/,
49 "payload" /* payload */,
50 "requestId" /*requestId*/,
51 "resultCode" /*resultCode*/,
52 "resultText" /*resultText*/,
53 "timestamp" /*timestamp*/
54 };
55
56 /*
57 ** Given the current request path string, this function returns true
58 ** if it refers to a JSON API path. i.e. if (1) it starts with /json
59 ** or (2) g.zCmdName is "server" or "cgi" and the path starts with
60 ** /somereponame/json. Specifically, it returns 1 in the former case
61 ** and 2 for the latter.
62 */
json_request_is_json_api(const char * zPathInfo)63 int json_request_is_json_api(const char * zPathInfo){
64 int rc = 0;
65 if(zPathInfo==0){
66 rc = 0;
67 }else if(0==strncmp("/json",zPathInfo,5)
68 && (zPathInfo[5]==0 || zPathInfo[5]=='/')){
69 rc = 1;
70 }else if(g.zCmdName!=0 && (0==strcmp("server",g.zCmdName)
71 || 0==strcmp("ui",g.zCmdName)
72 || 0==strcmp("cgi",g.zCmdName)
73 || 0==strcmp("http",g.zCmdName)) ){
74 /* When running in server/cgi "directory" mode, zPathInfo is
75 ** prefixed with the repository's name, so in order to determine
76 ** whether or not we're really running in json mode we have to try
77 ** a bit harder. Problem reported here:
78 ** https://fossil-scm.org/forum/forumpost/e4953666d6
79 */
80 ReCompiled * pReg = 0;
81 const char * zErr = re_compile(&pReg, "^/[^/]+/json(/.*)?", 0);
82 assert(zErr==0 && "Regex compilation failed?");
83 if(zErr==0 &&
84 re_match(pReg, (const unsigned char *)zPathInfo, -1)){
85 rc = 2;
86 }
87 re_free(pReg);
88 }
89 return rc;
90 }
91
92 /*
93 ** Returns true (non-0) if fossil appears to be running in JSON mode.
94 ** and either has JSON POSTed input awaiting consumption or fossil is
95 ** running in HTTP mode (in which case certain JSON data *might* be
96 ** available via GET parameters).
97 */
fossil_has_json()98 int fossil_has_json(){
99 return g.json.isJsonMode && (g.isHTTP || g.json.post.o);
100 }
101
102 /*
103 ** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
104 ** (but planned) pages/commands.
105 */
json_page_nyi()106 cson_value * json_page_nyi(){
107 g.json.resultCode = FSL_JSON_E_NYI;
108 return NULL;
109 }
110
111 /*
112 ** Given a FossilJsonCodes value, it returns a string suitable for use
113 ** as a resultCode string. Returns some unspecified non-empty string
114 ** if errCode is not one of the FossilJsonCodes values.
115 */
json_err_cstr(int errCode)116 static char const * json_err_cstr( int errCode ){
117 switch( errCode ){
118 case 0: return "Success";
119 #define C(X,V) case FSL_JSON_E_ ## X: return V
120
121 C(GENERIC,"Generic error");
122 C(INVALID_REQUEST,"Invalid request");
123 C(UNKNOWN_COMMAND,"Unknown command or subcommand");
124 C(UNKNOWN,"Unknown error");
125 C(TIMEOUT,"Timeout reached");
126 C(ASSERT,"Assertion failed");
127 C(ALLOC,"Resource allocation failed");
128 C(NYI,"Not yet implemented");
129 C(PANIC,"x");
130 C(MANIFEST_READ_FAILED,"Reading artifact manifest failed");
131 C(FILE_OPEN_FAILED,"Opening file failed");
132
133 C(AUTH,"Authentication error");
134 C(MISSING_AUTH,"Authentication info missing from request");
135 C(DENIED,"Access denied");
136 C(WRONG_MODE,"Request not allowed (wrong operation mode)");
137 C(LOGIN_FAILED,"Login failed");
138 C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed");
139 C(LOGIN_FAILED_NONAME,"Login failed - name not supplied");
140 C(LOGIN_FAILED_NOPW,"Login failed - password not supplied");
141 C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found");
142
143 C(USAGE,"Usage error");
144 C(INVALID_ARGS,"Invalid argument(s)");
145 C(MISSING_ARGS,"Missing argument(s)");
146 C(AMBIGUOUS_UUID,"Resource identifier is ambiguous");
147 C(UNRESOLVED_UUID,"Provided uuid/tag/branch could not be resolved");
148 C(RESOURCE_ALREADY_EXISTS,"Resource already exists");
149 C(RESOURCE_NOT_FOUND,"Resource not found");
150
151 C(DB,"Database error");
152 C(STMT_PREP,"Statement preparation failed");
153 C(STMT_BIND,"Statement parameter binding failed");
154 C(STMT_EXEC,"Statement execution/stepping failed");
155 C(DB_LOCKED,"Database is locked");
156 C(DB_NEEDS_REBUILD,"Fossil repository needs to be rebuilt");
157 C(DB_NOT_FOUND,"Fossil repository db file could not be found.");
158 C(DB_NOT_VALID, "Fossil repository db file is not valid.");
159 C(DB_NEEDS_CHECKOUT, "Command requires a local checkout.");
160 #undef C
161 default:
162 return "Unknown Error";
163 }
164 }
165
166 /*
167 ** Implements the cson_data_dest_f() interface and outputs the data to
168 ** a fossil Blob object. pState must be-a initialized (Blob*), to
169 ** which n bytes of src will be appended.
170 **/
cson_data_dest_Blob(void * pState,void const * src,unsigned int n)171 int cson_data_dest_Blob(void * pState, void const * src, unsigned int n){
172 Blob * b = (Blob*)pState;
173 blob_append( b, (char const *)src, (int)n ) /* will die on OOM */;
174 return 0;
175 }
176
177 /*
178 ** Implements the cson_data_source_f() interface and reads input from
179 ** a fossil Blob object. pState must be-a (Blob*) populated with JSON
180 ** data.
181 */
cson_data_src_Blob(void * pState,void * dest,unsigned int * n)182 int cson_data_src_Blob(void * pState, void * dest, unsigned int * n){
183 Blob * b = (Blob*)pState;
184 *n = blob_read( b, dest, *n );
185 return 0;
186 }
187
188 /*
189 ** Convenience wrapper around cson_output() which appends the output
190 ** to pDest. pOpt may be NULL, in which case g.json.outOpt will be used.
191 */
cson_output_Blob(cson_value const * pVal,Blob * pDest,cson_output_opt const * pOpt)192 int cson_output_Blob( cson_value const * pVal, Blob * pDest, cson_output_opt const * pOpt ){
193 return cson_output( pVal, cson_data_dest_Blob,
194 pDest, pOpt ? pOpt : &g.json.outOpt );
195 }
196
197 /*
198 ** Convenience wrapper around cson_parse() which reads its input
199 ** from pSrc. pSrc is rewound before parsing.
200 **
201 ** pInfo may be NULL. If it is not NULL then it will contain details
202 ** about the parse state when this function returns.
203 **
204 ** On success a new JSON Object or Array is returned (owned by the
205 ** caller). On error NULL is returned.
206 */
cson_parse_Blob(Blob * pSrc,cson_parse_info * pInfo)207 cson_value * cson_parse_Blob( Blob * pSrc, cson_parse_info * pInfo ){
208 cson_value * root = NULL;
209 blob_rewind( pSrc );
210 cson_parse( &root, cson_data_src_Blob, pSrc, NULL, pInfo );
211 return root;
212 }
213
214 /*
215 ** Implements the cson_data_dest_f() interface and outputs the data to
216 ** cgi_append_content(). pState is ignored.
217 **/
cson_data_dest_cgi(void * pState,void const * src,unsigned int n)218 int cson_data_dest_cgi(void * pState, void const * src, unsigned int n){
219 cgi_append_content( (char const *)src, (int)n );
220 return 0;
221 }
222
223 /*
224 ** Returns a string in the form FOSSIL-XXXX, where XXXX is a
225 ** left-zero-padded value of code. The returned buffer is static, and
226 ** must be copied if needed for later. The returned value will always
227 ** be 11 bytes long (not including the trailing NUL byte).
228 **
229 ** In practice we will only ever call this one time per app execution
230 ** when constructing the JSON response envelope, so the static buffer
231 ** "shouldn't" be a problem.
232 **
233 */
json_rc_cstr(int code)234 char const * json_rc_cstr( int code ){
235 enum { BufSize = 12 };
236 static char buf[BufSize] = {'F','O','S','S','I','L','-',0};
237 assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
238 sprintf(buf+7,"%04d", code);
239 return buf;
240 }
241
242 /*
243 ** Adds v to the API-internal cleanup mechanism. key is ignored
244 ** (legacy) but might be re-introduced and "should" be a unique
245 ** (app-wide) value. Failure to insert an item may be caused by any
246 ** of the following:
247 **
248 ** - Allocation error.
249 ** - g.json.gc.a is NULL
250 ** - key is NULL or empty.
251 **
252 ** Returns 0 on success.
253 **
254 ** Ownership of v is transfered to (or shared with) g.json.gc, and v
255 ** will be valid until that object is cleaned up or some internal code
256 ** incorrectly removes it from the gc (which we never do). If this
257 ** function fails, it is fatal to the app (as it indicates an
258 ** allocation error (more likely than not) or a serious internal error
259 ** such as numeric overflow).
260 */
json_gc_add(char const * key,cson_value * v)261 void json_gc_add( char const * key, cson_value * v ){
262 int const rc = cson_array_append( g.json.gc.a, v );
263
264 assert( NULL != g.json.gc.a );
265 if( 0 != rc ){
266 cson_value_free( v );
267 }
268 assert( (0==rc) && "Adding item to GC failed." );
269 if(0!=rc){
270 fprintf(stderr,"%s: FATAL: alloc error.\n", g.argv[0])
271 /* reminder: allocation error is the only reasonable cause of
272 error here, provided g.json.gc.a and v are not NULL.
273 */
274 ;
275 fossil_exit(1)/*not fossil_panic() b/c it might land us somewhere
276 where this function is called again.
277 */;
278 }
279 }
280
281
282 /*
283 ** Returns the value of json_rc_cstr(code) as a new JSON
284 ** string, which is owned by the caller and must eventually
285 ** be cson_value_free()d or transfered to a JSON container.
286 */
json_rc_string(int code)287 cson_value * json_rc_string( int code ){
288 return cson_value_new_string( json_rc_cstr(code), 11 );
289 }
290
json_new_string(char const * str)291 cson_value * json_new_string( char const * str ){
292 return str
293 ? cson_value_new_string(str,strlen(str))
294 : NULL;
295 }
296
json_new_string_f(char const * fmt,...)297 cson_value * json_new_string_f( char const * fmt, ... ){
298 cson_value * v;
299 char * zStr;
300 va_list vargs;
301 va_start(vargs,fmt);
302 zStr = vmprintf(fmt,vargs);
303 va_end(vargs);
304 v = cson_value_new_string(zStr, strlen(zStr));
305 free(zStr);
306 return v;
307 }
308
json_new_int(i64 v)309 cson_value * json_new_int( i64 v ){
310 return cson_value_new_integer((cson_int_t)v);
311 }
312
313 /*
314 ** Gets a POST/POST.payload/GET/COOKIE/ENV value. The returned memory
315 ** is owned by the g.json object (one of its sub-objects). Returns
316 ** NULL if no match is found.
317 **
318 ** ENV means the system environment (getenv()).
319 **
320 ** Precedence: POST.payload, GET/COOKIE/non-JSON POST, JSON POST, ENV.
321 **
322 ** FIXME: the precedence SHOULD be: GET, POST.payload, POST, COOKIE,
323 ** ENV, but the amalgamation of the GET/POST vars makes it effectively
324 ** impossible to do that. Since fossil only uses one cookie, cookie
325 ** precedence isn't a real/high-priority problem.
326 */
json_getenv(char const * zKey)327 cson_value * json_getenv( char const * zKey ){
328 cson_value * rc;
329 rc = g.json.reqPayload.o
330 ? cson_object_get( g.json.reqPayload.o, zKey )
331 : NULL;
332 if(rc){
333 return rc;
334 }
335 rc = cson_object_get( g.json.param.o, zKey );
336 if( rc ){
337 return rc;
338 }
339 rc = cson_object_get( g.json.post.o, zKey );
340 if(rc){
341 return rc;
342 }else{
343 char const * cv = PD(zKey,NULL);
344 if(!cv && !g.isHTTP){
345 /* reminder to self: in CLI mode i'd like to try
346 find_option(zKey,NULL,XYZ) here, but we don't have a sane
347 default for the XYZ param here.
348 */
349 cv = fossil_getenv(zKey);
350 }
351 if(cv){/*transform it to JSON for later use.*/
352 /* use sscanf() to figure out if it's an int,
353 and transform it to JSON int if it is.
354
355 FIXME: use strtol(), since it has more accurate
356 error handling.
357 */
358 int intVal = -1;
359 char endOfIntCheck;
360 int const scanRc = sscanf(cv,"%d%c",&intVal, &endOfIntCheck)
361 /* The %c bit there is to make sure that we don't accept 123x
362 as a number. sscanf() returns the number of tokens
363 successfully parsed, so an RC of 1 will be correct for "123"
364 but "123x" will have RC==2.
365
366 But it appears to not be working that way :/
367 */
368 ;
369 if(1==scanRc){
370 json_setenv( zKey, cson_value_new_integer(intVal) );
371 }else{
372 rc = cson_value_new_string(cv,strlen(cv));
373 json_setenv( zKey, rc );
374 }
375 return rc;
376 }else{
377 return NULL;
378 }
379 }
380 }
381
382 /*
383 ** Wrapper around json_getenv() which...
384 **
385 ** If it finds a value and that value is-a JSON number or is a string
386 ** which looks like an integer or is-a JSON bool/null then it is
387 ** converted to an int. If none of those apply then dflt is returned.
388 */
json_getenv_int(char const * pKey,int dflt)389 int json_getenv_int(char const * pKey, int dflt ){
390 cson_value const * v = json_getenv(pKey);
391 const cson_type_id type = v ? cson_value_type_id(v) : CSON_TYPE_UNDEF;
392 switch(type){
393 case CSON_TYPE_INTEGER:
394 case CSON_TYPE_DOUBLE:
395 return (int)cson_value_get_integer(v);
396 case CSON_TYPE_STRING: {
397 char const * sv = cson_string_cstr(cson_value_get_string(v));
398 assert( (NULL!=sv) && "This is quite unexpected." );
399 return sv ? atoi(sv) : dflt;
400 }
401 case CSON_TYPE_BOOL:
402 return cson_value_get_bool(v) ? 1 : 0;
403 case CSON_TYPE_NULL:
404 return 0;
405 default:
406 return dflt;
407 }
408 }
409
410
411 /*
412 ** Wrapper around json_getenv() which tries to evaluate a payload/env
413 ** value as a boolean. Uses mostly the same logic as
414 ** json_getenv_int(), with the addition that string values which
415 ** either start with a digit 1..9 or the letters [tTyY] are considered
416 ** to be true. If this function cannot find a matching key/value then
417 ** dflt is returned. e.g. if it finds the key but the value is-a
418 ** Object then dftl is returned.
419 **
420 ** If an entry is found, this function guarantees that it will return
421 ** either 0 or 1, as opposed to "0 or non-zero", so that clients can
422 ** pass a different value as dflt. Thus they can use, e.g. -1 to know
423 ** whether or not this function found a match (it will return -1 in
424 ** that case).
425 */
json_getenv_bool(char const * pKey,int dflt)426 int json_getenv_bool(char const * pKey, int dflt ){
427 cson_value const * v = json_getenv(pKey);
428 const cson_type_id type = v ? cson_value_type_id(v) : CSON_TYPE_UNDEF;
429 switch(type){
430 case CSON_TYPE_INTEGER:
431 case CSON_TYPE_DOUBLE:
432 return cson_value_get_integer(v) ? 1 : 0;
433 case CSON_TYPE_STRING: {
434 char const * sv = cson_string_cstr(cson_value_get_string(v));
435 assert( (NULL!=sv) && "This is quite unexpected." );
436 if(!*sv || ('0'==*sv)){
437 return 0;
438 }else{
439 return ((('1'<=*sv) && ('9'>=*sv))
440 || ('t'==*sv) || ('T'==*sv)
441 || ('y'==*sv) || ('Y'==*sv)
442 )
443 ? 1 : 0;
444 }
445 }
446 case CSON_TYPE_BOOL:
447 return cson_value_get_bool(v) ? 1 : 0;
448 case CSON_TYPE_NULL:
449 return 0;
450 default:
451 return dflt;
452 }
453 }
454
455 /*
456 ** Returns the string form of a json_getenv() value, but ONLY If that
457 ** value is-a String. Non-strings are not converted to strings for
458 ** this purpose. Returned memory is owned by g.json or fossil and is
459 ** valid until end-of-app or the given key is replaced in fossil's
460 ** internals via cgi_replace_parameter() and friends or json_setenv().
461 */
json_getenv_cstr(char const * zKey)462 char const * json_getenv_cstr( char const * zKey ){
463 return cson_value_get_cstr( json_getenv(zKey) );
464 }
465
466 /*
467 ** An extended form of find_option() which tries to look up a combo
468 ** GET/POST/CLI argument.
469 **
470 ** zKey must be the GET/POST parameter key. zCLILong must be the "long
471 ** form" CLI flag (NULL means to use zKey). zCLIShort may be NULL or
472 ** the "short form" CLI flag (if NULL, no short form is used).
473 **
474 ** If argPos is >=0 and no other match is found,
475 ** json_command_arg(argPos) is also checked.
476 **
477 ** On error (no match found) NULL is returned.
478 **
479 ** This ONLY works for String JSON/GET/CLI values, not JSON
480 ** booleans and whatnot.
481 */
json_find_option_cstr2(char const * zKey,char const * zCLILong,char const * zCLIShort,int argPos)482 char const * json_find_option_cstr2(char const * zKey,
483 char const * zCLILong,
484 char const * zCLIShort,
485 int argPos){
486 char const * rc = NULL;
487 assert(NULL != zKey);
488 if(!g.isHTTP){
489 rc = find_option(zCLILong ? zCLILong : zKey,
490 zCLIShort, 1);
491 }
492 if(!rc && fossil_has_json()){
493 rc = json_getenv_cstr(zKey);
494 if(!rc && zCLIShort){
495 rc = cson_value_get_cstr( cson_object_get( g.json.param.o, zCLIShort) );
496 }
497 }
498 if(!rc && (argPos>=0)){
499 rc = json_command_arg((unsigned char)argPos);
500 }
501 return rc;
502 }
503
504 /*
505 ** Short-hand form of json_find_option_cstr2(zKey,zCLILong,zCLIShort,-1).
506 */
json_find_option_cstr(char const * zKey,char const * zCLILong,char const * zCLIShort)507 char const * json_find_option_cstr(char const * zKey,
508 char const * zCLILong,
509 char const * zCLIShort){
510 return json_find_option_cstr2(zKey, zCLILong, zCLIShort, -1);
511 }
512
513 /*
514 ** The boolean equivalent of json_find_option_cstr().
515 ** If the option is not found, dftl is returned.
516 */
json_find_option_bool(char const * zKey,char const * zCLILong,char const * zCLIShort,int dflt)517 int json_find_option_bool(char const * zKey,
518 char const * zCLILong,
519 char const * zCLIShort,
520 int dflt ){
521 int rc = -1;
522 if(!g.isHTTP){
523 if(NULL != find_option(zCLILong ? zCLILong : zKey,
524 zCLIShort, 0)){
525 rc = 1;
526 }
527 }
528 if((-1==rc) && fossil_has_json()){
529 rc = json_getenv_bool(zKey,-1);
530 }
531 return (-1==rc) ? dflt : rc;
532 }
533
534 /*
535 ** The integer equivalent of json_find_option_cstr2().
536 ** If the option is not found, dftl is returned.
537 */
json_find_option_int(char const * zKey,char const * zCLILong,char const * zCLIShort,int dflt)538 int json_find_option_int(char const * zKey,
539 char const * zCLILong,
540 char const * zCLIShort,
541 int dflt ){
542 enum { Magic = -1947854832 };
543 int rc = Magic;
544 if(!g.isHTTP){
545 /* FIXME: use strtol() for better error/dflt handling. */
546 char const * opt = find_option(zCLILong ? zCLILong : zKey,
547 zCLIShort, 1);
548 if(NULL!=opt){
549 rc = atoi(opt);
550 }
551 }
552 if(Magic==rc){
553 rc = json_getenv_int(zKey,Magic);
554 }
555 return (Magic==rc) ? dflt : rc;
556 }
557
558
559 /*
560 ** Adds v to g.json.param.o using the given key. May cause any prior
561 ** item with that key to be destroyed (depends on current reference
562 ** count for that value). On success, transfers (or shares) ownership
563 ** of v to (or with) g.json.param.o. On error ownership of v is not
564 ** modified.
565 */
json_setenv(char const * zKey,cson_value * v)566 int json_setenv( char const * zKey, cson_value * v ){
567 return cson_object_set( g.json.param.o, zKey, v );
568 }
569
570 /*
571 ** Guesses a RESPONSE Content-Type value based (primarily) on the
572 ** HTTP_ACCEPT header.
573 **
574 ** It will try to figure out if the client can support
575 ** application/json or application/javascript, and will fall back to
576 ** text/plain if it cannot figure out anything more specific.
577 **
578 ** Returned memory is static and immutable, but if the environment
579 ** changes after calling this then subsequent calls to this function
580 ** might return different (also static/immutable) values.
581 */
json_guess_content_type()582 char const * json_guess_content_type(){
583 char const * cset;
584 char doUtf8;
585 cset = PD("HTTP_ACCEPT_CHARSET",NULL);
586 doUtf8 = ((NULL == cset) || (NULL!=strstr("utf-8",cset)))
587 ? 1 : 0;
588 if( g.json.jsonp ){
589 return doUtf8
590 ? "application/javascript; charset=utf-8"
591 : "application/javascript";
592 }else{
593 /*
594 Content-type
595
596 If the browser does not sent an ACCEPT for application/json
597 then we fall back to text/plain.
598 */
599 char const * cstr;
600 cstr = PD("HTTP_ACCEPT",NULL);
601 if( NULL == cstr ){
602 return doUtf8
603 ? "application/json; charset=utf-8"
604 : "application/json";
605 }else{
606 if( strstr( cstr, "application/json" )
607 || strstr( cstr, "*/*" ) ){
608 return doUtf8
609 ? "application/json; charset=utf-8"
610 : "application/json";
611 }else{
612 return "text/plain";
613 }
614 }
615 }
616 }
617
618 /*
619 ** Given a request CONTENT_TYPE value, this function returns true
620 ** if it is of a type which the JSON API can ostensibly read.
621 **
622 ** It accepts any of application/json, text/plain, or
623 ** application/javascript. The former is preferred, but was not
624 ** widespread when this API was initially built, so the latter forms
625 ** are permitted as fallbacks.
626 */
json_can_consume_content_type(const char * zType)627 int json_can_consume_content_type(const char * zType){
628 return fossil_strcmp(zType, "application/json")==0
629 || fossil_strcmp(zType,"text/plain")==0/*assume this MIGHT be JSON*/
630 || fossil_strcmp(zType,"application/javascript")==0;
631 }
632
633 /*
634 ** Sends pResponse to the output stream as the response object. This
635 ** function does no validation of pResponse except to assert() that it
636 ** is not NULL. The caller is responsible for ensuring that it meets
637 ** API response envelope conventions.
638 **
639 ** In CLI mode pResponse is sent to stdout immediately. In HTTP
640 ** mode pResponse replaces any current CGI content but cgi_reply()
641 ** is not called to flush the output.
642 **
643 ** If g.json.jsonp is not NULL then the content type is set to
644 ** application/javascript and the output is wrapped in a jsonp
645 ** wrapper.
646 */
json_send_response(cson_value const * pResponse)647 void json_send_response( cson_value const * pResponse ){
648 assert( NULL != pResponse );
649 if( g.isHTTP ){
650 cgi_reset_content();
651 if( g.json.jsonp ){
652 cgi_printf("%s(",g.json.jsonp);
653 }
654 cson_output( pResponse, cson_data_dest_cgi, NULL, &g.json.outOpt );
655 if( g.json.jsonp ){
656 cgi_append_content(")",1);
657 }
658 }else{/*CLI mode*/
659 if( g.json.jsonp ){
660 fprintf(stdout,"%s(",g.json.jsonp);
661 }
662 cson_output_FILE( pResponse, stdout, &g.json.outOpt );
663 if( g.json.jsonp ){
664 fwrite(")\n", 2, 1, stdout);
665 }
666 }
667 }
668
669 /*
670 ** Returns the current request's JSON authentication token, or NULL if
671 ** none is found. The token's memory is owned by (or shared with)
672 ** g.json.
673 **
674 ** If an auth token is found in the GET/POST request data then fossil
675 ** is given that data for use in authentication for this
676 ** session. i.e. the GET/POST data overrides fossil's authentication
677 ** cookie value (if any) and also works with clients which do not
678 ** support cookies.
679 **
680 ** Must be called once before login_check_credentials() is called or
681 ** we will not be able to replace fossil's internal idea of the auth
682 ** info in time (and future changes to that state may cause unexpected
683 ** results).
684 **
685 ** The result of this call are cached for future calls.
686 **
687 ** Special case: if g.useLocalauth is true (i.e. the --localauth flag
688 ** was used to start the fossil server instance) and the current
689 ** connection is "local", any authToken provided by the user is
690 ** ignored and no new token is created: localauth mode trumps the
691 ** authToken.
692 */
json_auth_token()693 cson_value * json_auth_token(){
694 assert(g.json.gc.a && "json_bootstrap_early() was not called!");
695 if( g.json.authToken==0 && g.noPswd==0
696 /* g.noPswd!=0 means the user was logged in via a local
697 connection using --localauth. */
698 ){
699 /* Try to get an authorization token from GET parameter, POSTed
700 JSON, or fossil cookie (in that order). */
701 g.json.authToken = json_getenv(FossilJsonKeys.authToken);
702 if(g.json.authToken
703 && cson_value_is_string(g.json.authToken)
704 && !PD(login_cookie_name(),NULL)){
705 /* tell fossil to use this login info.
706
707 FIXME?: because the JSON bits don't carry around
708 login_cookie_name(), there is(?) a potential(?) login hijacking
709 window here. We may need to change the JSON auth token to be in
710 the form: login_cookie_name()=...
711
712 Then again, the hardened cookie value helps ensure that
713 only a proper key/value match is valid.
714 */
715 cgi_replace_parameter( login_cookie_name(), cson_value_get_cstr(g.json.authToken) );
716 }else if( g.isHTTP ){
717 /* try fossil's conventional cookie. */
718 /* Reminder: chicken/egg scenario regarding db access in CLI
719 mode because login_cookie_name() needs the db. CLI
720 mode does not use any authentication, so we don't need
721 to support it here.
722 */
723 char const * zCookie = P(login_cookie_name());
724 if( zCookie && *zCookie ){
725 /* Transfer fossil's cookie to JSON for downstream convenience... */
726 cson_value * v = cson_value_new_string(zCookie, strlen(zCookie));
727 json_gc_add( FossilJsonKeys.authToken, v );
728 g.json.authToken = v;
729 }
730 }
731 }
732 return g.json.authToken;
733 }
734
735 /*
736 ** If g.json.reqPayload.o is NULL then NULL is returned, else the
737 ** given property is searched for in the request payload. If found it
738 ** is returned. The returned value is owned by (or shares ownership
739 ** with) g.json, and must NOT be cson_value_free()'d by the
740 ** caller.
741 */
json_req_payload_get(char const * pKey)742 cson_value * json_req_payload_get(char const *pKey){
743 return g.json.reqPayload.o
744 ? cson_object_get(g.json.reqPayload.o,pKey)
745 : NULL;
746 }
747
748
749 /*
750 ** Returns non-zero if the json_bootstrap_early() function has already
751 ** been called. In general, this function should be used sparingly,
752 ** e.g. from low-level support functions like fossil_warning() where
753 ** there is genuine uncertainty about whether (or not) the JSON setup
754 ** has already been called.
755 */
json_is_bootstrapped_early()756 int json_is_bootstrapped_early(){
757 return ((g.json.gc.v != NULL) && (g.json.gc.a != NULL));
758 }
759
760 /*
761 ** Initializes some JSON bits which need to be initialized relatively
762 ** early on. It should be called by any routine which might need to
763 ** call into JSON relatively early on in the init process.
764 ** Specifically, early on in cgi_init() and json_cmd_top(), but also
765 ** from any error reporting routines which might be triggered (early
766 ** on in those functions).
767 **
768 ** Initializes g.json.gc and g.json.param. This code does not (and
769 ** must not) rely on any of the fossil environment having been set
770 ** up. e.g. it must not use cgi_parameter() and friends because this
771 ** must be called before those data are initialized.
772 **
773 ** If called multiple times, calls after the first are a no-op.
774 */
json_bootstrap_early()775 void json_bootstrap_early(){
776 cson_value * v;
777
778 if(g.json.gc.v!=NULL){
779 /* Avoid multiple bootstrappings. */
780 return;
781 }
782 g.json.timerId = fossil_timer_start();
783 /* g.json.gc is our "garbage collector" - where we put JSON values
784 which need a long lifetime but don't have a logical parent to put
785 them in. */
786 v = cson_value_new_array();
787 g.json.gc.v = v;
788 assert(0 != g.json.gc.v);
789 g.json.gc.a = cson_value_get_array(v);
790 assert(0 != g.json.gc.a);
791 cson_value_add_reference(v)
792 /* Needed to allow us to include this value in other JSON
793 containers without transferring ownership to those containers.
794 All other persistent g.json.XXX.v values get appended to
795 g.json.gc.a, and therefore already have a live reference
796 for this purpose. */
797 ;
798
799 /*
800 g.json.param holds the JSONized counterpart of fossil's
801 cgi_parameter_xxx() family of data. We store them as JSON, as
802 opposed to using fossil's data directly, because we can retain
803 full type information for data this way (as opposed to it always
804 being of type string).
805 */
806 v = cson_value_new_object();
807 g.json.param.v = v;
808 g.json.param.o = cson_value_get_object(v);
809 json_gc_add("$PARAMS", v);
810 }
811
812 /*
813 ** Appends a warning object to the (pending) JSON response.
814 **
815 ** Code must be a FSL_JSON_W_xxx value from the FossilJsonCodes enum.
816 **
817 ** A Warning object has this JSON structure:
818 **
819 ** { "code":integer, "text":"string" }
820 **
821 ** But the text part is optional.
822 **
823 ** If msg is non-NULL and not empty then it is used as the "text"
824 ** property's value. It is copied, and need not refer to static
825 ** memory.
826 **
827 ** CURRENTLY this code only allows a given warning code to be
828 ** added one time, and elides subsequent warnings. The intention
829 ** is to remove that burden from loops which produce warnings.
830 **
831 ** FIXME: if msg is NULL then use a standard string for
832 ** the given code. If !*msg then elide the "text" property,
833 ** for consistency with how json_err() works.
834 */
json_warn(int code,char const * fmt,...)835 void json_warn( int code, char const * fmt, ... ){
836 cson_object * obj = NULL;
837 assert( (code>FSL_JSON_W_START)
838 && (code<FSL_JSON_W_END)
839 && "Invalid warning code.");
840 assert(g.json.gc.a && "json_bootstrap_early() was not called!");
841 if(!g.json.warnings){
842 g.json.warnings = cson_new_array();
843 assert((NULL != g.json.warnings) && "Alloc error.");
844 json_gc_add("$WARNINGS",cson_array_value(g.json.warnings));
845 }
846 obj = cson_new_object();
847 cson_array_append(g.json.warnings, cson_object_value(obj));
848 cson_object_set(obj,"code",cson_value_new_integer(code));
849 if(fmt && *fmt){
850 /* FIXME: treat NULL fmt as standard warning message for
851 the code, but we don't have those yet.
852 */
853 va_list vargs;
854 char * msg;
855 va_start(vargs,fmt);
856 msg = vmprintf(fmt,vargs);
857 va_end(vargs);
858 cson_object_set(obj,"text", cson_value_new_string(msg,strlen(msg)));
859 free(msg);
860 }
861 }
862
863 /*
864 ** Splits zStr (which must not be NULL) into tokens separated by the
865 ** given separator character. If doDeHttp is true then each element
866 ** will be passed through dehttpize(), otherwise they are used
867 ** as-is. Note that tokenization happens before dehttpize(),
868 ** which is significant if the ENcoded tokens might contain the
869 ** separator character.
870 **
871 ** Each new element is appended to the given target array object,
872 ** which must not be NULL and ownership of it is not changed by this
873 ** call.
874 **
875 ** On success, returns the number of tokens _encountered_. On error a
876 ** NEGATIVE number is returned - its absolute value is the number of
877 ** tokens encountered (i.e. it reveals which token in zStr was
878 ** problematic).
879 **
880 ** Achtung: leading and trailing whitespace of elements are elided.
881 **
882 ** Achtung: empty elements will be skipped, meaning consecutive empty
883 ** elements are collapsed.
884 */
json_string_split(char const * zStr,char separator,int doDeHttp,cson_array * target)885 int json_string_split( char const * zStr,
886 char separator,
887 int doDeHttp,
888 cson_array * target ){
889 char const * p = zStr /* current byte */;
890 char const * head /* current start-of-token */;
891 unsigned int len = 0 /* current token's length */;
892 int rc = 0 /* return code (number of added elements)*/;
893 assert( zStr && target );
894 while( fossil_isspace(*p) ){
895 ++p;
896 }
897 head = p;
898 for( ; ; ++p){
899 if( !*p || (separator == *p) ){
900 if( len ){/* append head..(head+len) as next array
901 element. */
902 cson_value * part = NULL;
903 char * zPart = NULL;
904 ++rc;
905 assert( head != p );
906 zPart = (char*)fossil_malloc(len+1);
907 memcpy(zPart, head, len);
908 zPart[len] = 0;
909 if(doDeHttp){
910 dehttpize(zPart);
911 }
912 if( *zPart ){ /* should only fail if someone manages to url-encoded a NUL byte */
913 part = cson_value_new_string(zPart, strlen(zPart));
914 if( 0 != cson_array_append( target, part ) ){
915 cson_value_free(part);
916 rc = -rc;
917 break;
918 }
919 }else{
920 assert(0 && "i didn't think this was possible!");
921 fprintf(stderr,"%s:%d: My God! It's full of stars!\n",
922 __FILE__, __LINE__);
923 fossil_exit(1)
924 /* Not fossil_panic() b/c this code needs to be able to
925 run before some of the fossil/json bits are initialized,
926 and fossil_panic() calls into the JSON API.
927 */
928 ;
929 }
930 fossil_free(zPart);
931 len = 0;
932 }
933 if( !*p ){
934 break;
935 }
936 head = p+1;
937 while( *head && fossil_isspace(*head) ){
938 ++head;
939 ++p;
940 }
941 if(!*head){
942 break;
943 }
944 continue;
945 }
946 ++len;
947 }
948 return rc;
949 }
950
951 /*
952 ** Wrapper around json_string_split(), taking the same first 3
953 ** parameters as this function, but returns the results as a JSON
954 ** Array (if splitting produced tokens) or NULL (if splitting failed
955 ** in any way or produced no tokens).
956 **
957 ** The returned value is owned by the caller. If not NULL then it
958 ** _will_ have a JSON type of Array.
959 */
json_string_split2(char const * zStr,char separator,int doDeHttp)960 cson_value * json_string_split2( char const * zStr,
961 char separator,
962 int doDeHttp ){
963 cson_array * a = cson_new_array();
964 int rc = json_string_split( zStr, separator, doDeHttp, a );
965 if( 0>=rc ){
966 cson_free_array(a);
967 a = NULL;
968 }
969 return a ? cson_array_value(a) : NULL;
970 }
971
972
973 /*
974 ** Performs some common initialization of JSON-related state. Must be
975 ** called by the json_page_top() and json_cmd_top() dispatching
976 ** functions to set up the JSON stat used by the dispatched functions.
977 **
978 ** Implicitly sets up the login information state in CGI mode, but
979 ** does not perform any permissions checking. It _might_ (haven't
980 ** tested this) die with an error if an auth cookie is malformed.
981 **
982 ** This must be called by the top-level JSON command dispatching code
983 ** before they do any work.
984 **
985 ** This must only be called once, or an assertion may be triggered.
986 */
json_bootstrap_late()987 void json_bootstrap_late(){
988 static char once = 0 /* guard against multiple runs */;
989 char const * zPath = P("PATH_INFO");
990 assert(g.json.gc.a && "json_bootstrap_early() was not called!");
991 assert( (0==once) && "json_bootstrap_late() called too many times!");
992 if( once ){
993 return;
994 }else{
995 once = 1;
996 }
997 assert(g.json.isJsonMode
998 && "g.json.isJsonMode should have been set up by now.");
999 g.json.resultCode = 0;
1000 g.json.cmd.offset = -1;
1001 g.json.jsonp = PD("jsonp",NULL)
1002 /* FIXME: do some sanity checking on g.json.jsonp and ignore it
1003 if it is not halfway reasonable.
1004 */
1005 ;
1006 if( !g.isHTTP && g.fullHttpReply ){
1007 /* workaround for server mode, so we see it as CGI mode. */
1008 g.isHTTP = 1;
1009 }
1010
1011 if(g.isHTTP){
1012 cgi_set_content_type(json_guess_content_type())
1013 /* reminder: must be done after g.json.jsonp is initialized */
1014 ;
1015 #if 0
1016 /* Calling this seems to trigger an SQLITE_MISUSE warning???
1017 Maybe it's not legal to set the logger more than once?
1018 */
1019 sqlite3_config(SQLITE_CONFIG_LOG, NULL, 0)
1020 /* avoids debug messages on stderr in JSON mode */
1021 ;
1022 #endif
1023 }
1024
1025 g.json.cmd.v = cson_value_new_array();
1026 g.json.cmd.a = cson_value_get_array(g.json.cmd.v);
1027 json_gc_add( FossilJsonKeys.commandPath, g.json.cmd.v );
1028 /*
1029 The following if/else block translates the PATH_INFO path (in
1030 CLI/server modes) or g.argv (CLI mode) into an internal list so
1031 that we can simplify command dispatching later on.
1032
1033 Note that translating g.argv this way is overkill but allows us to
1034 avoid CLI-only special-case handling in other code, e.g.
1035 json_command_arg().
1036 */
1037 if( zPath ){/* Either CGI or server mode... */
1038 /* Translate PATH_INFO into JSON array for later convenience. */
1039 json_string_split(zPath, '/', 1, g.json.cmd.a);
1040 }else{/* assume CLI mode */
1041 int i;
1042 char const * arg;
1043 cson_value * part;
1044 for(i = 1/*skip argv[0]*/; i < g.argc; ++i ){
1045 arg = g.argv[i];
1046 if( !arg || !*arg ){
1047 continue;
1048 }
1049 if('-' == *arg){
1050 /* workaround to skip CLI args so that
1051 json_command_arg() does not see them.
1052 This assumes that all arguments come LAST
1053 on the command line.
1054 */
1055 break;
1056 }
1057 part = cson_value_new_string(arg,strlen(arg));
1058 cson_array_append(g.json.cmd.a, part);
1059 }
1060 }
1061
1062 while(!g.isHTTP){
1063 /* Simulate JSON POST data via input file. Pedantic reminder:
1064 error handling does not honor user-supplied g.json.outOpt
1065 because outOpt cannot (generically) be configured until after
1066 POST-reading is finished.
1067 */
1068 FILE * inFile = NULL;
1069 char const * jfile = find_option("json-input",NULL,1);
1070 if(!jfile || !*jfile){
1071 break;
1072 }
1073 inFile = (0==strcmp("-",jfile))
1074 ? stdin
1075 : fossil_fopen(jfile,"rb");
1076 if(!inFile){
1077 g.json.resultCode = FSL_JSON_E_FILE_OPEN_FAILED;
1078 fossil_fatal("Could not open JSON file [%s].",jfile)
1079 /* Does not return. */
1080 ;
1081 }
1082 cgi_parse_POST_JSON(inFile, 0);
1083 fossil_fclose(inFile);
1084 break;
1085 }
1086
1087 /* g.json.reqPayload exists only to simplify some of our access to
1088 the request payload. We currently only use this in the context of
1089 Object payloads, not Arrays, strings, etc.
1090 */
1091 g.json.reqPayload.v = cson_object_get( g.json.post.o, FossilJsonKeys.payload );
1092 if( g.json.reqPayload.v ){
1093 g.json.reqPayload.o = cson_value_get_object( g.json.reqPayload.v )
1094 /* g.json.reqPayload.o may legally be NULL, which means only that
1095 g.json.reqPayload.v is-not-a Object.
1096 */;
1097 }
1098
1099 /* Anything which needs json_getenv() and friends should go after
1100 this point.
1101 */
1102
1103 if(1 == cson_array_length_get(g.json.cmd.a)){
1104 /* special case: if we're at the top path, look for
1105 a "command" request arg which specifies which command
1106 to run.
1107 */
1108 char const * cmd = json_getenv_cstr("command");
1109 if(cmd){
1110 json_string_split(cmd, '/', 0, g.json.cmd.a);
1111 g.json.cmd.commandStr = cmd;
1112 }
1113 }
1114
1115
1116 if(!g.json.jsonp){
1117 g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);
1118 }
1119 if(!g.isHTTP){
1120 g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
1121 }
1122
1123 {/* set up JSON output formatting options. */
1124 int indent = -1;
1125 indent = json_find_option_int("indent",NULL,"I",-1);
1126 g.json.outOpt.indentation = (0>indent)
1127 ? (g.isHTTP ? 0 : 1)
1128 : (unsigned char)indent;
1129 g.json.outOpt.addNewline = g.isHTTP
1130 ? 0
1131 : (g.json.jsonp ? 0 : 1);
1132 }
1133
1134 if( g.isHTTP ){
1135 json_auth_token()/* will copy our auth token, if any, to fossil's
1136 core, which we need before we call
1137 login_check_credentials(). */;
1138 login_check_credentials()/* populates g.perm */;
1139 }
1140 else{
1141 /* FIXME: we need an option which allows us to skip this. At least
1142 one known command (/json/version) does not need an opened
1143 repo. The problem here is we cannot know which functions need
1144 it from here (because command dispatching hasn't yet happened)
1145 and all other commands rely on the repo being opened before
1146 they are called. A textbook example of lack of foresight :/.
1147 */
1148 db_find_and_open_repository(OPEN_ANY_SCHEMA,0);
1149 }
1150 }
1151
1152 /*
1153 ** Returns the ndx'th item in the "command path", where index 0 is the
1154 ** position of the "json" part of the path. Returns NULL if ndx is out
1155 ** of bounds or there is no "json" path element.
1156 **
1157 ** In CLI mode the "path" is the list of arguments (skipping argv[0]).
1158 ** In server/CGI modes the path is taken from PATH_INFO.
1159 **
1160 ** The returned bytes are owned by g.json.cmd.v and _may_ be
1161 ** invalidated if that object is modified (depending on how it is
1162 ** modified).
1163 **
1164 ** Note that CLI options are not included in the command path. Use
1165 ** find_option() to get those.
1166 **
1167 */
json_command_arg(unsigned short ndx)1168 char const * json_command_arg(unsigned short ndx){
1169 cson_array * ar = g.json.cmd.a;
1170 assert((NULL!=ar) && "Internal error. Was json_bootstrap_late() called?");
1171 assert((g.argc>1) && "Internal error - we never should have gotten this far.");
1172 if( g.json.cmd.offset < 0 ){
1173 /* first-time setup. */
1174 short i = 0;
1175 #define NEXT cson_string_cstr( \
1176 cson_value_get_string( \
1177 cson_array_get(ar,i) \
1178 ))
1179 char const * tok = NEXT;
1180 while( tok ){
1181 if( g.isHTTP/*workaround for "abbreviated name" in CLI mode*/
1182 ? (0==strncmp("json",tok,4))
1183 : (0==strcmp(g.argv[1],tok))
1184 ){
1185 g.json.cmd.offset = i;
1186 break;
1187 }
1188 ++i;
1189 tok = NEXT;
1190 }
1191 }
1192 #undef NEXT
1193 if(g.json.cmd.offset < 0){
1194 return NULL;
1195 }else{
1196 ndx = g.json.cmd.offset + ndx;
1197 return cson_string_cstr(cson_value_get_string(cson_array_get( ar, g.json.cmd.offset + ndx )));
1198 }
1199 }
1200
1201 /* Returns the C-string form of json_auth_token(), or NULL
1202 ** if json_auth_token() returns NULL.
1203 */
json_auth_token_cstr()1204 char const * json_auth_token_cstr(){
1205 return cson_value_get_cstr( json_auth_token() );
1206 }
1207
1208 /*
1209 ** Returns the JsonPageDef with the given name, or NULL if no match is
1210 ** found.
1211 **
1212 ** head must be a pointer to an array of JsonPageDefs in which the
1213 ** last entry has a NULL name.
1214 */
json_handler_for_name(char const * name,JsonPageDef const * head)1215 JsonPageDef const * json_handler_for_name( char const * name, JsonPageDef const * head ){
1216 JsonPageDef const * pageDef = head;
1217 assert( head != NULL );
1218 if(name && *name) for( ; pageDef->name; ++pageDef ){
1219 if( 0 == strcmp(name, pageDef->name) ){
1220 return pageDef;
1221 }
1222 }
1223 return NULL;
1224 }
1225
1226 /*
1227 ** Given a Fossil/JSON result code, this function "dumbs it down"
1228 ** according to the current value of g.json.errorDetailParanoia. The
1229 ** dumbed-down value is returned.
1230 **
1231 ** This function assert()s that code is in the inclusive range 0 to
1232 ** 9999.
1233 **
1234 ** Note that WARNING codes (1..999) are never dumbed down.
1235 **
1236 */
json_dumbdown_rc(int code)1237 static int json_dumbdown_rc( int code ){
1238 if(!g.json.errorDetailParanoia
1239 || !code
1240 || ((code>=FSL_JSON_W_START) && (code<FSL_JSON_W_END))){
1241 return code;
1242 }else{
1243 int modulo = 0;
1244 assert((code >= 1000) && (code <= 9999) && "Invalid Fossil/JSON code.");
1245 switch( g.json.errorDetailParanoia ){
1246 case 1: modulo = 10; break;
1247 case 2: modulo = 100; break;
1248 case 3: modulo = 1000; break;
1249 default: break;
1250 }
1251 if( modulo ) code = code - (code % modulo);
1252 return code;
1253 }
1254 }
1255
1256 /*
1257 ** Convenience routine which converts a Julian time value into a Unix
1258 ** Epoch timestamp. Requires the db, so this cannot be used before the
1259 ** repo is opened (will trigger a fatal error in db_xxx()). The returned
1260 ** value is owned by the caller.
1261 */
json_julian_to_timestamp(double j)1262 cson_value * json_julian_to_timestamp(double j){
1263 return cson_value_new_integer((cson_int_t)
1264 db_int64(0,"SELECT cast(strftime('%%s',%lf) as int)",j)
1265 );
1266 }
1267
1268 /*
1269 ** Returns a timestamp value.
1270 */
json_timestamp()1271 cson_int_t json_timestamp(){
1272 return (cson_int_t)time(0);
1273 }
1274
1275 /*
1276 ** Returns a new JSON value (owned by the caller) representing
1277 ** a timestamp. If timeVal is < 0 then time(0) is used to fetch
1278 ** the time, else timeVal is used as-is. The returned value is
1279 ** owned by the caller.
1280 */
json_new_timestamp(cson_int_t timeVal)1281 cson_value * json_new_timestamp(cson_int_t timeVal){
1282 return cson_value_new_integer((timeVal<0) ? (cson_int_t)time(0) : timeVal);
1283 }
1284
1285 /*
1286 ** Internal helper for json_create_response(). Appends the first
1287 ** g.json.dispatchDepth elements of g.json.cmd.a, skipping the first
1288 ** one (the "json" part), to a string and returns that string value
1289 ** (which is owned by the caller).
1290 */
json_response_command_path()1291 static cson_value * json_response_command_path(){
1292 if(!g.json.cmd.a){
1293 return NULL;
1294 }else{
1295 cson_value * rc = NULL;
1296 Blob path = empty_blob;
1297 unsigned int aLen = g.json.dispatchDepth+1; /*cson_array_length_get(g.json.cmd.a);*/
1298 unsigned int i = 1;
1299 for( ; i < aLen; ++i ){
1300 char const * part = cson_string_cstr(cson_value_get_string(cson_array_get(g.json.cmd.a, i)));
1301 if(!part){
1302 #if 1
1303 fossil_warning("Iterating further than expected in %s.",
1304 __FILE__);
1305 #endif
1306 break;
1307 }
1308 blob_appendf(&path,"%s%s", (i>1 ? "/": ""), part);
1309 }
1310 rc = json_new_string((blob_size(&path)>0)
1311 ? blob_buffer(&path)
1312 : "")
1313 /* reminder; we need an empty string instead of NULL
1314 in this case, to avoid what outwardly looks like
1315 (but is not) an allocation error in
1316 json_create_response().
1317 */
1318 ;
1319 blob_reset(&path);
1320 return rc;
1321 }
1322 }
1323
1324 /*
1325 ** Returns a JSON Object representation of the global g object.
1326 ** Returned value is owned by the caller.
1327 */
json_g_to_json()1328 cson_value * json_g_to_json(){
1329 cson_object * o = NULL;
1330 cson_object * pay = NULL;
1331 pay = o = cson_new_object();
1332
1333 #define INT(OBJ,K) cson_object_set(o, #K, json_new_int(OBJ.K))
1334 #define CSTR(OBJ,K) cson_object_set(o, #K, OBJ.K ? json_new_string(OBJ.K) : cson_value_null())
1335 #define VAL(K,V) cson_object_set(o, #K, (V) ? (V) : cson_value_null())
1336 VAL(capabilities, json_cap_value());
1337 INT(g, argc);
1338 INT(g, isConst);
1339 CSTR(g, zConfigDbName);
1340 INT(g, repositoryOpen);
1341 INT(g, localOpen);
1342 INT(g, minPrefix);
1343 INT(g, fSqlTrace);
1344 INT(g, fSqlStats);
1345 INT(g, fSqlPrint);
1346 INT(g, fQuiet);
1347 INT(g, fHttpTrace);
1348 INT(g, fSystemTrace);
1349 INT(g, fNoSync);
1350 INT(g, iErrPriority);
1351 INT(g, sslNotAvailable);
1352 INT(g, cgiOutput);
1353 INT(g, xferPanic);
1354 INT(g, fullHttpReply);
1355 INT(g, xlinkClusterOnly);
1356 INT(g, fTimeFormat);
1357 INT(g, markPrivate);
1358 INT(g, clockSkewSeen);
1359 INT(g, isHTTP);
1360 INT(g.url, isFile);
1361 INT(g.url, isHttps);
1362 INT(g.url, isSsh);
1363 INT(g.url, port);
1364 INT(g.url, dfltPort);
1365 INT(g, useLocalauth);
1366 INT(g, noPswd);
1367 INT(g, userUid);
1368 INT(g, rcvid);
1369 INT(g, okCsrf);
1370 INT(g, thTrace);
1371 INT(g, isHome);
1372 INT(g, nAux);
1373 INT(g, allowSymlinks);
1374
1375 CSTR(g, zOpenRevision);
1376 CSTR(g, zLocalRoot);
1377 CSTR(g, zPath);
1378 CSTR(g, zExtra);
1379 CSTR(g, zBaseURL);
1380 CSTR(g, zTop);
1381 CSTR(g, zContentType);
1382 CSTR(g, zErrMsg);
1383 CSTR(g.url, name);
1384 CSTR(g.url, hostname);
1385 CSTR(g.url, protocol);
1386 CSTR(g.url, path);
1387 CSTR(g.url, user);
1388 CSTR(g.url, passwd);
1389 CSTR(g.url, canonical);
1390 CSTR(g.url, proxyAuth);
1391 CSTR(g.url, fossil);
1392 CSTR(g, zLogin);
1393 CSTR(g, zSSLIdentity);
1394 CSTR(g, zIpAddr);
1395 CSTR(g, zNonce);
1396 CSTR(g, zCsrfToken);
1397
1398 o = cson_new_object();
1399 cson_object_set(pay, "json", cson_object_value(o) );
1400 INT(g.json, isJsonMode);
1401 INT(g.json, resultCode);
1402 INT(g.json, errorDetailParanoia);
1403 INT(g.json, dispatchDepth);
1404 VAL(authToken, g.json.authToken);
1405 CSTR(g.json, jsonp);
1406 VAL(gc, g.json.gc.v);
1407 VAL(cmd, g.json.cmd.v);
1408 VAL(param, g.json.param.v);
1409 VAL(POST, g.json.post.v);
1410 VAL(warnings, cson_array_value(g.json.warnings));
1411 /*cson_output_opt outOpt;*/
1412
1413
1414 #undef INT
1415 #undef CSTR
1416 #undef VAL
1417 return cson_object_value(pay);
1418 }
1419
1420
1421 /*
1422 ** Creates a new Fossil/JSON response envelope skeleton. It is owned
1423 ** by the caller, who must eventually free it using cson_value_free(),
1424 ** or add it to a cson container to transfer ownership. Returns NULL
1425 ** on error.
1426 **
1427 ** If payload is not NULL and resultCode is 0 then it is set as the
1428 ** "payload" property of the returned object. If resultCode is 0 then
1429 ** it defaults to g.json.resultCode. If resultCode is (or defaults to)
1430 ** non-zero and payload is not NULL then this function calls
1431 ** cson_value_free(payload) and does not insert the payload into the
1432 ** response. In either case, ownership of payload is transfered to (or
1433 ** shared with, if the caller holds a reference) this function.
1434 **
1435 ** pMsg is an optional message string property (resultText) of the
1436 ** response. If resultCode is non-0 and pMsg is NULL then
1437 ** json_err_cstr() is used to get the error string. The caller may
1438 ** provide his own or may use an empty string to suppress the
1439 ** resultText property.
1440 **
1441 */
json_create_response(int resultCode,char const * pMsg,cson_value * payload)1442 static cson_value * json_create_response( int resultCode,
1443 char const * pMsg,
1444 cson_value * payload){
1445 cson_value * v = NULL;
1446 cson_value * tmp = NULL;
1447 cson_object * o = NULL;
1448 int rc;
1449 resultCode = json_dumbdown_rc(resultCode ? resultCode : g.json.resultCode);
1450 o = cson_new_object();
1451 v = cson_object_value(o);
1452 if( ! o ) return NULL;
1453 #define SET(K) if(!tmp) goto cleanup; \
1454 cson_value_add_reference(tmp); \
1455 rc = cson_object_set( o, K, tmp ); \
1456 cson_value_free(tmp); \
1457 if(rc) do{ \
1458 tmp = NULL; \
1459 goto cleanup; \
1460 }while(0)
1461
1462 tmp = json_new_string(MANIFEST_UUID);
1463 SET("fossil");
1464
1465 tmp = json_new_timestamp(-1);
1466 SET(FossilJsonKeys.timestamp);
1467
1468 if( 0 != resultCode ){
1469 if( ! pMsg ){
1470 pMsg = g.zErrMsg;
1471 if(!pMsg){
1472 pMsg = json_err_cstr(resultCode);
1473 }
1474 }
1475 tmp = json_new_string(json_rc_cstr(resultCode));
1476 SET(FossilJsonKeys.resultCode);
1477 }
1478
1479 if( pMsg && *pMsg ){
1480 tmp = json_new_string(pMsg);
1481 SET(FossilJsonKeys.resultText);
1482 }
1483
1484 if(g.json.cmd.commandStr){
1485 tmp = json_new_string(g.json.cmd.commandStr);
1486 }else{
1487 tmp = json_response_command_path();
1488 }
1489 if(!tmp){
1490 tmp = json_new_string("???");
1491 }
1492 SET("command");
1493
1494 tmp = json_getenv(FossilJsonKeys.requestId);
1495 if( tmp ) cson_object_set( o, FossilJsonKeys.requestId, tmp );
1496
1497 if(0){/* these are only intended for my own testing...*/
1498 if(g.json.cmd.v){
1499 tmp = g.json.cmd.v;
1500 SET("$commandPath");
1501 }
1502 if(g.json.param.v){
1503 tmp = g.json.param.v;
1504 SET("$params");
1505 }
1506 if(0){/*Only for debugging, add some info to the response.*/
1507 tmp = cson_value_new_integer( g.json.cmd.offset );
1508 SET("cmd.offset");
1509 tmp = cson_value_new_bool( g.isHTTP );
1510 SET("isCGI");
1511 }
1512 }
1513
1514 if(fossil_timer_is_active(g.json.timerId)){
1515 /* This is, philosophically speaking, not quite the right place
1516 for ending the timer, but this is the one function which all of
1517 the JSON exit paths use (and they call it after processing,
1518 just before they end).
1519 */
1520 sqlite3_uint64 span = fossil_timer_stop(g.json.timerId);
1521 /* I'm actually seeing sub-uSec runtimes in some tests, but a time of
1522 0 is "just kinda wrong".
1523 */
1524 cson_object_set(o,"procTimeUs", cson_value_new_integer((cson_int_t)span));
1525 span /= 1000/*for milliseconds */;
1526 cson_object_set(o,"procTimeMs", cson_value_new_integer((cson_int_t)span));
1527 assert(!fossil_timer_is_active(g.json.timerId));
1528 g.json.timerId = -1;
1529 }
1530 if(g.json.warnings){
1531 tmp = cson_array_value(g.json.warnings);
1532 SET("warnings");
1533 }
1534
1535 /* Only add the payload to SUCCESS responses. Else delete it. */
1536 if( NULL != payload ){
1537 if( resultCode ){
1538 cson_value_free(payload);
1539 payload = NULL;
1540 }else{
1541 tmp = payload;
1542 SET(FossilJsonKeys.payload);
1543 }
1544 }
1545
1546 if((g.perm.Admin||g.perm.Setup)
1547 && json_find_option_bool("debugFossilG","json-debug-g",NULL,0)
1548 ){
1549 tmp = json_g_to_json();
1550 SET("g");
1551 }
1552
1553 #undef SET
1554 goto ok;
1555 cleanup:
1556 cson_value_free(v);
1557 v = NULL;
1558 ok:
1559 return v;
1560 }
1561
1562 /*
1563 ** Outputs a JSON error response to either the cgi_xxx() family of
1564 ** buffers (in CGI/server mode) or stdout (in CLI mode). If rc is 0
1565 ** then g.json.resultCode is used. If that is also 0 then the "Unknown
1566 ** Error" code is used.
1567 **
1568 ** If g.isHTTP then the generated JSON error response object replaces
1569 ** any currently buffered page output. Because the output goes via
1570 ** the cgi_xxx() family of functions, this function inherits any
1571 ** compression which fossil does for its output.
1572 **
1573 ** If alsoOutput is true AND g.isHTTP then cgi_reply() is called to
1574 ** flush the output (and headers). Generally only do this if you are
1575 ** about to call exit().
1576 **
1577 ** If !g.isHTTP then alsoOutput is ignored and all output is sent to
1578 ** stdout immediately.
1579 **
1580 ** For generating the resultText property: if msg is not NULL then it
1581 ** is used as-is. If it is NULL then g.zErrMsg is checked, and if that
1582 ** is NULL then json_err_cstr(code) is used.
1583 */
json_err(int code,char const * msg,int alsoOutput)1584 void json_err( int code, char const * msg, int alsoOutput ){
1585 int rc = code ? code : (g.json.resultCode
1586 ? g.json.resultCode
1587 : FSL_JSON_E_UNKNOWN);
1588 cson_value * resp = NULL;
1589 if(g.json.isJsonMode==0) return;
1590 rc = json_dumbdown_rc(rc);
1591 if( rc && !msg ){
1592 msg = g.zErrMsg;
1593 if(!msg){
1594 msg = json_err_cstr(rc);
1595 }
1596 }
1597 resp = json_create_response(rc, msg, NULL);
1598 if(!resp){
1599 /* about the only error case here is out-of-memory. DO NOT
1600 call fossil_panic() or fossil_fatal() here because those
1601 allocate.
1602 */
1603 fprintf(stderr, "%s: Fatal error: could not allocate "
1604 "response object.\n", g.argv[0]);
1605 fossil_exit(1);
1606 }
1607 if( g.isHTTP ){
1608 if(alsoOutput){
1609 json_send_response(resp);
1610 }else{
1611 /* almost a duplicate of json_send_response() :( */
1612 cgi_reset_content();
1613 if( g.json.jsonp ){
1614 cgi_printf("%s(",g.json.jsonp);
1615 }
1616 cson_output( resp, cson_data_dest_cgi, NULL, &g.json.outOpt );
1617 if( g.json.jsonp ){
1618 cgi_append_content(")",1);
1619 }
1620 }
1621 }else{
1622 json_send_response(resp);
1623 }
1624 cson_value_free(resp);
1625 }
1626
1627 /*
1628 ** Sets g.json.resultCode and g.zErrMsg, but does not report the error
1629 ** via json_err(). Returns the code passed to it.
1630 **
1631 ** code must be in the inclusive range 1000..9999.
1632 */
json_set_err(int code,char const * fmt,...)1633 int json_set_err( int code, char const * fmt, ... ){
1634 assert( (code>=1000) && (code<=9999) );
1635 free(g.zErrMsg);
1636 g.json.resultCode = code;
1637 if(!fmt || !*fmt){
1638 g.zErrMsg = mprintf("%s", json_err_cstr(code));
1639 }else{
1640 va_list vargs;
1641 char * msg;
1642 va_start(vargs,fmt);
1643 msg = vmprintf(fmt, vargs);
1644 va_end(vargs);
1645 g.zErrMsg = msg;
1646 }
1647 return code;
1648 }
1649
1650 /*
1651 ** Iterates through a prepared SELECT statement and converts each row
1652 ** to a JSON object. If pTgt is not NULL then this function will
1653 ** append the results to pTgt and return cson_array_value(pTgt). If
1654 ** pTgt is NULL then a new Array object is created and returned (owned
1655 ** by the caller). Each row of pStmt is converted to an Object and
1656 ** appended to the array. If the result set has no rows AND pTgt is
1657 ** NULL then NULL (not an empty array) is returned.
1658 */
json_stmt_to_array_of_obj(Stmt * pStmt,cson_array * pTgt)1659 cson_value * json_stmt_to_array_of_obj(Stmt *pStmt,
1660 cson_array * pTgt){
1661 cson_array * a = pTgt;
1662 char const * warnMsg = NULL;
1663 cson_value * colNamesV = NULL;
1664 cson_array * colNames = NULL;
1665 while( (SQLITE_ROW==db_step(pStmt)) ){
1666 cson_value * row = NULL;
1667 if(!a){
1668 a = cson_new_array();
1669 assert(NULL!=a);
1670 }
1671 if(!colNames){
1672 colNamesV = cson_sqlite3_column_names(pStmt->pStmt);
1673 assert(NULL != colNamesV);
1674 /*Why? cson_value_add_reference(colNamesV) avoids an ownership problem*/;
1675 colNames = cson_value_get_array(colNamesV);
1676 assert(NULL != colNames);
1677 }
1678 row = cson_sqlite3_row_to_object2(pStmt->pStmt, colNames);
1679 if(!row && !warnMsg){
1680 warnMsg = "Could not convert at least one result row to JSON.";
1681 continue;
1682 }
1683 if( 0 != cson_array_append(a, row) ){
1684 cson_value_free(row);
1685 if(pTgt != a) {
1686 cson_free_array(a);
1687 }
1688 assert( 0 && "Alloc error.");
1689 return NULL;
1690 }
1691 }
1692 cson_value_free(colNamesV);
1693 if(warnMsg){
1694 json_warn( FSL_JSON_W_ROW_TO_JSON_FAILED, "%s", warnMsg );
1695 }
1696 return cson_array_value(a);
1697 }
1698
1699 /*
1700 ** Works just like json_stmt_to_array_of_obj(), but each row in the
1701 ** result set is represented as an Array of values instead of an
1702 ** Object (key/value pairs). If pTgt is NULL and the statement
1703 ** has no results then NULL is returned, not an empty array.
1704 */
json_stmt_to_array_of_array(Stmt * pStmt,cson_array * pTgt)1705 cson_value * json_stmt_to_array_of_array(Stmt *pStmt,
1706 cson_array * pTgt){
1707 cson_array * a = pTgt;
1708 while( (SQLITE_ROW==db_step(pStmt)) ){
1709 cson_value * row = NULL;
1710 if(!a){
1711 a = cson_new_array();
1712 assert(NULL!=a);
1713 }
1714 row = cson_sqlite3_row_to_array(pStmt->pStmt);
1715 cson_array_append(a, row);
1716 }
1717 return cson_array_value(a);
1718 }
1719
json_stmt_to_array_of_values(Stmt * pStmt,int resultColumn,cson_array * pTgt)1720 cson_value * json_stmt_to_array_of_values(Stmt *pStmt,
1721 int resultColumn,
1722 cson_array * pTgt){
1723 cson_array * a = pTgt;
1724 while( (SQLITE_ROW==db_step(pStmt)) ){
1725 cson_value * row = cson_sqlite3_column_to_value(pStmt->pStmt,
1726 resultColumn);
1727 if(row){
1728 if(!a){
1729 a = cson_new_array();
1730 assert(NULL!=a);
1731 }
1732 cson_array_append(a, row);
1733 }
1734 }
1735 return cson_array_value(a);
1736 }
1737
1738 /*
1739 ** Executes the given SQL and runs it through
1740 ** json_stmt_to_array_of_obj(), returning the result of that
1741 ** function. If resetBlob is true then blob_reset(pSql) is called
1742 ** after preparing the query.
1743 **
1744 ** pTgt has the same semantics as described for
1745 ** json_stmt_to_array_of_obj().
1746 **
1747 ** FIXME: change this to take a (char const *) instead of a blob,
1748 ** to simplify the trivial use-cases (which don't need a Blob).
1749 */
json_sql_to_array_of_obj(Blob * pSql,cson_array * pTgt,int resetBlob)1750 cson_value * json_sql_to_array_of_obj(Blob * pSql, cson_array * pTgt,
1751 int resetBlob){
1752 Stmt q = empty_Stmt;
1753 cson_value * pay = NULL;
1754 assert( blob_size(pSql) > 0 );
1755 db_prepare(&q, "%s", blob_str(pSql) /*safe-for-%s*/);
1756 if(resetBlob){
1757 blob_reset(pSql);
1758 }
1759 pay = json_stmt_to_array_of_obj(&q, pTgt);
1760 db_finalize(&q);
1761 return pay;
1762
1763 }
1764
1765 /*
1766 ** If the given COMMIT rid has any tags associated with it, this
1767 ** function returns a JSON Array containing the tag names (owned by
1768 ** the caller), else it returns NULL.
1769 **
1770 ** See info_tags_of_checkin() for more details (this is simply a JSON
1771 ** wrapper for that function).
1772 **
1773 ** If there are no tags then this function returns NULL, not an empty
1774 ** Array.
1775 */
json_tags_for_checkin_rid(int rid,int propagatingOnly)1776 cson_value * json_tags_for_checkin_rid(int rid, int propagatingOnly){
1777 cson_value * v = NULL;
1778 char * tags = info_tags_of_checkin(rid, propagatingOnly);
1779 if(tags){
1780 if(*tags){
1781 v = json_string_split2(tags,',',0);
1782 }
1783 free(tags);
1784 }
1785 return v;
1786 }
1787
1788 /*
1789 ** Returns a "new" value representing the boolean value of zVal
1790 ** (false if zVal is NULL). Note that cson does not really allocate
1791 ** any memory for boolean values, but they "should" (for reasons of
1792 ** style and philosophy) be cleaned up like any other values (but
1793 ** it's a no-op for bools).
1794 */
json_value_to_bool(cson_value const * zVal)1795 cson_value * json_value_to_bool(cson_value const * zVal){
1796 return cson_value_get_bool(zVal)
1797 ? cson_value_true()
1798 : cson_value_false();
1799 }
1800
1801 /*
1802 ** Impl of /json/resultCodes
1803 **
1804 */
json_page_resultCodes()1805 cson_value * json_page_resultCodes(){
1806 cson_array * list = cson_new_array();
1807 cson_object * obj = NULL;
1808 cson_string * kRC;
1809 cson_string * kSymbol;
1810 cson_string * kNumber;
1811 cson_string * kDesc;
1812 cson_array_reserve( list, 35 );
1813 kRC = cson_new_string("resultCode",10);
1814 kSymbol = cson_new_string("cSymbol",7);
1815 kNumber = cson_new_string("number",6);
1816 kDesc = cson_new_string("description",11);
1817 #define C(K) obj = cson_new_object(); \
1818 cson_object_set_s(obj, kRC, json_new_string(json_rc_cstr(FSL_JSON_E_##K)) ); \
1819 cson_object_set_s(obj, kSymbol, json_new_string("FSL_JSON_E_"#K) ); \
1820 cson_object_set_s(obj, kNumber, cson_value_new_integer(FSL_JSON_E_##K) ); \
1821 cson_object_set_s(obj, kDesc, json_new_string(json_err_cstr(FSL_JSON_E_##K))); \
1822 cson_array_append( list, cson_object_value(obj) ); obj = NULL;
1823
1824 C(GENERIC);
1825 C(INVALID_REQUEST);
1826 C(UNKNOWN_COMMAND);
1827 C(UNKNOWN);
1828 C(TIMEOUT);
1829 C(ASSERT);
1830 C(ALLOC);
1831 C(NYI);
1832 C(PANIC);
1833 C(MANIFEST_READ_FAILED);
1834 C(FILE_OPEN_FAILED);
1835
1836 C(AUTH);
1837 C(MISSING_AUTH);
1838 C(DENIED);
1839 C(WRONG_MODE);
1840 C(LOGIN_FAILED);
1841 C(LOGIN_FAILED_NOSEED);
1842 C(LOGIN_FAILED_NONAME);
1843 C(LOGIN_FAILED_NOPW);
1844 C(LOGIN_FAILED_NOTFOUND);
1845
1846 C(USAGE);
1847 C(INVALID_ARGS);
1848 C(MISSING_ARGS);
1849 C(AMBIGUOUS_UUID);
1850 C(UNRESOLVED_UUID);
1851 C(RESOURCE_ALREADY_EXISTS);
1852 C(RESOURCE_NOT_FOUND);
1853
1854 C(DB);
1855 C(STMT_PREP);
1856 C(STMT_BIND);
1857 C(STMT_EXEC);
1858 C(DB_LOCKED);
1859 C(DB_NEEDS_REBUILD);
1860 C(DB_NOT_FOUND);
1861 C(DB_NOT_VALID);
1862 #undef C
1863 return cson_array_value(list);
1864 }
1865
1866
1867 /*
1868 ** /json/version implementation.
1869 **
1870 ** Returns the payload object (owned by the caller).
1871 */
json_page_version()1872 cson_value * json_page_version(){
1873 cson_value * jval = NULL;
1874 cson_object * jobj = NULL;
1875 jval = cson_value_new_object();
1876 jobj = cson_value_get_object(jval);
1877 #define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X)))
1878 FSET(MANIFEST_UUID,"manifestUuid");
1879 FSET(MANIFEST_VERSION,"manifestVersion");
1880 FSET(MANIFEST_DATE,"manifestDate");
1881 FSET(MANIFEST_YEAR,"manifestYear");
1882 FSET(RELEASE_VERSION,"releaseVersion");
1883 cson_object_set( jobj, "releaseVersionNumber",
1884 cson_value_new_integer(RELEASE_VERSION_NUMBER) );
1885 cson_object_set( jobj, "resultCodeParanoiaLevel",
1886 cson_value_new_integer(g.json.errorDetailParanoia) );
1887 FSET(FOSSIL_JSON_API_VERSION, "jsonApiVersion" );
1888 #undef FSET
1889 return jval;
1890 }
1891
1892
1893 /*
1894 ** Returns the current user's capabilities string as a String value.
1895 ** Returned value is owned by the caller, and will only be NULL if
1896 ** g.userUid is invalid or an out of memory error. Or, it turns out,
1897 ** in CLI mode (where there is no logged-in user).
1898 */
json_cap_value()1899 cson_value * json_cap_value(){
1900 if(g.userUid<=0){
1901 return NULL;
1902 }else{
1903 Stmt q = empty_Stmt;
1904 cson_value * val = NULL;
1905 db_prepare(&q, "SELECT cap FROM user WHERE uid=%d", g.userUid);
1906 if( db_step(&q)==SQLITE_ROW ){
1907 char const * str = (char const *)sqlite3_column_text(q.pStmt,0);
1908 if( str ){
1909 val = json_new_string(str);
1910 }
1911 }
1912 db_finalize(&q);
1913 return val;
1914 }
1915 }
1916
1917 /*
1918 ** Implementation for /json/cap
1919 **
1920 ** Returned object contains details about the "capabilities" of the
1921 ** current user (what he may/may not do).
1922 **
1923 ** This is primarily intended for debuggering, but may have
1924 ** a use in client code. (?)
1925 */
json_page_cap()1926 cson_value * json_page_cap(){
1927 cson_value * payload = cson_value_new_object();
1928 cson_value * sub = cson_value_new_object();
1929 Stmt q;
1930 cson_object * obj = cson_value_get_object(payload);
1931 db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid);
1932 if( db_step(&q)==SQLITE_ROW ){
1933 /* reminder: we don't use g.zLogin because it's 0 for the guest
1934 user and the HTML UI appears to currently allow the name to be
1935 changed (but doing so would break other code). */
1936 char const * str = (char const *)sqlite3_column_text(q.pStmt,0);
1937 if( str ){
1938 cson_object_set( obj, "name",
1939 cson_value_new_string(str,strlen(str)) );
1940 }
1941 str = (char const *)sqlite3_column_text(q.pStmt,1);
1942 if( str ){
1943 cson_object_set( obj, "capabilities",
1944 cson_value_new_string(str,strlen(str)) );
1945 }
1946 }
1947 db_finalize(&q);
1948 cson_object_set( obj, "permissionFlags", sub );
1949 obj = cson_value_get_object(sub);
1950
1951 #define ADD(X,K) cson_object_set(obj, K, cson_value_new_bool(g.perm.X))
1952 ADD(Setup,"setup");
1953 ADD(Admin,"admin");
1954 ADD(Password,"password");
1955 ADD(Query,"query"); /* don't think this one is actually used */
1956 ADD(Write,"checkin");
1957 ADD(Read,"checkout");
1958 ADD(Hyperlink,"history");
1959 ADD(Clone,"clone");
1960 ADD(RdWiki,"readWiki");
1961 ADD(NewWiki,"createWiki");
1962 ADD(ApndWiki,"appendWiki");
1963 ADD(WrWiki,"editWiki");
1964 ADD(ModWiki,"moderateWiki");
1965 ADD(RdTkt,"readTicket");
1966 ADD(NewTkt,"createTicket");
1967 ADD(ApndTkt,"appendTicket");
1968 ADD(WrTkt,"editTicket");
1969 ADD(ModTkt,"moderateTicket");
1970 ADD(Attach,"attachFile");
1971 ADD(TktFmt,"createTicketReport");
1972 ADD(RdAddr,"readPrivate");
1973 ADD(Zip,"zip");
1974 ADD(Private,"xferPrivate");
1975 ADD(WrUnver,"writeUnversioned");
1976 ADD(RdForum,"readForum");
1977 ADD(WrForum,"writeForum");
1978 ADD(WrTForum,"writeTrustedForum");
1979 ADD(ModForum,"moderateForum");
1980 ADD(AdminForum,"adminForum");
1981 ADD(EmailAlert,"emailAlert");
1982 ADD(Announce,"announce");
1983 ADD(Debug,"debug");
1984 ADD(Chat,"chat");
1985 #undef ADD
1986 return payload;
1987 }
1988
1989 /*
1990 ** Implementation of the /json/stat page/command.
1991 **
1992 */
json_page_stat()1993 cson_value * json_page_stat(){
1994 i64 t, fsize;
1995 int n, m;
1996 int full;
1997 enum { BufLen = 1000 };
1998 char zBuf[BufLen];
1999 cson_value * jv = NULL;
2000 cson_object * jo = NULL;
2001 cson_value * jv2 = NULL;
2002 cson_object * jo2 = NULL;
2003 char * zTmp = NULL;
2004 if( !g.perm.Read ){
2005 json_set_err(FSL_JSON_E_DENIED,
2006 "Requires 'o' permissions.");
2007 return NULL;
2008 }
2009 full = json_find_option_bool("full",NULL,"f",
2010 json_find_option_bool("verbose",NULL,"v",0));
2011 #define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));
2012
2013 jv = cson_value_new_object();
2014 jo = cson_value_get_object(jv);
2015
2016 zTmp = db_get("project-name",NULL);
2017 cson_object_set(jo, "projectName", json_new_string(zTmp));
2018 free(zTmp);
2019 zTmp = db_get("project-description",NULL);
2020 cson_object_set(jo, "projectDescription", json_new_string(zTmp));
2021 free(zTmp);
2022 zTmp = NULL;
2023 fsize = file_size(g.zRepositoryName, ExtFILE);
2024 cson_object_set(jo, "repositorySize",
2025 cson_value_new_integer((cson_int_t)fsize));
2026
2027 if(full){
2028 n = db_int(0, "SELECT count(*) FROM blob");
2029 m = db_int(0, "SELECT count(*) FROM delta");
2030 cson_object_set(jo, "blobCount", cson_value_new_integer((cson_int_t)n));
2031 cson_object_set(jo, "deltaCount", cson_value_new_integer((cson_int_t)m));
2032 if( n>0 ){
2033 int a, b;
2034 Stmt q;
2035 db_prepare(&q, "SELECT total(size), avg(size), max(size)"
2036 " FROM blob WHERE size>0");
2037 db_step(&q);
2038 t = db_column_int64(&q, 0);
2039 cson_object_set(jo, "uncompressedArtifactSize",
2040 cson_value_new_integer((cson_int_t)t));
2041 cson_object_set(jo, "averageArtifactSize",
2042 cson_value_new_integer((cson_int_t)db_column_int(&q, 1)));
2043 cson_object_set(jo, "maxArtifactSize",
2044 cson_value_new_integer((cson_int_t)db_column_int(&q, 2)));
2045 db_finalize(&q);
2046 if( t/fsize < 5 ){
2047 b = 10;
2048 fsize /= 10;
2049 }else{
2050 b = 1;
2051 }
2052 a = t/fsize;
2053 sqlite3_snprintf(BufLen,zBuf, "%d:%d", a, b);
2054 SETBUF(jo, "compressionRatio");
2055 }
2056 n = db_int(0, "SELECT count(distinct mid) FROM mlink /*scan*/");
2057 cson_object_set(jo, "checkinCount", cson_value_new_integer((cson_int_t)n));
2058 n = db_int(0, "SELECT count(*) FROM filename /*scan*/");
2059 cson_object_set(jo, "fileCount", cson_value_new_integer((cson_int_t)n));
2060 n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
2061 " WHERE +tagname GLOB 'wiki-*'");
2062 cson_object_set(jo, "wikiPageCount", cson_value_new_integer((cson_int_t)n));
2063 n = db_int(0, "SELECT count(*) FROM tag /*scan*/"
2064 " WHERE +tagname GLOB 'tkt-*'");
2065 cson_object_set(jo, "ticketCount", cson_value_new_integer((cson_int_t)n));
2066 }/*full*/
2067 n = db_int(0, "SELECT julianday('now') - (SELECT min(mtime) FROM event)"
2068 " + 0.99");
2069 cson_object_set(jo, "ageDays", cson_value_new_integer((cson_int_t)n));
2070 cson_object_set(jo, "ageYears", cson_value_new_double(n/365.2425));
2071 sqlite3_snprintf(BufLen, zBuf, db_get("project-code",""));
2072 SETBUF(jo, "projectCode");
2073 cson_object_set(jo, "compiler", cson_value_new_string(COMPILER_NAME, strlen(COMPILER_NAME)));
2074
2075 jv2 = cson_value_new_object();
2076 jo2 = cson_value_get_object(jv2);
2077 cson_object_set(jo, "sqlite", jv2);
2078 sqlite3_snprintf(BufLen, zBuf, "%.19s [%.10s] (%s)",
2079 sqlite3_sourceid(), &sqlite3_sourceid()[20], sqlite3_libversion());
2080 SETBUF(jo2, "version");
2081 cson_object_set(jo2, "pageCount", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.page_count")));
2082 cson_object_set(jo2, "pageSize", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.page_size")));
2083 cson_object_set(jo2, "freeList", cson_value_new_integer((cson_int_t)db_int(0, "PRAGMA repository.freelist_count")));
2084 sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA repository.encoding"));
2085 SETBUF(jo2, "encoding");
2086 sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA repository.journal_mode"));
2087 cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
2088 return jv;
2089 #undef SETBUF
2090 }
2091
2092
2093
2094
2095 /*
2096 ** Creates a comma-separated list of command names
2097 ** taken from zPages. zPages must be an array of objects
2098 ** whose final entry MUST have a NULL name value or results
2099 ** are undefined.
2100 **
2101 ** The list is appended to pOut. The number of items (not bytes)
2102 ** appended are returned. If filterByMode is non-0 then the result
2103 ** list will contain only commands which are able to run in the
2104 ** current run mode (CLI vs. HTTP).
2105 */
json_pagedefs_to_string(JsonPageDef const * zPages,Blob * pOut,int filterByMode)2106 static int json_pagedefs_to_string(JsonPageDef const * zPages,
2107 Blob * pOut, int filterByMode){
2108 int i = 0;
2109 for( ; zPages->name; ++zPages, ++i ){
2110 if(filterByMode){
2111 if(g.isHTTP && zPages->runMode < 0) continue;
2112 else if(zPages->runMode > 0) continue;
2113 }
2114 blob_append(pOut, zPages->name, -1);
2115 if((zPages+1)->name){
2116 blob_append(pOut, ", ",2);
2117 }
2118 }
2119 return i;
2120 }
2121
2122 /*
2123 ** Creates an error message using zErrPrefix and the given array of
2124 ** JSON command definitions, and sets the g.json error state to
2125 ** reflect FSL_JSON_E_MISSING_ARGS. If zErrPrefix is NULL then
2126 ** some default is used (e.g. "Try one of: "). If it is "" then
2127 ** no prefix is used.
2128 **
2129 ** The intention is to provide the user (via the response.resultText)
2130 ** a list of available commands/subcommands.
2131 **
2132 */
json_dispatch_missing_args_err(JsonPageDef const * pCommands,char const * zErrPrefix)2133 void json_dispatch_missing_args_err( JsonPageDef const * pCommands,
2134 char const * zErrPrefix ){
2135 Blob cmdNames = empty_blob;
2136 blob_init(&cmdNames,NULL,0);
2137 if( !zErrPrefix ) {
2138 zErrPrefix = "Try one of: ";
2139 }
2140 blob_append( &cmdNames, zErrPrefix, strlen(zErrPrefix) );
2141 json_pagedefs_to_string(pCommands, &cmdNames, 1);
2142 json_set_err(FSL_JSON_E_MISSING_ARGS, "%s",
2143 blob_str(&cmdNames));
2144 blob_reset(&cmdNames);
2145 }
2146
json_page_dispatch_helper(JsonPageDef const * pages)2147 cson_value * json_page_dispatch_helper(JsonPageDef const * pages){
2148 JsonPageDef const * def;
2149 char const * cmd = json_command_arg(1+g.json.dispatchDepth);
2150 assert( NULL != pages );
2151 if( ! cmd ){
2152 json_dispatch_missing_args_err(pages,
2153 "No subcommand specified. "
2154 "Try one of: ");
2155 return NULL;
2156 }
2157 def = json_handler_for_name( cmd, pages );
2158 if(!def){
2159 json_set_err(FSL_JSON_E_UNKNOWN_COMMAND,
2160 "Unknown subcommand: %s", cmd);
2161 return NULL;
2162 }
2163 else{
2164 ++g.json.dispatchDepth;
2165 return (*def->func)();
2166 }
2167 }
2168
2169
2170 /*
2171 ** Impl of /json/rebuild. Requires admin privileges.
2172 */
json_page_rebuild()2173 static cson_value * json_page_rebuild(){
2174 if( !g.perm.Admin ){
2175 json_set_err(FSL_JSON_E_DENIED,"Requires 'a' privileges.");
2176 return NULL;
2177 }else{
2178 /* Reminder: the db_xxx() ops "should" fail via the fossil core
2179 error handlers, which will cause a JSON error and exit(). i.e. we
2180 don't handle the errors here. TODO: confirm that all these db
2181 routine fail gracefully in JSON mode.
2182
2183 On large repos (e.g. fossil's) this operation is likely to take
2184 longer than the client timeout, which will cause it to fail (but
2185 it's sqlite3, so it'll fail gracefully).
2186 */
2187 db_close(1);
2188 db_open_repository(g.zRepositoryName);
2189 db_begin_transaction();
2190 rebuild_db(0, 0, 0);
2191 db_end_transaction(0);
2192 return NULL;
2193 }
2194 }
2195
2196 /*
2197 ** Impl of /json/g. Requires admin/setup rights.
2198 */
json_page_g()2199 static cson_value * json_page_g(){
2200 if(!g.perm.Admin || !g.perm.Setup){
2201 json_set_err(FSL_JSON_E_DENIED,
2202 "Requires 'a' or 's' privileges.");
2203 return NULL;
2204 }
2205 return json_g_to_json();
2206 }
2207
2208 /* Impl in json_login.c. */
2209 cson_value * json_page_anon_password();
2210 /* Impl in json_artifact.c. */
2211 cson_value * json_page_artifact();
2212 /* Impl in json_branch.c. */
2213 cson_value * json_page_branch();
2214 /* Impl in json_diff.c. */
2215 cson_value * json_page_diff();
2216 /* Impl in json_dir.c. */
2217 cson_value * json_page_dir();
2218 /* Impl in json_login.c. */
2219 cson_value * json_page_login();
2220 /* Impl in json_login.c. */
2221 cson_value * json_page_logout();
2222 /* Impl in json_query.c. */
2223 cson_value * json_page_query();
2224 /* Impl in json_report.c. */
2225 cson_value * json_page_report();
2226 /* Impl in json_tag.c. */
2227 cson_value * json_page_tag();
2228 /* Impl in json_user.c. */
2229 cson_value * json_page_user();
2230 /* Impl in json_config.c. */
2231 cson_value * json_page_config();
2232 /* Impl in json_finfo.c. */
2233 cson_value * json_page_finfo();
2234 /* Impl in json_status.c. */
2235 cson_value * json_page_status();
2236
2237 /*
2238 ** Mapping of names to JSON pages/commands. Each name is a subpath of
2239 ** /json (in CGI mode) or a subcommand of the json command in CLI mode
2240 */
2241 static const JsonPageDef JsonPageDefs[] = {
2242 /* please keep alphabetically sorted (case-insensitive) for maintenance reasons. */
2243 {"anonymousPassword", json_page_anon_password, 0},
2244 {"artifact", json_page_artifact, 0},
2245 {"branch", json_page_branch,0},
2246 {"cap", json_page_cap, 0},
2247 {"config", json_page_config, 0 },
2248 {"diff", json_page_diff, 0},
2249 {"dir", json_page_dir, 0},
2250 {"finfo", json_page_finfo, 0},
2251 {"g", json_page_g, 0},
2252 {"HAI",json_page_version,0},
2253 {"login",json_page_login,0},
2254 {"logout",json_page_logout,0},
2255 {"query",json_page_query,0},
2256 {"rebuild",json_page_rebuild,0},
2257 {"report", json_page_report, 0},
2258 {"resultCodes", json_page_resultCodes,0},
2259 {"stat",json_page_stat,0},
2260 {"status", json_page_status, 0},
2261 {"tag", json_page_tag,0},
2262 /*{"ticket", json_page_nyi,0},*/
2263 {"timeline", json_page_timeline,0},
2264 {"user",json_page_user,0},
2265 {"version",json_page_version,0},
2266 {"whoami",json_page_whoami,0},
2267 {"wiki",json_page_wiki,0},
2268 /* Last entry MUST have a NULL name. */
2269 {NULL,NULL,0}
2270 };
2271
2272 /*
2273 ** Internal helper for json_cmd_top() and json_page_top().
2274 **
2275 ** Searches JsonPageDefs for a command with the given name. If found,
2276 ** it is used to generate and output a JSON response. If not found, it
2277 ** generates a JSON-style error response. Returns 0 on success, non-0
2278 ** on error. On error it will set g.json's error state.
2279 */
json_dispatch_root_command(char const * zCommand)2280 static int json_dispatch_root_command( char const * zCommand ){
2281 int rc = 0;
2282 cson_value * payload = NULL;
2283 JsonPageDef const * pageDef = NULL;
2284 pageDef = json_handler_for_name(zCommand,&JsonPageDefs[0]);
2285 if( ! pageDef ){
2286 rc = FSL_JSON_E_UNKNOWN_COMMAND;
2287 json_set_err( rc, "Unknown command: %s", zCommand );
2288 }else if( pageDef->runMode < 0 /*CLI only*/){
2289 rc = FSL_JSON_E_WRONG_MODE;
2290 }else if( (g.isHTTP && (pageDef->runMode < 0 /*CLI only*/))
2291 ||
2292 (!g.isHTTP && (pageDef->runMode > 0 /*HTTP only*/))
2293 ){
2294 rc = FSL_JSON_E_WRONG_MODE;
2295 }
2296 else{
2297 rc = 0;
2298 g.json.dispatchDepth = 1;
2299 payload = (*pageDef->func)();
2300 }
2301 payload = json_create_response(rc, NULL, payload);
2302 json_send_response(payload);
2303 cson_value_free(payload);
2304 return rc;
2305 }
2306
2307 #ifdef FOSSIL_ENABLE_JSON
2308 /* dupe ifdef needed for mkindex */
2309 /*
2310 ** WEBPAGE: json
2311 **
2312 ** Pages under /json/... must be entered into JsonPageDefs.
2313 ** This function dispatches them, and is the HTTP equivalent of
2314 ** json_cmd_top().
2315 */
json_page_top(void)2316 void json_page_top(void){
2317 char const * zCommand;
2318 assert(g.json.gc.a && "json_bootstrap_early() was not called!");
2319 assert(g.json.cmd.a && "json_bootstrap_late() was not called!");
2320 zCommand = json_command_arg(1);
2321 if(!zCommand || !*zCommand){
2322 json_dispatch_missing_args_err( JsonPageDefs,
2323 "No command (sub-path) specified."
2324 " Try one of: ");
2325 return;
2326 }
2327 json_dispatch_root_command( zCommand );
2328 }
2329 #endif /* FOSSIL_ENABLE_JSON for mkindex */
2330
2331 #ifdef FOSSIL_ENABLE_JSON
2332 /* dupe ifdef needed for mkindex */
2333 /*
2334 ** This function dispatches json commands and is the CLI equivalent of
2335 ** json_page_top().
2336 **
2337 ** COMMAND: json
2338 **
2339 ** Usage: %fossil json SUBCOMMAND ?OPTIONS?
2340 **
2341 ** In CLI mode, the -R REPO common option is supported. Due to limitations
2342 ** in the argument dispatching code, any -FLAGS must come after the final
2343 ** sub- (or subsub-) command.
2344 **
2345 ** The -json-input FILE option can be used to read JSON data and process
2346 ** it like the HTTP interface would. For example:
2347 **
2348 ** %fossil json -json-input my.json
2349 **
2350 ** The commands include:
2351 **
2352 ** anonymousPassword
2353 ** artifact
2354 ** branch
2355 ** cap
2356 ** config
2357 ** diff
2358 ** dir
2359 ** g
2360 ** login
2361 ** logout
2362 ** query
2363 ** rebuild
2364 ** report
2365 ** resultCodes
2366 ** stat
2367 ** tag
2368 ** timeline
2369 ** user
2370 ** version (alias: HAI)
2371 ** whoami
2372 ** wiki
2373 **
2374 ** Run '%fossil json' without any subcommand to see the full list (but be
2375 ** aware that some listed might not yet be fully implemented).
2376 **
2377 */
json_cmd_top(void)2378 void json_cmd_top(void){
2379 char const * cmd = NULL;
2380 int rc = 0;
2381 memset( &g.perm, 0xff, sizeof(g.perm) )
2382 /* In CLI mode fossil does not use permissions
2383 and they all default to false. We enable them
2384 here because (A) fossil doesn't use them in local
2385 mode but (B) having them set gives us one less
2386 difference in the CLI/CGI/Server-mode JSON
2387 handling.
2388 */
2389 ;
2390 json_bootstrap_early();
2391 json_bootstrap_late();
2392 if( 2 > cson_array_length_get(g.json.cmd.a) ){
2393 goto usage;
2394 }
2395 #if 0
2396 json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing.");
2397 json_warn(FSL_JSON_W_ROW_TO_JSON_FAILED, "Just testing again.");
2398 #endif
2399 cmd = json_command_arg(1);
2400 if( !cmd || !*cmd ){
2401 goto usage;
2402 }
2403 rc = json_dispatch_root_command( cmd );
2404 if(0 != rc){
2405 /* FIXME: we need a way of passing this error back
2406 up to the routine which called this callback.
2407 e.g. add g.errCode.
2408 */
2409 fossil_exit(1);
2410 }
2411 return;
2412 usage:
2413 {
2414 cson_value * payload;
2415 json_dispatch_missing_args_err( JsonPageDefs,
2416 "No subcommand specified."
2417 " Try one of: ");
2418 payload = json_create_response(0, NULL, NULL);
2419 json_send_response(payload);
2420 cson_value_free(payload);
2421 fossil_exit(1);
2422 }
2423 }
2424 #endif /* FOSSIL_ENABLE_JSON for mkindex */
2425
2426 #endif /* FOSSIL_ENABLE_JSON */
2427