Fossil SCM
lots of minor cleanups in JSON error handling (mostly cosmetic). Added some new FOSSIL-xxxx codes.
Commit
2e41514fb754131bc1206b7f42338da5a286846e
Parent
4fbf77d4f371c19…
5 files changed
+34
-23
+1
-1
+34
-31
+21
-12
+76
-62
+34
-23
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -206,29 +206,35 @@ | ||
| 206 | 206 | |
| 207 | 207 | C(GENERIC,"Generic error"); |
| 208 | 208 | C(INVALID_REQUEST,"Invalid request"); |
| 209 | 209 | C(UNKNOWN_COMMAND,"Unknown Command"); |
| 210 | 210 | C(UNKNOWN,"Unknown error"); |
| 211 | - C(RESOURCE_NOT_FOUND,"Resource not found"); | |
| 212 | 211 | C(TIMEOUT,"Timeout reached"); |
| 213 | 212 | C(ASSERT,"Assertion failed"); |
| 214 | 213 | C(ALLOC,"Resource allocation failed"); |
| 215 | 214 | C(NYI,"Not yet implemented"); |
| 215 | + C(PANIC,"x"); | |
| 216 | + C(MANIFEST_READ_FAILED,"Reading artifact manifest failed."); | |
| 217 | + C(FILE_OPEN_FAILED,"Opening file failed."); | |
| 218 | + | |
| 216 | 219 | C(AUTH,"Authentication error"); |
| 220 | + C(MISSING_AUTH,"Authentication info missing from request"); | |
| 221 | + C(DENIED,"Access denied"); | |
| 222 | + C(WRONG_MODE,"Request not allowed (wrong operation mode)"); | |
| 217 | 223 | C(LOGIN_FAILED,"Login failed"); |
| 218 | 224 | C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed"); |
| 219 | 225 | C(LOGIN_FAILED_NONAME,"Login failed - name not supplied"); |
| 220 | 226 | C(LOGIN_FAILED_NOPW,"Login failed - password not supplied"); |
| 221 | 227 | C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found"); |
| 222 | - C(MISSING_AUTH,"Authentication info missing from request"); | |
| 223 | - C(DENIED,"Access denied"); | |
| 224 | - C(WRONG_MODE,"Request not allowed (wrong operation mode)"); | |
| 225 | 228 | |
| 226 | 229 | C(USAGE,"Usage error"); |
| 227 | - C(INVALID_ARGS,"Invalid arguments"); | |
| 228 | - C(MISSING_ARGS,"Missing arguments"); | |
| 229 | - C(AMBIGUOUS_UUID,"Argument is ambiguous"); | |
| 230 | + C(INVALID_ARGS,"Invalid argument(s)"); | |
| 231 | + C(MISSING_ARGS,"Missing argument(s)"); | |
| 232 | + C(AMBIGUOUS_UUID,"Resource identifier is ambiguous"); | |
| 233 | + C(UNRESOLVED_UUID,"Provided uuid/tag/branch could not be resolved"); | |
| 234 | + C(RESOURCE_ALREADY_EXISTS,"Resource already exists"); | |
| 235 | + C(RESOURCE_NOT_FOUND,"Resource not found"); | |
| 230 | 236 | |
| 231 | 237 | C(DB,"Database error"); |
| 232 | 238 | C(STMT_PREP,"Statement preparation failed"); |
| 233 | 239 | C(STMT_BIND,"Statement parameter binding failed"); |
| 234 | 240 | C(STMT_EXEC,"Statement execution/stepping failed"); |
| @@ -882,18 +888,18 @@ | ||
| 882 | 888 | if( once ){ |
| 883 | 889 | return; |
| 884 | 890 | }else{ |
| 885 | 891 | once = 1; |
| 886 | 892 | } |
| 893 | + g.json.isJsonMode = 1; | |
| 894 | + g.json.resultCode = 0; | |
| 895 | + g.json.cmd.offset = -1; | |
| 887 | 896 | g.json.jsonp = PD("jsonp",NULL) |
| 888 | 897 | /* FIXME: do some sanity checking on g.json.jsonp and ignore it |
| 889 | 898 | if it is not halfway reasonable. |
| 890 | 899 | */ |
| 891 | 900 | ; |
| 892 | - g.json.isJsonMode = 1; | |
| 893 | - g.json.resultCode = 0; | |
| 894 | - g.json.cmd.offset = -1; | |
| 895 | 901 | if( !g.isHTTP && g.fullHttpReply ){ |
| 896 | 902 | /* workaround for server mode, so we see it as CGI mode. */ |
| 897 | 903 | g.isHTTP = 1; |
| 898 | 904 | } |
| 899 | 905 | |
| @@ -961,11 +967,11 @@ | ||
| 961 | 967 | } |
| 962 | 968 | inFile = (0==strcmp("-",jfile)) |
| 963 | 969 | ? stdin |
| 964 | 970 | : fopen(jfile,"rb"); |
| 965 | 971 | if(!inFile){ |
| 966 | - g.json.resultCode = FSL_JSON_E_UNKNOWN; | |
| 972 | + g.json.resultCode = FSL_JSON_E_FILE_OPEN_FAILED; | |
| 967 | 973 | fossil_fatal("Could not open JSON file [%s].",jfile) |
| 968 | 974 | /* Does not return. */ |
| 969 | 975 | ; |
| 970 | 976 | } |
| 971 | 977 | cgi_parse_POST_JSON(inFile, 0); |
| @@ -1555,11 +1561,12 @@ | ||
| 1555 | 1561 | cson_value * jv = NULL; |
| 1556 | 1562 | cson_object * jo = NULL; |
| 1557 | 1563 | cson_value * jv2 = NULL; |
| 1558 | 1564 | cson_object * jo2 = NULL; |
| 1559 | 1565 | if( !g.perm.Read ){ |
| 1560 | - g.json.resultCode = FSL_JSON_E_DENIED; | |
| 1566 | + json_set_err(FSL_JSON_E_DENIED, | |
| 1567 | + "Requires 'o' permissions."); | |
| 1561 | 1568 | return NULL; |
| 1562 | 1569 | } |
| 1563 | 1570 | if( g.isHTTP ){ |
| 1564 | 1571 | full = json_getenv_bool("full",0); |
| 1565 | 1572 | }else{ |
| @@ -1672,11 +1679,11 @@ | ||
| 1672 | 1679 | cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ |
| 1673 | 1680 | JsonPageDef const * def; |
| 1674 | 1681 | char const * cmd = json_command_arg(1+g.json.dispatchDepth); |
| 1675 | 1682 | assert( NULL != pages ); |
| 1676 | 1683 | if( ! cmd ){ |
| 1677 | - g.json.resultCode = FSL_JSON_E_MISSING_ARGS; | |
| 1684 | + json_set_err(FSL_JSON_E_MISSING_ARGS, "No subcommand specified."); | |
| 1678 | 1685 | return NULL; |
| 1679 | 1686 | } |
| 1680 | 1687 | def = json_handler_for_name( cmd, pages ); |
| 1681 | 1688 | if(!def){ |
| 1682 | 1689 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| @@ -1700,18 +1707,21 @@ | ||
| 1700 | 1707 | /* |
| 1701 | 1708 | ** Impl of /json/rebuild. Requires admin previleges. |
| 1702 | 1709 | */ |
| 1703 | 1710 | static cson_value * json_page_rebuild(){ |
| 1704 | 1711 | if( !g.perm.Admin ){ |
| 1705 | - g.json.resultCode = FSL_JSON_E_DENIED; | |
| 1712 | + json_set_err(FSL_JSON_E_DENIED,"Requires 'a' privileges."); | |
| 1706 | 1713 | return NULL; |
| 1707 | 1714 | }else{ |
| 1708 | - /* Reminder: the db_xxx() ops "should" fail via | |
| 1709 | - the fossil core error handlers, which will cause | |
| 1710 | - a JSON error and exit(). i.e. we don't handle | |
| 1711 | - the errors here. TODO: confirm that all these | |
| 1712 | - db routine fail gracefully in JSON mode. | |
| 1715 | + /* Reminder: the db_xxx() ops "should" fail via the fossil core | |
| 1716 | + error handlers, which will cause a JSON error and exit(). i.e. we | |
| 1717 | + don't handle the errors here. TODO: confirm that all these db | |
| 1718 | + routine fail gracefully in JSON mode. | |
| 1719 | + | |
| 1720 | + On large repos (e.g. fossil's) this operation is likely to take | |
| 1721 | + longer than the client timeout, which will cause it to fail (but | |
| 1722 | + it's sqlite3, so it'll fail gracefully). | |
| 1713 | 1723 | */ |
| 1714 | 1724 | db_close(1); |
| 1715 | 1725 | db_open_repository(g.zRepositoryName); |
| 1716 | 1726 | db_begin_transaction(); |
| 1717 | 1727 | rebuild_db(0, 0, 0); |
| @@ -1737,11 +1747,12 @@ | ||
| 1737 | 1747 | " mtime AS mtime" |
| 1738 | 1748 | " FROM user ORDER BY login"); |
| 1739 | 1749 | payV = json_stmt_to_array_of_obj(&q, NULL); |
| 1740 | 1750 | db_finalize(&q); |
| 1741 | 1751 | if(NULL == payV){ |
| 1742 | - g.json.resultCode = FSL_JSON_E_UNKNOWN; | |
| 1752 | + json_set_err(FSL_JSON_E_UNKNOWN, | |
| 1753 | + "Could not convert user list to JSON."); | |
| 1743 | 1754 | } |
| 1744 | 1755 | return payV; |
| 1745 | 1756 | } |
| 1746 | 1757 | |
| 1747 | 1758 | /* |
| @@ -1762,11 +1773,11 @@ | ||
| 1762 | 1773 | if we pass the name as part of the path, so we check the path |
| 1763 | 1774 | _before_ checking for name=XYZ. |
| 1764 | 1775 | */; |
| 1765 | 1776 | } |
| 1766 | 1777 | if(!pUser || !*pUser){ |
| 1767 | - g.json.resultCode = FSL_JSON_E_MISSING_ARGS; | |
| 1778 | + json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); | |
| 1768 | 1779 | return NULL; |
| 1769 | 1780 | } |
| 1770 | 1781 | db_prepare(&q,"SELECT uid AS uid," |
| 1771 | 1782 | " login AS name," |
| 1772 | 1783 | " cap AS capabilities," |
| @@ -1776,14 +1787,14 @@ | ||
| 1776 | 1787 | " WHERE login=%Q", |
| 1777 | 1788 | pUser); |
| 1778 | 1789 | if( (SQLITE_ROW == db_step(&q)) ){ |
| 1779 | 1790 | payV = cson_sqlite3_row_to_object(q.pStmt); |
| 1780 | 1791 | if(!payV){ |
| 1781 | - g.json.resultCode = FSL_JSON_E_UNKNOWN; | |
| 1792 | + json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert user row to JSON."); | |
| 1782 | 1793 | } |
| 1783 | 1794 | }else{ |
| 1784 | - g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; | |
| 1795 | + json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); | |
| 1785 | 1796 | } |
| 1786 | 1797 | db_finalize(&q); |
| 1787 | 1798 | return payV; |
| 1788 | 1799 | } |
| 1789 | 1800 | |
| 1790 | 1801 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -206,29 +206,35 @@ | |
| 206 | |
| 207 | C(GENERIC,"Generic error"); |
| 208 | C(INVALID_REQUEST,"Invalid request"); |
| 209 | C(UNKNOWN_COMMAND,"Unknown Command"); |
| 210 | C(UNKNOWN,"Unknown error"); |
| 211 | C(RESOURCE_NOT_FOUND,"Resource not found"); |
| 212 | C(TIMEOUT,"Timeout reached"); |
| 213 | C(ASSERT,"Assertion failed"); |
| 214 | C(ALLOC,"Resource allocation failed"); |
| 215 | C(NYI,"Not yet implemented"); |
| 216 | C(AUTH,"Authentication error"); |
| 217 | C(LOGIN_FAILED,"Login failed"); |
| 218 | C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed"); |
| 219 | C(LOGIN_FAILED_NONAME,"Login failed - name not supplied"); |
| 220 | C(LOGIN_FAILED_NOPW,"Login failed - password not supplied"); |
| 221 | C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found"); |
| 222 | C(MISSING_AUTH,"Authentication info missing from request"); |
| 223 | C(DENIED,"Access denied"); |
| 224 | C(WRONG_MODE,"Request not allowed (wrong operation mode)"); |
| 225 | |
| 226 | C(USAGE,"Usage error"); |
| 227 | C(INVALID_ARGS,"Invalid arguments"); |
| 228 | C(MISSING_ARGS,"Missing arguments"); |
| 229 | C(AMBIGUOUS_UUID,"Argument is ambiguous"); |
| 230 | |
| 231 | C(DB,"Database error"); |
| 232 | C(STMT_PREP,"Statement preparation failed"); |
| 233 | C(STMT_BIND,"Statement parameter binding failed"); |
| 234 | C(STMT_EXEC,"Statement execution/stepping failed"); |
| @@ -882,18 +888,18 @@ | |
| 882 | if( once ){ |
| 883 | return; |
| 884 | }else{ |
| 885 | once = 1; |
| 886 | } |
| 887 | g.json.jsonp = PD("jsonp",NULL) |
| 888 | /* FIXME: do some sanity checking on g.json.jsonp and ignore it |
| 889 | if it is not halfway reasonable. |
| 890 | */ |
| 891 | ; |
| 892 | g.json.isJsonMode = 1; |
| 893 | g.json.resultCode = 0; |
| 894 | g.json.cmd.offset = -1; |
| 895 | if( !g.isHTTP && g.fullHttpReply ){ |
| 896 | /* workaround for server mode, so we see it as CGI mode. */ |
| 897 | g.isHTTP = 1; |
| 898 | } |
| 899 | |
| @@ -961,11 +967,11 @@ | |
| 961 | } |
| 962 | inFile = (0==strcmp("-",jfile)) |
| 963 | ? stdin |
| 964 | : fopen(jfile,"rb"); |
| 965 | if(!inFile){ |
| 966 | g.json.resultCode = FSL_JSON_E_UNKNOWN; |
| 967 | fossil_fatal("Could not open JSON file [%s].",jfile) |
| 968 | /* Does not return. */ |
| 969 | ; |
| 970 | } |
| 971 | cgi_parse_POST_JSON(inFile, 0); |
| @@ -1555,11 +1561,12 @@ | |
| 1555 | cson_value * jv = NULL; |
| 1556 | cson_object * jo = NULL; |
| 1557 | cson_value * jv2 = NULL; |
| 1558 | cson_object * jo2 = NULL; |
| 1559 | if( !g.perm.Read ){ |
| 1560 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 1561 | return NULL; |
| 1562 | } |
| 1563 | if( g.isHTTP ){ |
| 1564 | full = json_getenv_bool("full",0); |
| 1565 | }else{ |
| @@ -1672,11 +1679,11 @@ | |
| 1672 | cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ |
| 1673 | JsonPageDef const * def; |
| 1674 | char const * cmd = json_command_arg(1+g.json.dispatchDepth); |
| 1675 | assert( NULL != pages ); |
| 1676 | if( ! cmd ){ |
| 1677 | g.json.resultCode = FSL_JSON_E_MISSING_ARGS; |
| 1678 | return NULL; |
| 1679 | } |
| 1680 | def = json_handler_for_name( cmd, pages ); |
| 1681 | if(!def){ |
| 1682 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| @@ -1700,18 +1707,21 @@ | |
| 1700 | /* |
| 1701 | ** Impl of /json/rebuild. Requires admin previleges. |
| 1702 | */ |
| 1703 | static cson_value * json_page_rebuild(){ |
| 1704 | if( !g.perm.Admin ){ |
| 1705 | g.json.resultCode = FSL_JSON_E_DENIED; |
| 1706 | return NULL; |
| 1707 | }else{ |
| 1708 | /* Reminder: the db_xxx() ops "should" fail via |
| 1709 | the fossil core error handlers, which will cause |
| 1710 | a JSON error and exit(). i.e. we don't handle |
| 1711 | the errors here. TODO: confirm that all these |
| 1712 | db routine fail gracefully in JSON mode. |
| 1713 | */ |
| 1714 | db_close(1); |
| 1715 | db_open_repository(g.zRepositoryName); |
| 1716 | db_begin_transaction(); |
| 1717 | rebuild_db(0, 0, 0); |
| @@ -1737,11 +1747,12 @@ | |
| 1737 | " mtime AS mtime" |
| 1738 | " FROM user ORDER BY login"); |
| 1739 | payV = json_stmt_to_array_of_obj(&q, NULL); |
| 1740 | db_finalize(&q); |
| 1741 | if(NULL == payV){ |
| 1742 | g.json.resultCode = FSL_JSON_E_UNKNOWN; |
| 1743 | } |
| 1744 | return payV; |
| 1745 | } |
| 1746 | |
| 1747 | /* |
| @@ -1762,11 +1773,11 @@ | |
| 1762 | if we pass the name as part of the path, so we check the path |
| 1763 | _before_ checking for name=XYZ. |
| 1764 | */; |
| 1765 | } |
| 1766 | if(!pUser || !*pUser){ |
| 1767 | g.json.resultCode = FSL_JSON_E_MISSING_ARGS; |
| 1768 | return NULL; |
| 1769 | } |
| 1770 | db_prepare(&q,"SELECT uid AS uid," |
| 1771 | " login AS name," |
| 1772 | " cap AS capabilities," |
| @@ -1776,14 +1787,14 @@ | |
| 1776 | " WHERE login=%Q", |
| 1777 | pUser); |
| 1778 | if( (SQLITE_ROW == db_step(&q)) ){ |
| 1779 | payV = cson_sqlite3_row_to_object(q.pStmt); |
| 1780 | if(!payV){ |
| 1781 | g.json.resultCode = FSL_JSON_E_UNKNOWN; |
| 1782 | } |
| 1783 | }else{ |
| 1784 | g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; |
| 1785 | } |
| 1786 | db_finalize(&q); |
| 1787 | return payV; |
| 1788 | } |
| 1789 | |
| 1790 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -206,29 +206,35 @@ | |
| 206 | |
| 207 | C(GENERIC,"Generic error"); |
| 208 | C(INVALID_REQUEST,"Invalid request"); |
| 209 | C(UNKNOWN_COMMAND,"Unknown Command"); |
| 210 | C(UNKNOWN,"Unknown error"); |
| 211 | C(TIMEOUT,"Timeout reached"); |
| 212 | C(ASSERT,"Assertion failed"); |
| 213 | C(ALLOC,"Resource allocation failed"); |
| 214 | C(NYI,"Not yet implemented"); |
| 215 | C(PANIC,"x"); |
| 216 | C(MANIFEST_READ_FAILED,"Reading artifact manifest failed."); |
| 217 | C(FILE_OPEN_FAILED,"Opening file failed."); |
| 218 | |
| 219 | C(AUTH,"Authentication error"); |
| 220 | C(MISSING_AUTH,"Authentication info missing from request"); |
| 221 | C(DENIED,"Access denied"); |
| 222 | C(WRONG_MODE,"Request not allowed (wrong operation mode)"); |
| 223 | C(LOGIN_FAILED,"Login failed"); |
| 224 | C(LOGIN_FAILED_NOSEED,"Anonymous login attempt was missing password seed"); |
| 225 | C(LOGIN_FAILED_NONAME,"Login failed - name not supplied"); |
| 226 | C(LOGIN_FAILED_NOPW,"Login failed - password not supplied"); |
| 227 | C(LOGIN_FAILED_NOTFOUND,"Login failed - no match found"); |
| 228 | |
| 229 | C(USAGE,"Usage error"); |
| 230 | C(INVALID_ARGS,"Invalid argument(s)"); |
| 231 | C(MISSING_ARGS,"Missing argument(s)"); |
| 232 | C(AMBIGUOUS_UUID,"Resource identifier is ambiguous"); |
| 233 | C(UNRESOLVED_UUID,"Provided uuid/tag/branch could not be resolved"); |
| 234 | C(RESOURCE_ALREADY_EXISTS,"Resource already exists"); |
| 235 | C(RESOURCE_NOT_FOUND,"Resource not found"); |
| 236 | |
| 237 | C(DB,"Database error"); |
| 238 | C(STMT_PREP,"Statement preparation failed"); |
| 239 | C(STMT_BIND,"Statement parameter binding failed"); |
| 240 | C(STMT_EXEC,"Statement execution/stepping failed"); |
| @@ -882,18 +888,18 @@ | |
| 888 | if( once ){ |
| 889 | return; |
| 890 | }else{ |
| 891 | once = 1; |
| 892 | } |
| 893 | g.json.isJsonMode = 1; |
| 894 | g.json.resultCode = 0; |
| 895 | g.json.cmd.offset = -1; |
| 896 | g.json.jsonp = PD("jsonp",NULL) |
| 897 | /* FIXME: do some sanity checking on g.json.jsonp and ignore it |
| 898 | if it is not halfway reasonable. |
| 899 | */ |
| 900 | ; |
| 901 | if( !g.isHTTP && g.fullHttpReply ){ |
| 902 | /* workaround for server mode, so we see it as CGI mode. */ |
| 903 | g.isHTTP = 1; |
| 904 | } |
| 905 | |
| @@ -961,11 +967,11 @@ | |
| 967 | } |
| 968 | inFile = (0==strcmp("-",jfile)) |
| 969 | ? stdin |
| 970 | : fopen(jfile,"rb"); |
| 971 | if(!inFile){ |
| 972 | g.json.resultCode = FSL_JSON_E_FILE_OPEN_FAILED; |
| 973 | fossil_fatal("Could not open JSON file [%s].",jfile) |
| 974 | /* Does not return. */ |
| 975 | ; |
| 976 | } |
| 977 | cgi_parse_POST_JSON(inFile, 0); |
| @@ -1555,11 +1561,12 @@ | |
| 1561 | cson_value * jv = NULL; |
| 1562 | cson_object * jo = NULL; |
| 1563 | cson_value * jv2 = NULL; |
| 1564 | cson_object * jo2 = NULL; |
| 1565 | if( !g.perm.Read ){ |
| 1566 | json_set_err(FSL_JSON_E_DENIED, |
| 1567 | "Requires 'o' permissions."); |
| 1568 | return NULL; |
| 1569 | } |
| 1570 | if( g.isHTTP ){ |
| 1571 | full = json_getenv_bool("full",0); |
| 1572 | }else{ |
| @@ -1672,11 +1679,11 @@ | |
| 1679 | cson_value * json_page_dispatch_helper(JsonPageDef const * pages){ |
| 1680 | JsonPageDef const * def; |
| 1681 | char const * cmd = json_command_arg(1+g.json.dispatchDepth); |
| 1682 | assert( NULL != pages ); |
| 1683 | if( ! cmd ){ |
| 1684 | json_set_err(FSL_JSON_E_MISSING_ARGS, "No subcommand specified."); |
| 1685 | return NULL; |
| 1686 | } |
| 1687 | def = json_handler_for_name( cmd, pages ); |
| 1688 | if(!def){ |
| 1689 | g.json.resultCode = FSL_JSON_E_UNKNOWN_COMMAND; |
| @@ -1700,18 +1707,21 @@ | |
| 1707 | /* |
| 1708 | ** Impl of /json/rebuild. Requires admin previleges. |
| 1709 | */ |
| 1710 | static cson_value * json_page_rebuild(){ |
| 1711 | if( !g.perm.Admin ){ |
| 1712 | json_set_err(FSL_JSON_E_DENIED,"Requires 'a' privileges."); |
| 1713 | return NULL; |
| 1714 | }else{ |
| 1715 | /* Reminder: the db_xxx() ops "should" fail via the fossil core |
| 1716 | error handlers, which will cause a JSON error and exit(). i.e. we |
| 1717 | don't handle the errors here. TODO: confirm that all these db |
| 1718 | routine fail gracefully in JSON mode. |
| 1719 | |
| 1720 | On large repos (e.g. fossil's) this operation is likely to take |
| 1721 | longer than the client timeout, which will cause it to fail (but |
| 1722 | it's sqlite3, so it'll fail gracefully). |
| 1723 | */ |
| 1724 | db_close(1); |
| 1725 | db_open_repository(g.zRepositoryName); |
| 1726 | db_begin_transaction(); |
| 1727 | rebuild_db(0, 0, 0); |
| @@ -1737,11 +1747,12 @@ | |
| 1747 | " mtime AS mtime" |
| 1748 | " FROM user ORDER BY login"); |
| 1749 | payV = json_stmt_to_array_of_obj(&q, NULL); |
| 1750 | db_finalize(&q); |
| 1751 | if(NULL == payV){ |
| 1752 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 1753 | "Could not convert user list to JSON."); |
| 1754 | } |
| 1755 | return payV; |
| 1756 | } |
| 1757 | |
| 1758 | /* |
| @@ -1762,11 +1773,11 @@ | |
| 1773 | if we pass the name as part of the path, so we check the path |
| 1774 | _before_ checking for name=XYZ. |
| 1775 | */; |
| 1776 | } |
| 1777 | if(!pUser || !*pUser){ |
| 1778 | json_set_err(FSL_JSON_E_MISSING_ARGS,"Missing 'name' property."); |
| 1779 | return NULL; |
| 1780 | } |
| 1781 | db_prepare(&q,"SELECT uid AS uid," |
| 1782 | " login AS name," |
| 1783 | " cap AS capabilities," |
| @@ -1776,14 +1787,14 @@ | |
| 1787 | " WHERE login=%Q", |
| 1788 | pUser); |
| 1789 | if( (SQLITE_ROW == db_step(&q)) ){ |
| 1790 | payV = cson_sqlite3_row_to_object(q.pStmt); |
| 1791 | if(!payV){ |
| 1792 | json_set_err(FSL_JSON_E_UNKNOWN,"Could not convert user row to JSON."); |
| 1793 | } |
| 1794 | }else{ |
| 1795 | json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,"User not found."); |
| 1796 | } |
| 1797 | db_finalize(&q); |
| 1798 | return payV; |
| 1799 | } |
| 1800 | |
| 1801 |
+1
-1
| --- src/json_artifact.c | ||
| +++ src/json_artifact.c | ||
| @@ -169,11 +169,11 @@ | ||
| 169 | 169 | json_gc_add("$EVENT_TYPE_LABEL(ticket)", eventTypeLabel, 1); |
| 170 | 170 | } |
| 171 | 171 | |
| 172 | 172 | pTktChng = manifest_get(rid, CFTYPE_TICKET); |
| 173 | 173 | if( pTktChng==0 ){ |
| 174 | - g.json.resultCode = FSL_JSON_E_UNKNOWN; | |
| 174 | + g.json.resultCode = FSL_JSON_E_MANIFEST_READ_FAILED; | |
| 175 | 175 | return NULL; |
| 176 | 176 | } |
| 177 | 177 | payV = cson_value_new_object(); |
| 178 | 178 | pay = cson_value_get_object(payV); |
| 179 | 179 | cson_object_set(pay, "eventType", eventTypeLabel ); |
| 180 | 180 |
| --- src/json_artifact.c | |
| +++ src/json_artifact.c | |
| @@ -169,11 +169,11 @@ | |
| 169 | json_gc_add("$EVENT_TYPE_LABEL(ticket)", eventTypeLabel, 1); |
| 170 | } |
| 171 | |
| 172 | pTktChng = manifest_get(rid, CFTYPE_TICKET); |
| 173 | if( pTktChng==0 ){ |
| 174 | g.json.resultCode = FSL_JSON_E_UNKNOWN; |
| 175 | return NULL; |
| 176 | } |
| 177 | payV = cson_value_new_object(); |
| 178 | pay = cson_value_get_object(payV); |
| 179 | cson_object_set(pay, "eventType", eventTypeLabel ); |
| 180 |
| --- src/json_artifact.c | |
| +++ src/json_artifact.c | |
| @@ -169,11 +169,11 @@ | |
| 169 | json_gc_add("$EVENT_TYPE_LABEL(ticket)", eventTypeLabel, 1); |
| 170 | } |
| 171 | |
| 172 | pTktChng = manifest_get(rid, CFTYPE_TICKET); |
| 173 | if( pTktChng==0 ){ |
| 174 | g.json.resultCode = FSL_JSON_E_MANIFEST_READ_FAILED; |
| 175 | return NULL; |
| 176 | } |
| 177 | payV = cson_value_new_object(); |
| 178 | pay = cson_value_get_object(payV); |
| 179 | cson_object_set(pay, "eventType", eventTypeLabel ); |
| 180 |
+34
-31
| --- src/json_detail.h | ||
| +++ src/json_detail.h | ||
| @@ -36,51 +36,54 @@ | ||
| 36 | 36 | ** value. |
| 37 | 37 | ** |
| 38 | 38 | */ |
| 39 | 39 | enum FossilJsonCodes { |
| 40 | 40 | FSL_JSON_W_START = 0, |
| 41 | -FSL_JSON_W_UNKNOWN = FSL_JSON_W_START + 1, | |
| 42 | -FSL_JSON_W_ROW_TO_JSON_FAILED = FSL_JSON_W_START + 2, | |
| 43 | -FSL_JSON_W_COL_TO_JSON_FAILED = FSL_JSON_W_START + 3, | |
| 44 | -FSL_JSON_W_STRING_TO_ARRAY_FAILED = FSL_JSON_W_START + 4, | |
| 41 | +FSL_JSON_W_UNKNOWN /*+1*/, | |
| 42 | +FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, | |
| 43 | +FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/, | |
| 44 | +FSL_JSON_W_STRING_TO_ARRAY_FAILED /*+4*/, | |
| 45 | 45 | |
| 46 | 46 | FSL_JSON_W_END = 1000, |
| 47 | 47 | FSL_JSON_E_GENERIC = 1000, |
| 48 | 48 | FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100, |
| 49 | -FSL_JSON_E_INVALID_REQUEST = FSL_JSON_E_GENERIC_SUB1 + 1, | |
| 50 | -FSL_JSON_E_UNKNOWN_COMMAND = FSL_JSON_E_GENERIC_SUB1 + 2, | |
| 51 | -FSL_JSON_E_UNKNOWN = FSL_JSON_E_GENERIC_SUB1 + 3, | |
| 52 | -FSL_JSON_E_RESOURCE_NOT_FOUND = FSL_JSON_E_GENERIC_SUB1 + 4, | |
| 53 | -FSL_JSON_E_TIMEOUT = FSL_JSON_E_GENERIC_SUB1 + 5, | |
| 54 | -FSL_JSON_E_ASSERT = FSL_JSON_E_GENERIC_SUB1 + 6, | |
| 55 | -FSL_JSON_E_ALLOC = FSL_JSON_E_GENERIC_SUB1 + 7, | |
| 56 | -FSL_JSON_E_NYI = FSL_JSON_E_GENERIC_SUB1 + 8, | |
| 57 | -FSL_JSON_E_PANIC = FSL_JSON_E_GENERIC_SUB1 + 9, | |
| 49 | +FSL_JSON_E_INVALID_REQUEST /*+1*/, | |
| 50 | +FSL_JSON_E_UNKNOWN_COMMAND /*+2*/, | |
| 51 | +FSL_JSON_E_UNKNOWN /*+3*/, | |
| 52 | +/*REUSE: +4*/ | |
| 53 | +FSL_JSON_E_TIMEOUT /*+5*/, | |
| 54 | +FSL_JSON_E_ASSERT /*+6*/, | |
| 55 | +FSL_JSON_E_ALLOC /*+7*/, | |
| 56 | +FSL_JSON_E_NYI /*+8*/, | |
| 57 | +FSL_JSON_E_PANIC /*+9*/, | |
| 58 | +FSL_JSON_E_MANIFEST_READ_FAILED /*+10*/, | |
| 59 | +FSL_JSON_E_FILE_OPEN_FAILED /*+11*/, | |
| 58 | 60 | |
| 59 | 61 | FSL_JSON_E_AUTH = 2000, |
| 60 | -FSL_JSON_E_MISSING_AUTH = FSL_JSON_E_AUTH + 1, | |
| 61 | -FSL_JSON_E_DENIED = FSL_JSON_E_AUTH + 2, | |
| 62 | -FSL_JSON_E_WRONG_MODE = FSL_JSON_E_AUTH + 3, | |
| 63 | -FSL_JSON_E_RESOURCE_ALREADY_EXISTS = FSL_JSON_E_AUTH + 4, | |
| 64 | - | |
| 65 | -FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH + 100, | |
| 66 | -FSL_JSON_E_LOGIN_FAILED_NOSEED = FSL_JSON_E_LOGIN_FAILED + 1, | |
| 67 | -FSL_JSON_E_LOGIN_FAILED_NONAME = FSL_JSON_E_LOGIN_FAILED + 2, | |
| 68 | -FSL_JSON_E_LOGIN_FAILED_NOPW = FSL_JSON_E_LOGIN_FAILED + 3, | |
| 69 | -FSL_JSON_E_LOGIN_FAILED_NOTFOUND = FSL_JSON_E_LOGIN_FAILED + 4, | |
| 62 | +FSL_JSON_E_MISSING_AUTH /*+1*/, | |
| 63 | +FSL_JSON_E_DENIED /*+2*/, | |
| 64 | +FSL_JSON_E_WRONG_MODE /*+3*/, | |
| 65 | + | |
| 66 | +FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH +100, | |
| 67 | +FSL_JSON_E_LOGIN_FAILED_NOSEED /*+1*/, | |
| 68 | +FSL_JSON_E_LOGIN_FAILED_NONAME /*+2*/, | |
| 69 | +FSL_JSON_E_LOGIN_FAILED_NOPW /*+3*/, | |
| 70 | +FSL_JSON_E_LOGIN_FAILED_NOTFOUND /*+4*/, | |
| 70 | 71 | |
| 71 | 72 | FSL_JSON_E_USAGE = 3000, |
| 72 | -FSL_JSON_E_INVALID_ARGS = FSL_JSON_E_USAGE + 1, | |
| 73 | -FSL_JSON_E_MISSING_ARGS = FSL_JSON_E_USAGE + 2, | |
| 74 | -FSL_JSON_E_AMBIGUOUS_UUID = FSL_JSON_E_USAGE + 3, | |
| 75 | - | |
| 73 | +FSL_JSON_E_INVALID_ARGS /*+1*/, | |
| 74 | +FSL_JSON_E_MISSING_ARGS /*+2*/, | |
| 75 | +FSL_JSON_E_AMBIGUOUS_UUID /*+3*/, | |
| 76 | +FSL_JSON_E_UNRESOLVED_UUID /*+4*/, | |
| 77 | +FSL_JSON_E_RESOURCE_ALREADY_EXISTS /*+5*/, | |
| 78 | +FSL_JSON_E_RESOURCE_NOT_FOUND /*+6*/, | |
| 76 | 79 | |
| 77 | 80 | FSL_JSON_E_DB = 4000, |
| 78 | -FSL_JSON_E_STMT_PREP = FSL_JSON_E_DB + 1, | |
| 79 | -FSL_JSON_E_STMT_BIND = FSL_JSON_E_DB + 2, | |
| 80 | -FSL_JSON_E_STMT_EXEC = FSL_JSON_E_DB + 3, | |
| 81 | -FSL_JSON_E_DB_LOCKED = FSL_JSON_E_DB + 4, | |
| 81 | +FSL_JSON_E_STMT_PREP /*+1*/, | |
| 82 | +FSL_JSON_E_STMT_BIND /*+2*/, | |
| 83 | +FSL_JSON_E_STMT_EXEC /*+3*/, | |
| 84 | +FSL_JSON_E_DB_LOCKED /*+4*/, | |
| 82 | 85 | |
| 83 | 86 | FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101 |
| 84 | 87 | |
| 85 | 88 | }; |
| 86 | 89 | |
| 87 | 90 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -36,51 +36,54 @@ | |
| 36 | ** value. |
| 37 | ** |
| 38 | */ |
| 39 | enum FossilJsonCodes { |
| 40 | FSL_JSON_W_START = 0, |
| 41 | FSL_JSON_W_UNKNOWN = FSL_JSON_W_START + 1, |
| 42 | FSL_JSON_W_ROW_TO_JSON_FAILED = FSL_JSON_W_START + 2, |
| 43 | FSL_JSON_W_COL_TO_JSON_FAILED = FSL_JSON_W_START + 3, |
| 44 | FSL_JSON_W_STRING_TO_ARRAY_FAILED = FSL_JSON_W_START + 4, |
| 45 | |
| 46 | FSL_JSON_W_END = 1000, |
| 47 | FSL_JSON_E_GENERIC = 1000, |
| 48 | FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100, |
| 49 | FSL_JSON_E_INVALID_REQUEST = FSL_JSON_E_GENERIC_SUB1 + 1, |
| 50 | FSL_JSON_E_UNKNOWN_COMMAND = FSL_JSON_E_GENERIC_SUB1 + 2, |
| 51 | FSL_JSON_E_UNKNOWN = FSL_JSON_E_GENERIC_SUB1 + 3, |
| 52 | FSL_JSON_E_RESOURCE_NOT_FOUND = FSL_JSON_E_GENERIC_SUB1 + 4, |
| 53 | FSL_JSON_E_TIMEOUT = FSL_JSON_E_GENERIC_SUB1 + 5, |
| 54 | FSL_JSON_E_ASSERT = FSL_JSON_E_GENERIC_SUB1 + 6, |
| 55 | FSL_JSON_E_ALLOC = FSL_JSON_E_GENERIC_SUB1 + 7, |
| 56 | FSL_JSON_E_NYI = FSL_JSON_E_GENERIC_SUB1 + 8, |
| 57 | FSL_JSON_E_PANIC = FSL_JSON_E_GENERIC_SUB1 + 9, |
| 58 | |
| 59 | FSL_JSON_E_AUTH = 2000, |
| 60 | FSL_JSON_E_MISSING_AUTH = FSL_JSON_E_AUTH + 1, |
| 61 | FSL_JSON_E_DENIED = FSL_JSON_E_AUTH + 2, |
| 62 | FSL_JSON_E_WRONG_MODE = FSL_JSON_E_AUTH + 3, |
| 63 | FSL_JSON_E_RESOURCE_ALREADY_EXISTS = FSL_JSON_E_AUTH + 4, |
| 64 | |
| 65 | FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH + 100, |
| 66 | FSL_JSON_E_LOGIN_FAILED_NOSEED = FSL_JSON_E_LOGIN_FAILED + 1, |
| 67 | FSL_JSON_E_LOGIN_FAILED_NONAME = FSL_JSON_E_LOGIN_FAILED + 2, |
| 68 | FSL_JSON_E_LOGIN_FAILED_NOPW = FSL_JSON_E_LOGIN_FAILED + 3, |
| 69 | FSL_JSON_E_LOGIN_FAILED_NOTFOUND = FSL_JSON_E_LOGIN_FAILED + 4, |
| 70 | |
| 71 | FSL_JSON_E_USAGE = 3000, |
| 72 | FSL_JSON_E_INVALID_ARGS = FSL_JSON_E_USAGE + 1, |
| 73 | FSL_JSON_E_MISSING_ARGS = FSL_JSON_E_USAGE + 2, |
| 74 | FSL_JSON_E_AMBIGUOUS_UUID = FSL_JSON_E_USAGE + 3, |
| 75 | |
| 76 | |
| 77 | FSL_JSON_E_DB = 4000, |
| 78 | FSL_JSON_E_STMT_PREP = FSL_JSON_E_DB + 1, |
| 79 | FSL_JSON_E_STMT_BIND = FSL_JSON_E_DB + 2, |
| 80 | FSL_JSON_E_STMT_EXEC = FSL_JSON_E_DB + 3, |
| 81 | FSL_JSON_E_DB_LOCKED = FSL_JSON_E_DB + 4, |
| 82 | |
| 83 | FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101 |
| 84 | |
| 85 | }; |
| 86 | |
| 87 |
| --- src/json_detail.h | |
| +++ src/json_detail.h | |
| @@ -36,51 +36,54 @@ | |
| 36 | ** value. |
| 37 | ** |
| 38 | */ |
| 39 | enum FossilJsonCodes { |
| 40 | FSL_JSON_W_START = 0, |
| 41 | FSL_JSON_W_UNKNOWN /*+1*/, |
| 42 | FSL_JSON_W_ROW_TO_JSON_FAILED /*+2*/, |
| 43 | FSL_JSON_W_COL_TO_JSON_FAILED /*+3*/, |
| 44 | FSL_JSON_W_STRING_TO_ARRAY_FAILED /*+4*/, |
| 45 | |
| 46 | FSL_JSON_W_END = 1000, |
| 47 | FSL_JSON_E_GENERIC = 1000, |
| 48 | FSL_JSON_E_GENERIC_SUB1 = FSL_JSON_E_GENERIC + 100, |
| 49 | FSL_JSON_E_INVALID_REQUEST /*+1*/, |
| 50 | FSL_JSON_E_UNKNOWN_COMMAND /*+2*/, |
| 51 | FSL_JSON_E_UNKNOWN /*+3*/, |
| 52 | /*REUSE: +4*/ |
| 53 | FSL_JSON_E_TIMEOUT /*+5*/, |
| 54 | FSL_JSON_E_ASSERT /*+6*/, |
| 55 | FSL_JSON_E_ALLOC /*+7*/, |
| 56 | FSL_JSON_E_NYI /*+8*/, |
| 57 | FSL_JSON_E_PANIC /*+9*/, |
| 58 | FSL_JSON_E_MANIFEST_READ_FAILED /*+10*/, |
| 59 | FSL_JSON_E_FILE_OPEN_FAILED /*+11*/, |
| 60 | |
| 61 | FSL_JSON_E_AUTH = 2000, |
| 62 | FSL_JSON_E_MISSING_AUTH /*+1*/, |
| 63 | FSL_JSON_E_DENIED /*+2*/, |
| 64 | FSL_JSON_E_WRONG_MODE /*+3*/, |
| 65 | |
| 66 | FSL_JSON_E_LOGIN_FAILED = FSL_JSON_E_AUTH +100, |
| 67 | FSL_JSON_E_LOGIN_FAILED_NOSEED /*+1*/, |
| 68 | FSL_JSON_E_LOGIN_FAILED_NONAME /*+2*/, |
| 69 | FSL_JSON_E_LOGIN_FAILED_NOPW /*+3*/, |
| 70 | FSL_JSON_E_LOGIN_FAILED_NOTFOUND /*+4*/, |
| 71 | |
| 72 | FSL_JSON_E_USAGE = 3000, |
| 73 | FSL_JSON_E_INVALID_ARGS /*+1*/, |
| 74 | FSL_JSON_E_MISSING_ARGS /*+2*/, |
| 75 | FSL_JSON_E_AMBIGUOUS_UUID /*+3*/, |
| 76 | FSL_JSON_E_UNRESOLVED_UUID /*+4*/, |
| 77 | FSL_JSON_E_RESOURCE_ALREADY_EXISTS /*+5*/, |
| 78 | FSL_JSON_E_RESOURCE_NOT_FOUND /*+6*/, |
| 79 | |
| 80 | FSL_JSON_E_DB = 4000, |
| 81 | FSL_JSON_E_STMT_PREP /*+1*/, |
| 82 | FSL_JSON_E_STMT_BIND /*+2*/, |
| 83 | FSL_JSON_E_STMT_EXEC /*+3*/, |
| 84 | FSL_JSON_E_DB_LOCKED /*+4*/, |
| 85 | |
| 86 | FSL_JSON_E_DB_NEEDS_REBUILD = FSL_JSON_E_DB + 101 |
| 87 | |
| 88 | }; |
| 89 | |
| 90 |
+21
-12
| --- src/json_timeline.c | ||
| +++ src/json_timeline.c | ||
| @@ -401,18 +401,20 @@ | ||
| 401 | 401 | } |
| 402 | 402 | payV = cson_value_new_object(); |
| 403 | 403 | pay = cson_value_get_object(payV); |
| 404 | 404 | check = json_timeline_setup_sql( "ci", &sql, pay ); |
| 405 | 405 | if(check){ |
| 406 | - g.json.resultCode = check; | |
| 406 | + json_set_err(check, "Query initialization failed."); | |
| 407 | 407 | goto error; |
| 408 | 408 | } |
| 409 | 409 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 410 | - g.json.resultCode = (cson_rc.AllocError==check) \ | |
| 411 | - ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ | |
| 410 | + json_set_err((cson_rc.AllocError==check) \ | |
| 411 | + ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN,\ | |
| 412 | + "Object property insertion failed"); \ | |
| 412 | 413 | goto error;\ |
| 413 | - } | |
| 414 | + } (void)0 | |
| 415 | + | |
| 414 | 416 | #if 0 |
| 415 | 417 | /* only for testing! */ |
| 416 | 418 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 417 | 419 | SET("timelineSql"); |
| 418 | 420 | #endif |
| @@ -489,19 +491,20 @@ | ||
| 489 | 491 | } |
| 490 | 492 | payV = cson_value_new_object(); |
| 491 | 493 | pay = cson_value_get_object(payV); |
| 492 | 494 | check = json_timeline_setup_sql( "w", &sql, pay ); |
| 493 | 495 | if(check){ |
| 494 | - g.json.resultCode = check; | |
| 496 | + json_set_err(check, "Query initialization failed."); | |
| 495 | 497 | goto error; |
| 496 | 498 | } |
| 497 | 499 | |
| 498 | 500 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 499 | - g.json.resultCode = (cson_rc.AllocError==check) \ | |
| 500 | - ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ | |
| 501 | + json_set_err((cson_rc.AllocError==check) \ | |
| 502 | + ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN, \ | |
| 503 | + "Object property insertion failed."); \ | |
| 501 | 504 | goto error;\ |
| 502 | - } | |
| 505 | + } (void)0 | |
| 503 | 506 | #if 0 |
| 504 | 507 | /* only for testing! */ |
| 505 | 508 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 506 | 509 | SET("timelineSql"); |
| 507 | 510 | #endif |
| @@ -562,28 +565,34 @@ | ||
| 562 | 565 | } |
| 563 | 566 | payV = cson_value_new_object(); |
| 564 | 567 | pay = cson_value_get_object(payV); |
| 565 | 568 | check = json_timeline_setup_sql( "t", &sql, pay ); |
| 566 | 569 | if(check){ |
| 567 | - g.json.resultCode = check; | |
| 570 | + json_set_err(check, "Query initialization failed."); | |
| 568 | 571 | goto error; |
| 569 | 572 | } |
| 570 | 573 | |
| 571 | 574 | db_multi_exec(blob_buffer(&sql)); |
| 572 | 575 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 573 | - g.json.resultCode = (cson_rc.AllocError==check) \ | |
| 574 | - ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ | |
| 576 | + json_set_err((cson_rc.AllocError==check) \ | |
| 577 | + ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN, \ | |
| 578 | + "Object property insertion failed."); \ | |
| 575 | 579 | goto error;\ |
| 576 | - } | |
| 580 | + } (void)0 | |
| 577 | 581 | |
| 578 | 582 | #if 0 |
| 579 | 583 | /* only for testing! */ |
| 580 | 584 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 581 | 585 | SET("timelineSql"); |
| 582 | 586 | #endif |
| 583 | 587 | |
| 584 | 588 | blob_reset(&sql); |
| 589 | + /* | |
| 590 | + REMINDER/FIXME(?): we have both uuid (the change uuid?) and | |
| 591 | + ticketUuid (the actual ticket). This is different from the wiki | |
| 592 | + timeline, where we only have the wiki page uuid. | |
| 593 | + */ | |
| 585 | 594 | db_prepare(&q, "SELECT rid AS rid," |
| 586 | 595 | " uuid AS uuid," |
| 587 | 596 | " mtime AS timestamp," |
| 588 | 597 | #if 0 |
| 589 | 598 | " timestampString AS timestampString," |
| 590 | 599 |
| --- src/json_timeline.c | |
| +++ src/json_timeline.c | |
| @@ -401,18 +401,20 @@ | |
| 401 | } |
| 402 | payV = cson_value_new_object(); |
| 403 | pay = cson_value_get_object(payV); |
| 404 | check = json_timeline_setup_sql( "ci", &sql, pay ); |
| 405 | if(check){ |
| 406 | g.json.resultCode = check; |
| 407 | goto error; |
| 408 | } |
| 409 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 410 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 411 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 412 | goto error;\ |
| 413 | } |
| 414 | #if 0 |
| 415 | /* only for testing! */ |
| 416 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 417 | SET("timelineSql"); |
| 418 | #endif |
| @@ -489,19 +491,20 @@ | |
| 489 | } |
| 490 | payV = cson_value_new_object(); |
| 491 | pay = cson_value_get_object(payV); |
| 492 | check = json_timeline_setup_sql( "w", &sql, pay ); |
| 493 | if(check){ |
| 494 | g.json.resultCode = check; |
| 495 | goto error; |
| 496 | } |
| 497 | |
| 498 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 499 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 500 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 501 | goto error;\ |
| 502 | } |
| 503 | #if 0 |
| 504 | /* only for testing! */ |
| 505 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 506 | SET("timelineSql"); |
| 507 | #endif |
| @@ -562,28 +565,34 @@ | |
| 562 | } |
| 563 | payV = cson_value_new_object(); |
| 564 | pay = cson_value_get_object(payV); |
| 565 | check = json_timeline_setup_sql( "t", &sql, pay ); |
| 566 | if(check){ |
| 567 | g.json.resultCode = check; |
| 568 | goto error; |
| 569 | } |
| 570 | |
| 571 | db_multi_exec(blob_buffer(&sql)); |
| 572 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 573 | g.json.resultCode = (cson_rc.AllocError==check) \ |
| 574 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN; \ |
| 575 | goto error;\ |
| 576 | } |
| 577 | |
| 578 | #if 0 |
| 579 | /* only for testing! */ |
| 580 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 581 | SET("timelineSql"); |
| 582 | #endif |
| 583 | |
| 584 | blob_reset(&sql); |
| 585 | db_prepare(&q, "SELECT rid AS rid," |
| 586 | " uuid AS uuid," |
| 587 | " mtime AS timestamp," |
| 588 | #if 0 |
| 589 | " timestampString AS timestampString," |
| 590 |
| --- src/json_timeline.c | |
| +++ src/json_timeline.c | |
| @@ -401,18 +401,20 @@ | |
| 401 | } |
| 402 | payV = cson_value_new_object(); |
| 403 | pay = cson_value_get_object(payV); |
| 404 | check = json_timeline_setup_sql( "ci", &sql, pay ); |
| 405 | if(check){ |
| 406 | json_set_err(check, "Query initialization failed."); |
| 407 | goto error; |
| 408 | } |
| 409 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 410 | json_set_err((cson_rc.AllocError==check) \ |
| 411 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN,\ |
| 412 | "Object property insertion failed"); \ |
| 413 | goto error;\ |
| 414 | } (void)0 |
| 415 | |
| 416 | #if 0 |
| 417 | /* only for testing! */ |
| 418 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 419 | SET("timelineSql"); |
| 420 | #endif |
| @@ -489,19 +491,20 @@ | |
| 491 | } |
| 492 | payV = cson_value_new_object(); |
| 493 | pay = cson_value_get_object(payV); |
| 494 | check = json_timeline_setup_sql( "w", &sql, pay ); |
| 495 | if(check){ |
| 496 | json_set_err(check, "Query initialization failed."); |
| 497 | goto error; |
| 498 | } |
| 499 | |
| 500 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 501 | json_set_err((cson_rc.AllocError==check) \ |
| 502 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN, \ |
| 503 | "Object property insertion failed."); \ |
| 504 | goto error;\ |
| 505 | } (void)0 |
| 506 | #if 0 |
| 507 | /* only for testing! */ |
| 508 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 509 | SET("timelineSql"); |
| 510 | #endif |
| @@ -562,28 +565,34 @@ | |
| 565 | } |
| 566 | payV = cson_value_new_object(); |
| 567 | pay = cson_value_get_object(payV); |
| 568 | check = json_timeline_setup_sql( "t", &sql, pay ); |
| 569 | if(check){ |
| 570 | json_set_err(check, "Query initialization failed."); |
| 571 | goto error; |
| 572 | } |
| 573 | |
| 574 | db_multi_exec(blob_buffer(&sql)); |
| 575 | #define SET(K) if(0!=(check=cson_object_set(pay,K,tmp))){ \ |
| 576 | json_set_err((cson_rc.AllocError==check) \ |
| 577 | ? FSL_JSON_E_ALLOC : FSL_JSON_E_UNKNOWN, \ |
| 578 | "Object property insertion failed."); \ |
| 579 | goto error;\ |
| 580 | } (void)0 |
| 581 | |
| 582 | #if 0 |
| 583 | /* only for testing! */ |
| 584 | tmp = cson_value_new_string(blob_buffer(&sql),strlen(blob_buffer(&sql))); |
| 585 | SET("timelineSql"); |
| 586 | #endif |
| 587 | |
| 588 | blob_reset(&sql); |
| 589 | /* |
| 590 | REMINDER/FIXME(?): we have both uuid (the change uuid?) and |
| 591 | ticketUuid (the actual ticket). This is different from the wiki |
| 592 | timeline, where we only have the wiki page uuid. |
| 593 | */ |
| 594 | db_prepare(&q, "SELECT rid AS rid," |
| 595 | " uuid AS uuid," |
| 596 | " mtime AS timestamp," |
| 597 | #if 0 |
| 598 | " timestampString AS timestampString," |
| 599 |
+76
-62
| --- src/json_wiki.c | ||
| +++ src/json_wiki.c | ||
| @@ -47,10 +47,79 @@ | ||
| 47 | 47 | */ |
| 48 | 48 | cson_value * json_page_wiki(){ |
| 49 | 49 | return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | + | |
| 53 | +/* | |
| 54 | +** Loads the given wiki page and creates a JSON object representation | |
| 55 | +** of it. If the page is not found then NULL is returned. If doParse | |
| 56 | +** is true then the page content is HTML-ized using fossil's | |
| 57 | +** conventional wiki format, else it is not parsed. | |
| 58 | +** | |
| 59 | +** The returned value, if not NULL, is-a JSON Object owned by the | |
| 60 | +** caller. | |
| 61 | +*/ | |
| 62 | +cson_value * json_get_wiki_page(char const * zPageName, char doParse){ | |
| 63 | + int rid; | |
| 64 | + Manifest *pWiki = 0; | |
| 65 | + char const * zBody = NULL; | |
| 66 | + char const * zFormat = NULL; | |
| 67 | + char * zUuid = NULL; | |
| 68 | + Stmt q; | |
| 69 | + db_prepare(&q, | |
| 70 | + "SELECT x.rid, b.uuid FROM tag t, tagxref x, blob b" | |
| 71 | + " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' " | |
| 72 | + " AND b.rid=x.rid" | |
| 73 | + " ORDER BY x.mtime DESC LIMIT 1", | |
| 74 | + zPageName | |
| 75 | + ); | |
| 76 | + if( (SQLITE_ROW != db_step(&q)) ){ | |
| 77 | + return NULL; | |
| 78 | + } | |
| 79 | + rid = db_column_int(&q,0); | |
| 80 | + zUuid = db_column_malloc(&q,1); | |
| 81 | + db_finalize(&q); | |
| 82 | + if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ | |
| 83 | + zBody = pWiki->zWiki; | |
| 84 | + } | |
| 85 | + | |
| 86 | + { | |
| 87 | + unsigned int len; | |
| 88 | + cson_value * payV = cson_value_new_object(); | |
| 89 | + cson_object * pay = cson_value_get_object(payV); | |
| 90 | + cson_object_set(pay,"name",json_new_string(zPageName)); | |
| 91 | + cson_object_set(pay,"uuid",json_new_string(zUuid)); | |
| 92 | + free(zUuid); | |
| 93 | + zUuid = NULL; | |
| 94 | + cson_object_set(pay,"rid",json_new_int((cson_int_t)rid)); | |
| 95 | + cson_object_set(pay,"lastSavedBy",json_new_string(pWiki->zUser)); | |
| 96 | + cson_object_set(pay,FossilJsonKeys.timestamp, json_julian_to_timestamp(pWiki->rDate)); | |
| 97 | + cson_object_set(pay,"contentFormat",json_new_string(zFormat)); | |
| 98 | + if( doParse ){ | |
| 99 | + Blob content = empty_blob; | |
| 100 | + Blob raw = empty_blob; | |
| 101 | + blob_append(&raw,zBody,-1); | |
| 102 | + wiki_convert(&raw,&content,0); | |
| 103 | + len = strlen(zBody); | |
| 104 | + len = (unsigned int)blob_size(&content); | |
| 105 | + cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); | |
| 106 | + cson_object_set(pay,"content", | |
| 107 | + cson_value_new_string(blob_buffer(&content),len)); | |
| 108 | + blob_reset(&content); | |
| 109 | + blob_reset(&raw); | |
| 110 | + }else{ | |
| 111 | + len = zBody ? strlen(zBody) : 0; | |
| 112 | + cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); | |
| 113 | + cson_object_set(pay,"content",cson_value_new_string(zBody,len)); | |
| 114 | + } | |
| 115 | + /*TODO: add 'T' (tag) fields*/ | |
| 116 | + /*TODO: add the 'A' card (file attachment) entries?*/ | |
| 117 | + manifest_destroy(pWiki); | |
| 118 | + return payV; | |
| 119 | + } | |
| 120 | +} | |
| 52 | 121 | |
| 53 | 122 | /* |
| 54 | 123 | ** Implementation of /json/wiki/get. |
| 55 | 124 | ** |
| 56 | 125 | */ |
| @@ -89,72 +158,11 @@ | ||
| 89 | 158 | zFormat = "raw"; |
| 90 | 159 | } |
| 91 | 160 | if( 'r' != *zFormat ){ |
| 92 | 161 | zFormat = "html"; |
| 93 | 162 | } |
| 94 | - db_prepare(&q, | |
| 95 | - "SELECT x.rid, b.uuid FROM tag t, tagxref x, blob b" | |
| 96 | - " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' " | |
| 97 | - " AND b.rid=x.rid" | |
| 98 | - " ORDER BY x.mtime DESC LIMIT 1", | |
| 99 | - zPageName | |
| 100 | - ); | |
| 101 | - if( (SQLITE_ROW != db_step(&q)) ){ | |
| 102 | - manifest_destroy(pWiki); | |
| 103 | - json_set_err(FSL_JSON_E_UNKNOWN, | |
| 104 | - "Error reading wiki page manifest."); | |
| 105 | - return NULL; | |
| 106 | - } | |
| 107 | - rid = db_column_int(&q,0); | |
| 108 | - zUuid = db_column_malloc(&q,1); | |
| 109 | - db_finalize(&q); | |
| 110 | - | |
| 111 | - if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ | |
| 112 | - zBody = pWiki->zWiki; | |
| 113 | - } | |
| 114 | - if( zBody==0 ){ | |
| 115 | - manifest_destroy(pWiki); | |
| 116 | - free(zUuid); | |
| 117 | - json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, | |
| 118 | - "Wiki body is empty (is that possible?)"); | |
| 119 | - return NULL; | |
| 120 | - } | |
| 121 | - | |
| 122 | - { | |
| 123 | - unsigned int len; | |
| 124 | - cson_value * payV = cson_value_new_object(); | |
| 125 | - cson_object * pay = cson_value_get_object(payV); | |
| 126 | - cson_object_set(pay,"name",json_new_string(zPageName)); | |
| 127 | - cson_object_set(pay,"uuid",json_new_string(zUuid)); | |
| 128 | - free(zUuid); | |
| 129 | - zUuid = NULL; | |
| 130 | - cson_object_set(pay,"rid",json_new_int((cson_int_t)rid)); | |
| 131 | - cson_object_set(pay,"lastSavedBy",json_new_string(pWiki->zUser)); | |
| 132 | - cson_object_set(pay,FossilJsonKeys.timestamp, json_julian_to_timestamp(pWiki->rDate)); | |
| 133 | - cson_object_set(pay,"contentFormat",json_new_string(zFormat)); | |
| 134 | - if( ('h'==*zFormat) ){ | |
| 135 | - Blob content = empty_blob; | |
| 136 | - Blob raw = empty_blob; | |
| 137 | - blob_append(&raw,zBody,-1); | |
| 138 | - wiki_convert(&raw,&content,0); | |
| 139 | - len = strlen(zBody); | |
| 140 | - len = (unsigned int)blob_size(&content); | |
| 141 | - cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); | |
| 142 | - cson_object_set(pay,"content", | |
| 143 | - cson_value_new_string(blob_buffer(&content),len)); | |
| 144 | - blob_reset(&content); | |
| 145 | - blob_reset(&raw); | |
| 146 | - }else{ | |
| 147 | - len = strlen(zBody); | |
| 148 | - cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); | |
| 149 | - cson_object_set(pay,"content",cson_value_new_string(zBody,len)); | |
| 150 | - } | |
| 151 | - /*TODO: add 'T' (tag) fields*/ | |
| 152 | - /*TODO: add the 'A' card (file attachment) entries?*/ | |
| 153 | - manifest_destroy(pWiki); | |
| 154 | - return payV; | |
| 155 | - } | |
| 163 | + return json_get_wiki_page(zPageName, 'h'==*zFormat); | |
| 156 | 164 | } |
| 157 | 165 | |
| 158 | 166 | /* |
| 159 | 167 | ** Internal impl of /wiki/save and /wiki/create. If createMode is 0 |
| 160 | 168 | ** and the page already exists then a |
| @@ -306,21 +314,27 @@ | ||
| 306 | 314 | listV = cson_value_new_array(); |
| 307 | 315 | list = cson_value_get_array(listV); |
| 308 | 316 | while( SQLITE_ROW == db_step(&q) ){ |
| 309 | 317 | cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0); |
| 310 | 318 | if(!v){ |
| 319 | + json_set_err(FSL_JSON_E_UNKNOWN, | |
| 320 | + "Could not convert wiki name column to JSON."); | |
| 311 | 321 | goto error; |
| 312 | 322 | }else if( 0 != cson_array_append( list, v ) ){ |
| 313 | 323 | cson_value_free(v); |
| 324 | + json_set_err(FSL_JSON_E_ALLOC,"Could not append wiki page name to array.") | |
| 325 | + /* OOM (or maybe numeric overflow) are the only realistic | |
| 326 | + error codes for that particular failure.*/; | |
| 314 | 327 | goto error; |
| 315 | 328 | } |
| 316 | 329 | } |
| 317 | 330 | goto end; |
| 318 | 331 | error: |
| 332 | + assert(0 != g.json.resultCode); | |
| 319 | 333 | cson_value_free(listV); |
| 320 | 334 | listV = NULL; |
| 321 | 335 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 322 | 336 | "Error creating wiki page list."); |
| 323 | 337 | end: |
| 324 | 338 | db_finalize(&q); |
| 325 | 339 | return listV; |
| 326 | 340 | } |
| 327 | 341 |
| --- src/json_wiki.c | |
| +++ src/json_wiki.c | |
| @@ -47,10 +47,79 @@ | |
| 47 | */ |
| 48 | cson_value * json_page_wiki(){ |
| 49 | return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | /* |
| 54 | ** Implementation of /json/wiki/get. |
| 55 | ** |
| 56 | */ |
| @@ -89,72 +158,11 @@ | |
| 89 | zFormat = "raw"; |
| 90 | } |
| 91 | if( 'r' != *zFormat ){ |
| 92 | zFormat = "html"; |
| 93 | } |
| 94 | db_prepare(&q, |
| 95 | "SELECT x.rid, b.uuid FROM tag t, tagxref x, blob b" |
| 96 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' " |
| 97 | " AND b.rid=x.rid" |
| 98 | " ORDER BY x.mtime DESC LIMIT 1", |
| 99 | zPageName |
| 100 | ); |
| 101 | if( (SQLITE_ROW != db_step(&q)) ){ |
| 102 | manifest_destroy(pWiki); |
| 103 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 104 | "Error reading wiki page manifest."); |
| 105 | return NULL; |
| 106 | } |
| 107 | rid = db_column_int(&q,0); |
| 108 | zUuid = db_column_malloc(&q,1); |
| 109 | db_finalize(&q); |
| 110 | |
| 111 | if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ |
| 112 | zBody = pWiki->zWiki; |
| 113 | } |
| 114 | if( zBody==0 ){ |
| 115 | manifest_destroy(pWiki); |
| 116 | free(zUuid); |
| 117 | json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND, |
| 118 | "Wiki body is empty (is that possible?)"); |
| 119 | return NULL; |
| 120 | } |
| 121 | |
| 122 | { |
| 123 | unsigned int len; |
| 124 | cson_value * payV = cson_value_new_object(); |
| 125 | cson_object * pay = cson_value_get_object(payV); |
| 126 | cson_object_set(pay,"name",json_new_string(zPageName)); |
| 127 | cson_object_set(pay,"uuid",json_new_string(zUuid)); |
| 128 | free(zUuid); |
| 129 | zUuid = NULL; |
| 130 | cson_object_set(pay,"rid",json_new_int((cson_int_t)rid)); |
| 131 | cson_object_set(pay,"lastSavedBy",json_new_string(pWiki->zUser)); |
| 132 | cson_object_set(pay,FossilJsonKeys.timestamp, json_julian_to_timestamp(pWiki->rDate)); |
| 133 | cson_object_set(pay,"contentFormat",json_new_string(zFormat)); |
| 134 | if( ('h'==*zFormat) ){ |
| 135 | Blob content = empty_blob; |
| 136 | Blob raw = empty_blob; |
| 137 | blob_append(&raw,zBody,-1); |
| 138 | wiki_convert(&raw,&content,0); |
| 139 | len = strlen(zBody); |
| 140 | len = (unsigned int)blob_size(&content); |
| 141 | cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); |
| 142 | cson_object_set(pay,"content", |
| 143 | cson_value_new_string(blob_buffer(&content),len)); |
| 144 | blob_reset(&content); |
| 145 | blob_reset(&raw); |
| 146 | }else{ |
| 147 | len = strlen(zBody); |
| 148 | cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); |
| 149 | cson_object_set(pay,"content",cson_value_new_string(zBody,len)); |
| 150 | } |
| 151 | /*TODO: add 'T' (tag) fields*/ |
| 152 | /*TODO: add the 'A' card (file attachment) entries?*/ |
| 153 | manifest_destroy(pWiki); |
| 154 | return payV; |
| 155 | } |
| 156 | } |
| 157 | |
| 158 | /* |
| 159 | ** Internal impl of /wiki/save and /wiki/create. If createMode is 0 |
| 160 | ** and the page already exists then a |
| @@ -306,21 +314,27 @@ | |
| 306 | listV = cson_value_new_array(); |
| 307 | list = cson_value_get_array(listV); |
| 308 | while( SQLITE_ROW == db_step(&q) ){ |
| 309 | cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0); |
| 310 | if(!v){ |
| 311 | goto error; |
| 312 | }else if( 0 != cson_array_append( list, v ) ){ |
| 313 | cson_value_free(v); |
| 314 | goto error; |
| 315 | } |
| 316 | } |
| 317 | goto end; |
| 318 | error: |
| 319 | cson_value_free(listV); |
| 320 | listV = NULL; |
| 321 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 322 | "Error creating wiki page list."); |
| 323 | end: |
| 324 | db_finalize(&q); |
| 325 | return listV; |
| 326 | } |
| 327 |
| --- src/json_wiki.c | |
| +++ src/json_wiki.c | |
| @@ -47,10 +47,79 @@ | |
| 47 | */ |
| 48 | cson_value * json_page_wiki(){ |
| 49 | return json_page_dispatch_helper(&JsonPageDefs_Wiki[0]); |
| 50 | } |
| 51 | |
| 52 | |
| 53 | /* |
| 54 | ** Loads the given wiki page and creates a JSON object representation |
| 55 | ** of it. If the page is not found then NULL is returned. If doParse |
| 56 | ** is true then the page content is HTML-ized using fossil's |
| 57 | ** conventional wiki format, else it is not parsed. |
| 58 | ** |
| 59 | ** The returned value, if not NULL, is-a JSON Object owned by the |
| 60 | ** caller. |
| 61 | */ |
| 62 | cson_value * json_get_wiki_page(char const * zPageName, char doParse){ |
| 63 | int rid; |
| 64 | Manifest *pWiki = 0; |
| 65 | char const * zBody = NULL; |
| 66 | char const * zFormat = NULL; |
| 67 | char * zUuid = NULL; |
| 68 | Stmt q; |
| 69 | db_prepare(&q, |
| 70 | "SELECT x.rid, b.uuid FROM tag t, tagxref x, blob b" |
| 71 | " WHERE x.tagid=t.tagid AND t.tagname='wiki-%q' " |
| 72 | " AND b.rid=x.rid" |
| 73 | " ORDER BY x.mtime DESC LIMIT 1", |
| 74 | zPageName |
| 75 | ); |
| 76 | if( (SQLITE_ROW != db_step(&q)) ){ |
| 77 | return NULL; |
| 78 | } |
| 79 | rid = db_column_int(&q,0); |
| 80 | zUuid = db_column_malloc(&q,1); |
| 81 | db_finalize(&q); |
| 82 | if( (pWiki = manifest_get(rid, CFTYPE_WIKI))!=0 ){ |
| 83 | zBody = pWiki->zWiki; |
| 84 | } |
| 85 | |
| 86 | { |
| 87 | unsigned int len; |
| 88 | cson_value * payV = cson_value_new_object(); |
| 89 | cson_object * pay = cson_value_get_object(payV); |
| 90 | cson_object_set(pay,"name",json_new_string(zPageName)); |
| 91 | cson_object_set(pay,"uuid",json_new_string(zUuid)); |
| 92 | free(zUuid); |
| 93 | zUuid = NULL; |
| 94 | cson_object_set(pay,"rid",json_new_int((cson_int_t)rid)); |
| 95 | cson_object_set(pay,"lastSavedBy",json_new_string(pWiki->zUser)); |
| 96 | cson_object_set(pay,FossilJsonKeys.timestamp, json_julian_to_timestamp(pWiki->rDate)); |
| 97 | cson_object_set(pay,"contentFormat",json_new_string(zFormat)); |
| 98 | if( doParse ){ |
| 99 | Blob content = empty_blob; |
| 100 | Blob raw = empty_blob; |
| 101 | blob_append(&raw,zBody,-1); |
| 102 | wiki_convert(&raw,&content,0); |
| 103 | len = strlen(zBody); |
| 104 | len = (unsigned int)blob_size(&content); |
| 105 | cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); |
| 106 | cson_object_set(pay,"content", |
| 107 | cson_value_new_string(blob_buffer(&content),len)); |
| 108 | blob_reset(&content); |
| 109 | blob_reset(&raw); |
| 110 | }else{ |
| 111 | len = zBody ? strlen(zBody) : 0; |
| 112 | cson_object_set(pay,"contentLength",json_new_int((cson_int_t)len)); |
| 113 | cson_object_set(pay,"content",cson_value_new_string(zBody,len)); |
| 114 | } |
| 115 | /*TODO: add 'T' (tag) fields*/ |
| 116 | /*TODO: add the 'A' card (file attachment) entries?*/ |
| 117 | manifest_destroy(pWiki); |
| 118 | return payV; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | /* |
| 123 | ** Implementation of /json/wiki/get. |
| 124 | ** |
| 125 | */ |
| @@ -89,72 +158,11 @@ | |
| 158 | zFormat = "raw"; |
| 159 | } |
| 160 | if( 'r' != *zFormat ){ |
| 161 | zFormat = "html"; |
| 162 | } |
| 163 | return json_get_wiki_page(zPageName, 'h'==*zFormat); |
| 164 | } |
| 165 | |
| 166 | /* |
| 167 | ** Internal impl of /wiki/save and /wiki/create. If createMode is 0 |
| 168 | ** and the page already exists then a |
| @@ -306,21 +314,27 @@ | |
| 314 | listV = cson_value_new_array(); |
| 315 | list = cson_value_get_array(listV); |
| 316 | while( SQLITE_ROW == db_step(&q) ){ |
| 317 | cson_value * v = cson_sqlite3_column_to_value(q.pStmt,0); |
| 318 | if(!v){ |
| 319 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 320 | "Could not convert wiki name column to JSON."); |
| 321 | goto error; |
| 322 | }else if( 0 != cson_array_append( list, v ) ){ |
| 323 | cson_value_free(v); |
| 324 | json_set_err(FSL_JSON_E_ALLOC,"Could not append wiki page name to array.") |
| 325 | /* OOM (or maybe numeric overflow) are the only realistic |
| 326 | error codes for that particular failure.*/; |
| 327 | goto error; |
| 328 | } |
| 329 | } |
| 330 | goto end; |
| 331 | error: |
| 332 | assert(0 != g.json.resultCode); |
| 333 | cson_value_free(listV); |
| 334 | listV = NULL; |
| 335 | json_set_err(FSL_JSON_E_UNKNOWN, |
| 336 | "Error creating wiki page list."); |
| 337 | end: |
| 338 | db_finalize(&q); |
| 339 | return listV; |
| 340 | } |
| 341 |