Fossil SCM
Enhanced /json/artifact/FILE_UUID a bit. Now only includes full content if explicitly told to, but returns more metadata even for binary files.
Commit
2165e77c856872a270c5d02a9a2092335d487e72
Parent
1d0a7103a35a77a…
2 files changed
+2
-1
+54
-47
+2
-1
| --- src/json.c | ||
| +++ src/json.c | ||
| @@ -1736,11 +1736,12 @@ | ||
| 1736 | 1736 | } |
| 1737 | 1737 | |
| 1738 | 1738 | /* |
| 1739 | 1739 | ** Works just like json_stmt_to_array_of_obj(), but each row in the |
| 1740 | 1740 | ** result set is represented as an Array of values instead of an |
| 1741 | -** Object (key/value pairs). | |
| 1741 | +** Object (key/value pairs). If pTgt is NULL and the statement | |
| 1742 | +** has no results then NULL is returned, not an empty array. | |
| 1742 | 1743 | */ |
| 1743 | 1744 | cson_value * json_stmt_to_array_of_array(Stmt *pStmt, |
| 1744 | 1745 | cson_value * pTgt){ |
| 1745 | 1746 | cson_value * v = pTgt; |
| 1746 | 1747 | cson_array * a = NULL; |
| 1747 | 1748 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -1736,11 +1736,12 @@ | |
| 1736 | } |
| 1737 | |
| 1738 | /* |
| 1739 | ** Works just like json_stmt_to_array_of_obj(), but each row in the |
| 1740 | ** result set is represented as an Array of values instead of an |
| 1741 | ** Object (key/value pairs). |
| 1742 | */ |
| 1743 | cson_value * json_stmt_to_array_of_array(Stmt *pStmt, |
| 1744 | cson_value * pTgt){ |
| 1745 | cson_value * v = pTgt; |
| 1746 | cson_array * a = NULL; |
| 1747 |
| --- src/json.c | |
| +++ src/json.c | |
| @@ -1736,11 +1736,12 @@ | |
| 1736 | } |
| 1737 | |
| 1738 | /* |
| 1739 | ** Works just like json_stmt_to_array_of_obj(), but each row in the |
| 1740 | ** result set is represented as an Array of values instead of an |
| 1741 | ** Object (key/value pairs). If pTgt is NULL and the statement |
| 1742 | ** has no results then NULL is returned, not an empty array. |
| 1743 | */ |
| 1744 | cson_value * json_stmt_to_array_of_array(Stmt *pStmt, |
| 1745 | cson_value * pTgt){ |
| 1746 | cson_value * v = pTgt; |
| 1747 | cson_array * a = NULL; |
| 1748 |
+54
-47
| --- src/json_artifact.c | ||
| +++ src/json_artifact.c | ||
| @@ -210,10 +210,18 @@ | ||
| 210 | 210 | {"ticket", json_artifact_ticket}, |
| 211 | 211 | {"wiki", json_artifact_wiki}, |
| 212 | 212 | /* Final entry MUST have a NULL name. */ |
| 213 | 213 | {NULL,NULL} |
| 214 | 214 | }; |
| 215 | + | |
| 216 | +/* | |
| 217 | +** Internal helper which returns true (non-0) if the includeContent | |
| 218 | +** (HTTP) or -content|-c flags (CLI) are set. | |
| 219 | +*/ | |
| 220 | +static char json_artifact_include_content_flag(){ | |
| 221 | + return json_find_option_bool("includeContent","content","c",0); | |
| 222 | +} | |
| 215 | 223 | |
| 216 | 224 | cson_value * json_artifact_wiki(int rid){ |
| 217 | 225 | if( ! g.perm.RdWiki ){ |
| 218 | 226 | json_set_err(FSL_JSON_E_DENIED, |
| 219 | 227 | "Requires 'j' privileges."); |
| @@ -225,13 +233,13 @@ | ||
| 225 | 233 | |
| 226 | 234 | cson_value * json_artifact_file(int rid){ |
| 227 | 235 | cson_value * payV = NULL; |
| 228 | 236 | cson_object * pay = NULL; |
| 229 | 237 | const char *zMime; |
| 230 | - const char *zRaw; | |
| 231 | - Blob content; | |
| 232 | - Stmt q; | |
| 238 | + Blob content = empty_blob; | |
| 239 | + Stmt q = empty_Stmt; | |
| 240 | + cson_array * checkin_arr = NULL; | |
| 233 | 241 | |
| 234 | 242 | if( ! g.perm.Read ){ |
| 235 | 243 | json_set_err(FSL_JSON_E_DENIED, |
| 236 | 244 | "Requires 'o' privileges."); |
| 237 | 245 | return NULL; |
| @@ -239,33 +247,31 @@ | ||
| 239 | 247 | |
| 240 | 248 | payV = cson_value_new_object(); |
| 241 | 249 | pay = cson_value_get_object(payV); |
| 242 | 250 | |
| 243 | 251 | content_get(rid, &content); |
| 252 | + cson_object_set(pay, "contentLength", | |
| 253 | + json_new_int( blob_size(&content) ) | |
| 254 | + /* achtung: overflow potential on 32-bit builds! */); | |
| 244 | 255 | zMime = mimetype_from_content(&content); |
| 245 | 256 | |
| 246 | - if (!zMime){ | |
| 247 | - cson_array * checkin_arr = NULL; | |
| 248 | - cson_value * checkin_list = NULL; | |
| 249 | - /*cson_string * tagKey = NULL;*/ | |
| 250 | - cson_value * checkinV = NULL; | |
| 251 | - cson_object * checkin = NULL; | |
| 252 | - | |
| 253 | - cson_int_t const rawLen = blob_size(&content); | |
| 254 | - zRaw = blob_str(&content); | |
| 255 | - checkin_list = cson_value_new_array(); | |
| 256 | - | |
| 257 | - cson_object_set(pay, "contentLength", | |
| 258 | - json_new_int( rawLen | |
| 259 | - /* achtung: overflow potential on 32-bit builds! */)); | |
| 260 | - cson_object_set(pay, "content", | |
| 261 | - cson_value_new_string(zRaw,(unsigned int)rawLen)); | |
| 262 | - cson_object_set(pay, "checkins", checkin_list); | |
| 263 | - | |
| 264 | - checkin_arr = cson_value_get_array(checkin_list); | |
| 265 | - | |
| 266 | - db_prepare(&q, | |
| 257 | + cson_object_set(pay, "contentType", | |
| 258 | + json_new_string(zMime ? zMime : "text/plain")); | |
| 259 | + if( json_artifact_include_content_flag() && !zMime ){ | |
| 260 | +#if 0 | |
| 261 | + /*see next #if block below*/ | |
| 262 | + cson_string * tagKey = NULL; | |
| 263 | + cson_value * checkinV = NULL; | |
| 264 | + cson_object * checkin = NULL; | |
| 265 | +#endif | |
| 266 | + cson_object_set(pay, "content", | |
| 267 | + cson_value_new_string(blob_str(&content), | |
| 268 | + (unsigned int)blob_size(&content))); | |
| 269 | + } | |
| 270 | + blob_reset(&content); | |
| 271 | + | |
| 272 | + db_prepare(&q, | |
| 267 | 273 | "SELECT filename.name AS name, " |
| 268 | 274 | " cast(strftime('%%s',event.mtime) as int) AS mtime," |
| 269 | 275 | " coalesce(event.ecomment,event.comment) as comment," |
| 270 | 276 | " coalesce(event.euser,event.user) as user," |
| 271 | 277 | " b.uuid as uuid, mlink.mperm as mperm,"/* WTF is mperm?*/ |
| @@ -278,36 +284,37 @@ | ||
| 278 | 284 | " AND b.rid=mlink.mid" |
| 279 | 285 | " AND mlink.fid=%d" |
| 280 | 286 | " ORDER BY filename.name, event.mtime", |
| 281 | 287 | TAG_BRANCH, rid |
| 282 | 288 | ); |
| 289 | + checkin_arr = cson_new_array(); | |
| 290 | + cson_object_set(pay, "checkins", cson_array_value(checkin_arr)); | |
| 283 | 291 | #if 0 |
| 284 | - /* Damn: json_tags_for_rid() only works for commits. | |
| 285 | - | |
| 286 | - FIXME: extend json_tags_for_rid() to accept file rids and then | |
| 287 | - implement this loop to add the tags to each object. | |
| 288 | - */ | |
| 289 | - | |
| 290 | - while( SQLITE_ROW == db_step(&q) ){ | |
| 291 | - checkinV = cson_sqlite3_row_to_object( q.pStmt ); | |
| 292 | - if(!checkinV){ | |
| 293 | - continue; | |
| 294 | - } | |
| 295 | - if(!tagKey) { | |
| 296 | - tagKey = cson_new_string("tags",4); | |
| 297 | - json_gc_add("artifact/file/tags", cson_string_value(tagKey)) | |
| 298 | - /*avoids a potential lifetime issue*/; | |
| 299 | - } | |
| 300 | - checkin = cson_value_get_object(checkinV); | |
| 301 | - cson_object_set_s(checkin, tagKey, json_tags_for_rid(rid,0)); | |
| 302 | - cson_array_append( checkin_arr, checkinV ); | |
| 303 | - } | |
| 292 | + /* Damn: json_tags_for_rid() only works for commits. | |
| 293 | + | |
| 294 | + FIXME: extend json_tags_for_rid() to accept file rids and then | |
| 295 | + implement this loop to add the tags to each object. | |
| 296 | + */ | |
| 297 | + | |
| 298 | + while( SQLITE_ROW == db_step(&q) ){ | |
| 299 | + checkinV = cson_sqlite3_row_to_object( q.pStmt ); | |
| 300 | + if(!checkinV){ | |
| 301 | + continue; | |
| 302 | + } | |
| 303 | + if(!tagKey) { | |
| 304 | + tagKey = cson_new_string("tags",4); | |
| 305 | + json_gc_add("artifact/file/tags", cson_string_value(tagKey)) | |
| 306 | + /*avoids a potential lifetime issue*/; | |
| 307 | + } | |
| 308 | + checkin = cson_value_get_object(checkinV); | |
| 309 | + cson_object_set_s(checkin, tagKey, json_tags_for_rid(rid,0)); | |
| 310 | + cson_array_append( checkin_arr, checkinV ); | |
| 311 | + } | |
| 304 | 312 | #else |
| 305 | - json_stmt_to_array_of_obj( &q, checkin_list ); | |
| 313 | + json_stmt_to_array_of_obj( &q, cson_array_value(checkin_arr) ); | |
| 306 | 314 | #endif |
| 307 | - db_finalize(&q); | |
| 308 | - } | |
| 315 | + db_finalize(&q); | |
| 309 | 316 | return payV; |
| 310 | 317 | } |
| 311 | 318 | |
| 312 | 319 | /* |
| 313 | 320 | ** Impl of /json/artifact. This basically just determines the type of |
| 314 | 321 |
| --- src/json_artifact.c | |
| +++ src/json_artifact.c | |
| @@ -210,10 +210,18 @@ | |
| 210 | {"ticket", json_artifact_ticket}, |
| 211 | {"wiki", json_artifact_wiki}, |
| 212 | /* Final entry MUST have a NULL name. */ |
| 213 | {NULL,NULL} |
| 214 | }; |
| 215 | |
| 216 | cson_value * json_artifact_wiki(int rid){ |
| 217 | if( ! g.perm.RdWiki ){ |
| 218 | json_set_err(FSL_JSON_E_DENIED, |
| 219 | "Requires 'j' privileges."); |
| @@ -225,13 +233,13 @@ | |
| 225 | |
| 226 | cson_value * json_artifact_file(int rid){ |
| 227 | cson_value * payV = NULL; |
| 228 | cson_object * pay = NULL; |
| 229 | const char *zMime; |
| 230 | const char *zRaw; |
| 231 | Blob content; |
| 232 | Stmt q; |
| 233 | |
| 234 | if( ! g.perm.Read ){ |
| 235 | json_set_err(FSL_JSON_E_DENIED, |
| 236 | "Requires 'o' privileges."); |
| 237 | return NULL; |
| @@ -239,33 +247,31 @@ | |
| 239 | |
| 240 | payV = cson_value_new_object(); |
| 241 | pay = cson_value_get_object(payV); |
| 242 | |
| 243 | content_get(rid, &content); |
| 244 | zMime = mimetype_from_content(&content); |
| 245 | |
| 246 | if (!zMime){ |
| 247 | cson_array * checkin_arr = NULL; |
| 248 | cson_value * checkin_list = NULL; |
| 249 | /*cson_string * tagKey = NULL;*/ |
| 250 | cson_value * checkinV = NULL; |
| 251 | cson_object * checkin = NULL; |
| 252 | |
| 253 | cson_int_t const rawLen = blob_size(&content); |
| 254 | zRaw = blob_str(&content); |
| 255 | checkin_list = cson_value_new_array(); |
| 256 | |
| 257 | cson_object_set(pay, "contentLength", |
| 258 | json_new_int( rawLen |
| 259 | /* achtung: overflow potential on 32-bit builds! */)); |
| 260 | cson_object_set(pay, "content", |
| 261 | cson_value_new_string(zRaw,(unsigned int)rawLen)); |
| 262 | cson_object_set(pay, "checkins", checkin_list); |
| 263 | |
| 264 | checkin_arr = cson_value_get_array(checkin_list); |
| 265 | |
| 266 | db_prepare(&q, |
| 267 | "SELECT filename.name AS name, " |
| 268 | " cast(strftime('%%s',event.mtime) as int) AS mtime," |
| 269 | " coalesce(event.ecomment,event.comment) as comment," |
| 270 | " coalesce(event.euser,event.user) as user," |
| 271 | " b.uuid as uuid, mlink.mperm as mperm,"/* WTF is mperm?*/ |
| @@ -278,36 +284,37 @@ | |
| 278 | " AND b.rid=mlink.mid" |
| 279 | " AND mlink.fid=%d" |
| 280 | " ORDER BY filename.name, event.mtime", |
| 281 | TAG_BRANCH, rid |
| 282 | ); |
| 283 | #if 0 |
| 284 | /* Damn: json_tags_for_rid() only works for commits. |
| 285 | |
| 286 | FIXME: extend json_tags_for_rid() to accept file rids and then |
| 287 | implement this loop to add the tags to each object. |
| 288 | */ |
| 289 | |
| 290 | while( SQLITE_ROW == db_step(&q) ){ |
| 291 | checkinV = cson_sqlite3_row_to_object( q.pStmt ); |
| 292 | if(!checkinV){ |
| 293 | continue; |
| 294 | } |
| 295 | if(!tagKey) { |
| 296 | tagKey = cson_new_string("tags",4); |
| 297 | json_gc_add("artifact/file/tags", cson_string_value(tagKey)) |
| 298 | /*avoids a potential lifetime issue*/; |
| 299 | } |
| 300 | checkin = cson_value_get_object(checkinV); |
| 301 | cson_object_set_s(checkin, tagKey, json_tags_for_rid(rid,0)); |
| 302 | cson_array_append( checkin_arr, checkinV ); |
| 303 | } |
| 304 | #else |
| 305 | json_stmt_to_array_of_obj( &q, checkin_list ); |
| 306 | #endif |
| 307 | db_finalize(&q); |
| 308 | } |
| 309 | return payV; |
| 310 | } |
| 311 | |
| 312 | /* |
| 313 | ** Impl of /json/artifact. This basically just determines the type of |
| 314 |
| --- src/json_artifact.c | |
| +++ src/json_artifact.c | |
| @@ -210,10 +210,18 @@ | |
| 210 | {"ticket", json_artifact_ticket}, |
| 211 | {"wiki", json_artifact_wiki}, |
| 212 | /* Final entry MUST have a NULL name. */ |
| 213 | {NULL,NULL} |
| 214 | }; |
| 215 | |
| 216 | /* |
| 217 | ** Internal helper which returns true (non-0) if the includeContent |
| 218 | ** (HTTP) or -content|-c flags (CLI) are set. |
| 219 | */ |
| 220 | static char json_artifact_include_content_flag(){ |
| 221 | return json_find_option_bool("includeContent","content","c",0); |
| 222 | } |
| 223 | |
| 224 | cson_value * json_artifact_wiki(int rid){ |
| 225 | if( ! g.perm.RdWiki ){ |
| 226 | json_set_err(FSL_JSON_E_DENIED, |
| 227 | "Requires 'j' privileges."); |
| @@ -225,13 +233,13 @@ | |
| 233 | |
| 234 | cson_value * json_artifact_file(int rid){ |
| 235 | cson_value * payV = NULL; |
| 236 | cson_object * pay = NULL; |
| 237 | const char *zMime; |
| 238 | Blob content = empty_blob; |
| 239 | Stmt q = empty_Stmt; |
| 240 | cson_array * checkin_arr = NULL; |
| 241 | |
| 242 | if( ! g.perm.Read ){ |
| 243 | json_set_err(FSL_JSON_E_DENIED, |
| 244 | "Requires 'o' privileges."); |
| 245 | return NULL; |
| @@ -239,33 +247,31 @@ | |
| 247 | |
| 248 | payV = cson_value_new_object(); |
| 249 | pay = cson_value_get_object(payV); |
| 250 | |
| 251 | content_get(rid, &content); |
| 252 | cson_object_set(pay, "contentLength", |
| 253 | json_new_int( blob_size(&content) ) |
| 254 | /* achtung: overflow potential on 32-bit builds! */); |
| 255 | zMime = mimetype_from_content(&content); |
| 256 | |
| 257 | cson_object_set(pay, "contentType", |
| 258 | json_new_string(zMime ? zMime : "text/plain")); |
| 259 | if( json_artifact_include_content_flag() && !zMime ){ |
| 260 | #if 0 |
| 261 | /*see next #if block below*/ |
| 262 | cson_string * tagKey = NULL; |
| 263 | cson_value * checkinV = NULL; |
| 264 | cson_object * checkin = NULL; |
| 265 | #endif |
| 266 | cson_object_set(pay, "content", |
| 267 | cson_value_new_string(blob_str(&content), |
| 268 | (unsigned int)blob_size(&content))); |
| 269 | } |
| 270 | blob_reset(&content); |
| 271 | |
| 272 | db_prepare(&q, |
| 273 | "SELECT filename.name AS name, " |
| 274 | " cast(strftime('%%s',event.mtime) as int) AS mtime," |
| 275 | " coalesce(event.ecomment,event.comment) as comment," |
| 276 | " coalesce(event.euser,event.user) as user," |
| 277 | " b.uuid as uuid, mlink.mperm as mperm,"/* WTF is mperm?*/ |
| @@ -278,36 +284,37 @@ | |
| 284 | " AND b.rid=mlink.mid" |
| 285 | " AND mlink.fid=%d" |
| 286 | " ORDER BY filename.name, event.mtime", |
| 287 | TAG_BRANCH, rid |
| 288 | ); |
| 289 | checkin_arr = cson_new_array(); |
| 290 | cson_object_set(pay, "checkins", cson_array_value(checkin_arr)); |
| 291 | #if 0 |
| 292 | /* Damn: json_tags_for_rid() only works for commits. |
| 293 | |
| 294 | FIXME: extend json_tags_for_rid() to accept file rids and then |
| 295 | implement this loop to add the tags to each object. |
| 296 | */ |
| 297 | |
| 298 | while( SQLITE_ROW == db_step(&q) ){ |
| 299 | checkinV = cson_sqlite3_row_to_object( q.pStmt ); |
| 300 | if(!checkinV){ |
| 301 | continue; |
| 302 | } |
| 303 | if(!tagKey) { |
| 304 | tagKey = cson_new_string("tags",4); |
| 305 | json_gc_add("artifact/file/tags", cson_string_value(tagKey)) |
| 306 | /*avoids a potential lifetime issue*/; |
| 307 | } |
| 308 | checkin = cson_value_get_object(checkinV); |
| 309 | cson_object_set_s(checkin, tagKey, json_tags_for_rid(rid,0)); |
| 310 | cson_array_append( checkin_arr, checkinV ); |
| 311 | } |
| 312 | #else |
| 313 | json_stmt_to_array_of_obj( &q, cson_array_value(checkin_arr) ); |
| 314 | #endif |
| 315 | db_finalize(&q); |
| 316 | return payV; |
| 317 | } |
| 318 | |
| 319 | /* |
| 320 | ** Impl of /json/artifact. This basically just determines the type of |
| 321 |