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