9d1ec0b71d69b1cddfa545f24c7b20fda4e17751
[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 #define _GNU_SOURCE
21
22 #include <stdio.h>
23 #include <stdarg.h>
24 #include <string.h>
25
26 /*
27 #include <dirent.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 */
31
32 #include "local-def.h"
33 #include "afb-req-itf.h"
34
35 // handle to hold queryAll values
36 typedef struct {
37      char    *msg;
38      size_t  idx;
39      size_t  len;
40 } queryHandleT;
41
42 // Error code are requested through function to manage json usage count
43 typedef struct {
44   int   level;
45   const char* label;
46   json_object *json;
47 } AFB_errorT;
48
49 static AFB_errorT   AFBerr [AFB_UNAUTH+1];
50 static json_object *jTypeStatic;
51
52 PUBLIC int verbose;
53
54 static const char *ERROR_LABEL[] = {"false", "true", "fatal", "fail", "warning", "empty", "success", "done", "unauth"};
55
56
57 // Helper to retrieve argument from  connection
58 const char* getQueryValue(const AFB_request * request, const char *name) {
59     return afb_req_argument(*request->areq, name);
60 }
61
62 static int getQueryCB (queryHandleT *query, struct afb_arg arg) {
63     if (query->idx >= query->len)
64         return 0;
65     query->idx += (unsigned)snprintf (&query->msg[query->idx], query->len-query->idx, " %s: %s\'%s\',", arg.name, arg.is_file?"FILE=":"", arg.value);
66     return 1; /* continue to iterate */
67 }
68
69 // Helper to retrieve argument from  connection
70 size_t getQueryAll(AFB_request * request, char *buffer, size_t len) {
71     queryHandleT query;
72     buffer[0] = '\0'; // start with an empty string
73     query.msg = buffer;
74     query.len = len;
75     query.idx = 0;
76
77     afb_req_iterate(*request->areq, (void*)getQueryCB, &query);
78     buffer[len-1] = 0;
79     return query.idx >= len ? len - 1 : query.idx;
80 }
81
82 #if 0
83 char* getPostPath (AFB_request *request) {
84     AFB_PostHandle *postHandle = getPostHandle(request);
85     AFB_PostCtx *postFileCtx;
86     
87     if (postHandle == NULL) return NULL;
88     
89     postFileCtx = (AFB_PostCtx*) postHandle->ctx;
90     if (postFileCtx == NULL) return NULL;
91   
92     return (postFileCtx->path);
93 }
94
95 json_object* getPostFile (AFB_request *request, AFB_PostItem *item, char* destination) {
96
97     AFB_PostHandle *postHandle = getPostHandle(request);
98     AFB_PostCtx *postFileCtx;
99     char filepath[512];
100     ssize_t len;
101             
102     // This is called after PostForm and then after DonePostForm
103     if (item == NULL) {
104         json_object* jresp;
105         postFileCtx = (AFB_PostCtx*) postHandle->ctx;
106         
107         // No Post Application Context [something really bad happen]
108         if (postFileCtx == NULL) {
109             request->errcode = MHD_HTTP_EXPECTATION_FAILED;
110             return(jsonNewMessage(AFB_FAIL,"Error: PostForm no PostContext to free\n"));          
111         }
112         
113         // We have a context but last Xform iteration fail or application set a message
114         if (request->jresp != NULL) {
115             jresp = request->jresp;  // retrieve previous error from postCtx
116         } else jresp = jsonNewMessage(AFB_SUCCESS,"getPostFile Post Request done");
117         
118         // Error or not let's free all resources
119         close(postFileCtx->fd);
120         free (postFileCtx->path);
121         free (postFileCtx);
122         return (jresp);  
123     }
124 #if defined(PLEASE_FIX_ME_THE_ERROR_IS_postFileCtx_NOT_INITIALIZED)
125     // Make sure it's a valid PostForm request
126     if (!request->post && request->post->type != AFB_POST_FORM) {
127         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"This is not a valid PostForm request\n");
128         goto ExitOnError;
129     } 
130     
131     // Check this is a file element
132     if (item->filename == NULL) {
133         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"No Filename attached to key=%s\n", item->key);
134         goto ExitOnError;
135     }
136     
137     // Check we got something in buffer
138     if (item->len <= 0) {       
139         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Buffer size NULL key=%s]\n", item->key);
140         goto ExitOnError;
141     }
142 #endif
143     // Extract Application Context from posthandle [NULL == 1st iteration]    
144     postFileCtx = (AFB_PostCtx*) postHandle->ctx;
145
146     // This is the 1st Item iteration let's open output file and allocate necessary resources
147     if (postFileCtx == NULL)  {
148         DIR* destDir;
149         
150         // Create an application specific context
151         postFileCtx = calloc (1, sizeof(AFB_PostCtx)); // May place anything here until post->completeCB handle resources liberation
152         
153         // attach application to postHandle
154         postHandle->ctx = (void*) postFileCtx;   // May place anything here until post->completeCB handle resources liberation  
155         
156         // Build destination directory full path
157         if (destination[0] != '/') {
158            strncpy (filepath, request->config->sessiondir, sizeof(filepath)); 
159            strncat (filepath, "/", sizeof(filepath));
160            strncat (filepath, destination, sizeof(filepath)); 
161         } else strncpy (filepath, destination, sizeof(filepath));
162
163         
164         // make sure destination directory exist
165         destDir = opendir (filepath);
166         if (destDir == NULL) {
167           if (mkdir(filepath,O_RDWR | S_IRWXU | S_IRGRP) < 0) {
168             postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to Create destination directory=[%s] error=%s\n", filepath, strerror(errno));
169             goto ExitOnError;
170           }
171         } else closedir (destDir);
172         
173         strncat (filepath, "/", sizeof(filepath));
174         strncat (filepath, item->filename, sizeof(filepath));  
175
176         postFileCtx->path = strdup (filepath);       
177         if (verbose) fprintf(stderr, "getPostFile path=%s\n", filepath);
178        
179         if((postFileCtx->fd = open(filepath, O_RDWR |O_CREAT, S_IRWXU|S_IRGRP)) <= 0) {
180             postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to Create destination File=[%s] error=%s\n", filepath, strerror(errno));
181             goto ExitOnError;
182         } 
183     } else {     
184         // reuse existing application context
185         postFileCtx = (AFB_PostCtx*) postHandle->ctx;  
186     } 
187
188     // Check we successfully wrote full buffer
189     len = write (postFileCtx->fd, item->data, item->len);
190     if ((ssize_t)item->len != len) {
191         postFileCtx->jresp= jsonNewMessage(AFB_FAIL,"Fail to write file [%s] at [%s] error=\n", item->filename, strerror(errno));
192         goto ExitOnError;
193     }
194   
195     // every intermediary iteration should return Success & NULL
196     request->errcode = MHD_HTTP_OK;
197     return NULL;
198     
199 ExitOnError:    
200     request->errcode = MHD_HTTP_EXPECTATION_FAILED;
201     return NULL;
202 }
203
204 #endif
205
206 static void jsoninit()
207 {
208   int idx, verbosesav;
209
210   if (jTypeStatic)
211         return;
212
213   // initialise JSON constant messages and increase reference count to make them permanent
214   verbosesav = verbose;
215   verbose = 0;  // run initialisation in silent mode
216   jTypeStatic = json_object_new_string ("AFB_message");
217   for (idx = 0; idx <= AFB_UNAUTH; idx++) {
218      AFBerr[idx].level = idx;
219      AFBerr[idx].label = ERROR_LABEL [idx];
220      AFBerr[idx].json  = jsonNewMessage (idx, NULL);
221   }
222   verbose = verbosesav;
223 }
224
225
226 // build an ERROR message and return it as a valid json object
227 json_object *json_add_status (json_object *obj, const char *status, const char *info)
228 {
229         if (obj == NULL)
230                 obj = json_object_new_object();
231         json_object_object_add(obj, "status", json_object_new_string(status));
232         if (info)
233                 json_object_object_add(obj, "info", json_object_new_string(info));
234         return obj;
235 }
236
237 // build an ERROR message and return it as a valid json object
238 json_object *json_add_status_v (json_object *obj, const char *status, const char *info, va_list args)
239 {
240         char *message;
241         if (info == NULL || vasprintf(&message, info, args) < 0)
242                 message = NULL;
243         obj = json_add_status(obj, status, message);
244         free(message);
245         return obj;
246 }
247
248
249 // build an ERROR message and return it as a valid json object
250 json_object *json_add_status_f (json_object *obj, const char *status, const char *info, ...)
251 {
252         va_list args;
253         va_start(args, info);
254         obj = json_add_status_v(obj, status, info, args);
255         va_end(args);
256         return obj;
257 }
258
259
260
261 // build an ERROR message and return it as a valid json object
262 struct json_object *jsonNewMessage (AFB_error level, char* format, ...) {
263    static int count = 0;
264    json_object * AFBResponse;
265    va_list args;
266    char message [512];
267
268   jsoninit();
269
270    // format message
271    if (format != NULL) {
272        va_start(args, format);
273        vsnprintf (message, sizeof (message), format, args);
274        va_end(args);
275    }
276
277    AFBResponse = json_object_new_object();
278    json_object_object_add (AFBResponse, "jtype", json_object_get (jTypeStatic));
279    json_object_object_add (AFBResponse, "status" , json_object_new_string (ERROR_LABEL[level]));
280    if (format != NULL) {
281         json_object_object_add (AFBResponse, "info"   , json_object_new_string (message));
282    }
283    if (verbose) {
284         fprintf (stderr, "AFB:%-6s [%3d]: ", AFBerr [level].label, count++);
285         if (format != NULL) {
286             fprintf (stderr, "%s", message);
287         } else {
288             fprintf (stderr, "No Message");
289         }
290         fprintf (stderr, "\n");
291    }
292
293    return (AFBResponse);
294 }
295
296 #if 0
297 {
298   jtype: "AFB_message"
299   request:
300     {
301       prefix: "",
302       api: "",
303       status: "", /* exist, fail, empty, null, processed */
304       info: "",
305       uuid: "",
306       token: "",
307       timeout: ""
308     }
309   response: ...
310 }
311 #endif
312