Fossil

Check-in [5b4a3f12dc]
Login

Check-in [5b4a3f12dc]

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Consolidated more of the CLI/HTTP/CLI-with-POST arg handling disparities into the json_find_option() family of functions.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | json-multitag-test | json
Files: files | file ages | folders
SHA1: 5b4a3f12dc47424fbce0a77a046e03663cfdaa56
User & Date: stephan 2011-10-05 02:26:38.201
Context
2011-10-05
02:31
fixed a mis-sorting in the json timeline, caused by refactoring in a recent commit. ... (check-in: 824ed5fb65 user: stephan tags: json-multitag-test, json)
02:26
Consolidated more of the CLI/HTTP/CLI-with-POST arg handling disparities into the json_find_option() family of functions. ... (check-in: 5b4a3f12dc user: stephan tags: json-multitag-test, json)
00:53
lots of minor cleanups in JSON error handling (mostly cosmetic). Added some new FOSSIL-xxxx codes. ... (check-in: 2e41514fb7 user: stephan tags: json-multitag-test, json)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/json.c.
181
182
183
184
185
186
187




















188
189
190
191
192
193
194

#else
#define BEGIN_TIMER 
#define END_TIMER 0.0
#define HAS_TIMER 0
#endif





















/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.
*/
cson_value * json_page_nyi(){
  g.json.resultCode = FSL_JSON_E_NYI;
  return NULL;







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214

#else
#define BEGIN_TIMER 
#define END_TIMER 0.0
#define HAS_TIMER 0
#endif


/*
** Returns true if fossil is running in JSON mode and we are either
** running in HTTP mode OR g.json.post.o is not NULL (meaning POST
** data was fed in from CLI mode).
**
** Specifically, it will return false when any of these apply:
**
** a) Not running in JSON mode (via json command or /json path).
**
** b) We are running in JSON CLI mode, but no POST data has been fed
** in.
**
** Whether or not we need to take args from CLI or POST data makes a
** difference in argument/parameter handling in many JSON rountines.
*/
char fossil_is_json(){
  return g.json.isJsonMode && (g.isHTTP || g.json.post.o);
}

/*
** Placeholder /json/XXX page impl for NYI (Not Yet Implemented)
** (but planned) pages/commands.
*/
cson_value * json_page_nyi(){
  g.json.resultCode = FSL_JSON_E_NYI;
  return NULL;
464
465
466
467
468
469
470






471
472
473
474
475
476
477
** Wrapper around json_getenv() which tries to evaluate a payload/env
** value as a boolean. Uses mostly the same logic as
** json_getenv_int(), with the addition that string values which
** either start with a digit 1..9 or the letters [tT] are considered
** to be true. If this function cannot find a matching key/value then
** dflt is returned. e.g. if it finds the key but the value is-a
** Object then dftl is returned.






*/
char json_getenv_bool(char const * pKey, char dflt ){
  cson_value const * v = json_getenv(pKey);
  if(!v){
    return dflt;
  }else if( cson_value_is_number(v) ){
    return cson_value_get_integer(v) ? 1 : 0;







>
>
>
>
>
>







484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
** Wrapper around json_getenv() which tries to evaluate a payload/env
** value as a boolean. Uses mostly the same logic as
** json_getenv_int(), with the addition that string values which
** either start with a digit 1..9 or the letters [tT] are considered
** to be true. If this function cannot find a matching key/value then
** dflt is returned. e.g. if it finds the key but the value is-a
** Object then dftl is returned.
**
** If an entry is found, this function guarantees that it will return
** either 0 or 1, as opposed to "0 or non-zero", so that clients can
** pass a different value as dflt. Thus they can use, e.g. -1 to know
** whether or not this function found a match (it will return -1 in
** that case).
*/
char json_getenv_bool(char const * pKey, char dflt ){
  cson_value const * v = json_getenv(pKey);
  if(!v){
    return dflt;
  }else if( cson_value_is_number(v) ){
    return cson_value_get_integer(v) ? 1 : 0;
499
500
501
502
503
504
505








































































506
507
508
509
510
511
512
** this purpose. Returned memory is owned by g.json or fossil and is
** valid until end-of-app or the given key is replaced in fossil's
** internals via cgi_replace_parameter() and friends or json_setenv().
*/
char const * json_getenv_cstr( char const * zKey ){
  return cson_value_get_cstr( json_getenv(zKey) );
}










































































/*
** Adds v to g.json.param.o using the given key. May cause any prior
** item with that key to be destroyed (depends on current reference
** count for that value). On success, transfers (or shares) ownership
** of v to (or with) g.json.param.o. On error ownership of v is not







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
** this purpose. Returned memory is owned by g.json or fossil and is
** valid until end-of-app or the given key is replaced in fossil's
** internals via cgi_replace_parameter() and friends or json_setenv().
*/
char const * json_getenv_cstr( char const * zKey ){
  return cson_value_get_cstr( json_getenv(zKey) );
}

/*
** An extended form of find_option() which tries to look up a combo
** GET/POST/CLI argument.
**
** zKey must be the GET/POST parameter key. zCLILong must be the "long
** form" CLI flag (NULL means to use zKey). zCLIShort may be NUL or
** the "short form" CLI flag.
**
** On error (no match found) NULL is returned.
**
** This ONLY works for String JSON/GET/CLI values, not JSON
** booleans and whatnot.
*/
char const * json_find_option_cstr(char const * zKey,
                                   char const * zCLILong,
                                   char const * zCLIShort){
  char const * rc = NULL;
  assert(NULL != zKey);
  if(!g.isHTTP){
    rc = find_option(zCLILong ? zCLILong : zKey,
                     zCLIShort, 1);
  }
  if(!rc && fossil_is_json()){
    rc = json_getenv_cstr(zKey);
  }
  return rc;
}

/*
** The boolean equivalent of json_find_option_cstr().
** If the option is not found, dftl is returned.
*/
char json_find_option_bool(char const * zKey,
                           char const * zCLILong,
                           char const * zCLIShort,
                           char dflt ){
  char rc = -1;
  if(!g.isHTTP){
    if(NULL != find_option(zCLILong ? zCLILong : zKey,
                           zCLIShort, 0)){
      rc = 1;
    }
  }
  if((-1==rc) && fossil_is_json()){
    rc = json_getenv_bool(zKey,-1);
  }
  return (-1==rc) ? dflt : rc;
}

/*
** The integer equivalent of json_find_option_cstr().
** If the option is not found, dftl is returned.
*/
int json_find_option_int(char const * zKey,
                         char const * zCLILong,
                         char const * zCLIShort,
                         int dflt ){
  enum { Magic = -947 };
  int rc = Magic;
  if(!g.isHTTP){
    char const * opt = find_option(zCLILong ? zCLILong : zKey,
                                   zCLIShort, 1);
    if(NULL!=opt){
      rc = atoi(opt);
    }
  }
  if(Magic==rc){
    rc = json_getenv_int(zKey,Magic);
  }
  return (Magic==rc) ? dflt : rc;
}


/*
** Adds v to g.json.param.o using the given key. May cause any prior
** item with that key to be destroyed (depends on current reference
** count for that value). On success, transfers (or shares) ownership
** of v to (or with) g.json.param.o. On error ownership of v is not
1004
1005
1006
1007
1008
1009
1010
1011

1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
    */
    char const * cmd = json_getenv_cstr("command");
    if(cmd){
      json_string_split(cmd, '/', 0, g.json.cmd.a);
      g.json.cmd.commandStr = cmd;
    }
  }
  

  if(!g.json.jsonp && g.json.post.o){
    g.json.jsonp =
      json_getenv_cstr("jsonp")
      /*cson_string_cstr(cson_value_get_string(cson_object_get(g.json.post.o,"jsonp")))*/
      ;
  }
  if( !g.isHTTP ){
    g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;
    if(!g.json.jsonp){
      g.json.jsonp = find_option("jsonp",NULL,1);
    }
  }

  {/* set up JSON output formatting options. */
    unsigned char indent = g.isHTTP ? 0 : 1;
    char const * indentStr = NULL;
    char checkAgain = 1;
    if( g.json.post.v ){
      cson_value const * check = json_getenv("indent");
      if(check){
        checkAgain = 0;
        indent = (unsigned char)json_getenv_int("indent",(int)indent);
      }
    }
    if(!g.isHTTP && checkAgain){/*CLI mode*/
      indentStr = find_option("indent","I",1);
      if(indentStr){
        int const n = atoi(indentStr);
        indent = (n>0)
          ? (unsigned char)n
          : 0;
      }
    }
    g.json.outOpt.indentation = indent;
    g.json.outOpt.addNewline = g.isHTTP
      ? 0
      : (g.json.jsonp ? 0 : 1);
  }

  if( g.isHTTP ){
    json_auth_token()/* will copy our auth token, if any, to fossil's







|
>
|
|
<
<
<

|

<
<
<



|

|
|
<
<
<
<
<
<
|
<
<
<
<
|
<
<
<
<







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
    */
    char const * cmd = json_getenv_cstr("command");
    if(cmd){
      json_string_split(cmd, '/', 0, g.json.cmd.a);
      g.json.cmd.commandStr = cmd;
    }
  }

  
  if(!g.json.jsonp){
    g.json.jsonp = json_find_option_cstr("jsonp",NULL,NULL);



  }
  if(!g.isHTTP){
    g.json.errorDetailParanoia = 0 /*disable error code dumb-down for CLI mode*/;



  }

  {/* set up JSON output formatting options. */
    int indent = -1;
    char const * indentStr = NULL;
    indent = json_find_option_int("indent",NULL,"I",-1);
    g.json.outOpt.indentation = (0>indent)






      ? (g.isHTTP ? 0 : 1)




      : (unsigned char)indent;




    g.json.outOpt.addNewline = g.isHTTP
      ? 0
      : (g.json.jsonp ? 0 : 1);
  }

  if( g.isHTTP ){
    json_auth_token()/* will copy our auth token, if any, to fossil's
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
  cson_value * jv2 = NULL;
  cson_object * jo2 = NULL;
  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' permissions.");
    return NULL;
  }
  if( g.isHTTP ){
    full = json_getenv_bool("full",0);
  }else{
    full = (0!=find_option("full","f",0));
  }
#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));

  jv = cson_value_new_object();
  jo = cson_value_get_object(jv);

  sqlite3_snprintf(BufLen, zBuf, db_get("project-name",""));
  SETBUF(jo, "projectName");







<
|
<
<
<







1642
1643
1644
1645
1646
1647
1648

1649



1650
1651
1652
1653
1654
1655
1656
  cson_value * jv2 = NULL;
  cson_object * jo2 = NULL;
  if( !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' permissions.");
    return NULL;
  }

  full = json_find_option_bool("full",NULL,"f",0);



#define SETBUF(O,K) cson_object_set(O, K, cson_value_new_string(zBuf, strlen(zBuf)));

  jv = cson_value_new_object();
  jo = cson_value_get_object(jv);

  sqlite3_snprintf(BufLen, zBuf, db_get("project-name",""));
  SETBUF(jo, "projectName");
Changes to src/json_artifact.c.
223
224
225
226
227
228
229
230
231
232
233
234
235
236

237
238
239
240
241
242
243
  char const * zType = NULL;
  char const * zUuid = NULL;
  cson_value * entry = NULL;
  Blob uuid = empty_blob;
  int rc;
  int rid = 0;
  ArtifactDispatchEntry const * dispatcher = &ArtifactDispatchList[0];
  zName = g.isHTTP
    ? json_getenv_cstr("uuid")
    : find_option("uuid","u",1);
  if(!zName||!*zName){
    zName = json_command_arg(g.json.dispatchDepth+1);
    if(!zName || !*zName) {
      g.json.resultCode = FSL_JSON_E_MISSING_ARGS;

      return NULL;
    }
  }

  if( validate16(zName, strlen(zName)) ){
    if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
      zType = "ticket";







<
|
<



|
>







223
224
225
226
227
228
229

230

231
232
233
234
235
236
237
238
239
240
241
242
  char const * zType = NULL;
  char const * zUuid = NULL;
  cson_value * entry = NULL;
  Blob uuid = empty_blob;
  int rc;
  int rid = 0;
  ArtifactDispatchEntry const * dispatcher = &ArtifactDispatchList[0];

  zName = json_find_option_cstr("uuid",NULL,"u");

  if(!zName||!*zName){
    zName = json_command_arg(g.json.dispatchDepth+1);
    if(!zName || !*zName) {
      json_set_err(FSL_JSON_E_MISSING_ARGS,
                   "Missing 'uuid' argument.");
      return NULL;
    }
  }

  if( validate16(zName, strlen(zName)) ){
    if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
      zType = "ticket";
Changes to src/json_branch.c.
75
76
77
78
79
80
81



82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
                 "Requires 'o' permissions.");
    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);



  if(!g.isHTTP){
    range = find_option("range","r",1);
    if(!range||!*range){
      range = find_option("all","a",0);
      if(range && *range){
        range = "a";
      }else{
        range = find_option("closed","c",0);
        if(range&&*range){
          range = "c";
        }
      }
    }
  }else{
    range = json_getenv_cstr("range");
  }
  if(!range || !*range){
    range = "o";
  }
  assert( (NULL != range) && *range );
  switch(*range){
    case 'c':
      range = "closed";
      which = -1;
      break;
    case 'a':
      range = "all";
      which = 1;
      break;
    default:
      range = "open";
      which = 0;
      break;
  };
  cson_object_set(pay,"range",cson_value_new_string(range,strlen(range)));

  if( g.localOpen ){ /* add "current" property (branch name). */
    int vid = db_lget_int("checkout", 0);
    char const * zCurrent = vid
      ? db_text(0, "SELECT value FROM tagxref"
                " WHERE rid=%d AND tagid=%d",
                vid, TAG_BRANCH)







>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
<
<
|



|














|







75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97


98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
                 "Requires 'o' permissions.");
    return NULL;
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  listV = cson_value_new_array();
  list = cson_value_get_array(listV);
  if(fossil_is_json()){
      range = json_getenv_cstr("range");
  }

  range = json_find_option_cstr("range",NULL,"r");
  if((!range||!*range) && !g.isHTTP){
    range = find_option("all","a",0);
    if(range && *range){
      range = "a";
    }else{
      range = find_option("closed","c",0);
      if(range&&*range){
        range = "c";
      }
    }
  }



  if(!range || !*range){
    range = "o";
  }
  /* Normalize range values... */
  switch(*range){
    case 'c':
      range = "closed";
      which = -1;
      break;
    case 'a':
      range = "all";
      which = 1;
      break;
    default:
      range = "open";
      which = 0;
      break;
  };
  cson_object_set(pay,"range",json_new_string(range));

  if( g.localOpen ){ /* add "current" property (branch name). */
    int vid = db_lget_int("checkout", 0);
    char const * zCurrent = vid
      ? db_text(0, "SELECT value FROM tagxref"
                " WHERE rid=%d AND tagid=%d",
                vid, TAG_BRANCH)
322
323
324
325
326
327
328

329
330
331
332
333
334
335
336
337

338
339
340
341
342
343

344
345
346

347
348
349






350
351
352
353
354
355
356
357
358
359
360
361
362
363
364

365
366
367
368
369
370
371
  char * zUuid = NULL;
  int rid = 0;
  if( !g.perm.Write ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'i' permissions.");
    return NULL;
  }

  if(0){
    char const * x = json_command_arg(g.json.dispatchDepth+1);
    fprintf(stderr,"command arg=%s\n",x);
    assert(0);
  }
  memset(&opt,0,sizeof(BranchCreateOptions));
  opt.zName = g.json.post.v
    ? json_getenv_cstr("name")
    : json_command_arg(g.json.dispatchDepth+1);


  if(!opt.zName){
    json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter was not specified." );
    return NULL;
  }


  opt.zBasis = g.json.post.v
    ? json_getenv_cstr("basis")
    : json_command_arg(g.json.dispatchDepth+2);

  if(!opt.zBasis || ('-'==*opt.zBasis/*assume CLI flag*/)){
    opt.zBasis = "trunk";
  }







  opt.zColor = g.json.post.v
    ? json_getenv_cstr("bgColor")
    : find_option("bgcolor","",1);

  opt.isPrivate = g.json.post.v
    ? json_getenv_bool("private",0)
    : (NULL != find_option("private","",0))
    ;
  
  rc = json_branch_new( &opt, &rid );
  if(rc){
    json_set_err(rc, opt.rcErrMsg );
    goto error;
  }

  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);

  cson_object_set(pay,"name",json_new_string(opt.zName));
  cson_object_set(pay,"basis",json_new_string(opt.zBasis));
  cson_object_set(pay,"rid",json_new_int(rid));
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);







>
|
|
<
<

|
|
<
|
>






>
|
|
|
>
|


>
>
>
>
>
>
|
<
<
<
|
<
<
<
<



|


>







323
324
325
326
327
328
329
330
331
332


333
334
335

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358



359




360
361
362
363
364
365
366
367
368
369
370
371
372
373
  char * zUuid = NULL;
  int rid = 0;
  if( !g.perm.Write ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'i' permissions.");
    return NULL;
  }
  memset(&opt,0,sizeof(BranchCreateOptions));
  if(fossil_is_json()){
    opt.zName = json_getenv_cstr("name");


  }

  if(!opt.zName){

    opt.zName = json_command_arg(g.json.dispatchDepth+1);
  }

  if(!opt.zName){
    json_set_err(FSL_JSON_E_MISSING_ARGS, "'name' parameter was not specified." );
    return NULL;
  }

  opt.zColor = json_find_option_cstr("bgColor","bgcolor",NULL);
  opt.zBasis = json_find_option_cstr("basis",NULL,NULL);
  if(!opt.zBasis && !g.isHTTP){
    opt.zBasis = json_command_arg(g.json.dispatchDepth+2);
  }
  if(!opt.zBasis){
    opt.zBasis = "trunk";
  }
  opt.isPrivate = json_find_option_bool("private",NULL,NULL,-1);
  if(-1==opt.isPrivate){
    if(!g.isHTTP){
      opt.isPrivate = (NULL != find_option("private","",0));
    }else{
      opt.isPrivate = 0;
    }



  }




  
  rc = json_branch_new( &opt, &rid );
  if(rc){
    json_set_err(rc, opt.rcErrMsg);
    goto error;
  }
  assert(0 != rid);
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);

  cson_object_set(pay,"name",json_new_string(opt.zName));
  cson_object_set(pay,"basis",json_new_string(opt.zBasis));
  cson_object_set(pay,"rid",json_new_int(rid));
  zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
Changes to src/json_timeline.c.
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
                                                cson_object * pPayload){
  char const * zTag = NULL;
  char const * zBranch = NULL;
  int tagid = 0;
  if(! g.perm.Read ){
    return 0;
  }
  else if(g.isHTTP){
    zTag = json_getenv_cstr("tag");
  }else{
    zTag = find_option("tag",NULL,1);
  }
  if(!zTag || !*zTag){
    if(g.isHTTP){
      zBranch = json_getenv_cstr("branch");
    }else{
      zBranch = find_option("branch",NULL,1);
    }
    if(!zBranch || !*zBranch){
      return 0;
    }
    zTag = zBranch;
  }
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
                 zTag);







<
|
<
<
<

<
|
<
<
<







151
152
153
154
155
156
157

158



159

160



161
162
163
164
165
166
167
                                                cson_object * pPayload){
  char const * zTag = NULL;
  char const * zBranch = NULL;
  int tagid = 0;
  if(! g.perm.Read ){
    return 0;
  }

  zTag = json_find_option_cstr("tag",NULL,NULL);



  if(!zTag || !*zTag){

    zBranch = json_find_option_cstr("branch",NULL,NULL);



    if(!zBranch || !*zBranch){
      return 0;
    }
    zTag = zBranch;
  }
  tagid = db_int(0, "SELECT tagid FROM tag WHERE tagname='sym-%q'",
                 zTag);
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275



276
277
278
279
280
281
282
**
** Returns -1 if it adds a "before" clause, 1 if it adds
** an "after" clause, and 0 if adds only an order-by clause.
*/
static char json_timeline_add_time_clause(Blob *pSql){
  char const * zAfter = NULL;
  char const * zBefore = NULL;
  if( g.isHTTP ){
    /**
       FIXME: we are only honoring STRING values here, not int (for
       passing Unix Epoch times).
    */
    zAfter = json_getenv_cstr("after");
    if(!zAfter || !*zAfter){
      zAfter = json_getenv_cstr("a");
    }
    if(!zAfter){
      zBefore = json_getenv_cstr("before");
      if(!zBefore||!*zBefore){
        zBefore = json_getenv_cstr("b");
      }
    }
  }else{
    zAfter = find_option("after","a",1);
    zBefore = zAfter ? NULL : find_option("before","b",1);
  }
  if(zAfter&&*zAfter){
    while( fossil_isspace(*zAfter) ) ++zAfter;
    blob_appendf(pSql,
                 " AND event.mtime>=(SELECT julianday(%Q,'utc')) "
                 " ORDER BY event.mtime ASC ",
                 zAfter);
    return 1;
  }else if(zBefore && *zBefore){
    while( fossil_isspace(*zBefore) ) ++zBefore;
    blob_appendf(pSql,
                 " AND event.mtime<=(SELECT julianday(%Q,'utc')) "
                 " ORDER BY event.mtime DESC ",
                 zBefore);
    return -1;
  }else{
    blob_append(pSql," ORDER BY event.mtime DESC ", -1);
    return 0;
  }

}

/*
** Tries to figure out a timeline query length limit base on
** environment parameters. If it can it returns that value,
** else it returns some statically defined default value.
**
** Never returns a negative value. 0 means no limit.
*/
static int json_timeline_limit(){
  static const int defaultLimit = 20;
  int limit = -1;
  if( g.json.post.v ){
    limit = json_getenv_int("limit",-1);
    if(limit<0){
      limit = json_getenv_int("n",-1);
    }
  }else if(!g.isHTTP){/* CLI mode */
    char const * arg = find_option("limit","n",1);
    if(arg && *arg){
      limit = atoi(arg);
    }
  }



  return (limit<0) ? defaultLimit : limit;
}

/*
** Internal helper for the json_timeline_EVENTTYPE() family of
** functions. zEventType must be one of (ci, w, t). pSql must be a
** cleanly-initialized, empty Blob to store the sql in. If pPayload is







<
<
<
<
<
|
<
|
<
<
<
<
<
<
<
<
<
|
|






|






|
<
<
<

>












<
<
<
<
<
|





>
>
>







201
202
203
204
205
206
207





208

209









210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225



226
227
228
229
230
231
232
233
234
235
236
237
238
239





240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
**
** Returns -1 if it adds a "before" clause, 1 if it adds
** an "after" clause, and 0 if adds only an order-by clause.
*/
static char json_timeline_add_time_clause(Blob *pSql){
  char const * zAfter = NULL;
  char const * zBefore = NULL;





  int rc = 0;

  zAfter = json_find_option_cstr("after",NULL,"a");









  zBefore = zAfter ? NULL : json_find_option_cstr("before",NULL,"b");

  if(zAfter&&*zAfter){
    while( fossil_isspace(*zAfter) ) ++zAfter;
    blob_appendf(pSql,
                 " AND event.mtime>=(SELECT julianday(%Q,'utc')) "
                 " ORDER BY event.mtime ASC ",
                 zAfter);
    rc = 1;
  }else if(zBefore && *zBefore){
    while( fossil_isspace(*zBefore) ) ++zBefore;
    blob_appendf(pSql,
                 " AND event.mtime<=(SELECT julianday(%Q,'utc')) "
                 " ORDER BY event.mtime DESC ",
                 zBefore);
    rc = -1;



  }
  return rc;
}

/*
** Tries to figure out a timeline query length limit base on
** environment parameters. If it can it returns that value,
** else it returns some statically defined default value.
**
** Never returns a negative value. 0 means no limit.
*/
static int json_timeline_limit(){
  static const int defaultLimit = 20;
  int limit = -1;





  if(!g.isHTTP){/* CLI mode */
    char const * arg = find_option("limit","n",1);
    if(arg && *arg){
      limit = atoi(arg);
    }
  }
  if( (limit<0) && fossil_is_json() ){
    limit = json_getenv_int("limit",-1);
  }
  return (limit<0) ? defaultLimit : limit;
}

/*
** Internal helper for the json_timeline_EVENTTYPE() family of
** functions. zEventType must be one of (ci, w, t). pSql must be a
** cleanly-initialized, empty Blob to store the sql in. If pPayload is
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
static cson_value * json_timeline_ci(){
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_value * tmp = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  int check = 0;
  int showFiles = 0;
  Stmt q = empty_Stmt;
  char warnRowToJsonFailed = 0;
  char warnStringToArrayFailed = 0;
  Blob sql = empty_blob;
  if( !g.perm.Read ){
    /* IMO this falls more under the category of g.perm.History, but
       i'm following the original timeline impl here.
    */
    json_set_err( FSL_JSON_E_DENIED, "Checkin timeline requires 'o' access." );
    return NULL;
  }
  if( g.isHTTP ){
    showFiles = json_getenv_bool("files",0);
  }else{
    showFiles = 0!=find_option("files", "f",0);
  }
  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "ci", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }







|











<
|
<
<
<







351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369

370



371
372
373
374
375
376
377
static cson_value * json_timeline_ci(){
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_value * tmp = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  int check = 0;
  char showFiles = -1/*magic number*/;
  Stmt q = empty_Stmt;
  char warnRowToJsonFailed = 0;
  char warnStringToArrayFailed = 0;
  Blob sql = empty_blob;
  if( !g.perm.Read ){
    /* IMO this falls more under the category of g.perm.History, but
       i'm following the original timeline impl here.
    */
    json_set_err( FSL_JSON_E_DENIED, "Checkin timeline requires 'o' access." );
    return NULL;
  }

  showFiles = json_find_option_bool("files",NULL,"f",0);



  payV = cson_value_new_object();
  pay = cson_value_get_object(payV);
  check = json_timeline_setup_sql( "ci", &sql, pay );
  if(check){
    json_set_err(check, "Query initialization failed.");
    goto error;
  }
Changes to src/json_wiki.c.
132
133
134
135
136
137
138
139
140

141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
  char * zUuid = NULL;
  Stmt q;
  if( !g.perm.RdWiki && !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' or 'j' access.");
    return NULL;
  }
  zPageName = (g.isHTTP || g.json.post.o)
    ? json_getenv_cstr("page")

    : find_option("page","p",1);
  if( !zPageName && cson_value_is_string(g.json.reqPayload.v) ){
      zPageName = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v));
  }
  if(!zPageName){
    zPageName = json_command_arg(g.json.dispatchDepth+1);
  }
  if(!zPageName||!*zPageName){
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "'page' argument is missing.");
    return NULL;
  }

  zFormat = g.isHTTP
    ? json_getenv_cstr("format")
    : find_option("format","f",1);
  if(!zFormat || !*zFormat){
    zFormat = "raw";
  }
  if( 'r' != *zFormat ){
    zFormat = "html";
  }
  return json_get_wiki_page(zPageName, 'h'==*zFormat);







|
|
>
|












|
<
<







132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155


156
157
158
159
160
161
162
  char * zUuid = NULL;
  Stmt q;
  if( !g.perm.RdWiki && !g.perm.Read ){
    json_set_err(FSL_JSON_E_DENIED,
                 "Requires 'o' or 'j' access.");
    return NULL;
  }
  if(fossil_is_json()){
    zPageName = json_getenv_cstr("page");
  }
  zPageName = json_find_option_cstr("page",NULL,"p");
  if( !zPageName && cson_value_is_string(g.json.reqPayload.v) ){
      zPageName = cson_string_cstr(cson_value_get_string(g.json.reqPayload.v));
  }
  if(!zPageName){
    zPageName = json_command_arg(g.json.dispatchDepth+1);
  }
  if(!zPageName||!*zPageName){
    json_set_err(FSL_JSON_E_MISSING_ARGS,
                 "'page' argument is missing.");
    return NULL;
  }

  zFormat = json_find_option_cstr("format",NULL,"f");


  if(!zFormat || !*zFormat){
    zFormat = "raw";
  }
  if( 'r' != *zFormat ){
    zFormat = "html";
  }
  return json_get_wiki_page(zPageName, 'h'==*zFormat);
Changes to src/main.c.
205
206
207
208
209
210
211
212



213
214
215
216
217
218
219
                               */
      char const * commandStr  /*"command" request param.*/;
    } cmd;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_object * o;
    } post;
    struct {                   /* GET/COOKIE params in JSON mode. */



      cson_value * v;
      cson_object * o;
    } param;
    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */







|
>
>
>







205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
                               */
      char const * commandStr  /*"command" request param.*/;
    } cmd;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_object * o;
    } post;
    struct {                   /* GET/COOKIE params in JSON mode.
                                  FIXME (stephan): verify that this is
                                  still used and remove if it is not.
                               */
      cson_value * v;
      cson_object * o;
    } param;
    struct {
      cson_value * v;
      cson_object * o;
    } reqPayload;              /* request payload object (if any) */