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