summaryrefslogtreecommitdiffstats
path: root/plugins/samples/SamplePost.c
blob: 85485015fb858ee0086eb1a9833043399e59fe5b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/*
 * Copyright (C) 2015 "IoT.bzh"
 * Author "Fulup Ar Foll"
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "local-def.h"


// In this case or handle is quite basic
typedef struct {
   int   fd; 
   char *path; 
   json_object* jerror;
} appPostCtx;

// With content-type=json data are directly avaliable in request->post->data
STATIC json_object* GetJsonByPost (AFB_request *request) {
    json_object* jresp;
    char query [256];
    int  len;
    
    // check if we have some post data
    if (request->post == NULL)  request->post->data="NoData"; 
    
    // Get all query string [Note real app should probably use value=getQueryValue(request,"key")]
    len = getQueryAll (request, query, sizeof(query));
    if (len == 0) strncpy (query, "NoSearchQueryList", sizeof(query));
    
    // for debug/test return response to caller
    jresp = jsonNewMessage(AFB_SUCCESS, "GetJsonByPost query={%s} PostData: [%s]", query, request->post->data);
    
    return (jresp);    
}

// This function is call when PostForm processing is completed
STATIC void DonePostForm (AFB_request *request) { 
    
    // Retrieve PostHandle Context from request
    AFB_PostHandle *postHandle = getPostHandle(request);
    appPostCtx *appCtx= (appPostCtx*) postHandle->ctx;

    if (verbose) fprintf (stderr, "DonePostForm file=[%s]upload done\n", appCtx->path);
    

}


// PostForm callback is called multiple times (one or each key within form, or once per file buffer)
// When processing POST_FORM request->data holds a PostHandle and not data directly as for POST_JSON
// When file has been fully uploaded call is call with item==NULL it is application responsibility to free appPostCtx
STATIC json_object* UploadFile (AFB_request *request, AFB_PostItem *item) {

    AFB_PostHandle *postHandle = getPostHandle(request);
    appPostCtx *appCtx;
    char filepath[512];
    int len;
            
    // This is called after PostForm and then after DonePostForm
    if (item == NULL) {
        json_object* jresp;
        appCtx = (appPostCtx*) postHandle->ctx;
        
        // No Post Application Context [something really bad happen]
        if (appCtx == NULL) {
            request->errcode = MHD_HTTP_EXPECTATION_FAILED;
            return(jsonNewMessage(AFB_FAIL,"Error: PostForm no PostContext to free\n"));          
        }
        
        // We have a context but last Xform iteration fail.
        if (appCtx->jerror != NULL) {
            request->errcode = MHD_HTTP_EXPECTATION_FAILED;
            jresp = appCtx->jerror;  // retrieve previous error from postCtx
        } else jresp = jsonNewMessage(AFB_FAIL,"UploadFile Post Request file=[%s] done", appCtx->path);
        
        // Error or not let's free all resources
        close(appCtx->fd);
        free (appCtx->path);
        free (appCtx);
        return (jresp);  
    }
    
    // Make sure it's a valid PostForm request
    if (!request->post && request->post->type != AFB_POST_FORM) {
        appCtx->jerror= jsonNewMessage(AFB_FAIL,"This is not a valid PostForm request\n");
        goto ExitOnError;
    } 
    
    // Check this is a file element
    if (item->filename == NULL) {
        appCtx->jerror= jsonNewMessage(AFB_FAIL,"No Filename attached to key=%s\n", item->key);
        goto ExitOnError;
    }
    
    // Check we got something in buffer
    if (item->len <= 0) {       
        appCtx->jerror= jsonNewMessage(AFB_FAIL,"Buffer size NULL key=%s]\n", item->key);
        goto ExitOnError;
    }

    // Extract Application Context from posthandle [NULL == 1st iteration]    
    appCtx = (appPostCtx*) postHandle->ctx;

    // This is the 1st Item iteration let's open output file and allocate necessary resources
    if (appCtx == NULL)  {
        // Create an application specific context
        appCtx = calloc (1, sizeof(appPostCtx)); // May place anything here until post->completeCB handle resources liberation
        appCtx->path = strdup (filepath);
        
        // attach application to postHandle
        postHandle->ctx = (void*) appCtx;   // May place anything here until post->completeCB handle resources liberation  
        
        // Allocate an application specific handle to this post
        strncpy (filepath, request->config->sessiondir, sizeof(filepath));
        strncat (filepath, "/", sizeof(filepath));
        strncat (filepath, item->filename, sizeof(filepath));  

        if((appCtx->fd = open(filepath, O_RDWR |O_CREAT, S_IRWXU|S_IRGRP)) < 0) {
            appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to Create destination=[%s] error=%s\n", filepath, strerror(errno));
            goto ExitOnError;
        } 
    } else {     
        // reuse existing application context
        appCtx = (appPostCtx*) postHandle->ctx;  
    } 

    // Check we successfully wrote full buffer
    len = write (appCtx->fd, item->data, item->len);
    if (item->len != len) {
        appCtx->jerror= jsonNewMessage(AFB_FAIL,"Fail to write file [%s] at [%s] error=\n", item->filename, strerror(errno));
        goto ExitOnError;
    }
  
    // every intermediary iteration should return Success & NULL
    request->errcode = MHD_HTTP_OK;
    return NULL;
    
ExitOnError:    
    request->errcode = MHD_HTTP_EXPECTATION_FAILED;
    return NULL;
}


// NOTE: this sample does not use session to keep test a basic as possible
//       in real application upload-xxx should be protected with AFB_SESSION_CHECK
STATIC  AFB_restapi pluginApis[]= {
  {"ping"         , AFB_SESSION_NONE  , (AFB_apiCB)apiPingTest    ,"Ping Rest Test Service"},
  {"upload-json"  , AFB_SESSION_NONE  , (AFB_apiCB)GetJsonByPost  ,"Demo for Json Buffer on Post"},
  {"upload-image" , AFB_SESSION_NONE  , (AFB_apiCB)UploadFile     ,"Demo for file upload"},
  {"upload-music" , AFB_SESSION_NONE  , (AFB_apiCB)UploadFile     ,"Demo for file upload"},
  {"upload-appli" , AFB_SESSION_NONE  , (AFB_apiCB)UploadFile     ,"Demo for file upload"},
  {NULL}
};

PUBLIC AFB_plugin *samplePostRegister () {
    AFB_plugin *plugin = malloc (sizeof (AFB_plugin));
    plugin->type  = AFB_PLUGIN_JSON; 
    plugin->info  = "Application Framework Binder Service";
    plugin->prefix= "post";  // url base
    plugin->apis  = pluginApis;
    plugin->handle= (void*) "What ever you want";
    
    return (plugin);
};