Switch to a plugin model
[src/app-framework-binder.git] / plugins / samples / SamplePost.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 "local-def.h"
21
22
23 // In this case or handle is quite basic
24 typedef struct {
25    int   fd; 
26    char *path; 
27    json_object* jerror;
28 } appPostCtx;
29
30 // With content-type=json data are directly avaliable in request->post->data
31 STATIC json_object* GetJsonByPost (AFB_request *request) {
32     json_object* jresp;
33     char query [256];
34     int  len;
35     
36     // check if we have some post data
37     if (request->post == NULL)  request->post->data="NoData"; 
38     
39     // Get all query string [Note real app should probably use value=getQueryValue(request,"key")]
40     len = getQueryAll (request, query, sizeof(query));
41     if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query));
42     
43     // for debug/test return response to caller
44     jresp = jsonNewMessage(AFB_SUCCESS, "GetJsonByPost query={%s} PostData: [%s]", query, request->post->data);
45     
46     return (jresp);    
47 }
48
49 // This function is call when PostForm processing is completed
50 STATIC void DonePostForm (AFB_request *request) { 
51     
52     // Retrieve PostHandle Context from request
53     AFB_PostHandle *postHandle = getPostHandle(request);
54     appPostCtx *appCtx= (appPostCtx*) postHandle->ctx;
55
56     if (verbose) fprintf (stderr, "DonePostForm file=[%s]upload done\n", appCtx->path);
57     
58
59 }
60
61
62 // PostForm callback is called multiple times (one or each key within form, or once per file buffer)
63 // When processing POST_FORM request->data holds a PostHandle and not data directly as for POST_JSON
64 // When file has been fully uploaded call is call with item==NULL it is application responsibility to free appPostCtx
65 STATIC json_object* UploadFile (AFB_request *request, AFB_PostItem *item) {
66
67     AFB_PostHandle *postHandle = getPostHandle(request);
68     appPostCtx *appCtx;
69     char filepath[512];
70     int len;
71             
72     // This is called after PostForm and then after DonePostForm
73     if (item == NULL) {
74         json_object* jresp;
75         appCtx = (appPostCtx*) postHandle->ctx;
76         
77         // No Post Application Context [something really bad happen]
78         if (appCtx == NULL) {
79             request->errcode = MHD_HTTP_EXPECTATION_FAILED;
80             return(jsonNewMessage(AFB_FAIL,"Error: PostForm no PostContext to free\n"));          
81         }
82         
83         // We have a context but last Xform iteration fail.
84         if (appCtx->jerror != NULL) {
85             request->errcode = MHD_HTTP_EXPECTATION_FAILED;
86             jresp = appCtx->jerror;  // retrieve previous error from postCtx
87         } else jresp = jsonNewMessage(AFB_FAIL,"UploadFile Post Request file=[%s] done", appCtx->path);
88         
89         // Error or not let's free all resources
90         close(appCtx->fd);
91         free (appCtx->path);
92         free (appCtx);
93         return (jresp);  
94     }
95     
96     // Make sure it's a valid PostForm request
97     if (!request->post && request->post->type != AFB_POST_FORM) {
98         appCtx->jerror= jsonNewMessage(AFB_FAIL,"This is not a valid PostForm request\n");
99         goto ExitOnError;
100     } 
101     
102     // Check this is a file element
103     if (item->filename == NULL) {
104         appCtx->jerror= jsonNewMessage(AFB_FAIL,"No Filename attached to key=%s\n", item->key);
105         goto ExitOnError;
106     }
107     
108     // Check we got something in buffer
109     if (item->len <= 0) {       
110         appCtx->jerror= jsonNewMessage(AFB_FAIL,"Buffer size NULL key=%s]\n", item->key);
111         goto ExitOnError;
112     }
113
114     // Extract Application Context from posthandle [NULL == 1st iteration]    
115     appCtx = (appPostCtx*) postHandle->ctx;
116
117     // This is the 1st Item iteration let's open output file and allocate necessary resources
118     if (appCtx == NULL)  {
119         // Create an application specific context
120         appCtx = calloc (1, sizeof(appPostCtx)); // May place anything here until post->completeCB handle resources liberation
121         appCtx->path = strdup (filepath);
122         
123         // attach application to postHandle
124         postHandle->ctx = (void*) appCtx;   // May place anything here until post->completeCB handle resources liberation  
125         
126         // Allocate an application specific handle to this post
127         strncpy (filepath, request->config->sessiondir, sizeof(filepath));
128         strncat (filepath, "/", sizeof(filepath));
129         strncat (filepath, item->filename, sizeof(filepath));  
130
131         if((appCtx->fd = open(filepath, O_RDWR |O_CREAT, S_IRWXU|S_IRGRP)) < 0) {
132             appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to Create destination=[%s] error=%s\n", filepath, strerror(errno));
133             goto ExitOnError;
134         } 
135     } else {     
136         // reuse existing application context
137         appCtx = (appPostCtx*) postHandle->ctx;  
138     } 
139
140     // Check we successfully wrote full buffer
141     len = write (appCtx->fd, item->data, item->len);
142     if (item->len != len) {
143         appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to write file [%s] at [%s] error=\n", item->filename, strerror(errno));
144         goto ExitOnError;
145     }
146   
147     // every intermediary iteration should return Success & NULL
148     request->errcode = MHD_HTTP_OK;
149     return NULL;
150     
151 ExitOnError:    
152     request->errcode = MHD_HTTP_EXPECTATION_FAILED;
153     return NULL;
154 }
155
156
157 // NOTE: this sample does not use session to keep test a basic as possible
158 //       in real application upload-xxx should be protected with AFB_SESSION_CHECK
159 STATIC  AFB_restapi pluginApis[]= {
160   {"ping"         , AFB_SESSION_NONE  , (AFB_apiCB)apiPingTest    ,"Ping Rest Test Service"},
161   {"upload-json"  , AFB_SESSION_NONE  , (AFB_apiCB)GetJsonByPost  ,"Demo for Json Buffer on Post"},
162   {"upload-image" , AFB_SESSION_NONE  , (AFB_apiCB)UploadFile     ,"Demo for file upload"},
163   {"upload-music" , AFB_SESSION_NONE  , (AFB_apiCB)UploadFile     ,"Demo for file upload"},
164   {"upload-appli" , AFB_SESSION_NONE  , (AFB_apiCB)UploadFile     ,"Demo for file upload"},
165   {NULL}
166 };
167
168 PUBLIC AFB_plugin *pluginRegister () {
169     AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
170     plugin->type  = AFB_PLUGIN_JSON; 
171     plugin->info  = "Application Framework Binder Service";
172     plugin->prefix= "post";  // url base
173     plugin->apis  = pluginApis;
174     plugin->handle= (void*) "What ever you want";
175     
176     return (plugin);
177 };