Fossil

Check-in [c24b445012]
Login

Check-in [c24b445012]

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

Overview
Comment:Changed fossil_json_f() callback interface. Refactored json command dispatching a bit. Fixed an ordering problem in the json timelines. Pulled in latest cson_amalgamation.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | json
Files: files | file ages | folders
SHA1: c24b44501290ad103bd73ed3ee8b0f36fff6d802
User & Date: stephan 2011-09-23 10:52:41.812
Context
2011-09-23
12:29
Implemented /json/timeline/ticket, cleaned up timeline/ci|wiki. ... (check-in: 42900f3029 user: stephan tags: json)
10:52
Changed fossil_json_f() callback interface. Refactored json command dispatching a bit. Fixed an ordering problem in the json timelines. Pulled in latest cson_amalgamation. ... (check-in: c24b445012 user: stephan tags: json)
01:00
Corrected /json/wiki/save|create to honor the proper g.perm.WrWiki/NewWiki perm, instead of just WrWiki. ... (check-in: d3759cd40f user: stephan tags: json)
Changes
Unified Diff Ignore Whitespace Patch
Changes to src/cson_amalgamation.c.
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442

2443
2444
2445
2446
2447
2448
2449
    else
    {
#if CSON_VOID_PTR_IS_BIG
        cson_value_clean( val );
        val->value = (void *)v;
#else
        cson_int_t * iv = NULL;
        cson_value_clean( val );
        iv = (cson_int_t*)cson_malloc(sizeof(cson_int_t), "cson_int_t");
        if( ! iv ) return cson_rc.AllocError;

        *iv = v;
        val->value = iv;
#endif
        val->api = &cson_value_api_integer;
        return 0;
    }
}







<


>







2433
2434
2435
2436
2437
2438
2439

2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
    else
    {
#if CSON_VOID_PTR_IS_BIG
        cson_value_clean( val );
        val->value = (void *)v;
#else
        cson_int_t * iv = NULL;

        iv = (cson_int_t*)cson_malloc(sizeof(cson_int_t), "cson_int_t");
        if( ! iv ) return cson_rc.AllocError;
        cson_value_clean( val );
        *iv = v;
        val->value = iv;
#endif
        val->api = &cson_value_api_integer;
        return 0;
    }
}
Changes to src/json.c.
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
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
** either their payload object or NULL. Note that NULL is a legal
** success value - it simply means the response will contain no
** payload. If g.json.resultCode is non-zero when this function
** returns then the top-level dispatcher will destroy any payload
** returned by this function and will output a JSON error response
** instead.
**
** The depth parameter comes from the dispatcher and tells the
** callback what index in json_command_arg() its command/path starts.
** e.g. when dispatching /json/foo/bar, the foo callback will get a
** depth of 1 and the bar callback will get a depth of 2. This is only
** useful for callbacks which use json_command_arg(), and the only
** callbacks which do that are ones which dispatch to sub-pages
** (e.g. /json/wiki/...).  This is a parameter, as opposed to simply
** hard-coding the offset in each callback impl, so that refactoring
** will (or should) be easier if we later reimplement the path/command
** handling and arguments/path parts get moved around.
**
** All of the setup/response code is handled by the top dispatcher
** functions and the callbacks concern themselves only with generating
** the payload.
**
** It is imperitive that NO callback functions EVER output ANYTHING to
** stdout, as that will effectively corrupt any JSON output, and
** almost certainly will corrupt any HTTP response headers. Output
** sent to stderr ends up in my apache log, so that might be useful
** for debuggering in some cases, but so such code should be left
** enabled for non-debuggering builds.
*/
typedef cson_value * (*fossil_json_f)(unsigned int depth);

/*
** Internal helpers to manipulate a byte array as a bitset. The B
** argument must be-a array at least (BIT/8+1) bytes long.
** The BIT argument is the bit number to query/set/clear/toggle.
*/
#define BITSET_BYTEFOR(B,BIT) ((B)[ BIT / 8 ])
#define BITSET_SET(B,BIT) ((BITSET_BYTEFOR(B,BIT) |= (0x01 << (BIT%8))),0x01)
#define BITSET_UNSET(B,BIT) ((BITSET_BYTEFOR(B,BIT) &= ~(0x01 << (BIT%8))),0x00)
#define BITSET_GET(B,BIT) ((BITSET_BYTEFOR(B,BIT) & (0x01 << (BIT%8))) ? 0x01 : 0x00)
#define BITSET_TOGGLE(B,BIT) (BITSET_GET(B,BIT) ? (BITSET_UNSET(B,BIT)) : (BITSET_SET(B,BIT)))


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

/*
** Holds keys used for various JSON API properties.
*/







<
<
<
<
<
<
<
<
<
<
<











|

















|







56
57
58
59
60
61
62











63
64
65
66
67
68
69
70
71
72
73
74
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
** either their payload object or NULL. Note that NULL is a legal
** success value - it simply means the response will contain no
** payload. If g.json.resultCode is non-zero when this function
** returns then the top-level dispatcher will destroy any payload
** returned by this function and will output a JSON error response
** instead.
**











** All of the setup/response code is handled by the top dispatcher
** functions and the callbacks concern themselves only with generating
** the payload.
**
** It is imperitive that NO callback functions EVER output ANYTHING to
** stdout, as that will effectively corrupt any JSON output, and
** almost certainly will corrupt any HTTP response headers. Output
** sent to stderr ends up in my apache log, so that might be useful
** for debuggering in some cases, but so such code should be left
** enabled for non-debuggering builds.
*/
typedef cson_value * (*fossil_json_f)();

/*
** Internal helpers to manipulate a byte array as a bitset. The B
** argument must be-a array at least (BIT/8+1) bytes long.
** The BIT argument is the bit number to query/set/clear/toggle.
*/
#define BITSET_BYTEFOR(B,BIT) ((B)[ BIT / 8 ])
#define BITSET_SET(B,BIT) ((BITSET_BYTEFOR(B,BIT) |= (0x01 << (BIT%8))),0x01)
#define BITSET_UNSET(B,BIT) ((BITSET_BYTEFOR(B,BIT) &= ~(0x01 << (BIT%8))),0x00)
#define BITSET_GET(B,BIT) ((BITSET_BYTEFOR(B,BIT) & (0x01 << (BIT%8))) ? 0x01 : 0x00)
#define BITSET_TOGGLE(B,BIT) (BITSET_GET(B,BIT) ? (BITSET_UNSET(B,BIT)) : (BITSET_SET(B,BIT)))


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

/*
** Holds keys used for various JSON API properties.
*/
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
}

/*
** /json/version implementation.
**
** Returns the payload object (owned by the caller).
*/
cson_value * json_page_version(unsigned int depth){
  cson_value * jval = NULL;
  cson_object * jobj = NULL;
  jval = cson_value_new_object();
  jobj = cson_value_get_object(jval);
#define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X)))
  FSET(MANIFEST_UUID,"manifestUuid");
  FSET(MANIFEST_VERSION,"manifestVersion");







|







1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
}

/*
** /json/version implementation.
**
** Returns the payload object (owned by the caller).
*/
cson_value * json_page_version(){
  cson_value * jval = NULL;
  cson_object * jobj = NULL;
  jval = cson_value_new_object();
  jobj = cson_value_get_object(jval);
#define FSET(X,K) cson_object_set( jobj, K, cson_value_new_string(X,strlen(X)))
  FSET(MANIFEST_UUID,"manifestUuid");
  FSET(MANIFEST_VERSION,"manifestVersion");
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
**
** Returned object contains details about the "capabilities" of the
** current user (what he may/may not do).
**
** This is primarily intended for debuggering, but may have
** a use in client code. (?)
*/
cson_value * json_page_cap(unsigned int depth){
  cson_value * payload = cson_value_new_object();
  cson_value * sub = cson_value_new_object();
  Stmt q;
  cson_object * obj = cson_value_get_object(payload);
  db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid);
  if( db_step(&q)==SQLITE_ROW ){
    /* reminder: we don't use g.zLogin because it's 0 for the guest







|







1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
**
** Returned object contains details about the "capabilities" of the
** current user (what he may/may not do).
**
** This is primarily intended for debuggering, but may have
** a use in client code. (?)
*/
cson_value * json_page_cap(){
  cson_value * payload = cson_value_new_object();
  cson_value * sub = cson_value_new_object();
  Stmt q;
  cson_object * obj = cson_value_get_object(payload);
  db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid);
  if( db_step(&q)==SQLITE_ROW ){
    /* reminder: we don't use g.zLogin because it's 0 for the guest
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
  return payload;
}

/*
** Implementation of the /json/login page.
**
*/
cson_value * json_page_login(unsigned int depth){
  static char preciseErrors = /* if true, "complete" JSON error codes are used,
                                 else they are "dumbed down" to a generic login
                                 error code.
                              */
#if 0
    g.json.errorDetailParanoia ? 0 : 1
#else







|







1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
  return payload;
}

/*
** Implementation of the /json/login page.
**
*/
cson_value * json_page_login(){
  static char preciseErrors = /* if true, "complete" JSON error codes are used,
                                 else they are "dumbed down" to a generic login
                                 error code.
                              */
#if 0
    g.json.errorDetailParanoia ? 0 : 1
#else
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
#endif
}

/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(unsigned int depth){
  cson_value const *token = g.json.authToken;
    /* Remember that json_mode_bootstrap() replaces the login cookie
       with the JSON auth token if the request contains it. If the
       reqest is missing the auth token then this will fetch fossil's
       original cookie. Either way, it's what we want :).

       We require the auth token to avoid someone maliciously







|







1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
#endif
}

/*
** Impl of /json/logout.
**
*/
cson_value * json_page_logout(){
  cson_value const *token = g.json.authToken;
    /* Remember that json_mode_bootstrap() replaces the login cookie
       with the JSON auth token if the request contains it. If the
       reqest is missing the auth token then this will fetch fossil's
       original cookie. Either way, it's what we want :).

       We require the auth token to avoid someone maliciously
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
  }
  return NULL;
}

/*
** Implementation of the /json/anonymousPassword page.
*/
cson_value * json_page_anon_password(unsigned int depth){
  cson_value * v = cson_value_new_object();
  cson_object * o = cson_value_get_object(v);
  unsigned const int seed = captcha_seed();
  char const * zCaptcha = captcha_decode(seed);
  cson_object_set(o, "seed",
                  cson_value_new_integer( (cson_int_t)seed )
                  );
  cson_object_set(o, "password",
                  cson_value_new_string( zCaptcha, strlen(zCaptcha) )
                  );
  return v;
}


/*
** Implementation of the /json/stat page/command.
**
*/
cson_value * json_page_stat(unsigned int depth){
  i64 t, fsize;
  int n, m;
  const char *zDb;
  enum { BufLen = 1000 };
  char zBuf[BufLen];
  cson_value * jv = NULL;
  cson_object * jo = NULL;







|


















|







1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
  }
  return NULL;
}

/*
** Implementation of the /json/anonymousPassword page.
*/
cson_value * json_page_anon_password(){
  cson_value * v = cson_value_new_object();
  cson_object * o = cson_value_get_object(v);
  unsigned const int seed = captcha_seed();
  char const * zCaptcha = captcha_decode(seed);
  cson_object_set(o, "seed",
                  cson_value_new_integer( (cson_int_t)seed )
                  );
  cson_object_set(o, "password",
                  cson_value_new_string( zCaptcha, strlen(zCaptcha) )
                  );
  return v;
}


/*
** Implementation of the /json/stat page/command.
**
*/
cson_value * json_page_stat(){
  i64 t, fsize;
  int n, m;
  const char *zDb;
  enum { BufLen = 1000 };
  char zBuf[BufLen];
  cson_value * jv = NULL;
  cson_object * jo = NULL;
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574

1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593

1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.journal_mode", zDb));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}


static cson_value * json_wiki_list(unsigned int depth);
static cson_value * json_wiki_get(unsigned int depth);
static cson_value * json_wiki_save(unsigned int depth);
static cson_value * json_wiki_create(unsigned int depth);

/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Wiki[] = {
{"create", json_wiki_create, 1},
{"get", json_wiki_get, 0},
{"list", json_wiki_list, 0},
{"save", json_wiki_save, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** A page/command dispatch helper for fossil_json_f() implementations.
** depth should be the depth parameter passed to the fossil_json_f().
** pages must be an array of JsonPageDef commands which we can
** dispatch.  The final item in the array MUST have a NULL name
** element.
**
** This function takes json_comand_arg(1+depth) and searches pages
** for a matching name. If found then that page's func() is called
** to fetch the payload, which is returned to the caller.

**
** On error, g.json.resultCode is set to one of the FossilJsonCodes
** values.
*/
static cson_value * json_page_dispatch_helper(unsigned int depth,
                                              JsonPageDef const * pages){
  JsonPageDef const * def;
  char const * cmd = json_command_arg(1+depth);
  assert( NULL != pages );
  if( ! cmd ){
    g.json.resultCode = FSL_JSON_E_MISSING_ARGS;
    return NULL;
  }
  def = json_handler_for_name( cmd, pages );
  if(!def){
    g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
    return NULL;
  }
  else{

    return (*def->func)(1+depth);
  }
}

/*
** Implements the /json/wiki family of pages/commands. Far from
** complete.
**
*/
static cson_value * json_page_wiki(unsigned int depth){
  return json_page_dispatch_helper(depth,&JsonPageDefs_Wiki[0]);
}


/*
** Implementation of /json/wiki/get.
**
** TODO: add option to parse wiki output. It is currently
** unparsed.
*/
static cson_value * json_wiki_get(unsigned int depth){
  int rid;
  Manifest *pWiki = 0;
  char const * zBody = NULL;
  char const * zPageName;
  char doParse = 0/*not yet implemented*/;

  if( !g.perm.RdWiki ){







|
|
|
|




















|
|
|
>




|
<

|











>
|








|
|









|







1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569

1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
  sqlite3_snprintf(BufLen, zBuf, "%s", db_text(0, "PRAGMA %s.journal_mode", zDb));
  cson_object_set(jo2, "journalMode", *zBuf ? cson_value_new_string(zBuf, strlen(zBuf)) : cson_value_null());
  return jv;
#undef SETBUF
}


static cson_value * json_wiki_list();
static cson_value * json_wiki_get();
static cson_value * json_wiki_save();
static cson_value * json_wiki_create();

/*
** Mapping of /json/wiki/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Wiki[] = {
{"create", json_wiki_create, 1},
{"get", json_wiki_get, 0},
{"list", json_wiki_list, 0},
{"save", json_wiki_save, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** A page/command dispatch helper for fossil_json_f() implementations.
** depth should be the depth parameter passed to the fossil_json_f().
** pages must be an array of JsonPageDef commands which we can
** dispatch.  The final item in the array MUST have a NULL name
** element.
**
** This function takes json_comand_arg(1+g.json.dispatchDepth) and
** searches pages for a matching name. If found then that page's
** func() is called to fetch the payload, which is returned to the
** caller.
**
** On error, g.json.resultCode is set to one of the FossilJsonCodes
** values.
*/
static cson_value * json_page_dispatch_helper(JsonPageDef const * pages){

  JsonPageDef const * def;
  char const * cmd = json_command_arg(1+g.json.dispatchDepth);
  assert( NULL != pages );
  if( ! cmd ){
    g.json.resultCode = FSL_JSON_E_MISSING_ARGS;
    return NULL;
  }
  def = json_handler_for_name( cmd, pages );
  if(!def){
    g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND;
    return NULL;
  }
  else{
    ++g.json.dispatchDepth;
    return (*def->func)();
  }
}

/*
** Implements the /json/wiki family of pages/commands. Far from
** complete.
**
*/
static cson_value * json_page_wiki(){
  return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]);
}


/*
** Implementation of /json/wiki/get.
**
** TODO: add option to parse wiki output. It is currently
** unparsed.
*/
static cson_value * json_wiki_get(){
  int rid;
  Manifest *pWiki = 0;
  char const * zBody = NULL;
  char const * zPageName;
  char doParse = 0/*not yet implemented*/;

  if( !g.perm.RdWiki ){
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
  return payV;

}

/*
** Implementation of /json/wiki/create.
*/
static cson_value * json_wiki_create(unsigned int depth){
  return json_wiki_create_or_save(1);
}

/*
** Implementation of /json/wiki/save.
*/
static cson_value * json_wiki_save(unsigned int depth){
  /* FIXME: add GET/POST.payload bool option createIfNotExists. */
  return json_wiki_create_or_save(0);
}

/*
** Implementation of /json/wiki/list.
*/
static cson_value * json_wiki_list(unsigned int depth){
  cson_value * listV = NULL;
  cson_array * list = NULL;
  Stmt q;
  if( !g.perm.RdWiki ){
    g.json.resultCode = FSL_JSON_E_DENIED;
    return NULL;
  }







|






|







|







1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
  return payV;

}

/*
** Implementation of /json/wiki/create.
*/
static cson_value * json_wiki_create(){
  return json_wiki_create_or_save(1);
}

/*
** Implementation of /json/wiki/save.
*/
static cson_value * json_wiki_save(){
  /* FIXME: add GET/POST.payload bool option createIfNotExists. */
  return json_wiki_create_or_save(0);
}

/*
** Implementation of /json/wiki/list.
*/
static cson_value * json_wiki_list(){
  cson_value * listV = NULL;
  cson_array * list = NULL;
  Stmt q;
  if( !g.perm.RdWiki ){
    g.json.resultCode = FSL_JSON_E_DENIED;
    return NULL;
  }
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
  listV = NULL;
  end:
  db_finalize(&q);
  return listV;
}


static cson_value * json_branch_list(unsigned int depth);
/*
** Mapping of /json/branch/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Branch[] = {
{"list", json_branch_list, 0},
{"create", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Implements the /json/branch family of pages/commands. Far from
** complete.
**
*/
static cson_value * json_page_branch(unsigned int depth){
  return json_page_dispatch_helper(depth,&JsonPageDefs_Branch[0]);
}

/*
** Impl for /json/branch/list
**
**
** CLI mode options:
**
**  --range X | -r X, where X is one of (open,closed,all)
**    (only the first letter is significant, default=open).
**  -a (same as --range a)
**  -c (same as --range c)
**
** HTTP mode options:
**
** "range" GET/POST.payload parameter. FIXME: currently we also use
** POST, but really want to restrict this to POST.payload.
*/
static cson_value * json_branch_list(unsigned int depth){
  cson_value * payV;
  cson_object * pay;
  cson_value * listV;
  cson_array * list;
  char const * range = NULL;
  int which = 0;
  Stmt q;







|















|
|


















|







1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
  listV = NULL;
  end:
  db_finalize(&q);
  return listV;
}


static cson_value * json_branch_list();
/*
** Mapping of /json/branch/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Branch[] = {
{"list", json_branch_list, 0},
{"create", json_page_nyi, 1},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Implements the /json/branch family of pages/commands. Far from
** complete.
**
*/
static cson_value * json_page_branch(){
  return json_page_dispatch_helper(&JsonPageDefs_Branch[0]);
}

/*
** Impl for /json/branch/list
**
**
** CLI mode options:
**
**  --range X | -r X, where X is one of (open,closed,all)
**    (only the first letter is significant, default=open).
**  -a (same as --range a)
**  -c (same as --range c)
**
** HTTP mode options:
**
** "range" GET/POST.payload parameter. FIXME: currently we also use
** POST, but really want to restrict this to POST.payload.
*/
static cson_value * json_branch_list(){
  cson_value * payV;
  cson_object * pay;
  cson_value * listV;
  cson_array * list;
  char const * range = NULL;
  int which = 0;
  Stmt q;
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
      json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,msg);
      free(msg);
    }
  }
  return payV;
}

static cson_value * json_timeline_ci(unsigned int depth);
static cson_value * json_timeline_wiki(unsigned int depth);
/*
** Mapping of /json/timeline/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Timeline[] = {
{"ci", json_timeline_ci, 0},
{"wiki", json_timeline_wiki, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Implements the /json/timeline family of pages/commands. Far from
** complete.
**
*/
static cson_value * json_page_timeline(unsigned int depth){
  return json_page_dispatch_helper(depth,&JsonPageDefs_Timeline[0]);
}

/*
** Create a temporary table suitable for storing timeline data.
*/
static void json_timeline_temp_table(void){
  /* Field order MUST match that from json_timeline_query()!!! */







|
|















|
|







1918
1919
1920
1921
1922
1923
1924
1925
1926
1927
1928
1929
1930
1931
1932
1933
1934
1935
1936
1937
1938
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
      json_warn(FSL_JSON_W_COL_TO_JSON_FAILED,msg);
      free(msg);
    }
  }
  return payV;
}

static cson_value * json_timeline_ci();
static cson_value * json_timeline_wiki();
/*
** Mapping of /json/timeline/XXX commands/paths to callbacks.
*/
static const JsonPageDef JsonPageDefs_Timeline[] = {
{"ci", json_timeline_ci, 0},
{"wiki", json_timeline_wiki, 0},
/* Last entry MUST have a NULL name. */
{NULL,NULL,0}
};

/*
** Implements the /json/timeline family of pages/commands. Far from
** complete.
**
*/
static cson_value * json_page_timeline(){
  return json_page_dispatch_helper(&JsonPageDefs_Timeline[0]);
}

/*
** Create a temporary table suitable for storing timeline data.
*/
static void json_timeline_temp_table(void){
  /* Field order MUST match that from json_timeline_query()!!! */
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
** Helper for the timeline family of functions.  Possibly appends 1
** AND clause and an ORDER BY clause to pSql, depending on the state
** of the "after" ("a") or "before" ("b") environment parameters.
** This function gives "after" precedence over "before", and only
** applies one of them.
**
** Returns -1 if it adds a "before" clause, 1 if it adds
** an "after" clause, and 0 if it does not change pSql.
*/
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







|







2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
** Helper for the timeline family of functions.  Possibly appends 1
** AND clause and an ORDER BY clause to pSql, depending on the state
** of the "after" ("a") or "before" ("b") environment parameters.
** This function gives "after" precedence over "before", and only
** applies one of them.
**
** 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
2050
2051
2052
2053
2054
2055
2056

2057
2058
2059
2060
2061
2062
2063
    while( fossil_isspace(*zBefore) ) ++zBefore;
    blob_appendf(pSql,
                 " AND event.mtime<=(SELECT julianday(%Q,'utc')) "
                 " ORDER BY event.mtime DESC ",
                 zBefore);
    return -1;
  }else{

    return 0;
  }
}

/*
** Tries to figure out a timeline query length limit base on
** environment parameters. If it can it returns that value,







>







2040
2041
2042
2043
2044
2045
2046
2047
2048
2049
2050
2051
2052
2053
2054
    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,
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
}
/*
** Implementation of /json/timeline/ci.
**
** Still a few TODOs (like figuring out how to structure
** inheritance info).
*/
static cson_value * json_timeline_ci(unsigned int depth){
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_value * tmp = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  int limit;
  int check = 0;







|







2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
}
/*
** Implementation of /json/timeline/ci.
**
** Still a few TODOs (like figuring out how to structure
** inheritance info).
*/
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 limit;
  int check = 0;
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
  return payV;
}

/*
** Implementation of /json/timeline/wiki.
**
*/
static cson_value * json_timeline_wiki(unsigned int depth){
  /* This code is 95% the same as json_timeline_ci(), by the way. */
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_value * tmp = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  int limit;







|







2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
  return payV;
}

/*
** Implementation of /json/timeline/wiki.
**
*/
static cson_value * json_timeline_wiki(){
  /* This code is 95% the same as json_timeline_ci(), by the way. */
  cson_value * payV = NULL;
  cson_object * pay = NULL;
  cson_value * tmp = NULL;
  cson_value * listV = NULL;
  cson_array * list = NULL;
  int limit;
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
  return payV;
}


/*
** Implements the /json/whoami page/command.
*/
static cson_value * json_page_whoami(unsigned int depth){
  cson_value * payload = NULL;
  cson_object * obj = NULL;
  Stmt q;
  db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid);
  if( db_step(&q)==SQLITE_ROW ){

    /* reminder: we don't use g.zLogin because it's 0 for the guest







|







2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
  return payV;
}


/*
** Implements the /json/whoami page/command.
*/
static cson_value * json_page_whoami(){
  cson_value * payload = NULL;
  cson_object * obj = NULL;
  Stmt q;
  db_prepare(&q, "SELECT login, cap FROM user WHERE uid=%d", g.userUid);
  if( db_step(&q)==SQLITE_ROW ){

    /* reminder: we don't use g.zLogin because it's 0 for the guest
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429

2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
** This function dispatches them, and is the HTTP equivalent of
** json_cmd_top().
*/
void json_page_top(void){
  int rc = FSL_JSON_E_UNKNOWN_COMMAND;
  char const * cmd;
  cson_value * payload = NULL;
  cson_value * root = NULL;
  JsonPageDef const * pageDef = NULL;
  json_mode_bootstrap();
  cmd = json_command_arg(1);
  /*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/
  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 );
    return;
  }else if( pageDef->runMode < 0 /*CLI only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{
    rc = 0;

    payload = (*pageDef->func)(1);
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 0);
  }else{
    root = json_create_response(rc, NULL, payload);
    json_send_response(payload);
    cson_value_free(root);
  }
}

/*
** This function dispatches json commands and is the CLI equivalent of
** json_page_top().







<












>
|




|
|







2401
2402
2403
2404
2405
2406
2407

2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
** This function dispatches them, and is the HTTP equivalent of
** json_cmd_top().
*/
void json_page_top(void){
  int rc = FSL_JSON_E_UNKNOWN_COMMAND;
  char const * cmd;
  cson_value * payload = NULL;

  JsonPageDef const * pageDef = NULL;
  json_mode_bootstrap();
  cmd = json_command_arg(1);
  /*cgi_printf("{\"cmd\":\"%s\"}\n",cmd); return;*/
  pageDef = json_handler_for_name(cmd,&JsonPageDefs[0]);
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 0 );
    return;
  }else if( pageDef->runMode < 0 /*CLI only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{
    rc = 0;
    g.json.dispatchDepth = 1;
    payload = (*pageDef->func)();
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 0);
  }else{
    cson_value * root = json_create_response(rc, NULL, payload);
    json_send_response(root);
    cson_value_free(root);
  }
}

/*
** This function dispatches json commands and is the CLI equivalent of
** json_page_top().
2495
2496
2497
2498
2499
2500
2501

2502
2503
2504
2505
2506
2507
2508
2509
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 );
    return;
  }else if( pageDef->runMode > 0 /*HTTP only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{
    rc = 0;

    payload = (*pageDef->func)(1);
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 1);
  }else{
    payload = json_create_response(rc, NULL, payload);
    json_send_response(payload);
    cson_value_free( payload );







>
|







2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
  if( ! pageDef ){
    json_err( FSL_JSON_E_UNKNOWN_COMMAND, NULL, 1 );
    return;
  }else if( pageDef->runMode > 0 /*HTTP only*/){
    rc = FSL_JSON_E_WRONG_MODE;
  }else{
    rc = 0;
    g.json.dispatchDepth = 1;
    payload = (*pageDef->func)();
  }
  if( g.json.resultCode ){
    json_err(g.json.resultCode, NULL, 1);
  }else{
    payload = json_create_response(rc, NULL, payload);
    json_send_response(payload);
    cson_value_free( payload );
Changes to src/main.c.
180
181
182
183
184
185
186





187
188
189
190
191
192
193
                                  code 0 to avoid an HTTP 500 error.
                               */
    int resultCode;            /* used for passing back specific codes from /json callbacks. */
    int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
    cson_output_opt outOpt;    /* formatting options for JSON mode. */
    cson_value * authToken;    /* authentication token */
    char const * jsonp;        /* Name of JSONP function wrapper. */





    struct {                   /* "garbage collector" */
      cson_value * v;
      cson_object * o;
    } gc;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_array * a;







>
>
>
>
>







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
                                  code 0 to avoid an HTTP 500 error.
                               */
    int resultCode;            /* used for passing back specific codes from /json callbacks. */
    int errorDetailParanoia;   /* 0=full error codes, 1=%10, 2=%100, 3=%1000 */
    cson_output_opt outOpt;    /* formatting options for JSON mode. */
    cson_value * authToken;    /* authentication token */
    char const * jsonp;        /* Name of JSONP function wrapper. */
    unsigned char dispatchDepth /* Tells JSON command dispatching
                                   which argument we are currently
                                   working on. For this purpose, arg#0
                                   is the "json" path/CLI arg.
                                */;
    struct {                   /* "garbage collector" */
      cson_value * v;
      cson_object * o;
    } gc;
    struct {                   /* JSON POST data. */
      cson_value * v;
      cson_array * a;