#include "VERSION.h"
#include "config.h"
#include "json_artifact.h"
#if INTERFACE
#include "json_detail.h"
#endif
/*
** Internal callback for /json/artifact handlers. rid and uid refer to
** the rid/uid of a given type of artifact, and each callback is
** specialized to return a JSON form of one type of artifact.
*/
typedef cson_value * (*artifact_f)( int rid, char const * uid );
typedef struct ArtifactDispatchEntry {
/**
Artifact type name, e.g. "checkin".
*/
char const * name;
/**
JSON construction callback.
*/
artifact_f func;
/**
Must return true if g.perm has the proper permissions to fetch
this info, else false. If it returns false, func() is skipped
(producing no extra payload output).
*/
char (*permCheck)();
} ArtifactDispatchEntry;
/*
** Generates an artifact Object for the given rid/zUuid. rid
** must refer to a Checkin.
**
** Returned value is NULL or an Object owned by the caller.
*/
cson_value * json_artifact_for_ci( int rid, char const * zUuid, char showFiles ){
char const * zParent = NULL;
cson_value * v = NULL;
Stmt q;
assert( NULL != zUuid );
zParent = db_text(0,
"SELECT uuid FROM plink, blob"
" WHERE plink.cid=%d AND blob.rid=plink.pid AND plink.isprim",
rid
);
db_prepare(&q,
"SELECT uuid, mtime, user, comment,"
" omtime"
" FROM blob, event"
" WHERE blob.rid=%d"
" AND event.objid=%d",
rid, rid
);
if( db_step(&q)==SQLITE_ROW ){
cson_object * o;
cson_value * tmpV = NULL;
v = cson_value_new_object();
o = cson_value_get_object(v);
/*const char *zUuid = db_column_text(&q, 0);*/
char * zTmp;
const char *zUser;
const char *zComment;
char * zEUser, * zEComment;
int mtime, omtime;
#define SET(K,V) cson_object_set(o,(K), (V))
SET("isLeaf", cson_value_new_bool(is_a_leaf(rid)));
SET("uuid",json_new_string(zUuid));
zUser = db_column_text(&q,2);
SET("user",json_new_string(zUser));
zEUser = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_USER, rid);
if(zEUser){
SET("editedBy", json_new_string(zEUser));
free(zEUser);
}
zComment = db_column_text(&q,3);
SET("comment",json_new_string(zComment));
zEComment = db_text(0,
"SELECT value FROM tagxref WHERE tagid=%d AND rid=%d",
TAG_COMMENT, rid);
if(zEComment){
SET("editedComment", json_new_string(zEComment));
free(zEComment);
}
mtime = db_column_int(&q,1);
SET("mtime",json_new_int(mtime));
omtime = db_column_int(&q,4);
if(omtime && (omtime!=mtime)){
SET("omtime",json_new_int(omtime));
}
if(zParent){
SET("parentUuid", json_new_string(zParent));
}
tmpV = json_tags_for_rid(rid);
if(tmpV){
SET("tags",tmpV);
}
if( showFiles ){
cson_value * fileList = json_get_changed_files(rid);
if(fileList){
SET("files",fileList);
}
}
#undef SET
}
db_finalize(&q);
return v;
}
/*
** Sub-impl of /json/artifact for checkins.
*/
static cson_value * json_artifact_ci( int rid, char const * zUuid ){
return json_artifact_for_ci(rid, zUuid, 1);
}
static char perms_can_read(){
return g.perm.Read ? 1 : 0;
}
static ArtifactDispatchEntry ArtifactDispatchList[] = {
{"checkin", json_artifact_ci, perms_can_read},
{"tag", NULL, perms_can_read},
{"ticket", NULL, perms_can_read},
{"wiki", NULL, perms_can_read},
{NULL,NULL,NULL}
};
/*
** Impl of /json/artifact
*/
cson_value * json_page_artifact(){
cson_value * payV = NULL;
cson_object * pay = NULL;
char const * zName = NULL;
char const * zType = NULL;
char const * zUuid = NULL;
Blob uuid = empty_blob;
int rc;
int rid;
zName = g.isHTTP
? json_getenv_cstr("uuid")
: find_option("uuid","u",1);
if(!zName||!*zName){
zName = json_command_arg(g.json.dispatchDepth+1);
if(!zName || !*zName) {
g.json.resultCode = FSL_JSON_E_MISSING_ARGS;
return NULL;
}
}
if( validate16(zName, strlen(zName)) ){
if( db_exists("SELECT 1 FROM ticket WHERE tkt_uuid GLOB '%q*'", zName) ){
zType = "ticket";
goto end_ok;
}
if( db_exists("SELECT 1 FROM tag WHERE tagname GLOB 'event-%q*'", zName) ){
zType = "tag";
goto end_ok;
}
}
blob_set(&uuid,zName);
rc = name_to_uuid(&uuid,-1,"*");
if(1==rc){
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
goto error;
}else if(2==rc){
g.json.resultCode = FSL_JSON_E_AMBIGUOUS_UUID;
goto error;
}
zUuid = blob_str(&uuid);
rid = db_int(0, "SELECT rid FROM blob WHERE uuid='%s'", zUuid);
if(0==rid){
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
goto error;
}
if( db_exists("SELECT 1 FROM mlink WHERE mid=%d", rid)
|| db_exists("SELECT 1 FROM plink WHERE cid=%d", rid)
|| db_exists("SELECT 1 FROM plink WHERE pid=%d", rid)){
zType = "checkin";
goto end_ok;
}else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
" WHERE rid=%d AND tagname LIKE 'wiki-%%'", rid) ){
zType = "wiki";
goto end_ok;
}else if( db_exists("SELECT 1 FROM tagxref JOIN tag USING(tagid)"
" WHERE rid=%d AND tagname LIKE 'tkt-%%'", rid) ){
zType = "ticket";
goto end_ok;
}else{
g.json.resultCode = FSL_JSON_E_RESOURCE_NOT_FOUND;
goto error;
}
error:
assert( 0 != g.json.resultCode );
goto veryend;
end_ok:
payV = cson_value_new_object();
pay = cson_value_get_object(payV);
assert( NULL != zType );
cson_object_set( pay, "type", json_new_string(zType) );
/*cson_object_set( pay, "uuid", json_new_string(zUuid) );*/
cson_object_set( pay, "name", json_new_string(zName ? zName : zUuid) );
cson_object_set( pay, "rid", cson_value_new_integer(rid) );
ArtifactDispatchEntry const * disp = &ArtifactDispatchList[0];
for( ; disp->name; ++disp ){
if(0!=strcmp(disp->name, zType)){
continue;
}else{
cson_value * entry;
if( ! (*disp->permCheck)() ){
break;
}
entry = (*disp->func)(rid, zUuid);
if(entry){
cson_object_set(pay, "artifact", entry);
}
break;
}
}
if( !disp->name ){
cson_object_set(pay,"artifact",
json_new_string("TODO: handle this artifact type!"));
}
veryend:
blob_reset(&uuid);
return payV;
}