summaryrefslogtreecommitdiff
path: root/src/json_dir.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/json_dir.c')
-rw-r--r--src/json_dir.c294
1 files changed, 294 insertions, 0 deletions
diff --git a/src/json_dir.c b/src/json_dir.c
new file mode 100644
index 0000000..47526b6
--- /dev/null
+++ b/src/json_dir.c
@@ -0,0 +1,294 @@
+#ifdef FOSSIL_ENABLE_JSON
+/*
+** Copyright (c) 2011 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** This program is distributed in the hope that it will be useful,
+** but without any warranty; without even the implied warranty of
+** merchantability or fitness for a particular purpose.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*/
+#include "VERSION.h"
+#include "config.h"
+#include "json_dir.h"
+
+#if INTERFACE
+#include "json_detail.h"
+#endif
+
+static cson_value * json_page_dir_list(void);
+/*
+** Mapping of /json/wiki/XXX commands/paths to callbacks.
+*/
+#if 0 /* TODO: Not used? */
+static const JsonPageDef JsonPageDefs_Dir[] = {
+/* Last entry MUST have a NULL name. */
+{NULL,NULL,0}
+};
+#endif
+
+#if 0 /* TODO: Not used? */
+static char const * json_dir_path_extra(void){
+ static char const * zP = NULL;
+ if( !zP ){
+ zP = g.zExtra;
+ while(zP && *zP && ('/'==*zP)){
+ ++zP;
+ }
+ }
+ return zP;
+}
+#endif
+
+/*
+** Impl of /json/dir. 98% of it was taken directly
+** from browse.c::page_dir()
+*/
+static cson_value * json_page_dir_list(void){
+ cson_object * zPayload = NULL; /* return value */
+ cson_array * zEntries = NULL; /* accumulated list of entries. */
+ cson_object * zEntry = NULL; /* a single dir/file entry. */
+ cson_array * keyStore = NULL; /* garbage collector for shared strings. */
+ cson_string * zKeyName = NULL;
+ cson_string * zKeySize = NULL;
+ cson_string * zKeyIsDir = NULL;
+ cson_string * zKeyUuid = NULL;
+ cson_string * zKeyTime = NULL;
+ cson_string * zKeyRaw = NULL;
+ char * zD = NULL;
+ char const * zDX = NULL;
+ int nD;
+ char * zUuid = NULL;
+ char const * zCI = NULL;
+ Manifest * pM = NULL;
+ Stmt q = empty_Stmt;
+ int rid = 0;
+ if( !g.perm.Read ){
+ json_set_err(FSL_JSON_E_DENIED, "Requires 'o' permissions.");
+ return NULL;
+ }
+ zCI = json_find_option_cstr("checkin",NULL,"ci" );
+
+ /* If a specific check-in is requested, fetch and parse it. If the
+ ** specific check-in does not exist, clear zCI. zCI==0 will cause all
+ ** files from all check-ins to be displayed.
+ */
+ if( zCI && *zCI ){
+ pM = manifest_get_by_name(zCI, &rid);
+ if( pM ){
+ zUuid = db_text(0, "SELECT uuid FROM blob WHERE rid=%d", rid);
+ }else{
+ json_set_err(FSL_JSON_E_UNRESOLVED_UUID,
+ "Check-in name [%s] is unresolved.",
+ zCI);
+ return NULL;
+ }
+ }
+
+ /* Jump through some hoops to find the directory name... */
+ zDX = json_find_option_cstr("name",NULL,NULL);
+ if(!zDX && !g.isHTTP){
+ zDX = json_command_arg(g.json.dispatchDepth+1);
+ }
+ if(zDX && (!*zDX || (0==strcmp(zDX,"/")))){
+ zDX = NULL;
+ }
+ zD = zDX ? fossil_strdup(zDX) : NULL;
+ nD = zD ? strlen(zD)+1 : 0;
+ while( nD>1 && zD[nD-2]=='/' ){ zD[(--nD)-1] = 0; }
+
+ sqlite3_create_function(g.db, "pathelement", 2, SQLITE_UTF8, 0,
+ pathelementFunc, 0, 0);
+
+ /* Compute the temporary table "localfiles" containing the names
+ ** of all files and subdirectories in the zD[] directory.
+ **
+ ** Subdirectory names begin with "/". This causes them to sort
+ ** first and it also gives us an easy way to distinguish files
+ ** from directories in the loop that follows.
+ */
+
+ if( zCI ){
+ Stmt ins;
+ ManifestFile *pFile;
+ ManifestFile *pPrev = 0;
+ int nPrev = 0;
+ int c;
+
+ db_multi_exec(
+ "CREATE TEMP TABLE json_dir_files("
+ " n UNIQUE NOT NULL," /* file name */
+ " fn UNIQUE NOT NULL," /* full file name */
+ " u DEFAULT NULL," /* file uuid */
+ " sz DEFAULT -1," /* file size */
+ " mtime DEFAULT NULL" /* file mtime in unix epoch format */
+ ");"
+ );
+
+ db_prepare(&ins,
+ "INSERT OR IGNORE INTO json_dir_files (n,fn,u,sz,mtime) "
+ "SELECT"
+ " pathelement(:path,0),"
+ " CASE WHEN %Q IS NULL THEN '' ELSE %Q||'/' END ||:abspath,"
+ " a.uuid,"
+ " a.size,"
+ " CAST(strftime('%%s',e.mtime) AS INTEGER) "
+ "FROM"
+ " mlink m, "
+ " event e,"
+ " blob a,"
+ " blob b "
+ "WHERE"
+ " e.objid=m.mid"
+ " AND a.rid=m.fid"/*FILE artifact*/
+ " AND b.rid=m.mid"/*CHECKIN artifact*/
+ " AND a.uuid=:uuid",
+ zD, zD
+ );
+ manifest_file_rewind(pM);
+ while( (pFile = manifest_file_next(pM,0))!=0 ){
+ if( nD>0
+ && ((pFile->zName[nD-1]!='/') || (0!=memcmp(pFile->zName, zD, nD-1)))
+ ){
+ continue;
+ }
+ /*printf("zD=%s, nD=%d, pFile->zName=%s\n", zD, nD, pFile->zName);*/
+ if( pPrev
+ && memcmp(&pFile->zName[nD],&pPrev->zName[nD],nPrev)==0
+ && (pFile->zName[nD+nPrev]==0 || pFile->zName[nD+nPrev]=='/')
+ ){
+ continue;
+ }
+ db_bind_text( &ins, ":path", &pFile->zName[nD] );
+ db_bind_text( &ins, ":abspath", &pFile->zName[nD] );
+ db_bind_text( &ins, ":uuid", pFile->zUuid );
+ db_step(&ins);
+ db_reset(&ins);
+ pPrev = pFile;
+ for(nPrev=0; (c=pPrev->zName[nD+nPrev]) && c!='/'; nPrev++){}
+ if( c=='/' ) nPrev++;
+ }
+ db_finalize(&ins);
+ }else if( zD && *zD ){
+ db_multi_exec(
+ "CREATE TEMP VIEW json_dir_files AS"
+ " SELECT DISTINCT(pathelement(name,%d)) AS n,"
+ " %Q||'/'||name AS fn,"
+ " NULL AS u, NULL AS sz, NULL AS mtime"
+ " FROM filename"
+ " WHERE name GLOB '%q/*'"
+ " GROUP BY n",
+ nD, zD, zD
+ );
+ }else{
+ db_multi_exec(
+ "CREATE TEMP VIEW json_dir_files"
+ " AS SELECT DISTINCT(pathelement(name,0)) AS n, NULL AS fn"
+ " FROM filename"
+ );
+ }
+
+ if(zCI){
+ db_prepare( &q, "SELECT"
+ " n as name,"
+ " fn as fullname,"
+ " u as uuid,"
+ " sz as size,"
+ " mtime as mtime "
+ "FROM json_dir_files ORDER BY n");
+ }else{/* UUIDs are all NULL. */
+ db_prepare( &q, "SELECT n, fn FROM json_dir_files ORDER BY n");
+ }
+
+ zKeyName = cson_new_string("name",4);
+ zKeyUuid = cson_new_string("uuid",4);
+ zKeyIsDir = cson_new_string("isDir",5);
+ keyStore = cson_new_array();
+ cson_array_append( keyStore, cson_string_value(zKeyName) );
+ cson_array_append( keyStore, cson_string_value(zKeyUuid) );
+ cson_array_append( keyStore, cson_string_value(zKeyIsDir) );
+
+ if( zCI ){
+ zKeySize = cson_new_string("size",4);
+ cson_array_append( keyStore, cson_string_value(zKeySize) );
+ zKeyTime = cson_new_string("timestamp",9);
+ cson_array_append( keyStore, cson_string_value(zKeyTime) );
+ zKeyRaw = cson_new_string("downloadPath",12);
+ cson_array_append( keyStore, cson_string_value(zKeyRaw) );
+ }
+ zPayload = cson_new_object();
+ cson_object_set_s( zPayload, zKeyName,
+ json_new_string((zD&&*zD) ? zD : "/") );
+ if( zUuid ){
+ cson_object_set( zPayload, "checkin", json_new_string(zUuid) );
+ }
+
+ while( (SQLITE_ROW==db_step(&q)) ){
+ cson_value * name = NULL;
+ char const * n = db_column_text(&q,0);
+ char const isDir = ('/'==*n);
+ zEntry = cson_new_object();
+ if(!zEntries){
+ zEntries = cson_new_array();
+ cson_object_set( zPayload, "entries", cson_array_value(zEntries) );
+ }
+ cson_array_append(zEntries, cson_object_value(zEntry) );
+ if(isDir){
+ name = json_new_string( n+1 );
+ cson_object_set_s(zEntry, zKeyIsDir, cson_value_true() );
+ } else{
+ name = json_new_string( n );
+ }
+ cson_object_set_s(zEntry, zKeyName, name );
+ if( zCI && !isDir){
+ /* Don't add the uuid/size for dir entries - that data refers to
+ one of the files in that directory :/. Entries with no
+ --checkin may refer to N versions, and therefore we cannot
+ associate a single size and uuid with them (and fetching all
+ would be overkill for most use cases).
+ */
+ char const * fullName = db_column_text(&q,1);
+ char const * u = db_column_text(&q,2);
+ sqlite_int64 const sz = db_column_int64(&q,3);
+ sqlite_int64 const ts = db_column_int64(&q,4);
+ cson_object_set_s(zEntry, zKeyUuid, json_new_string( u ) );
+ cson_object_set_s(zEntry, zKeySize,
+ cson_value_new_integer( (cson_int_t)sz ));
+ cson_object_set_s(zEntry, zKeyTime,
+ cson_value_new_integer( (cson_int_t)ts ));
+ cson_object_set_s(zEntry, zKeyRaw,
+ json_new_string_f("/raw/%T?name=%t",
+ fullName, u));
+ }
+ }
+ db_finalize(&q);
+ if(pM){
+ manifest_destroy(pM);
+ }
+ cson_free_array( keyStore );
+
+ free( zUuid );
+ free( zD );
+ return cson_object_value(zPayload);
+}
+
+/*
+** Implements the /json/dir family of pages/commands.
+**
+*/
+cson_value * json_page_dir(void){
+#if 1
+ return json_page_dir_list();
+#else
+ return json_page_dispatch_helper(&JsonPageDefs_Dir[0]);
+#endif
+}
+
+#endif /* FOSSIL_ENABLE_JSON */