|
1
|
#ifdef FOSSIL_ENABLE_JSON |
|
2
|
/* |
|
3
|
** Copyright (c) 2011 D. Richard Hipp |
|
4
|
** |
|
5
|
** This program is free software; you can redistribute it and/or |
|
6
|
** modify it under the terms of the Simplified BSD License (also |
|
7
|
** known as the "2-Clause License" or "FreeBSD License".) |
|
8
|
** |
|
9
|
** This program is distributed in the hope that it will be useful, |
|
10
|
** but without any warranty; without even the implied warranty of |
|
11
|
** merchantability or fitness for a particular purpose. |
|
12
|
** |
|
13
|
** Author contact information: |
|
14
|
** [email protected] |
|
15
|
** http://www.hwaci.com/drh/ |
|
16
|
** |
|
17
|
*/ |
|
18
|
#include "VERSION.h" |
|
19
|
#include "config.h" |
|
20
|
#include "json_artifact.h" |
|
21
|
|
|
22
|
#if INTERFACE |
|
23
|
#include "json_detail.h" |
|
24
|
#endif |
|
25
|
|
|
26
|
/* |
|
27
|
** Internal callback for /json/artifact handlers. rid refers to |
|
28
|
** the rid of a given type of artifact, and each callback is |
|
29
|
** specialized to return a JSON form of one type of artifact. |
|
30
|
** |
|
31
|
** Implementations may assert() that rid refers to requested artifact |
|
32
|
** type, since mismatches in the artifact types come from |
|
33
|
** json_page_artifact() as opposed to client data. |
|
34
|
** |
|
35
|
** The pParent parameter points to the response payload object. It |
|
36
|
** _may_ be used to populate "top-level" information in the response |
|
37
|
** payload, but normally this is neither necessary nor desired. |
|
38
|
*/ |
|
39
|
typedef cson_value * (*artifact_f)( cson_object * pParent, int rid ); |
|
40
|
|
|
41
|
/* |
|
42
|
** Internal per-artifact-type dispatching helper. |
|
43
|
*/ |
|
44
|
typedef struct ArtifactDispatchEntry { |
|
45
|
/** |
|
46
|
Artifact type name, e.g. "checkin", "ticket", "wiki". |
|
47
|
*/ |
|
48
|
char const * name; |
|
49
|
|
|
50
|
/** |
|
51
|
JSON construction callback. Creates the contents for the |
|
52
|
payload.artifact property of /json/artifact responses. |
|
53
|
*/ |
|
54
|
artifact_f func; |
|
55
|
} ArtifactDispatchEntry; |
|
56
|
|
|
57
|
|
|
58
|
/* |
|
59
|
** Generates a JSON Array reference holding the parent UUIDs (as strings). |
|
60
|
** If it finds no matches then it returns NULL (OOM is a fatal error). |
|
61
|
** |
|
62
|
** Returned value is NULL or an Array owned by the caller. |
|
63
|
*/ |
|
64
|
cson_value * json_parent_uuids_for_ci( int rid ){ |
|
65
|
Stmt q = empty_Stmt; |
|
66
|
cson_array * pParents = NULL; |
|
67
|
db_prepare( &q, |
|
68
|
"SELECT uuid FROM plink, blob" |
|
69
|
" WHERE plink.cid=%d AND blob.rid=plink.pid" |
|
70
|
" ORDER BY plink.isprim DESC", |
|
71
|
rid ); |
|
72
|
while( SQLITE_ROW==db_step(&q) ){ |
|
73
|
if(!pParents) { |
|
74
|
pParents = cson_new_array(); |
|
75
|
} |
|
76
|
cson_array_append( pParents, cson_sqlite3_column_to_value( q.pStmt, 0 ) ); |
|
77
|
} |
|
78
|
db_finalize(&q); |
|
79
|
return cson_array_value(pParents); |
|
80
|
} |
|
81
|
|
|
82
|
/* |
|
83
|
** Generates an artifact Object for the given rid, |
|
84
|
** which must refer to a Check-in. |
|
85
|
** |
|
86
|
** Returned value is NULL or an Object owned by the caller. |
|
87
|
*/ |
|
88
|
cson_value * json_artifact_for_ci( int rid, char showFiles ){ |
|
89
|
cson_value * v = NULL; |
|
90
|
Stmt q = empty_Stmt; |
|
91
|
static cson_value * eventTypeLabel = NULL; |
|
92
|
if(!eventTypeLabel){ |
|
93
|
eventTypeLabel = json_new_string("checkin"); |
|
94
|
json_gc_add("$EVENT_TYPE_LABEL(commit)", eventTypeLabel); |
|
95
|
} |
|
96
|
|
|
97
|
db_prepare(&q, |
|
98
|
"SELECT b.uuid, " |
|
99
|
" cast(strftime('%%s',e.mtime) as int), " |
|
100
|
" strftime('%%s',e.omtime)," |
|
101
|
" e.user, " |
|
102
|
" e.comment" |
|
103
|
" FROM blob b, event e" |
|
104
|
" WHERE b.rid=%d" |
|
105
|
" AND e.objid=%d", |
|
106
|
rid, rid |
|
107
|
); |
|
108
|
if( db_step(&q)==SQLITE_ROW ){ |
|
109
|
cson_object * o; |
|
110
|
cson_value * tmpV = NULL; |
|
111
|
const char *zUuid = db_column_text(&q, 0); |
|
112
|
const char *zUser; |
|
113
|
const char *zComment; |
|
114
|
char * zEUser, * zEComment; |
|
115
|
i64 mtime, omtime; |
|
116
|
v = cson_value_new_object(); |
|
117
|
o = cson_value_get_object(v); |
|
118
|
#define SET(K,V) cson_object_set(o,(K), (V)) |
|
119
|
SET("type", eventTypeLabel ); |
|
120
|
SET("uuid",json_new_string(zUuid)); |
|
121
|
SET("isLeaf", cson_value_new_bool(is_a_leaf(rid))); |
|
122
|
|
|
123
|
mtime = db_column_int64(&q,1); |
|
124
|
SET("timestamp",json_new_int(mtime)); |
|
125
|
omtime = db_column_int64(&q,2); |
|
126
|
if(omtime && (omtime!=mtime)){ |
|
127
|
SET("originTime",json_new_int(omtime)); |
|
128
|
} |
|
129
|
|
|
130
|
zUser = db_column_text(&q,3); |
|
131
|
zEUser = db_text(0, |
|
132
|
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
|
133
|
TAG_USER, rid); |
|
134
|
if(zEUser){ |
|
135
|
SET("user", json_new_string(zEUser)); |
|
136
|
if(0!=fossil_strcmp(zEUser,zUser)){ |
|
137
|
SET("originUser",json_new_string(zUser)); |
|
138
|
} |
|
139
|
free(zEUser); |
|
140
|
}else{ |
|
141
|
SET("user",json_new_string(zUser)); |
|
142
|
} |
|
143
|
|
|
144
|
zComment = db_column_text(&q,4); |
|
145
|
zEComment = db_text(0, |
|
146
|
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d", |
|
147
|
TAG_COMMENT, rid); |
|
148
|
if(zEComment){ |
|
149
|
SET("comment",json_new_string(zEComment)); |
|
150
|
if(0 != fossil_strcmp(zEComment,zComment)){ |
|
151
|
SET("originComment", json_new_string(zComment)); |
|
152
|
} |
|
153
|
free(zEComment); |
|
154
|
}else{ |
|
155
|
SET("comment",json_new_string(zComment)); |
|
156
|
} |
|
157
|
|
|
158
|
tmpV = json_parent_uuids_for_ci(rid); |
|
159
|
if(tmpV){ |
|
160
|
SET("parents", tmpV); |
|
161
|
} |
|
162
|
|
|
163
|
tmpV = json_tags_for_checkin_rid(rid,0); |
|
164
|
if(tmpV){ |
|
165
|
SET("tags",tmpV); |
|
166
|
} |
|
167
|
|
|
168
|
if( showFiles ){ |
|
169
|
tmpV = json_get_changed_files(rid, 1); |
|
170
|
if(tmpV){ |
|
171
|
SET("files",tmpV); |
|
172
|
} |
|
173
|
} |
|
174
|
|
|
175
|
#undef SET |
|
176
|
} |
|
177
|
db_finalize(&q); |
|
178
|
return v; |
|
179
|
} |
|
180
|
|
|
181
|
/* |
|
182
|
** Very incomplete/incorrect impl of /json/artifact/TICKET_ID. |
|
183
|
*/ |
|
184
|
cson_value * json_artifact_ticket( cson_object * zParent, int rid ){ |
|
185
|
cson_object * pay = NULL; |
|
186
|
Manifest *pTktChng = NULL; |
|
187
|
static cson_value * eventTypeLabel = NULL; |
|
188
|
if(! g.perm.RdTkt ){ |
|
189
|
g.json.resultCode = FSL_JSON_E_DENIED; |
|
190
|
return NULL; |
|
191
|
} |
|
192
|
if(!eventTypeLabel){ |
|
193
|
eventTypeLabel = json_new_string("ticket"); |
|
194
|
json_gc_add("$EVENT_TYPE_LABEL(ticket)", eventTypeLabel); |
|
195
|
} |
|
196
|
|
|
197
|
pTktChng = manifest_get(rid, CFTYPE_TICKET, 0); |
|
198
|
if( pTktChng==0 ){ |
|
199
|
g.json.resultCode = FSL_JSON_E_MANIFEST_READ_FAILED; |
|
200
|
return NULL; |
|
201
|
} |
|
202
|
pay = cson_new_object(); |
|
203
|
cson_object_set(pay, "eventType", eventTypeLabel ); |
|
204
|
cson_object_set(pay, "uuid", json_new_string(pTktChng->zTicketUuid)); |
|
205
|
cson_object_set(pay, "user", json_new_string(pTktChng->zUser)); |
|
206
|
cson_object_set(pay, "timestamp", json_julian_to_timestamp(pTktChng->rDate)); |
|
207
|
manifest_destroy(pTktChng); |
|
208
|
return cson_object_value(pay); |
|
209
|
} |
|
210
|
|
|
211
|
/* |
|
212
|
** Sub-impl of /json/artifact for check-ins. |
|
213
|
*/ |
|
214
|
static cson_value * json_artifact_ci( cson_object * zParent, int rid ){ |
|
215
|
if(!g.perm.Read){ |
|
216
|
json_set_err( FSL_JSON_E_DENIED, |
|
217
|
"Viewing check-ins requires 'o' privileges." ); |
|
218
|
return NULL; |
|
219
|
}else{ |
|
220
|
cson_value * artV = json_artifact_for_ci(rid, 1); |
|
221
|
cson_object * art = cson_value_get_object(artV); |
|
222
|
if(art){ |
|
223
|
cson_object_merge( zParent, art, CSON_MERGE_REPLACE ); |
|
224
|
cson_free_object(art); |
|
225
|
} |
|
226
|
return cson_object_value(zParent); |
|
227
|
} |
|
228
|
} |
|
229
|
|
|
230
|
/* |
|
231
|
** Internal mapping of /json/artifact/FOO commands/callbacks. |
|
232
|
*/ |
|
233
|
static ArtifactDispatchEntry ArtifactDispatchList[] = { |
|
234
|
{"checkin", json_artifact_ci}, |
|
235
|
{"file", json_artifact_file}, |
|
236
|
/*{"tag", NULL}, //impl missing */ |
|
237
|
/*{"technote", NULL}, //impl missing */ |
|
238
|
{"ticket", json_artifact_ticket}, |
|
239
|
{"wiki", json_artifact_wiki}, |
|
240
|
/* Final entry MUST have a NULL name. */ |
|
241
|
{NULL,NULL} |
|
242
|
}; |
|
243
|
|
|
244
|
/* |
|
245
|
** Internal helper which returns: |
|
246
|
** |
|
247
|
** If the "format" (CLI: -f) flag is set function returns the same as |
|
248
|
** json_wiki_get_content_format_flag(), else it returns true (non-0) |
|
249
|
** if either the includeContent (HTTP) or -content|-c boolean flags |
|
250
|
** (CLI) are set. |
|
251
|
*/ |
|
252
|
static int json_artifact_get_content_format_flag(void){ |
|
253
|
enum { MagicValue = -9 }; |
|
254
|
int contentFormat = json_wiki_get_content_format_flag(MagicValue); |
|
255
|
if(MagicValue == contentFormat){ |
|
256
|
contentFormat = json_find_option_bool("includeContent", |
|
257
|
"content","c",0) /* deprecated */ ? -1 : 0; |
|
258
|
} |
|
259
|
return contentFormat; |
|
260
|
} |
|
261
|
|
|
262
|
extern int json_wiki_get_content_format_flag(int defaultValue) /* json_wiki.c*/; |
|
263
|
|
|
264
|
cson_value * json_artifact_wiki(cson_object * zParent, int rid){ |
|
265
|
if( ! g.perm.RdWiki ){ |
|
266
|
json_set_err(FSL_JSON_E_DENIED, |
|
267
|
"Requires 'j' privileges."); |
|
268
|
return NULL; |
|
269
|
}else{ |
|
270
|
enum { MagicValue = -9 }; |
|
271
|
int const contentFormat = json_artifact_get_content_format_flag(); |
|
272
|
return json_get_wiki_page_by_rid(rid, contentFormat); |
|
273
|
} |
|
274
|
} |
|
275
|
|
|
276
|
/* |
|
277
|
** Internal helper for routines which add a "status" flag to file |
|
278
|
** artifact data. isNew and isDel should be the "is this object new?" |
|
279
|
** and "is this object removed?" flags of the underlying query. This |
|
280
|
** function returns a static string from the set (added, removed, |
|
281
|
** modified), depending on the combination of the two args. |
|
282
|
** |
|
283
|
** Reminder to self: (mlink.pid==0) AS isNew, (mlink.fid==0) AS isDel |
|
284
|
*/ |
|
285
|
char const * json_artifact_status_to_string( char isNew, char isDel ){ |
|
286
|
return isNew |
|
287
|
? "added" |
|
288
|
: (isDel |
|
289
|
? "removed" |
|
290
|
: "modified"); |
|
291
|
} |
|
292
|
|
|
293
|
cson_value * json_artifact_file(cson_object * zParent, int rid){ |
|
294
|
cson_object * pay = NULL; |
|
295
|
Stmt q = empty_Stmt; |
|
296
|
cson_array * checkin_arr = NULL; |
|
297
|
int contentFormat; |
|
298
|
i64 contentSize = -1; |
|
299
|
char * parentUuid; |
|
300
|
if( ! g.perm.Read ){ |
|
301
|
json_set_err(FSL_JSON_E_DENIED, |
|
302
|
"Requires 'o' privileges."); |
|
303
|
return NULL; |
|
304
|
} |
|
305
|
|
|
306
|
pay = zParent; |
|
307
|
|
|
308
|
contentFormat = json_artifact_get_content_format_flag(); |
|
309
|
if( 0 != contentFormat ){ |
|
310
|
Blob content = empty_blob; |
|
311
|
const char *zMime; |
|
312
|
char const * zFormat = (contentFormat<1) ? "raw" : "html"; |
|
313
|
content_get(rid, &content); |
|
314
|
zMime = mimetype_from_content(&content); |
|
315
|
cson_object_set(zParent, "contentType", |
|
316
|
json_new_string(zMime ? zMime : "text/plain")); |
|
317
|
if(!zMime){/* text/plain */ |
|
318
|
if(0 < blob_size(&content)){ |
|
319
|
if( 0 < contentFormat ){/*HTML-size it*/ |
|
320
|
Blob html = empty_blob; |
|
321
|
wiki_convert(&content, &html, 0); |
|
322
|
assert( blob_size(&content) < blob_size(&html) ); |
|
323
|
blob_swap( &html, &content ); |
|
324
|
assert( blob_size(&content) > blob_size(&html) ); |
|
325
|
blob_reset( &html ); |
|
326
|
}/*else as-is*/ |
|
327
|
} |
|
328
|
cson_object_set(zParent, "content", |
|
329
|
cson_value_new_string(blob_str(&content), |
|
330
|
(unsigned int)blob_size(&content))); |
|
331
|
}/*else binary: ignore*/ |
|
332
|
contentSize = blob_size(&content); |
|
333
|
cson_object_set(zParent, "contentSize", json_new_int(contentSize) ); |
|
334
|
cson_object_set(zParent, "contentFormat", json_new_string(zFormat) ); |
|
335
|
blob_reset(&content); |
|
336
|
} |
|
337
|
contentSize = db_int64(-1, "SELECT size FROM blob WHERE rid=%d", rid); |
|
338
|
assert( -1 < contentSize ); |
|
339
|
cson_object_set(zParent, "size", json_new_int(contentSize) ); |
|
340
|
|
|
341
|
parentUuid = db_text(NULL, |
|
342
|
"SELECT DISTINCT p.uuid " |
|
343
|
"FROM blob p, blob f, mlink m " |
|
344
|
"WHERE m.pid=p.rid " |
|
345
|
"AND m.fid=f.rid " |
|
346
|
"AND f.rid=%d", |
|
347
|
rid |
|
348
|
); |
|
349
|
if(parentUuid){ |
|
350
|
cson_object_set( zParent, "parent", json_new_string(parentUuid) ); |
|
351
|
fossil_free(parentUuid); |
|
352
|
} |
|
353
|
|
|
354
|
/* Find check-ins associated with this file... */ |
|
355
|
db_prepare(&q, |
|
356
|
"SELECT filename.name AS name, " |
|
357
|
" (mlink.pid==0) AS isNew," |
|
358
|
" (mlink.fid==0) AS isDel," |
|
359
|
" cast(strftime('%%s',event.mtime) as int) AS timestamp," |
|
360
|
" coalesce(event.ecomment,event.comment) as comment," |
|
361
|
" coalesce(event.euser,event.user) as user," |
|
362
|
#if 0 |
|
363
|
" a.size AS size," /* same for all check-ins. */ |
|
364
|
#endif |
|
365
|
" b.uuid as checkin, " |
|
366
|
#if 0 |
|
367
|
" mlink.mperm as mperm," |
|
368
|
#endif |
|
369
|
" coalesce((SELECT value FROM tagxref" |
|
370
|
" WHERE tagid=%d AND tagtype>0 AND " |
|
371
|
" rid=mlink.mid),'trunk') as branch" |
|
372
|
" FROM mlink, filename, event, blob a, blob b" |
|
373
|
" WHERE filename.fnid=mlink.fnid" |
|
374
|
" AND event.objid=mlink.mid" |
|
375
|
" AND a.rid=mlink.fid" |
|
376
|
" AND b.rid=mlink.mid" |
|
377
|
" AND mlink.fid=%d" |
|
378
|
" ORDER BY filename.name, event.mtime", |
|
379
|
TAG_BRANCH, rid |
|
380
|
); |
|
381
|
/* TODO: add a "state" flag for the file in each check-in, |
|
382
|
e.g. "modified", "new", "deleted". |
|
383
|
*/ |
|
384
|
checkin_arr = cson_new_array(); |
|
385
|
cson_object_set(pay, "checkins", cson_array_value(checkin_arr)); |
|
386
|
while( (SQLITE_ROW==db_step(&q) ) ){ |
|
387
|
cson_object * row = cson_value_get_object( |
|
388
|
cson_sqlite3_row_to_object(q.pStmt)); |
|
389
|
/* FIXME: move this isNew/isDel stuff into an SQL CASE statement. */ |
|
390
|
char const isNew = cson_value_get_bool(cson_object_get(row,"isNew")); |
|
391
|
char const isDel = cson_value_get_bool(cson_object_get(row,"isDel")); |
|
392
|
cson_object_set(row, "isNew", NULL); |
|
393
|
cson_object_set(row, "isDel", NULL); |
|
394
|
cson_object_set(row, "state", json_new_string( |
|
395
|
json_artifact_status_to_string(isNew, isDel))); |
|
396
|
cson_array_append( checkin_arr, cson_object_value(row) ); |
|
397
|
} |
|
398
|
db_finalize(&q); |
|
399
|
return cson_object_value(pay); |
|
400
|
} |
|
401
|
|
|
402
|
/* |
|
403
|
** Impl of /json/artifact. This basically just determines the type of |
|
404
|
** an artifact and forwards the real work to another function. |
|
405
|
*/ |
|
406
|
cson_value * json_page_artifact(void){ |
|
407
|
cson_object * pay = NULL; |
|
408
|
char const * zName = NULL; |
|
409
|
char const * zType = NULL; |
|
410
|
char const * zUuid = NULL; |
|
411
|
cson_value * entry = NULL; |
|
412
|
Blob uuid = empty_blob; |
|
413
|
int rc; |
|
414
|
int rid = 0; |
|
415
|
ArtifactDispatchEntry const * dispatcher = &ArtifactDispatchList[0]; |
|
416
|
zName = json_find_option_cstr2("name", NULL, NULL, g.json.dispatchDepth+1); |
|
417
|
if(!zName || !*zName) { |
|
418
|
json_set_err(FSL_JSON_E_MISSING_ARGS, |
|
419
|
"Missing 'name' argument."); |
|
420
|
return NULL; |
|
421
|
} |
|
422
|
|
|
423
|
if( validate16(zName, strlen(zName)) ){ |
|
424
|
if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){ |
|
425
|
zType = "ticket"; |
|
426
|
goto handle_entry; |
|
427
|
} |
|
428
|
if( db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'event-%q*'", zName) ){ |
|
429
|
zType = "tag"; |
|
430
|
goto handle_entry; |
|
431
|
} |
|
432
|
} |
|
433
|
blob_set(&uuid,zName); |
|
434
|
rc = name_to_uuid(&uuid,-1,"*"); |
|
435
|
/* FIXME: check for a filename if all else fails. */ |
|
436
|
if(1==rc){ |
|
437
|
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; |
|
438
|
goto error; |
|
439
|
}else if(2==rc){ |
|
440
|
g.json.resultCode = FSL_JSON_E_AMBIGUOUS_UUID; |
|
441
|
goto error; |
|
442
|
} |
|
443
|
zUuid = blob_str(&uuid); |
|
444
|
rid = db_int(0, "SELECT rid FROM blob WHERE uuid=%Q", zUuid); |
|
445
|
if(0==rid){ |
|
446
|
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; |
|
447
|
goto error; |
|
448
|
} |
|
449
|
|
|
450
|
if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid) |
|
451
|
|| db_exists("SELECT 1 FROM plink WHERE cid=%d", rid) |
|
452
|
|| db_exists("SELECT 1 FROM plink WHERE pid=%d", rid)){ |
|
453
|
zType = "checkin"; |
|
454
|
goto handle_entry; |
|
455
|
}else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" |
|
456
|
" WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){ |
|
457
|
zType = "wiki"; |
|
458
|
goto handle_entry; |
|
459
|
}else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)" |
|
460
|
" WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){ |
|
461
|
zType = "ticket"; |
|
462
|
goto handle_entry; |
|
463
|
}else if ( db_exists("SELECT 1 FROM mlink WHERE fid = %d", rid) ){ |
|
464
|
zType = "file"; |
|
465
|
goto handle_entry; |
|
466
|
}else{ |
|
467
|
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND; |
|
468
|
goto error; |
|
469
|
} |
|
470
|
|
|
471
|
error: |
|
472
|
assert( 0 != g.json.resultCode ); |
|
473
|
goto veryend; |
|
474
|
|
|
475
|
handle_entry: |
|
476
|
pay = cson_new_object(); |
|
477
|
assert( (NULL != zType) && "Internal dispatching error." ); |
|
478
|
for( ; dispatcher->name; ++dispatcher ){ |
|
479
|
if(0!=fossil_strcmp(dispatcher->name, zType)){ |
|
480
|
continue; |
|
481
|
}else{ |
|
482
|
entry = (*dispatcher->func)(pay, rid); |
|
483
|
break; |
|
484
|
} |
|
485
|
} |
|
486
|
if(entry==0){ |
|
487
|
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND |
|
488
|
/* This is not quite right. We need a new result code |
|
489
|
for this case. */; |
|
490
|
g.zErrMsg = mprintf("Missing implementation for " |
|
491
|
"artifacts of this type."); |
|
492
|
goto error; |
|
493
|
} |
|
494
|
if(!g.json.resultCode){ |
|
495
|
assert( NULL != entry ); |
|
496
|
assert( NULL != zType ); |
|
497
|
cson_object_set( pay, "type", json_new_string(zType) ); |
|
498
|
cson_object_set( pay, "uuid", json_new_string(zUuid) ); |
|
499
|
/*cson_object_set( pay, "name", json_new_string(zName ? zName : zUuid) );*/ |
|
500
|
/*cson_object_set( pay, "rid", cson_value_new_integer(rid) );*/ |
|
501
|
if(cson_value_is_object(entry) && (cson_value_get_object(entry) != pay)){ |
|
502
|
cson_object_set(pay, "artifact", entry); |
|
503
|
} |
|
504
|
} |
|
505
|
veryend: |
|
506
|
blob_reset(&uuid); |
|
507
|
if(g.json.resultCode && pay){ |
|
508
|
cson_free_object(pay); |
|
509
|
pay = NULL; |
|
510
|
} |
|
511
|
return cson_object_value(pay); |
|
512
|
} |
|
513
|
|
|
514
|
#endif /* FOSSIL_ENABLE_JSON */ |
|
515
|
|