| | @@ -481,10 +481,22 @@ |
| 481 | 481 | } |
| 482 | 482 | } |
| 483 | 483 | } |
| 484 | 484 | return g.json.authToken; |
| 485 | 485 | } |
| 486 | + |
| 487 | +/* |
| 488 | +** IFF json.reqPayload.o is not NULL then this returns |
| 489 | +** cson_object_get(json.reqPayload.o,pKey), else it returns NULL. |
| 490 | +** |
| 491 | +** The returned value is owned by (or shared with) json.reqPayload.v. |
| 492 | +*/ |
| 493 | +cson_value * json_req_payload_get(char const *pKey){ |
| 494 | + return g.json.reqPayload.o |
| 495 | + ? cson_object_get(g.json.reqPayload.o,pKey) |
| 496 | + : NULL; |
| 497 | +} |
| 486 | 498 | |
| 487 | 499 | /* |
| 488 | 500 | ** Initializes some JSON bits which need to be initialized relatively |
| 489 | 501 | ** early on. It should only be called from cgi_init() or |
| 490 | 502 | ** json_cmd_top() (early on in those functions). |
| | @@ -942,10 +954,24 @@ |
| 942 | 954 | static cson_value * json_julian_to_timestamp(double j){ |
| 943 | 955 | return cson_value_new_integer((cson_int_t) |
| 944 | 956 | db_int64(0,"SELECT strftime('%%s',%lf)",j) |
| 945 | 957 | ); |
| 946 | 958 | } |
| 959 | +/* |
| 960 | +** Returns a timestamp value. |
| 961 | +*/ |
| 962 | +static cson_int_t json_timestamp(){ |
| 963 | + return (cson_int_t)time(0); |
| 964 | +} |
| 965 | +/* |
| 966 | +** Returns a new JSON value (owned by the caller) representing |
| 967 | +** a timestamp. If timeVal is < 0 then time(0) is used to fetch |
| 968 | +** the time, else timeVal is used as-is |
| 969 | +*/ |
| 970 | +static cson_value * json_new_timestamp(cson_int_t timeVal){ |
| 971 | + return cson_value_new_integer((timeVal<0) ? (cson_int_t)time(0) : timeVal); |
| 972 | +} |
| 947 | 973 | |
| 948 | 974 | /* |
| 949 | 975 | ** Creates a new Fossil/JSON response envelope skeleton. It is owned |
| 950 | 976 | ** by the caller, who must eventually free it using cson_value_free(), |
| 951 | 977 | ** or add it to a cson container to transfer ownership. Returns NULL |
| | @@ -986,35 +1012,11 @@ |
| 986 | 1012 | |
| 987 | 1013 | tmp = cson_value_new_string(MANIFEST_UUID,strlen(MANIFEST_UUID)); |
| 988 | 1014 | SET("fossil"); |
| 989 | 1015 | |
| 990 | 1016 | {/* timestamp */ |
| 991 | | - cson_int_t jsTime; |
| 992 | | -#if 1 |
| 993 | | - jsTime = (cson_int_t)time(0); |
| 994 | | -#elif 1 |
| 995 | | - /* Ge Weijers has pointed out that time(0) commonly returns |
| 996 | | - UTC, but is not required to by The Standard. |
| 997 | | - |
| 998 | | - There is a mkfmtime() function in cgi.c but it requires |
| 999 | | - a (tm *), and i don't have that without calling gmtime() |
| 1000 | | - or populating the tm myself (which is what i'm trying to |
| 1001 | | - have done for me!). |
| 1002 | | - */ |
| 1003 | | - time_t const t = (time_t)time(0); |
| 1004 | | - struct tm gt = *gmtime(&t); |
| 1005 | | - gt.tm_isdst = -1; |
| 1006 | | - jsTime = (cson_int_t)mktime(>); |
| 1007 | | -#else |
| 1008 | | - /* i'm not 100% sure that the above actually does what i expect, |
| 1009 | | - but we can't use the following because this function can be |
| 1010 | | - called in response to error handling if the db cannot be opened |
| 1011 | | - (or before that). |
| 1012 | | - */ |
| 1013 | | - jsTime = (cson_int_t)db_int64(0, "SELECT strftime('%%s','now')"); |
| 1014 | | -#endif |
| 1015 | | - tmp = cson_value_new_integer(jsTime); |
| 1017 | + tmp = json_new_timestamp(-1); |
| 1016 | 1018 | SET(FossilJsonKeys.timestamp); |
| 1017 | 1019 | } |
| 1018 | 1020 | if( 0 != resultCode ){ |
| 1019 | 1021 | if( ! pMsg ) pMsg = json_err_str(resultCode); |
| 1020 | 1022 | tmp = json_rc_string(resultCode); |
| | @@ -1478,17 +1480,18 @@ |
| 1478 | 1480 | } |
| 1479 | 1481 | |
| 1480 | 1482 | |
| 1481 | 1483 | static cson_value * json_wiki_list(unsigned int depth); |
| 1482 | 1484 | static cson_value * json_wiki_get(unsigned int depth); |
| 1485 | +static cson_value * json_wiki_save(unsigned int depth); |
| 1483 | 1486 | /* |
| 1484 | 1487 | ** Mapping of /json/wiki/XXX commands/paths to callbacks. |
| 1485 | 1488 | */ |
| 1486 | 1489 | static const JsonPageDef JsonPageDefs_Wiki[] = { |
| 1487 | 1490 | {"get", json_wiki_get, 0}, |
| 1488 | 1491 | {"list", json_wiki_list, 0}, |
| 1489 | | -{"save", json_page_nyi, 1}, |
| 1492 | +{"save", json_wiki_save, 1}, |
| 1490 | 1493 | /* Last entry MUST have a NULL name. */ |
| 1491 | 1494 | {NULL,NULL,0} |
| 1492 | 1495 | }; |
| 1493 | 1496 | |
| 1494 | 1497 | /* |
| | @@ -1578,20 +1581,62 @@ |
| 1578 | 1581 | cson_object_set(pay,"version",json_new_string(pWiki->zBaseline)) |
| 1579 | 1582 | /*FIXME: pWiki->zBaseline is NULL. How to get the version number?*/ |
| 1580 | 1583 | ; |
| 1581 | 1584 | cson_object_set(pay,"rid",cson_value_new_integer((cson_int_t)rid)); |
| 1582 | 1585 | cson_object_set(pay,"lastSavedBy",json_new_string(pWiki->zUser)); |
| 1583 | | - cson_object_set(pay,"timestamp", json_julian_to_timestamp(pWiki->rDate)); |
| 1586 | + cson_object_set(pay,FossilJsonKeys.timestamp, json_julian_to_timestamp(pWiki->rDate)); |
| 1584 | 1587 | cson_object_set(pay,"contentLength",cson_value_new_integer((cson_int_t)len)); |
| 1585 | 1588 | cson_object_set(pay,"contentFormat",json_new_string(doParse?"html":"raw")); |
| 1586 | 1589 | cson_object_set(pay,"content",cson_value_new_string(zBody,len)); |
| 1587 | 1590 | /*TODO: add 'T' (tag) fields*/ |
| 1588 | 1591 | /*TODO: add the 'A' card (file attachment) entries?*/ |
| 1589 | 1592 | manifest_destroy(pWiki); |
| 1590 | 1593 | return payV; |
| 1591 | 1594 | } |
| 1592 | 1595 | } |
| 1596 | + |
| 1597 | +/* |
| 1598 | +** Implementation of /json/wiki/save. |
| 1599 | +*/ |
| 1600 | +static cson_value * json_wiki_save(unsigned int depth){ |
| 1601 | + Blob content = empty_blob; |
| 1602 | + cson_value * nameV; |
| 1603 | + cson_value * contentV; |
| 1604 | + cson_value * payV = NULL; |
| 1605 | + cson_object * pay = NULL; |
| 1606 | + cson_string const * jstr = NULL; |
| 1607 | + char const * zContent; |
| 1608 | + char const * zBody = NULL; |
| 1609 | + char const * zPageName; |
| 1610 | + if( !g.perm.WrWiki ){ |
| 1611 | + g.json.resultCode = FSL_JSON_E_DENIED; |
| 1612 | + return NULL; |
| 1613 | + } |
| 1614 | + nameV = json_req_payload_get("name"); |
| 1615 | + contentV = nameV ? json_req_payload_get("content") : NULL; |
| 1616 | + if(!nameV || !contentV){ |
| 1617 | + g.json.resultCode = FSL_JSON_E_MISSING_ARGS; |
| 1618 | + return NULL; |
| 1619 | + } |
| 1620 | + if( !cson_value_is_string(nameV) |
| 1621 | + || !cson_value_is_string(contentV)){ |
| 1622 | + g.json.resultCode = FSL_JSON_E_INVALID_ARGS; |
| 1623 | + return NULL; |
| 1624 | + } |
| 1625 | + zPageName = cson_string_cstr(cson_value_get_string(nameV)); |
| 1626 | + jstr = cson_value_get_string(contentV); |
| 1627 | + blob_append(&content, cson_string_cstr(jstr),(int)cson_string_length_bytes(jstr)); |
| 1628 | + wiki_cmd_commit(zPageName, 0, &content); |
| 1629 | + blob_reset(&content); |
| 1630 | + |
| 1631 | + payV = cson_value_new_object(); |
| 1632 | + pay = cson_value_get_object(payV); |
| 1633 | + cson_object_set( pay, "name", nameV ); |
| 1634 | + cson_object_set( pay, FossilJsonKeys.timestamp, |
| 1635 | + json_new_timestamp(-1) ); |
| 1636 | + return payV; |
| 1637 | +} |
| 1593 | 1638 | |
| 1594 | 1639 | /* |
| 1595 | 1640 | ** Implementation of /json/wiki/list. |
| 1596 | 1641 | */ |
| 1597 | 1642 | static cson_value * json_wiki_list(unsigned int depth){ |
| | @@ -1987,11 +2032,11 @@ |
| 1987 | 2032 | {"tag", json_page_nyi,0}, |
| 1988 | 2033 | {"ticket", json_page_nyi,0}, |
| 1989 | 2034 | {"timeline", json_page_timeline,0}, |
| 1990 | 2035 | {"user", json_page_nyi,0}, |
| 1991 | 2036 | {"version",json_page_version,0}, |
| 1992 | | -{"whoami",json_page_whoami,1}, |
| 2037 | +{"whoami",json_page_whoami,1/*FIXME: work in CLI mode*/}, |
| 1993 | 2038 | {"wiki",json_page_wiki,0}, |
| 1994 | 2039 | /* Last entry MUST have a NULL name. */ |
| 1995 | 2040 | {NULL,NULL,0} |
| 1996 | 2041 | }; |
| 1997 | 2042 | |
| 1998 | 2043 | |