My initial commit message
[src/app-framework-binder.git] / src / rest-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  * Contain all generic part to handle REST/API
19  */
20
21
22 #include <microhttpd.h>
23 #include <sys/stat.h>
24 #include "../include/local-def.h"
25
26 // proto missing from GCC
27 char *strcasestr(const char *haystack, const char *needle);
28
29
30 // Because of POST call multiple time requestApi we need to free POST handle here
31 STATIC void endRequest(void *cls, struct MHD_Connection *connection, void **con_cls, enum MHD_RequestTerminationCode toe) {
32     AFB_HttpPost *posthandle = *con_cls;
33
34     // if post handle was used let's free everything
35     if (posthandle) {
36         if (verbose) fprintf(stderr, "End Post Request UID=%d\n", posthandle->uid);
37         free(posthandle->data);
38         free(posthandle);
39     }
40 }
41
42
43 PUBLIC json_object* pingSample (AFB_plugin *plugin, AFB_session *session, AFB_request *post) {
44     static pingcount=0;
45     json_object *response;
46     response = jsonNewMessage(AFB_SUCCESS, "Ping Binder Daemon %d", pingcount++);
47     if (verbose) fprintf(stderr, "%d: \n", pingcount);
48     return (response);
49 }
50
51 // Check of apiurl is declare in this plugin and call it
52 STATIC json_object * callPluginApi (AFB_plugin *plugin, AFB_session *session,  AFB_request *request) {
53     json_object *response;
54     int idx;
55     
56     // If a plugin hold this urlpath call its callback
57     for (idx=0; plugin->apis[idx].callback != NULL; idx++) {
58         if (!strcmp (plugin->apis[idx].name, request->api)) {
59            response = plugin->apis[idx].callback (session, request);
60            if (response != NULL) {
61                json_object_object_add (response, "jtype" ,plugin->jtype);
62            }
63            return (response);
64         }   
65     }
66     return (NULL);
67 }
68
69
70 // process rest API query
71 PUBLIC int doRestApi(struct MHD_Connection *connection, AFB_session *session, const char *method, const char* url) {
72
73     char *baseurl, *baseapi, *urlcpy;
74     json_object *jsonResponse, *errMessage;
75     struct MHD_Response *webResponse;
76     const char *serialized, parsedurl;
77     AFB_request request;
78     int  idx, ret;  
79
80     // Extract plugin urlpath from request
81     urlcpy=strdup (url);
82     baseurl = strsep(&urlcpy, "/");
83     if (baseurl == NULL) {
84         errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s", url);
85         goto ExitOnError;
86     }
87     
88     baseapi = strsep(&urlcpy, "/");
89     if (baseapi == NULL) {
90         errMessage = jsonNewMessage(AFB_FATAL, "Invalid Plugin/API call url=%s/%s", baseurl, url);
91         goto ExitOnError;
92     }
93     
94     // build request structure
95     memset (&request, 0, sizeof (request));
96     request.connection = connection;
97     request.url        = url;
98     request.plugin     = baseurl;
99     request.api        = baseapi;
100
101     // if post wait as data may come in multiple calls
102     if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) {
103    
104         request.post="TO Be DONE"; 
105     } else {
106         request.post=NULL;
107     };
108     
109     // Search for a plugin with this urlpath
110     for (idx=0; session->plugins[idx] != NULL; idx++) {
111         if (!strcmp (session->plugins[idx]->prefix, baseurl)) {
112            jsonResponse = callPluginApi (session->plugins[idx], session, &request );
113            free (urlcpy);
114            break;
115         }
116         errMessage = jsonNewMessage(AFB_FATAL, "No Plugin for %s", baseurl);
117         free (urlcpy);
118         goto ExitOnError;
119
120     }
121
122     // plugin callback did not return a valid Json Object
123     if (jsonResponse == NULL) {
124        errMessage = jsonNewMessage(AFB_FATAL, "No Plugin/API for %s/%s", baseurl, baseapi);
125        goto ExitOnError;
126     }
127
128     serialized = json_object_to_json_string(jsonResponse);
129     webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY);
130
131     ret = MHD_queue_response(connection, MHD_HTTP_OK, webResponse);
132     MHD_destroy_response(webResponse);
133     json_object_put(jsonResponse); // decrease reference rqtcount to free the json object
134     return ret;
135
136 ExitOnError:
137     serialized = json_object_to_json_string(errMessage);
138     webResponse = MHD_create_response_from_buffer(strlen(serialized), (void*) serialized, MHD_RESPMEM_MUST_COPY);
139     ret = MHD_queue_response(connection, MHD_HTTP_BAD_REQUEST, webResponse);
140     MHD_destroy_response(webResponse);
141     json_object_put(errMessage); // decrease reference rqtcount to free the json object
142     return ret;
143 }
144
145 // Helper to retreive argument from  connection
146 PUBLIC const char* getQueryValue (AFB_request * request, char *name) {
147     const char *value;
148     
149     value=MHD_lookup_connection_value(request->connection, MHD_GET_ARGUMENT_KIND, name);
150     return (value);
151 }
152
153
154 void *initPlugins (AFB_session *session) {
155     static AFB_plugin *plugins[10]; // no more than 10 plugins !!!
156     AFB_plugin *plugin;
157     int idx;
158     
159     // simulate dynamic library load for plugins
160     // need to implement mods argument to activate only requested mods
161     idx=0;
162
163     // Minimal check before accepting a new plugin
164     plugin =  afsvRegister (session);
165     if (plugin->type != AFB_PLUGIN) {
166         fprintf (stderr, "ERROR: AFSV plugin invalid type=%d!=%d\n", plugin->type, AFB_PLUGIN);
167     } else {
168         // Prepare Plugin name to be added to API response
169         plugin->jtype = json_object_new_string (plugin->prefix);
170         json_object_get (plugin->jtype); // increase reference count to make it permanent
171         plugins[idx++]= plugin;
172     }
173     
174     session->plugins= plugins;   
175 }