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