aboutsummaryrefslogtreecommitdiff
path: root/src/rest-plugins/plugin_rest_gns.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/rest-plugins/plugin_rest_gns.c')
-rw-r--r--src/rest-plugins/plugin_rest_gns.c476
1 files changed, 476 insertions, 0 deletions
diff --git a/src/rest-plugins/plugin_rest_gns.c b/src/rest-plugins/plugin_rest_gns.c
new file mode 100644
index 000000000..0bf4198fc
--- /dev/null
+++ b/src/rest-plugins/plugin_rest_gns.c
@@ -0,0 +1,476 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2015 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18/**
19 * @author Philippe Buschmann
20 * @file gns/plugin_rest_gns.c
21 * @brief GNUnet Gns REST plugin
22 */
23
24#include "platform.h"
25#include "gnunet_rest_plugin.h"
26#include "gnunet_rest_lib.h"
27#include "gnunet_json_lib.h"
28#include "gnunet_gnsrecord_lib.h"
29#include "gnunet_gns_service.h"
30#include "microhttpd.h"
31#include <jansson.h>
32
33/**
34 * Rest API GNS Namespace
35 */
36#define GNUNET_REST_API_NS_GNS "/gns"
37
38/**
39 * Rest API GNS Parameter record_type
40 */
41#define GNUNET_REST_GNS_PARAM_RECORD_TYPE "record_type"
42
43/**
44 * Rest API GNS ERROR Unknown Error
45 */
46#define GNUNET_REST_GNS_ERROR_UNKNOWN "Unknown Error"
47
48/**
49 * Rest API GNS ERROR Record not found
50 */
51#define GNUNET_REST_GNS_NOT_FOUND "Record not found"
52
53/**
54 * The configuration handle
55 */
56const struct GNUNET_CONFIGURATION_Handle *cfg;
57
58/**
59 * HTTP methods allows for this plugin
60 */
61static char* allow_methods;
62
63/**
64 * @brief struct returned by the initialization function of the plugin
65 */
66struct Plugin
67{
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
69};
70
71/**
72 * The request handle
73 */
74struct RequestHandle
75{
76
77 /**
78 * Connection to GNS
79 */
80 struct GNUNET_GNS_Handle *gns;
81
82 /**
83 * Active GNS lookup
84 */
85 struct GNUNET_GNS_LookupWithTldRequest *gns_lookup;
86
87 /**
88 * Name to look up
89 */
90 char *name;
91
92 /**
93 * Record type to look up
94 */
95 int record_type;
96
97 /**
98 * Rest connection
99 */
100 struct GNUNET_REST_RequestHandle *rest_handle;
101
102 /**
103 * Desired timeout for the lookup (default is no timeout).
104 */
105 struct GNUNET_TIME_Relative timeout;
106
107 /**
108 * ID of a task associated with the resolution process.
109 */
110 struct GNUNET_SCHEDULER_Task *timeout_task;
111
112 /**
113 * The plugin result processor
114 */
115 GNUNET_REST_ResultProcessor proc;
116
117 /**
118 * The closure of the result processor
119 */
120 void *proc_cls;
121
122 /**
123 * The url
124 */
125 char *url;
126
127 /**
128 * Error response message
129 */
130 char *emsg;
131
132 /**
133 * Response code
134 */
135 int response_code;
136
137};
138
139
140/**
141 * Cleanup lookup handle
142 * @param handle Handle to clean up
143 */
144static void
145cleanup_handle (void *cls)
146{
147 struct RequestHandle *handle = cls;
148 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
149 "Cleaning up\n");
150
151 if (NULL != handle->gns_lookup)
152 {
153 GNUNET_GNS_lookup_with_tld_cancel (handle->gns_lookup);
154 handle->gns_lookup = NULL;
155 }
156 if (NULL != handle->gns)
157 {
158 GNUNET_GNS_disconnect (handle->gns);
159 handle->gns = NULL;
160 }
161
162 if (NULL != handle->timeout_task)
163 {
164 GNUNET_SCHEDULER_cancel (handle->timeout_task);
165 handle->timeout_task = NULL;
166 }
167 if (NULL != handle->url)
168 GNUNET_free (handle->url);
169 if (NULL != handle->name)
170 GNUNET_free (handle->name);
171 if (NULL != handle->emsg)
172 GNUNET_free (handle->emsg);
173
174 GNUNET_free (handle);
175}
176
177
178/**
179 * Task run on errors. Reports an error and cleans up everything.
180 *
181 * @param cls the `struct RequestHandle`
182 */
183static void
184do_error (void *cls)
185{
186 struct RequestHandle *handle = cls;
187 struct MHD_Response *resp;
188 json_t *json_error = json_object();
189 char *response;
190
191 if (NULL == handle->emsg)
192 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_ERROR_UNKNOWN);
193
194 json_object_set_new(json_error,"error", json_string(handle->emsg));
195
196 if (0 == handle->response_code)
197 handle->response_code = MHD_HTTP_OK;
198 response = json_dumps (json_error, 0);
199 resp = GNUNET_REST_create_response (response);
200 handle->proc (handle->proc_cls, resp, handle->response_code);
201 json_decref(json_error);
202 GNUNET_free(response);
203 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
204}
205
206
207/**
208 * Iterator called on obtained result for a GNS lookup.
209 *
210 * @param cls closure with the object
211 * @param was_gns #GNUNET_NO if name was not a GNS name
212 * @param rd_count number of records in @a rd
213 * @param rd the records in reply
214 */
215static void
216handle_gns_response (void *cls,
217 int was_gns,
218 uint32_t rd_count,
219 const struct GNUNET_GNSRECORD_Data *rd)
220{
221 struct RequestHandle *handle = cls;
222 struct MHD_Response *resp;
223 json_t *result_array;
224 json_t *record_obj;
225 char *result;
226
227 handle->gns_lookup = NULL;
228
229 if (GNUNET_NO == was_gns)
230 {
231 handle->response_code = MHD_HTTP_NOT_FOUND;
232 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
233 GNUNET_SCHEDULER_add_now (&do_error, handle);
234 return;
235 }
236
237 result_array = json_array();
238 for (uint32_t i=0;i<rd_count;i++)
239 {
240 if ((rd[i].record_type != handle->record_type) &&
241 (GNUNET_GNSRECORD_TYPE_ANY != handle->record_type) )
242 {
243 continue;
244 }
245
246 record_obj = GNUNET_JSON_from_gns_record(NULL,&rd[i]);
247 json_array_append (result_array, record_obj);
248 json_decref (record_obj);
249 }
250
251 result = json_dumps(result_array, 0);
252 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result);
253 resp = GNUNET_REST_create_response (result);
254 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
255 GNUNET_free (result);
256 json_decref (result_array);
257 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
258}
259
260
261/**
262 * Handle gns GET request
263 *
264 * @param con_handle the connection handle
265 * @param url the url
266 * @param cls the RequestHandle
267 */
268void
269get_gns_cont (struct GNUNET_REST_RequestHandle *con_handle,
270 const char* url,
271 void *cls)
272{
273 struct RequestHandle *handle = cls;
274 struct GNUNET_HashCode key;
275 char *record_type;
276 char *name;
277
278 name = NULL;
279 handle->name = NULL;
280 if (strlen (GNUNET_REST_API_NS_GNS) < strlen (handle->url))
281 {
282 name = &handle->url[strlen (GNUNET_REST_API_NS_GNS) + 1];
283 }
284
285 if (NULL == name)
286 {
287 handle->response_code = MHD_HTTP_NOT_FOUND;
288 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
289 GNUNET_SCHEDULER_add_now (&do_error, handle);
290 return;
291 }
292 if (0 >= strlen (name))
293 {
294 handle->response_code = MHD_HTTP_NOT_FOUND;
295 handle->emsg = GNUNET_strdup(GNUNET_REST_GNS_NOT_FOUND);
296 GNUNET_SCHEDULER_add_now (&do_error, handle);
297 return;
298 }
299 handle->name = GNUNET_strdup(name);
300
301 handle->record_type = UINT32_MAX;
302 GNUNET_CRYPTO_hash (GNUNET_REST_GNS_PARAM_RECORD_TYPE,
303 strlen (GNUNET_REST_GNS_PARAM_RECORD_TYPE),
304 &key);
305 if ( GNUNET_YES
306 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
307 &key))
308 {
309 record_type = GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
310 handle->record_type = GNUNET_GNSRECORD_typename_to_number(record_type);
311 }
312
313 if(UINT32_MAX == handle->record_type)
314 {
315 handle->record_type = GNUNET_GNSRECORD_TYPE_ANY;
316 }
317
318 handle->gns_lookup = GNUNET_GNS_lookup_with_tld (handle->gns,
319 handle->name,
320 handle->record_type,
321 GNUNET_NO,
322 &handle_gns_response,
323 handle);
324}
325
326
327
328/**
329 * Respond to OPTIONS request
330 *
331 * @param con_handle the connection handle
332 * @param url the url
333 * @param cls the RequestHandle
334 */
335static void
336options_cont (struct GNUNET_REST_RequestHandle *con_handle,
337 const char* url,
338 void *cls)
339{
340 struct MHD_Response *resp;
341 struct RequestHandle *handle = cls;
342
343 //independent of path return all options
344 resp = GNUNET_REST_create_response (NULL);
345 MHD_add_response_header (resp,
346 "Access-Control-Allow-Methods",
347 allow_methods);
348 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
349 GNUNET_SCHEDULER_add_now(&cleanup_handle, handle);
350 return;
351}
352
353
354/**
355 * Handle rest request
356 *
357 * @param handle the request handle
358 */
359static void
360init_cont (struct RequestHandle *handle)
361{
362 struct GNUNET_REST_RequestHandlerError err;
363 static const struct GNUNET_REST_RequestHandler handlers[] = {
364 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_GNS, &get_gns_cont},
365 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_GNS, &options_cont},
366 GNUNET_REST_HANDLER_END
367 };
368
369 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
370 handlers,
371 &err,
372 handle))
373 {
374 handle->response_code = err.error_code;
375 GNUNET_SCHEDULER_add_now (&do_error, handle);
376 }
377}
378
379
380/**
381 * Function processing the REST call
382 *
383 * @param method HTTP method
384 * @param url URL of the HTTP request
385 * @param data body of the HTTP request (optional)
386 * @param data_size length of the body
387 * @param proc callback function for the result
388 * @param proc_cls closure for callback function
389 * @return GNUNET_OK if request accepted
390 */
391static void
392rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
393 GNUNET_REST_ResultProcessor proc,
394 void *proc_cls)
395{
396 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
397
398 handle->response_code = 0;
399 handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 60);
400 handle->proc_cls = proc_cls;
401 handle->proc = proc;
402 handle->rest_handle = rest_handle;
403
404 handle->url = GNUNET_strdup (rest_handle->url);
405 if (handle->url[strlen (handle->url)-1] == '/')
406 handle->url[strlen (handle->url)-1] = '\0';
407 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
408 handle->gns = GNUNET_GNS_connect (cfg);
409 init_cont(handle);
410
411 handle->timeout_task =
412 GNUNET_SCHEDULER_add_delayed (handle->timeout,
413 &do_error,
414 handle);
415
416 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
417}
418
419
420/**
421 * Entry point for the plugin.
422 *
423 * @param cls Config info
424 * @return NULL on error, otherwise the plugin context
425 */
426void *
427libgnunet_plugin_rest_gns_init (void *cls)
428{
429 static struct Plugin plugin;
430 struct GNUNET_REST_Plugin *api;
431
432 cfg = cls;
433 if (NULL != plugin.cfg)
434 return NULL; /* can only initialize once! */
435 memset (&plugin, 0, sizeof (struct Plugin));
436 plugin.cfg = cfg;
437 api = GNUNET_new (struct GNUNET_REST_Plugin);
438 api->cls = &plugin;
439 api->name = GNUNET_REST_API_NS_GNS;
440 api->process_request = &rest_process_request;
441 GNUNET_asprintf (&allow_methods,
442 "%s, %s, %s, %s, %s",
443 MHD_HTTP_METHOD_GET,
444 MHD_HTTP_METHOD_POST,
445 MHD_HTTP_METHOD_PUT,
446 MHD_HTTP_METHOD_DELETE,
447 MHD_HTTP_METHOD_OPTIONS);
448
449 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
450 _("Gns REST API initialized\n"));
451 return api;
452}
453
454
455/**
456 * Exit point from the plugin.
457 *
458 * @param cls the plugin context (as returned by "init")
459 * @return always NULL
460 */
461void *
462libgnunet_plugin_rest_gns_done (void *cls)
463{
464 struct GNUNET_REST_Plugin *api = cls;
465 struct Plugin *plugin = api->cls;
466 plugin->cfg = NULL;
467
468 GNUNET_free_non_null (allow_methods);
469 GNUNET_free (api);
470 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471 "Gns REST plugin is finished\n");
472 return NULL;
473}
474
475/* end of plugin_rest_gns.c */
476