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