aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/peerinfo/Makefile.am22
-rw-r--r--src/peerinfo/plugin_rest_peerinfo.c641
2 files changed, 663 insertions, 0 deletions
diff --git a/src/peerinfo/Makefile.am b/src/peerinfo/Makefile.am
index eeb5ee54e..5e96250b1 100644
--- a/src/peerinfo/Makefile.am
+++ b/src/peerinfo/Makefile.am
@@ -5,6 +5,8 @@ pkgcfgdir= $(pkgdatadir)/config.d/
5 5
6libexecdir= $(pkglibdir)/libexec/ 6libexecdir= $(pkglibdir)/libexec/
7 7
8plugindir = $(libdir)/gnunet
9
8pkgcfg_DATA = \ 10pkgcfg_DATA = \
9 peerinfo.conf 11 peerinfo.conf
10 12
@@ -25,6 +27,8 @@ libgnunetpeerinfo_la_SOURCES = \
25libgnunetpeerinfo_la_LIBADD = \ 27libgnunetpeerinfo_la_LIBADD = \
26 $(top_builddir)/src/hello/libgnunethello.la \ 28 $(top_builddir)/src/hello/libgnunethello.la \
27 $(top_builddir)/src/util/libgnunetutil.la \ 29 $(top_builddir)/src/util/libgnunetutil.la \
30 $(top_builddir)/src/json/libgnunetjson.la \
31 $(top_builddir)/src/transport/libgnunettransport.la \
28 $(XLIB) \ 32 $(XLIB) \
29 $(LTLIBINTL) 33 $(LTLIBINTL)
30libgnunetpeerinfo_la_LDFLAGS = \ 34libgnunetpeerinfo_la_LDFLAGS = \
@@ -35,12 +39,30 @@ libgnunetpeerinfo_la_LDFLAGS = \
35libexec_PROGRAMS = \ 39libexec_PROGRAMS = \
36 gnunet-service-peerinfo 40 gnunet-service-peerinfo
37 41
42if HAVE_MHD
43if HAVE_JSON
44plugin_LTLIBRARIES = \
45 libgnunet_plugin_rest_peerinfo.la
46endif
47endif
48
38gnunet_service_peerinfo_SOURCES = \ 49gnunet_service_peerinfo_SOURCES = \
39 gnunet-service-peerinfo.c 50 gnunet-service-peerinfo.c
40gnunet_service_peerinfo_LDADD = \ 51gnunet_service_peerinfo_LDADD = \
41 $(top_builddir)/src/hello/libgnunethello.la \ 52 $(top_builddir)/src/hello/libgnunethello.la \
42 $(top_builddir)/src/statistics/libgnunetstatistics.la \ 53 $(top_builddir)/src/statistics/libgnunetstatistics.la \
43 $(top_builddir)/src/util/libgnunetutil.la 54 $(top_builddir)/src/util/libgnunetutil.la
55
56
57libgnunet_plugin_rest_peerinfo_la_SOURCES = \
58 plugin_rest_peerinfo.c
59libgnunet_plugin_rest_peerinfo_la_LIBADD = \
60 libgnunetpeerinfo.la \
61 $(top_builddir)/src/rest/libgnunetrest.la \
62 $(top_builddir)/src/util/libgnunetutil.la $(XLIBS) \
63 $(LTLIBINTL) -ljansson -lmicrohttpd
64libgnunet_plugin_rest_peerinfo_la_LDFLAGS = \
65 $(GN_PLUGIN_LDFLAGS)
44 66
45if HAVE_BENCHMARKS 67if HAVE_BENCHMARKS
46 PEERINFO_BENCHMARKS = \ 68 PEERINFO_BENCHMARKS = \
diff --git a/src/peerinfo/plugin_rest_peerinfo.c b/src/peerinfo/plugin_rest_peerinfo.c
new file mode 100644
index 000000000..408f9a6f7
--- /dev/null
+++ b/src/peerinfo/plugin_rest_peerinfo.c
@@ -0,0 +1,641 @@
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 Martin Schanzenbach
20 * @author Philippe Buschmann
21 * @file peerinfo/plugin_rest_peerinfo.c
22 * @brief GNUnet Peerinfo REST plugin
23 */
24
25#include "platform.h"
26#include "gnunet_rest_plugin.h"
27#include "gnunet_peerinfo_service.h"
28#include "gnunet_transport_service.h"
29#include "gnunet_rest_lib.h"
30#include "gnunet_json_lib.h"
31#include "microhttpd.h"
32#include <jansson.h>
33
34#define GNUNET_REST_API_NS_PEERINFO "/peerinfo"
35#define GNUNET_REST_API_PEERINFO_PEER "peer"
36#define GNUNET_REST_API_PEERINFO_FRIEND "friend"
37
38//TODO define other variables
39#define GNUNET_REST_ERROR_UNKNOWN "Unkown Error"
40
41/**
42 * The configuration handle
43 */
44const struct GNUNET_CONFIGURATION_Handle *cfg;
45
46/**
47 * HTTP methods allows for this plugin
48 */
49static char* allow_methods;
50
51/**
52 * @brief struct returned by the initialization function of the plugin
53 */
54struct Plugin
55{
56 const struct GNUNET_CONFIGURATION_Handle *cfg;
57};
58
59//TODO add specific structs
60
61
62
63struct RequestHandle
64{
65 //TODO add specific entries
66 json_t *temp_array;
67 char *expiration_str;
68 const char *address;
69
70 /**
71 * Iteration peer public key
72 */
73 char *pubkey;
74
75 /**
76 * JSON array response
77 */
78 json_t *response;
79
80 /**
81 * Handle to PEERINFO it
82 */
83 struct GNUNET_PEERINFO_IteratorContext *list_it;
84
85 /**
86 * Handle to PEERINFO
87 */
88 struct GNUNET_PEERINFO_Handle *peerinfo_handle;
89
90 /**
91 * Rest connection
92 */
93 struct GNUNET_REST_RequestHandle *rest_handle;
94
95 /**
96 * Desired timeout for the lookup (default is no timeout).
97 */
98 struct GNUNET_TIME_Relative timeout;
99
100 /**
101 * ID of a task associated with the resolution process.
102 */
103 struct GNUNET_SCHEDULER_Task *timeout_task;
104
105 /**
106 * The plugin result processor
107 */
108 GNUNET_REST_ResultProcessor proc;
109
110 /**
111 * The closure of the result processor
112 */
113 void *proc_cls;
114
115 /**
116 * The url
117 */
118 char *url;
119
120 /**
121 * Error response message
122 */
123 char *emsg;
124
125 /**
126 * Reponse code
127 */
128 int response_code;
129
130};
131
132
133/**
134 * Cleanup lookup handle
135 * @param handle Handle to clean up
136 */
137static void
138cleanup_handle (void *cls)
139{
140 struct RequestHandle *handle = cls;
141
142 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Test: %i\n", NULL == handle);
143
144 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
145 "Cleaning up\n");
146 if (NULL != handle->timeout_task)
147 {
148 GNUNET_SCHEDULER_cancel (handle->timeout_task);
149 handle->timeout_task = NULL;
150 }
151 if (NULL != handle->url)
152 GNUNET_free (handle->url);
153 if (NULL != handle->emsg)
154 GNUNET_free (handle->emsg);
155
156 if (NULL != handle->response)
157 {
158 json_decref(handle->response);
159 handle->response = NULL;
160 }
161
162 if (NULL != handle->list_it)
163 {
164 GNUNET_PEERINFO_iterate_cancel(handle->list_it);
165 handle->list_it = NULL;
166 }
167
168 if (NULL != handle->peerinfo_handle)
169 {
170 GNUNET_PEERINFO_disconnect(handle->peerinfo_handle);
171 handle->peerinfo_handle = NULL;
172 }
173
174 //TODO add specific cleanup
175
176 GNUNET_free (handle);
177}
178
179
180/**
181 * Task run on errors. Reports an error and cleans up everything.
182 *
183 * @param cls the `struct RequestHandle`
184 */
185static void
186do_error (void *cls)
187{
188 struct RequestHandle *handle = cls;
189 struct MHD_Response *resp;
190 json_t *json_error = json_object();
191 char *response;
192
193 if (NULL == handle->emsg)
194 handle->emsg = GNUNET_strdup(GNUNET_REST_ERROR_UNKNOWN);
195
196 json_object_set_new(json_error,"error", json_string(handle->emsg));
197
198 if (0 == handle->response_code)
199 handle->response_code = MHD_HTTP_OK;
200 response = json_dumps (json_error, 0);
201 resp = GNUNET_REST_create_response (response);
202 handle->proc (handle->proc_cls, resp, handle->response_code);
203 json_decref(json_error);
204 GNUNET_free(response);
205 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
206}
207
208
209/**
210 * Function that assembles our response.
211 */
212static void
213peerinfo_list_finished (void *cls)
214{
215 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "6\n");
216 struct RequestHandle *handle = cls;
217 char *result_str;
218 struct MHD_Response *resp;
219
220 if (NULL == handle->response)
221 {
222 handle->emsg = GNUNET_strdup ("No peers found");
223 GNUNET_SCHEDULER_add_now (&do_error, handle);
224 return;
225 }
226
227 result_str = json_dumps (handle->response, 0);
228 GNUNET_log(GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
229 resp = GNUNET_REST_create_response (result_str);
230 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
231 GNUNET_free_non_null (result_str);
232 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
233}
234
235
236/**
237 * Set @a cls to #GNUNET_YES (we have an address!).
238 *
239 * @param cls closure, an `int *`
240 * @param address the address (ignored)
241 * @param expiration expiration time (call is ignored if this is in the past)
242 * @return #GNUNET_SYSERR to stop iterating (unless expiration has occured)
243 */
244static int
245check_has_addr (void *cls,
246 const struct GNUNET_HELLO_Address *address,
247 struct GNUNET_TIME_Absolute expiration)
248{
249 int *arg = cls;
250 if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
251 {
252 return GNUNET_YES; /* ignore this address */
253 }
254 *arg = GNUNET_YES;
255 return GNUNET_SYSERR;
256}
257
258static void
259create_array(void *cls)
260{
261 struct RequestHandle *handle = cls;
262// json_t *object;
263// object = json_object();
264//
265// json_object_set(object,"address",json_string(handle->address));
266// json_object_set(object,"expires",json_string(handle->expiration_str));
267//
268// if(NULL == handle->temp_array)
269// {
270// handle->temp_array = json_array();
271// }
272//
273// json_array_append(handle->temp_array,object);
274//
275// json_decref(object);
276 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "5\n");
277 json_object_set(handle->response, handle->pubkey, handle->temp_array);
278 json_decref (handle->temp_array);
279}
280
281static void
282create_tmp_array (void *cls)
283{
284 struct RequestHandle *handle = cls;
285 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "4\n");
286 json_t *object;
287 json_t *address_json = json_string (handle->address);
288 json_t *expires_json = json_string (handle->expiration_str);
289 object = json_object ();
290
291 json_object_set (object, "address", address_json);
292 json_decref(address_json);
293 json_object_set (object, "expires", expires_json);
294 json_decref(expires_json);
295 GNUNET_free(handle->expiration_str);
296
297 if (NULL == handle->temp_array)
298 {
299 handle->temp_array = json_array ();
300 }
301
302 json_array_append (handle->temp_array, object);
303
304 json_decref (object);
305}
306
307static void
308addr_to_str_cb (void *cls,
309 const char *address,
310 int res)
311{
312 struct RequestHandle *handle = cls;
313 if (NULL == address)
314 {
315 return;
316 }
317 if (GNUNET_OK != res)
318 {
319 return;
320 }
321 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "3\n");
322 handle->address = GNUNET_strdup(address);
323 GNUNET_assert(false);
324}
325
326/**
327 * Set @a cls to #GNUNET_YES (we have an address!).
328 *
329 * @param cls closure
330 * @param address the address (ignored)
331 * @param expiration expiration time (call is ignored if this is in the past)
332 * @return #GNUNET_SYSERR to stop iterating (unless expiration has occured)
333 */
334static int
335address_iteration (void *cls,
336 const struct GNUNET_HELLO_Address *address,
337 struct GNUNET_TIME_Absolute expiration)
338{
339 struct RequestHandle *handle = cls;
340 char *expiration_tmp;
341
342 if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
343 {
344 return GNUNET_YES; /* ignore this address */
345 }
346 expiration_tmp = GNUNET_STRINGS_absolute_time_to_string(expiration);
347 handle->expiration_str = GNUNET_strdup(expiration_tmp);
348 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "2\n");
349 GNUNET_TRANSPORT_address_to_string(cfg,
350 address,
351 GNUNET_NO,
352 GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10),
353 &addr_to_str_cb,
354 handle);
355
356 GNUNET_SCHEDULER_add_now(&create_tmp_array,handle);
357
358// GNUNET_SCHEDULER_add_delayed (
359// GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 11),
360// &create_array,
361// handle);
362 return GNUNET_YES;
363}
364
365
366/**
367 * Callback that processes each of the known HELLOs for the
368 * iteration response construction.
369 *
370 * @param cls closure, NULL
371 * @param peer id of the peer, NULL for last call
372 * @param hello hello message for the peer (can be NULL)
373 * @param err_msg message
374 */
375void
376peerinfo_list_iteration(void *cls,
377 const struct GNUNET_PeerIdentity *peer,
378 const struct GNUNET_HELLO_Message *hello,
379 const char *err_msg)
380{
381 struct RequestHandle *handle = cls;
382 int has_addr;
383
384 if (NULL == handle->response)
385 {
386 handle->response = json_object();
387 }
388
389 if (NULL != err_msg)
390 {
391 GNUNET_assert (NULL == peer);
392 handle->list_it = NULL;
393 handle->emsg = GNUNET_strdup ("Error in communication with peerinfo");
394 handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
395 GNUNET_SCHEDULER_add_now (&do_error, handle);
396 return;
397 }
398 if (NULL == peer)
399 {
400 handle->list_it = NULL;
401 GNUNET_SCHEDULER_add_now (&peerinfo_list_finished, handle);
402 return;
403 }
404 if (NULL == hello)
405 return;
406 has_addr = GNUNET_NO;
407 GNUNET_HELLO_iterate_addresses (hello,
408 GNUNET_NO,
409 &check_has_addr,
410 &has_addr);
411 if (GNUNET_NO == has_addr)
412 {
413 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
414 "HELLO for peer `%4s' has no address, not suitable for hostlist!\n",
415 GNUNET_i2s (peer));
416 return;
417 }
418
419 if (NULL != handle->pubkey)
420 GNUNET_free (handle->pubkey);
421 handle->pubkey = GNUNET_CRYPTO_eddsa_public_key_to_string(&peer->public_key);
422 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "1\n");
423 GNUNET_HELLO_iterate_addresses (hello,
424 GNUNET_NO,
425 &address_iteration,
426 handle);
427 GNUNET_SCHEDULER_add_now(&create_array,handle);
428}
429
430/**
431 * Handle peerinfo GET request
432 *
433 * @param con_handle the connection handle
434 * @param url the url
435 * @param cls the RequestHandle
436 */
437void
438peerinfo_get (struct GNUNET_REST_RequestHandle *con_handle,
439 const char* url,
440 void *cls)
441{
442 struct RequestHandle *handle = cls;
443 struct GNUNET_HashCode key;
444 const struct GNUNET_PeerIdentity *specific_peer;
445 GNUNET_PEER_Id peer_id;
446 int include_friend_only;
447
448 include_friend_only = GNUNET_NO;
449 GNUNET_CRYPTO_hash (GNUNET_REST_API_PEERINFO_FRIEND,
450 strlen (GNUNET_REST_API_PEERINFO_FRIEND),
451 &key);
452 if ( GNUNET_YES
453 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
454 &key))
455 {
456 include_friend_only = *(int*)GNUNET_CONTAINER_multihashmap_get (
457 con_handle->url_param_map, &key);
458 }
459 if(GNUNET_YES != include_friend_only)
460 {
461 include_friend_only = GNUNET_NO;
462 }
463
464 specific_peer = NULL;
465 GNUNET_CRYPTO_hash (GNUNET_REST_API_PEERINFO_PEER,
466 strlen (GNUNET_REST_API_PEERINFO_PEER),
467 &key);
468 if ( GNUNET_YES
469 == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
470 &key))
471 {
472 peer_id = *(unsigned int*)GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
473 specific_peer = GNUNET_PEER_resolve2(peer_id);
474 }
475
476
477 //TODO friend_only and special peer
478
479 //TODO add behaviour and response
480 //TODO maybe notify better than iteration
481
482 handle->list_it = GNUNET_PEERINFO_iterate(handle->peerinfo_handle,
483 include_friend_only,
484 specific_peer,
485 &peerinfo_list_iteration,
486 handle);
487}
488
489
490
491/**
492 * Respond to OPTIONS request
493 *
494 * @param con_handle the connection handle
495 * @param url the url
496 * @param cls the RequestHandle
497 */
498static void
499options_cont (struct GNUNET_REST_RequestHandle *con_handle,
500 const char* url,
501 void *cls)
502{
503 struct MHD_Response *resp;
504 struct RequestHandle *handle = cls;
505
506 //independent of path return all options
507 resp = GNUNET_REST_create_response (NULL);
508 MHD_add_response_header (resp,
509 "Access-Control-Allow-Methods",
510 allow_methods);
511 handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
512 GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
513 return;
514}
515
516
517/**
518 * Handle rest request
519 *
520 * @param handle the request handle
521 */
522static void
523init_cont (struct RequestHandle *handle)
524{
525 //TODO specify parameter of init_cont if necessary
526 struct GNUNET_REST_RequestHandlerError err;
527 static const struct GNUNET_REST_RequestHandler handlers[] = {
528 {MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_PEERINFO, &peerinfo_get},
529 {MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PEERINFO, &options_cont},
530 GNUNET_REST_HANDLER_END
531 };
532
533 if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
534 handlers,
535 &err,
536 handle))
537 {
538 handle->response_code = err.error_code;
539 GNUNET_SCHEDULER_add_now (&do_error, handle);
540 }
541}
542
543
544/**
545 * Function processing the REST call
546 *
547 * @param method HTTP method
548 * @param url URL of the HTTP request
549 * @param data body of the HTTP request (optional)
550 * @param data_size length of the body
551 * @param proc callback function for the result
552 * @param proc_cls closure for callback function
553 * @return GNUNET_OK if request accepted
554 */
555static void
556rest_process_request(struct GNUNET_REST_RequestHandle *rest_handle,
557 GNUNET_REST_ResultProcessor proc,
558 void *proc_cls)
559{
560 struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
561
562 handle->response_code = 0;
563 handle->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
564 handle->proc_cls = proc_cls;
565 handle->proc = proc;
566 handle->rest_handle = rest_handle;
567
568 handle->url = GNUNET_strdup (rest_handle->url);
569 if (handle->url[strlen (handle->url)-1] == '/')
570 handle->url[strlen (handle->url)-1] = '\0';
571 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
572 //TODO connect to specific service
573 //connect ( cfg, [..., &callback_function, handle]);
574 handle->peerinfo_handle = GNUNET_PEERINFO_connect(cfg);
575 init_cont(handle);
576 handle->timeout_task =
577 GNUNET_SCHEDULER_add_delayed (handle->timeout,
578 &do_error,
579 handle);
580
581 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
582}
583
584
585/**
586 * Entry point for the plugin.
587 *
588 * @param cls Config info
589 * @return NULL on error, otherwise the plugin context
590 */
591void *
592libgnunet_plugin_rest_peerinfo_init (void *cls)
593{
594 static struct Plugin plugin;
595 struct GNUNET_REST_Plugin *api;
596
597 cfg = cls;
598 if (NULL != plugin.cfg)
599 return NULL; /* can only initialize once! */
600 memset (&plugin, 0, sizeof (struct Plugin));
601 plugin.cfg = cfg;
602 api = GNUNET_new (struct GNUNET_REST_Plugin);
603 api->cls = &plugin;
604 api->name = GNUNET_REST_API_NS_PEERINFO;
605 api->process_request = &rest_process_request;
606 GNUNET_asprintf (&allow_methods,
607 "%s, %s, %s, %s, %s",
608 MHD_HTTP_METHOD_GET,
609 MHD_HTTP_METHOD_POST,
610 MHD_HTTP_METHOD_PUT,
611 MHD_HTTP_METHOD_DELETE,
612 MHD_HTTP_METHOD_OPTIONS);
613
614 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
615 _("Peerinfo REST API initialized\n"));
616 return api;
617}
618
619
620/**
621 * Exit point from the plugin.
622 *
623 * @param cls the plugin context (as returned by "init")
624 * @return always NULL
625 */
626void *
627libgnunet_plugin_rest_peerinfo_done (void *cls)
628{
629 struct GNUNET_REST_Plugin *api = cls;
630 struct Plugin *plugin = api->cls;
631 plugin->cfg = NULL;
632
633 GNUNET_free_non_null (allow_methods);
634 GNUNET_free (api);
635 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
636 "Peerinfo REST plugin is finished\n");
637 return NULL;
638}
639
640/* end of plugin_rest_peerinfo.c */
641