simplification of config
[src/app-framework-binder.git] / src / helper-api.c
1 /*
2  * Copyright (C) 2015 "IoT.bzh"
3  * Author "Fulup Ar Foll"
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  * 
18  */
19
20 #include "../include/local-def.h"
21 #include <dirent.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <stdarg.h>
25
26
27 // handle to hold queryAll values
28 typedef struct {
29      char    *msg;
30      int     idx;
31      size_t  len;
32 } queryHandleT;
33
34 // Error code are requested through function to manage json usage count
35 typedef struct {
36   int   level;
37   const char* label;
38   json_object *json;
39 } AFB_errorT;
40
41 static AFB_errorT   AFBerr [AFB_SUCCESS+1];
42 static json_object *jTypeStatic;
43
44 PUBLIC int verbose;
45
46 static const char *ERROR_LABEL[] = {"false", "true", "fatal", "fail", "warning", "empty", "success"};
47
48 /* ------------------------------------------------------------------------------
49  * Get localtime and return in a string
50  * ------------------------------------------------------------------------------ */
51
52 PUBLIC char * configTime (void) {
53   static char reqTime [26];
54   time_t tt;
55   struct tm *rt;
56
57   /* Get actual Date and Time */
58   time (&tt);
59   rt = localtime (&tt);
60
61   strftime (reqTime, sizeof (reqTime), "(%d-%b %H:%M)",rt);
62
63   // return pointer on static data
64   return (reqTime);
65 }
66
67
68 // Sample Generic Ping Debug API
69 json_object* getPingTest(AFB_request *request) {
70     static int pingcount = 0;
71     json_object *response;
72     char query  [256];
73     char session[256];
74     int len;
75     
76     // request all query key/value
77     len = getQueryAll (request, query, sizeof(query));
78     if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query));
79     
80     // check if we have some post data
81     if (request->post == NULL)  request->post->data="NoData"; 
82           
83     // return response to caller
84     response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon count=%d uuid=%s query={%s} session={0x%x} PostData: [%s] "
85                , pingcount++, request->uuid, query, session, request->post->data);
86     return (response);
87 }
88
89
90 // Helper to retrieve argument from  connection
91 const char* getQueryValue(const AFB_request * request, const char *name) {
92     const char *value;
93
94     value = MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name);
95     return (value);
96 }
97
98 static int getQueryCB (void*handle, enum MHD_ValueKind kind, const char *key, const char *value) {
99     queryHandleT *query = (queryHandleT*)handle;
100         
101     query->idx += snprintf (&query->msg[query->idx],query->len," %s: \'%s\',", key, value);
102     return MHD_YES; /* continue to iterate */
103 }
104
105 // Helper to retrieve argument from  connection
106 int getQueryAll(AFB_request * request, char *buffer, size_t len) {
107     queryHandleT query;
108     buffer[0] = '\0'; // start with an empty string
109     query.msg = buffer;
110     query.len = len;
111     query.idx = 0;
112
113     MHD_get_connection_values (request->connection, MHD_GET_ARGUMENT_KIND, getQueryCB, &query);
114     return (len);
115 }
116
117 // Helper to retrieve POST handle
118 AFB_PostHandle* getPostHandle (AFB_request *request) {
119     if (request->post == NULL) return (NULL);
120     return ((AFB_PostHandle*) request->post->data);
121 }
122
123 // Helper to retrieve POST file context
124 AFB_PostCtx* getPostContext (AFB_request *request) {
125     AFB_PostHandle* postHandle;
126     if (request->post == NULL) return (NULL);
127     
128     postHandle = (AFB_PostHandle*) request->post->data;
129     if (postHandle == NULL) return NULL;
130        
131     return ((AFB_PostCtx*) postHandle->ctx);
132 }
133
134 char* getPostPath (AFB_request *request) {
135     AFB_PostHandle *postHandle = getPostHandle(request);
136     AFB_PostCtx *postFileCtx;
137     
138     if (postHandle == NULL) return NULL;
139     
140     postFileCtx = (AFB_PostCtx*) postHandle->ctx;
141     if (postFileCtx == NULL) return NULL;
142   
143     return (postFileCtx->path);
144 }
145
146 json_object* getPostFile (AFB_request *request, AFB_PostItem *item, char* destination) {
147
148     AFB_PostHandle *postHandle = getPostHandle(request);
149     AFB_PostCtx *postFileCtx;
150     char filepath[512];
151     ssize_t len;
152             
153     // This is called after PostForm and then after DonePostForm
154     if (item == NULL) {
155         json_object* jresp;
156         postFileCtx = (AFB_PostCtx*) postHandle->ctx;
157         
158         // No Post Application Context [something really bad happen]
159         if (postFileCtx == NULL) {
160             request->errcode = MHD_HTTP_EXPECTATION_FAILED;
161             return(jsonNewMessage(AFB_FAIL,"Error: PostForm no PostContext to free\n"));          
162         }
163         
164         // We have a context but last Xform iteration fail or application set a message
165         if (request->jresp != NULL) {
166             jresp = request->jresp;  // retrieve previous error from postCtx
167         } else jresp = jsonNewMessage(AFB_SUCCESS,"getPostFile Post Request done");
168         
169         // Error or not let's free all resources
170         close(postFileCtx->fd);
171         free (postFileCtx->path);
172         free (postFileCtx);
173         return (jresp);  
174     }
175 #if defined(PLEASE_FIX_ME_THE_ERROR_IS_postFileCtx_NOT_INITIALIZED)
176     // Make sure it's a valid PostForm request
177     if (!request->post && request->post->type != AFB_POST_FORM) {
178         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"This is not a valid PostForm request\n");
179         goto ExitOnError;
180     } 
181     
182     // Check this is a file element
183     if (item->filename == NULL) {
184         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"No Filename attached to key=%s\n", item->key);
185         goto ExitOnError;
186     }
187     
188     // Check we got something in buffer
189     if (item->len <= 0) {       
190         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Buffer size NULL key=%s]\n", item->key);
191         goto ExitOnError;
192     }
193 #endif
194     // Extract Application Context from posthandle [NULL == 1st iteration]    
195     postFileCtx = (AFB_PostCtx*) postHandle->ctx;
196
197     // This is the 1st Item iteration let's open output file and allocate necessary resources
198     if (postFileCtx == NULL)  {
199         DIR* destDir;
200         
201         // Create an application specific context
202         postFileCtx = calloc (1, sizeof(AFB_PostCtx)); // May place anything here until post->completeCB handle resources liberation
203         
204         // attach application to postHandle
205         postHandle->ctx = (void*) postFileCtx;   // May place anything here until post->completeCB handle resources liberation  
206         
207         // Build destination directory full path
208         if (destination[0] != '/') {
209            strncpy (filepath, request->config->sessiondir, sizeof(filepath)); 
210            strncat (filepath, "/", sizeof(filepath));
211            strncat (filepath, destination, sizeof(filepath)); 
212         } else strncpy (filepath, destination, sizeof(filepath));
213
214         
215         // make sure destination directory exist
216         destDir = opendir (filepath);
217         if (destDir == NULL) {
218           if (mkdir(filepath,O_RDWR | S_IRWXU | S_IRGRP) < 0) {
219             postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to Create destination directory=[%s] error=%s\n", filepath, strerror(errno));
220             goto ExitOnError;
221           }
222         } else closedir (destDir);
223         
224         strncat (filepath, "/", sizeof(filepath));
225         strncat (filepath, item->filename, sizeof(filepath));  
226
227         postFileCtx->path = strdup (filepath);       
228         if (verbose) fprintf(stderr, "getPostFile path=%s\n", filepath);
229        
230         if((postFileCtx->fd = open(filepath, O_RDWR |O_CREAT, S_IRWXU|S_IRGRP)) <= 0) {
231             postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to Create destination File=[%s] error=%s\n", filepath, strerror(errno));
232             goto ExitOnError;
233         } 
234     } else {     
235         // reuse existing application context
236         postFileCtx = (AFB_PostCtx*) postHandle->ctx;  
237     } 
238
239     // Check we successfully wrote full buffer
240     len = write (postFileCtx->fd, item->data, item->len);
241     if ((ssize_t)item->len != len) {
242         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to write file [%s] at [%s] error=\n", item->filename, strerror(errno));
243         goto ExitOnError;
244     }
245   
246     // every intermediary iteration should return Success & NULL
247     request->errcode = MHD_HTTP_OK;
248     return NULL;
249     
250 ExitOnError:    
251     request->errcode = MHD_HTTP_EXPECTATION_FAILED;
252     return NULL;
253 }
254
255
256
257 static void jsoninit()
258 {
259   int idx, verbosesav;
260
261   if (jTypeStatic)
262         return;
263
264   // initialise JSON constant messages and increase reference count to make them permanent
265   verbosesav = verbose;
266   verbose = 0;  // run initialisation in silent mode
267   jTypeStatic = json_object_new_string ("AFB_message");
268   for (idx = 0; idx <= AFB_SUCCESS; idx++) {
269      AFBerr[idx].level = idx;
270      AFBerr[idx].label = ERROR_LABEL [idx];
271      AFBerr[idx].json  = jsonNewMessage (idx, NULL);
272   }
273   verbose = verbosesav;
274 }
275
276
277
278 // get JSON object from error level and increase its reference count
279 struct json_object *jsonNewStatus (AFB_error level)
280 {
281   jsoninit();
282   json_object *target =  AFBerr[level].json;
283   json_object_get (target);
284
285   return (target);
286 }
287
288 // get AFB object type with adequate usage count
289 struct json_object *jsonNewjtype (void)
290 {
291   jsoninit();
292   json_object_get (jTypeStatic); // increase reference count
293   return (jTypeStatic);
294 }
295
296 // build an ERROR message and return it as a valid json object
297 struct json_object *jsonNewMessage (AFB_error level, char* format, ...) {
298    static int count = 0;
299    json_object * AFBResponse;
300    va_list args;
301    char message [512];
302
303   jsoninit();
304
305    // format message
306    if (format != NULL) {
307        va_start(args, format);
308        vsnprintf (message, sizeof (message), format, args);
309        va_end(args);
310    }
311
312    AFBResponse = json_object_new_object();
313    json_object_object_add (AFBResponse, "jtype", jsonNewjtype ());
314    json_object_object_add (AFBResponse, "status" , json_object_new_string (ERROR_LABEL[level]));
315    if (format != NULL) {
316         json_object_object_add (AFBResponse, "info"   , json_object_new_string (message));
317    }
318    if (verbose) {
319         fprintf (stderr, "AFB:%-6s [%3d]: ", AFBerr [level].label, count++);
320         if (format != NULL) {
321             fprintf (stderr, "%s", message);
322         } else {
323             fprintf (stderr, "No Message");
324         }
325         fprintf (stderr, "\n");
326    }
327
328    return (AFBResponse);
329 }
330
331 // Dump a message on stderr
332 void jsonDumpObject (struct json_object * jObject) {
333
334    if (verbose) {
335         fprintf (stderr, "AFB:dump [%s]\n", json_object_to_json_string(jObject));
336    }
337 }
338