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 ** drh@hwaci.com
15 ** http://www.hwaci.com/drh/
16 **
17 */
18
19 #include "config.h"
20 #include "json_report.h"
21
22 #if INTERFACE
23 #include "json_detail.h"
24 #endif
25
26
27 static cson_value * json_report_create();
28 static cson_value * json_report_get();
29 static cson_value * json_report_list();
30 static cson_value * json_report_run();
31 static cson_value * json_report_save();
32
33 /*
34 ** Mapping of /json/report/XXX commands/paths to callbacks.
35 */
36 static const JsonPageDef JsonPageDefs_Report[] = {
37 {"create", json_report_create, 0},
38 {"get", json_report_get, 0},
39 {"list", json_report_list, 0},
40 {"run", json_report_run, 0},
41 {"save", json_report_save, 0},
42 /* Last entry MUST have a NULL name. */
43 {NULL,NULL,0}
44 };
45 /*
46 ** Implementation of the /json/report page.
47 **
48 **
49 */
json_page_report()50 cson_value * json_page_report(){
51 if(!g.perm.RdTkt && !g.perm.NewTkt ){
52 json_set_err(FSL_JSON_E_DENIED,
53 "Requires 'r' or 'n' permissions.");
54 return NULL;
55 }
56 return json_page_dispatch_helper(JsonPageDefs_Report);
57 }
58
59 /*
60 ** Searches the environment for a "report" parameter
61 ** (CLI: -report/-r #).
62 **
63 ** If one is not found and argPos is >0 then json_command_arg()
64 ** is checked.
65 **
66 ** Returns >0 (the report number) on success .
67 */
json_report_get_number(int argPos)68 static int json_report_get_number(int argPos){
69 int nReport = json_find_option_int("report",NULL,"r",-1);
70 if( (nReport<=0) && cson_value_is_integer(g.json.reqPayload.v)){
71 nReport = cson_value_get_integer(g.json.reqPayload.v);
72 }
73 if( (nReport <= 0) && (argPos>0) ){
74 char const * arg = json_command_arg(argPos);
75 if(arg && fossil_isdigit(*arg)) {
76 nReport = atoi(arg);
77 }
78 }
79 return nReport;
80 }
81
json_report_create()82 static cson_value * json_report_create(){
83 json_set_err(FSL_JSON_E_NYI, NULL);
84 return NULL;
85 }
86
json_report_get()87 static cson_value * json_report_get(){
88 int nReport;
89 Stmt q = empty_Stmt;
90 cson_value * pay = NULL;
91
92 if(!g.perm.TktFmt){
93 json_set_err(FSL_JSON_E_DENIED,
94 "Requires 't' privileges.");
95 return NULL;
96 }
97 nReport = json_report_get_number(3);
98 if(nReport <=0){
99 json_set_err(FSL_JSON_E_MISSING_ARGS,
100 "Missing or invalid 'report' (-r) parameter.");
101 return NULL;
102 }
103
104 db_prepare(&q,"SELECT rn AS report,"
105 " owner AS owner,"
106 " title AS title,"
107 " cast(strftime('%%s',mtime) as int) as timestamp,"
108 " cols as columns,"
109 " sqlcode as sqlCode"
110 " FROM reportfmt"
111 " WHERE rn=%d",
112 nReport);
113 if( SQLITE_ROW != db_step(&q) ){
114 db_finalize(&q);
115 json_set_err(FSL_JSON_E_RESOURCE_NOT_FOUND,
116 "Report #%d not found.", nReport);
117 return NULL;
118 }
119 pay = cson_sqlite3_row_to_object(q.pStmt);
120 db_finalize(&q);
121 return pay;
122 }
123
124 /*
125 ** Impl of /json/report/list.
126 */
json_report_list()127 static cson_value * json_report_list(){
128 Blob sql = empty_blob;
129 cson_value * pay = NULL;
130 if(!g.perm.RdTkt){
131 json_set_err(FSL_JSON_E_DENIED,
132 "Requires 'r' privileges.");
133 return NULL;
134 }
135 blob_append(&sql, "SELECT"
136 " rn AS report,"
137 " title as title,"
138 " owner as owner"
139 " FROM reportfmt"
140 " WHERE 1"
141 " ORDER BY title",
142 -1);
143 pay = json_sql_to_array_of_obj(&sql, NULL, 1);
144 if(!pay){
145 json_set_err(FSL_JSON_E_UNKNOWN,
146 "Quite unexpected: no ticket reports found.");
147 }
148 return pay;
149 }
150
151 /*
152 ** Impl for /json/report/run
153 **
154 ** Options/arguments:
155 **
156 ** report=int (CLI: -report # or -r #) is the report number to run.
157 **
158 ** limit=int (CLI: -limit # or -n #) -n is for compat. with other commands.
159 **
160 ** format=a|o Specifies result format: a=each row is an arry, o=each
161 ** row is an object. Default=o.
162 */
json_report_run()163 static cson_value * json_report_run(){
164 int nReport;
165 Stmt q = empty_Stmt;
166 cson_object * pay = NULL;
167 cson_array * tktList = NULL;
168 char const * zFmt;
169 char * zTitle = NULL;
170 Blob sql = empty_blob;
171 int limit = 0;
172 cson_value * colNames = NULL;
173 int i;
174
175 if(!g.perm.RdTkt){
176 json_set_err(FSL_JSON_E_DENIED,
177 "Requires 'r' privileges.");
178 return NULL;
179 }
180 nReport = json_report_get_number(3);
181 if(nReport <=0){
182 json_set_err(FSL_JSON_E_MISSING_ARGS,
183 "Missing or invalid 'number' (-n) parameter.");
184 goto error;
185 }
186 zFmt = json_find_option_cstr2("format",NULL,"f",3);
187 if(!zFmt) zFmt = "o";
188 db_prepare(&q,
189 "SELECT sqlcode, "
190 " title"
191 " FROM reportfmt"
192 " WHERE rn=%d",
193 nReport);
194 if(SQLITE_ROW != db_step(&q)){
195 json_set_err(FSL_JSON_E_INVALID_ARGS,
196 "Report number %d not found.",
197 nReport);
198 db_finalize(&q);
199 goto error;
200 }
201
202 limit = json_find_option_int("limit",NULL,"n",-1);
203
204
205 /* Copy over report's SQL...*/
206 blob_append(&sql, db_column_text(&q,0), -1);
207 zTitle = mprintf("%s", db_column_text(&q,1));
208 db_finalize(&q);
209 db_prepare(&q, "%s", blob_sql_text(&sql));
210
211 /** Build the response... */
212 pay = cson_new_object();
213
214 cson_object_set(pay, "report", json_new_int(nReport));
215 cson_object_set(pay, "title", json_new_string(zTitle));
216 if(limit>0){
217 cson_object_set(pay, "limit", json_new_int((limit<0) ? 0 : limit));
218 }
219 free(zTitle);
220 zTitle = NULL;
221
222 if(g.perm.TktFmt){
223 cson_object_set(pay, "sqlcode",
224 cson_value_new_string(blob_str(&sql),
225 (unsigned int)blob_size(&sql)));
226 }
227 blob_reset(&sql);
228
229 colNames = cson_sqlite3_column_names(q.pStmt);
230 cson_object_set( pay, "columnNames", colNames);
231 for( i = 0 ; ((limit>0) ?(i < limit) : 1)
232 && (SQLITE_ROW == db_step(&q));
233 ++i){
234 cson_value * row = ('a'==*zFmt)
235 ? cson_sqlite3_row_to_array(q.pStmt)
236 : cson_sqlite3_row_to_object2(q.pStmt,
237 cson_value_get_array(colNames));
238 ;
239 if(row && !tktList){
240 tktList = cson_new_array();
241 }
242 cson_array_append(tktList, row);
243 }
244 db_finalize(&q);
245 cson_object_set(pay, "tickets",
246 tktList ? cson_array_value(tktList) : cson_value_null());
247
248 goto end;
249
250 error:
251 assert(0 != g.json.resultCode);
252 cson_value_free( cson_object_value(pay) );
253 pay = NULL;
254 end:
255
256 return pay ? cson_object_value(pay) : NULL;
257
258 }
259
json_report_save()260 static cson_value * json_report_save(){
261 return NULL;
262 }
263 #endif /* FOSSIL_ENABLE_JSON */
264