-remove debug message
[oweals/gnunet.git] / src / peerinfo-tool / plugin_rest_peerinfo.c
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      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @author Martin Schanzenbach
22  * @author Philippe Buschmann
23  * @file peerinfo/plugin_rest_peerinfo.c
24  * @brief GNUnet Peerinfo REST plugin
25  */
26
27 #include "platform.h"
28 #include "gnunet_rest_plugin.h"
29 #include "gnunet_peerinfo_service.h"
30 #include "gnunet_transport_service.h"
31 #include "gnunet_rest_lib.h"
32 #include "gnunet_json_lib.h"
33 #include "microhttpd.h"
34 #include <jansson.h>
35
36 /**
37  * Peerinfo Namespace
38  */
39 #define GNUNET_REST_API_NS_PEERINFO "/peerinfo"
40
41 /**
42  * Peerinfo parameter peer
43  */
44 #define GNUNET_REST_PEERINFO_PEER "peer"
45
46 /**
47  * Peerinfo parameter friend
48  */
49 #define GNUNET_REST_PEERINFO_FRIEND "friend"
50
51 /**
52  * Peerinfo parameter array
53  */
54 #define GNUNET_REST_PEERINFO_ARRAY "array"
55
56 /**
57  * Error message Unknown Error
58  */
59 #define GNUNET_REST_PEERINFO_ERROR_UNKNOWN "Unknown Error"
60
61 /**
62  * How long until we time out during address lookup?
63  */
64 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
65 /**
66  * The configuration handle
67  */
68 const struct GNUNET_CONFIGURATION_Handle *cfg;
69
70 /**
71  * HTTP methods allows for this plugin
72  */
73 static char*allow_methods;
74
75 /**
76  * @brief struct returned by the initialization function of the plugin
77  */
78 struct Plugin
79 {
80   const struct GNUNET_CONFIGURATION_Handle *cfg;
81 };
82
83
84 /**
85  * Record we keep for each printable address.
86  */
87 struct AddressRecord
88 {
89   /**
90    * Current address-to-string context (if active, otherwise NULL).
91    */
92   struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
93
94   /**
95    * Address expiration time
96    */
97   struct GNUNET_TIME_Absolute expiration;
98
99   /**
100    * Printable address.
101    */
102   char *result;
103
104   /**
105    * Print context this address record belongs to.
106    */
107   struct PrintContext *pc;
108 };
109
110
111 /**
112  * Structure we use to collect printable address information.
113  */
114 struct PrintContext
115 {
116   /**
117    * Kept in DLL.
118    */
119   struct PrintContext *next;
120
121   /**
122    * Kept in DLL.
123    */
124   struct PrintContext *prev;
125
126   /**
127    * Identity of the peer.
128    */
129   struct GNUNET_PeerIdentity peer;
130
131   /**
132    * List of printable addresses.
133    */
134   struct AddressRecord *address_list;
135
136   /**
137    * Number of completed addresses in @e address_list.
138    */
139   unsigned int num_addresses;
140
141   /**
142    * Number of addresses allocated in @e address_list.
143    */
144   unsigned int address_list_size;
145
146   /**
147    * Current offset in @e address_list (counted down).
148    */
149   unsigned int off;
150
151   /**
152    * Hello was friend only, #GNUNET_YES or #GNUNET_NO
153    */
154   int friend_only;
155
156   /**
157    * RequestHandle
158    */
159   struct RequestHandle *handle;
160 };
161
162 /**
163  * Head of list of print contexts.
164  */
165 static struct PrintContext *pc_head;
166
167 /**
168  * Tail of list of print contexts.
169  */
170 static struct PrintContext *pc_tail;
171
172 /**
173  * The request handle
174  */
175 struct RequestHandle
176 {
177   /**
178    * JSON temporary array
179    */
180   json_t *temp_array;
181
182   /**
183    * Expiration time string
184    */
185   char *expiration_str;
186
187   /**
188    * Address string
189    */
190   const char *address;
191
192   /**
193    * Iteration peer public key
194    */
195   char *pubkey;
196
197   /**
198    * JSON response
199    */
200   json_t *response;
201
202   /**
203    * Handle to PEERINFO it
204    */
205   struct GNUNET_PEERINFO_IteratorContext *list_it;
206
207   /**
208    * Handle to PEERINFO
209    */
210   struct GNUNET_PEERINFO_Handle *peerinfo_handle;
211
212   /**
213    * Rest connection
214    */
215   struct GNUNET_REST_RequestHandle *rest_handle;
216
217   /**
218    * Desired timeout for the lookup (default is no timeout).
219    */
220   struct GNUNET_TIME_Relative timeout;
221
222   /**
223    * ID of a task associated with the resolution process.
224    */
225   struct GNUNET_SCHEDULER_Task *timeout_task;
226
227   /**
228    * The plugin result processor
229    */
230   GNUNET_REST_ResultProcessor proc;
231
232   /**
233    * The closure of the result processor
234    */
235   void *proc_cls;
236
237   /**
238    * The url
239    */
240   char *url;
241
242   /**
243    * Error response message
244    */
245   char *emsg;
246
247   /**
248    * Reponse code
249    */
250   int response_code;
251 };
252
253
254 /**
255  * Cleanup lookup handle
256  * @param handle Handle to clean up
257  */
258 static void
259 cleanup_handle (void *cls)
260 {
261   struct RequestHandle *handle = cls;
262
263   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
264               "Cleaning up\n");
265   if (NULL != handle->timeout_task)
266   {
267     GNUNET_SCHEDULER_cancel (handle->timeout_task);
268     handle->timeout_task = NULL;
269   }
270   if (NULL != handle->url)
271     GNUNET_free (handle->url);
272   if (NULL != handle->emsg)
273     GNUNET_free (handle->emsg);
274   if (NULL != handle->address)
275     GNUNET_free_nz ((char *) handle->address);
276   if (NULL != handle->expiration_str)
277     GNUNET_free (handle->expiration_str);
278   if (NULL != handle->pubkey)
279     GNUNET_free (handle->pubkey);
280
281   if (NULL != handle->temp_array)
282   {
283     json_decref (handle->temp_array);
284     handle->temp_array = NULL;
285   }
286   if (NULL != handle->response)
287   {
288     json_decref (handle->response);
289     handle->response = NULL;
290   }
291
292   if (NULL != handle->list_it)
293   {
294     GNUNET_PEERINFO_iterate_cancel (handle->list_it);
295     handle->list_it = NULL;
296   }
297   if (NULL != handle->peerinfo_handle)
298   {
299     GNUNET_PEERINFO_disconnect (handle->peerinfo_handle);
300     handle->peerinfo_handle = NULL;
301   }
302
303   GNUNET_free (handle);
304 }
305
306
307 /**
308  * Task run on errors.  Reports an error and cleans up everything.
309  *
310  * @param cls the `struct RequestHandle`
311  */
312 static void
313 do_error (void *cls)
314 {
315   struct RequestHandle *handle = cls;
316   struct MHD_Response *resp;
317   json_t *json_error = json_object ();
318   char *response;
319
320   if (NULL == handle->emsg)
321     handle->emsg = GNUNET_strdup (GNUNET_REST_PEERINFO_ERROR_UNKNOWN);
322
323   json_object_set_new (json_error, "error", json_string (handle->emsg));
324
325   if (0 == handle->response_code)
326     handle->response_code = MHD_HTTP_OK;
327   response = json_dumps (json_error, 0);
328   resp = GNUNET_REST_create_response (response);
329   MHD_add_response_header (resp, "Content-Type", "application/json");
330   handle->proc (handle->proc_cls, resp, handle->response_code);
331   json_decref (json_error);
332   GNUNET_free (response);
333   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
334 }
335
336
337 /**
338  * Function that assembles the response.
339  *
340  * @param cls the `struct RequestHandle`
341  */
342 static void
343 peerinfo_list_finished (void *cls)
344 {
345   struct RequestHandle *handle = cls;
346   char *result_str;
347   struct MHD_Response *resp;
348
349   if (NULL == handle->response)
350   {
351     handle->response_code = MHD_HTTP_NOT_FOUND;
352     handle->emsg = GNUNET_strdup ("No peers found");
353     GNUNET_SCHEDULER_add_now (&do_error, handle);
354     return;
355   }
356
357   result_str = json_dumps (handle->response, 0);
358   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Result %s\n", result_str);
359   resp = GNUNET_REST_create_response (result_str);
360   MHD_add_response_header (resp, "Content-Type", "application/json");
361   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
362   GNUNET_free_non_null (result_str);
363   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
364 }
365
366
367 /**
368  * Iterator callback to go over all addresses and count them.
369  *
370  * @param cls `struct PrintContext *` with `off` to increment
371  * @param address the address
372  * @param expiration expiration time
373  * @return #GNUNET_OK to keep the address and continue
374  */
375 static int
376 count_address (void *cls,
377                const struct GNUNET_HELLO_Address *address,
378                struct GNUNET_TIME_Absolute expiration)
379 {
380   struct PrintContext *pc = cls;
381
382   if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
383   {
384     return GNUNET_OK;          /* ignore expired address */
385   }
386
387   pc->off++;
388   return GNUNET_OK;
389 }
390
391
392 /**
393  * Print the collected address information to the console and free @a pc.
394  *
395  * @param pc printing context
396  */
397 static void
398 dump_pc (struct PrintContext *pc)
399 {
400   struct RequestHandle *handle;
401   unsigned int i;
402   json_t *response_entry;
403   json_t *temp_array;
404   json_t *object;
405   json_t *address;
406   json_t *expires;
407   json_t *friend_and_peer_json;
408   char *friend_and_peer;
409
410   temp_array = json_array ();
411   response_entry = json_object ();
412
413   for (i = 0; i < pc->num_addresses; i++)
414   {
415     if (NULL != pc->address_list[i].result)
416     {
417       object = json_object ();
418       address = json_string (pc->address_list[i].result);
419       expires = json_string (
420         GNUNET_STRINGS_absolute_time_to_string (
421           pc->address_list[i].expiration));
422       json_object_set (object, "address", address);
423       json_object_set (object, "expires", expires);
424
425       json_decref (address);
426       json_decref (expires);
427
428       json_array_append (temp_array, object);
429       json_decref (object);
430       GNUNET_free (pc->address_list[i].result);
431     }
432   }
433
434   if (0 < json_array_size (temp_array))
435   {
436     GNUNET_asprintf (&friend_and_peer,
437                      "%s%s",
438                      (GNUNET_YES == pc->friend_only) ? "F2F:" : "",
439                      GNUNET_i2s_full (&pc->peer));
440     friend_and_peer_json = json_string (friend_and_peer);
441     json_object_set (response_entry,
442                      GNUNET_REST_PEERINFO_PEER,
443                      friend_and_peer_json);
444     json_object_set (response_entry,
445                      GNUNET_REST_PEERINFO_ARRAY,
446                      temp_array);
447     json_array_append (pc->handle->response, response_entry);
448     json_decref (friend_and_peer_json);
449     GNUNET_free (friend_and_peer);
450   }
451
452   json_decref (temp_array);
453   json_decref (response_entry);
454
455   GNUNET_free_non_null (pc->address_list);
456   GNUNET_CONTAINER_DLL_remove (pc_head,
457                                pc_tail,
458                                pc);
459   handle = pc->handle;
460   GNUNET_free (pc);
461
462   if ((NULL == pc_head) &&
463       (NULL == handle->list_it))
464   {
465     GNUNET_SCHEDULER_add_now (&peerinfo_list_finished, handle);
466   }
467 }
468
469
470 /**
471  * Function to call with a human-readable format of an address
472  *
473  * @param cls closure
474  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
475  * @param res result of the address to string conversion:
476  *        if #GNUNET_OK: address was valid (conversion to
477  *                       string might still have failed)
478  *        if #GNUNET_SYSERR: address is invalid
479  */
480 static void
481 process_resolved_address (void *cls,
482                           const char *address,
483                           int res)
484 {
485   struct AddressRecord *ar = cls;
486   struct PrintContext *pc = ar->pc;
487
488   if (NULL != address)
489   {
490     if (0 != strlen (address))
491     {
492       if (NULL != ar->result)
493         GNUNET_free (ar->result);
494       ar->result = GNUNET_strdup (address);
495     }
496     return;
497   }
498   ar->atsc = NULL;
499   if (GNUNET_SYSERR == res)
500     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
501                 _ ("Failure: Cannot convert address to string for peer `%s'\n"),
502                 GNUNET_i2s (&ar->pc->peer));
503   pc->num_addresses++;
504   if (pc->num_addresses == pc->address_list_size)
505     dump_pc (ar->pc);
506 }
507
508
509 /**
510  * Iterator callback to go over all addresses.
511  *
512  * @param cls closure
513  * @param address the address
514  * @param expiration expiration time
515  * @return #GNUNET_OK to keep the address and continue
516  */
517 static int
518 print_address (void *cls,
519                const struct GNUNET_HELLO_Address *address,
520                struct GNUNET_TIME_Absolute expiration)
521 {
522   struct PrintContext *pc = cls;
523   struct AddressRecord *ar;
524
525   if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us)
526   {
527     return GNUNET_OK;          /* ignore expired address */
528   }
529
530   GNUNET_assert (0 < pc->off);
531   ar = &pc->address_list[--pc->off];
532   ar->pc = pc;
533   ar->expiration = expiration;
534   GNUNET_asprintf (&ar->result,
535                    "%s:%u:%u",
536                    address->transport_name,
537                    address->address_length,
538                    address->local_info);
539   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg,
540                                                  address,
541                                                  GNUNET_NO,
542                                                  TIMEOUT,
543                                                  &process_resolved_address,
544                                                  ar);
545   return GNUNET_OK;
546 }
547
548
549 /**
550  * Callback that processes each of the known HELLOs for the
551  * iteration response construction.
552  *
553  * @param cls closure, NULL
554  * @param peer id of the peer, NULL for last call
555  * @param hello hello message for the peer (can be NULL)
556  * @param err_msg message
557  */
558 void
559 peerinfo_list_iteration (void *cls,
560                          const struct GNUNET_PeerIdentity *peer,
561                          const struct GNUNET_HELLO_Message *hello,
562                          const char *err_msg)
563 {
564   struct RequestHandle *handle = cls;
565   struct PrintContext *pc;
566   int friend_only;
567
568   if (NULL == handle->response)
569   {
570     handle->response = json_array ();
571   }
572
573   if (NULL == peer)
574   {
575     handle->list_it = NULL;
576     handle->emsg = GNUNET_strdup ("Error in communication with peerinfo");
577     if (NULL != err_msg)
578     {
579       GNUNET_free (handle->emsg);
580       handle->emsg = GNUNET_strdup (err_msg);
581       handle->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
582     }
583     if (NULL == pc_head)
584       GNUNET_SCHEDULER_add_now (&do_error, handle);
585     return;
586   }
587   if (NULL == hello)
588     return;
589
590   friend_only = GNUNET_NO;
591   if (NULL != hello)
592     friend_only = GNUNET_HELLO_is_friend_only (hello);
593
594   pc = GNUNET_new (struct PrintContext);
595   GNUNET_CONTAINER_DLL_insert (pc_head,
596                                pc_tail,
597                                pc);
598   pc->peer = *peer;
599   pc->friend_only = friend_only;
600   pc->handle = handle;
601   GNUNET_HELLO_iterate_addresses (hello,
602                                   GNUNET_NO,
603                                   &count_address,
604                                   pc);
605   if (0 == pc->off)
606   {
607     dump_pc (pc);
608     return;
609   }
610   pc->address_list_size = pc->off;
611   pc->address_list = GNUNET_malloc (
612     sizeof(struct AddressRecord) * pc->off);
613   GNUNET_HELLO_iterate_addresses (hello,
614                                   GNUNET_NO,
615                                   &print_address,
616                                   pc);
617 }
618
619
620 /**
621  * Handle peerinfo GET request
622  *
623  * @param con_handle the connection handle
624  * @param url the url
625  * @param cls the RequestHandle
626  */
627 void
628 peerinfo_get (struct GNUNET_REST_RequestHandle *con_handle,
629               const char*url,
630               void *cls)
631 {
632   struct RequestHandle *handle = cls;
633   struct GNUNET_HashCode key;
634   const struct GNUNET_PeerIdentity *specific_peer;
635   // GNUNET_PEER_Id peer_id;
636   int include_friend_only;
637   char*include_friend_only_str;
638
639   include_friend_only = GNUNET_NO;
640   GNUNET_CRYPTO_hash (GNUNET_REST_PEERINFO_FRIEND,
641                       strlen (GNUNET_REST_PEERINFO_FRIEND),
642                       &key);
643   if (GNUNET_YES
644       == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
645                                                  &key))
646   {
647     include_friend_only_str = GNUNET_CONTAINER_multihashmap_get (
648       con_handle->url_param_map, &key);
649     if (0 == strcmp (include_friend_only_str, "yes"))
650     {
651       include_friend_only = GNUNET_YES;
652     }
653   }
654
655   specific_peer = NULL;
656   GNUNET_CRYPTO_hash (GNUNET_REST_PEERINFO_PEER,
657                       strlen (GNUNET_REST_PEERINFO_PEER),
658                       &key);
659   if (GNUNET_YES
660       == GNUNET_CONTAINER_multihashmap_contains (con_handle->url_param_map,
661                                                  &key))
662   {
663     // peer_id = *(unsigned int*)GNUNET_CONTAINER_multihashmap_get (con_handle->url_param_map, &key);
664     // specific_peer = GNUNET_PEER_resolve2(peer_id);
665   }
666
667   handle->list_it = GNUNET_PEERINFO_iterate (handle->peerinfo_handle,
668                                              include_friend_only,
669                                              specific_peer,
670                                              &peerinfo_list_iteration,
671                                              handle);
672 }
673
674
675 /**
676  * Respond to OPTIONS request
677  *
678  * @param con_handle the connection handle
679  * @param url the url
680  * @param cls the RequestHandle
681  */
682 static void
683 options_cont (struct GNUNET_REST_RequestHandle *con_handle,
684               const char*url,
685               void *cls)
686 {
687   struct MHD_Response *resp;
688   struct RequestHandle *handle = cls;
689
690   // independent of path return all options
691   resp = GNUNET_REST_create_response (NULL);
692   MHD_add_response_header (resp,
693                            "Access-Control-Allow-Methods",
694                            allow_methods);
695   handle->proc (handle->proc_cls, resp, MHD_HTTP_OK);
696   GNUNET_SCHEDULER_add_now (&cleanup_handle, handle);
697   return;
698 }
699
700
701 /**
702  * Handle rest request
703  *
704  * @param handle the request handle
705  */
706 static void
707 init_cont (struct RequestHandle *handle)
708 {
709   struct GNUNET_REST_RequestHandlerError err;
710   static const struct GNUNET_REST_RequestHandler handlers[] = {
711     { MHD_HTTP_METHOD_GET, GNUNET_REST_API_NS_PEERINFO, &peerinfo_get },
712     { MHD_HTTP_METHOD_OPTIONS, GNUNET_REST_API_NS_PEERINFO, &options_cont },
713     GNUNET_REST_HANDLER_END
714   };
715
716   if (GNUNET_NO == GNUNET_REST_handle_request (handle->rest_handle,
717                                                handlers,
718                                                &err,
719                                                handle))
720   {
721     handle->response_code = err.error_code;
722     GNUNET_SCHEDULER_add_now (&do_error, handle);
723   }
724 }
725
726
727 /**
728  * Function processing the REST call
729  *
730  * @param method HTTP method
731  * @param url URL of the HTTP request
732  * @param data body of the HTTP request (optional)
733  * @param data_size length of the body
734  * @param proc callback function for the result
735  * @param proc_cls closure for callback function
736  * @return GNUNET_OK if request accepted
737  */
738 static void
739 rest_process_request (struct GNUNET_REST_RequestHandle *rest_handle,
740                       GNUNET_REST_ResultProcessor proc,
741                       void *proc_cls)
742 {
743   struct RequestHandle *handle = GNUNET_new (struct RequestHandle);
744
745   handle->response_code = 0;
746   handle->timeout = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS,
747                                                    60);
748   handle->proc_cls = proc_cls;
749   handle->proc = proc;
750   handle->rest_handle = rest_handle;
751
752   handle->url = GNUNET_strdup (rest_handle->url);
753   if (handle->url[strlen (handle->url) - 1] == '/')
754     handle->url[strlen (handle->url) - 1] = '\0';
755   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connecting...\n");
756   handle->peerinfo_handle = GNUNET_PEERINFO_connect (cfg);
757   init_cont (handle);
758   handle->timeout_task =
759     GNUNET_SCHEDULER_add_delayed (handle->timeout,
760                                   &do_error,
761                                   handle);
762
763   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Connected\n");
764 }
765
766
767 /**
768  * Entry point for the plugin.
769  *
770  * @param cls Config info
771  * @return NULL on error, otherwise the plugin context
772  */
773 void *
774 libgnunet_plugin_rest_peerinfo_init (void *cls)
775 {
776   static struct Plugin plugin;
777   struct GNUNET_REST_Plugin *api;
778
779   cfg = cls;
780   if (NULL != plugin.cfg)
781     return NULL;                /* can only initialize once! */
782   memset (&plugin, 0, sizeof(struct Plugin));
783   plugin.cfg = cfg;
784   api = GNUNET_new (struct GNUNET_REST_Plugin);
785   api->cls = &plugin;
786   api->name = GNUNET_REST_API_NS_PEERINFO;
787   api->process_request = &rest_process_request;
788   GNUNET_asprintf (&allow_methods,
789                    "%s, %s, %s, %s, %s",
790                    MHD_HTTP_METHOD_GET,
791                    MHD_HTTP_METHOD_POST,
792                    MHD_HTTP_METHOD_PUT,
793                    MHD_HTTP_METHOD_DELETE,
794                    MHD_HTTP_METHOD_OPTIONS);
795
796   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
797               _ ("Peerinfo REST API initialized\n"));
798   return api;
799 }
800
801
802 /**
803  * Exit point from the plugin.
804  *
805  * @param cls the plugin context (as returned by "init")
806  * @return always NULL
807  */
808 void *
809 libgnunet_plugin_rest_peerinfo_done (void *cls)
810 {
811   struct GNUNET_REST_Plugin *api = cls;
812   struct Plugin *plugin = api->cls;
813
814   plugin->cfg = NULL;
815
816   GNUNET_free_non_null (allow_methods);
817   GNUNET_free (api);
818   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
819               "Peerinfo REST plugin is finished\n");
820   return NULL;
821 }
822
823
824 /* end of plugin_rest_peerinfo.c */