peerinfo API change + changes in code using api
[oweals/gnunet.git] / src / peerinfo-tool / gnunet-peerinfo.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001-2012 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file peerinfo-tool/gnunet-peerinfo.c
23  * @brief Print information about other known peers.
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include "gnunet_crypto_lib.h"
28 #include "gnunet_configuration_lib.h"
29 #include "gnunet_getopt_lib.h"
30 #include "gnunet_program_lib.h"
31 #include "gnunet_hello_lib.h"
32 #include "gnunet_transport_service.h"
33 #include "gnunet_peerinfo_service.h"
34 #include "gnunet-peerinfo_plugins.h"
35
36 /**
37  * How long until we time out during peerinfo iterations?
38  */
39 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
40
41 /**
42  * Structure we use to collect printable address information.
43  */
44 struct PrintContext;
45
46 /**
47  * Record we keep for each printable address.
48  */
49 struct AddressRecord
50 {
51   /**
52    * Current address-to-string context (if active, otherwise NULL).
53    */
54   struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
55
56   /**
57    * Printable address.
58    */
59   char *result;
60   
61   /**
62    * Print context this address record belongs to.
63    */
64   struct PrintContext *pc;
65 };
66
67
68 /**
69  * Structure we use to collect printable address information.
70  */
71 struct PrintContext
72 {
73
74   /**
75    * Kept in DLL.
76    */
77   struct PrintContext *next;
78
79   /**
80    * Kept in DLL.
81    */
82   struct PrintContext *prev;
83
84   /**
85    * Identity of the peer.
86    */
87   struct GNUNET_PeerIdentity peer;
88   
89   /**
90    * List of printable addresses.
91    */
92   struct AddressRecord *address_list;
93
94   /**
95    * Number of completed addresses in 'address_list'.
96    */
97   unsigned int num_addresses;
98
99   /**
100    * Number of addresses allocated in 'address_list'.
101    */
102   unsigned int address_list_size;
103
104   /**
105    * Current offset in 'address_list' (counted down).
106    */
107   unsigned int off;
108
109   /**
110    * Hello was friend only, GNUNET_YES or GNUNET_NO
111    */
112   int friend_only;
113
114 };
115
116
117 /**
118  * Option '-n'
119  */
120 static int no_resolve;
121
122 /**
123  * Option '-q'
124  */
125 static int be_quiet;
126
127 /**
128  * Option '-f'
129  */
130 static int include_friend_only;
131
132 /**
133  * Option '-s'
134  */
135 static int get_self;
136
137 /**
138  * Option 
139  */
140 static int get_uri;
141
142 /**
143  * Option '-i'
144  */
145 static int get_info;
146
147 /**
148  * Option 
149  */
150 static char *put_uri;
151
152 /**
153  * Handle to peerinfo service.
154  */
155 static struct GNUNET_PEERINFO_Handle *peerinfo;
156
157 /**
158  * Configuration handle.
159  */
160 static const struct GNUNET_CONFIGURATION_Handle *cfg;
161
162 /**
163  * Main state machine task (if active).
164  */
165 static GNUNET_SCHEDULER_TaskIdentifier tt;
166
167 /**
168  * Current iterator context (if active, otherwise NULL).
169  */
170 static struct GNUNET_PEERINFO_IteratorContext *pic;
171
172 /**
173  * My peer identity.
174  */
175 static struct GNUNET_PeerIdentity my_peer_identity;
176
177 /**
178  * My public key.
179  */
180 static struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded my_public_key;
181
182 /**
183  * Head of list of print contexts.
184  */
185 static struct PrintContext *pc_head;
186
187 /**
188  * Tail of list of print contexts.
189  */
190 static struct PrintContext *pc_tail;
191
192 /**
193  * Handle to current 'GNUNET_PEERINFO_add_peer' operation.
194  */
195 static struct GNUNET_PEERINFO_AddContext *ac;
196
197
198 /**
199  * Main state machine that goes over all options and
200  * runs the next requested function.
201  *
202  * @param cls unused
203  * @param tc unused
204  */
205 static void
206 state_machine (void *cls,
207                const struct GNUNET_SCHEDULER_TaskContext *tc);
208
209
210 /* ********************* 'get_info' ******************* */
211
212 /**
213  * Print the collected address information to the console and free 'pc'.
214  *
215  * @param pc printing context
216  */
217 static void
218 dump_pc (struct PrintContext *pc)
219 {
220   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
221   unsigned int i;
222
223   GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc);
224   printf (_("%sPeer `%s'\n"),
225         (GNUNET_YES == pc->friend_only) ? "F2F: " : "",
226           (const char *) &enc);
227   for (i = 0; i < pc->num_addresses; i++)
228   {
229     if (NULL != pc->address_list[i].result)
230     {
231       printf ("\t%s\n", pc->address_list[i].result);
232       GNUNET_free (pc->address_list[i].result);
233     }
234   }
235   printf ("\n");
236   GNUNET_free_non_null (pc->address_list);
237   GNUNET_CONTAINER_DLL_remove (pc_head,
238                                pc_tail,
239                                pc);
240   GNUNET_free (pc);
241   if ( (NULL == pc_head) &&
242        (NULL == pic) )
243     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);  
244 }
245
246
247 /* ************************* list all known addresses **************** */
248
249
250 /**
251  * Function to call with a human-readable format of an address
252  *
253  * @param cls closure
254  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
255  */
256 static void
257 process_resolved_address (void *cls, const char *address)
258 {
259   struct AddressRecord * ar = cls;
260   struct PrintContext *pc = ar->pc;
261
262   if (NULL != address)
263   {
264     if (NULL == ar->result)
265       ar->result = GNUNET_strdup (address);
266     return;
267   }
268   ar->atsc = NULL;
269   pc->num_addresses++;
270   if (pc->num_addresses == pc->address_list_size)
271     dump_pc (pc);
272 }
273
274
275 /**
276  * Iterator callback to go over all addresses and count them.
277  *
278  * @param cls 'struct PrintContext' with 'off' to increment
279  * @param address the address
280  * @param expiration expiration time
281  * @return GNUNET_OK to keep the address and continue
282  */
283 static int
284 count_address (void *cls, const struct GNUNET_HELLO_Address *address,
285                struct GNUNET_TIME_Absolute expiration)
286 {
287   struct PrintContext *pc = cls;
288
289   pc->off++;
290   return GNUNET_OK;
291 }
292
293
294 /**
295  * Iterator callback to go over all addresses.
296  *
297  * @param cls closure
298  * @param address the address
299  * @param expiration expiration time
300  * @return GNUNET_OK to keep the address and continue
301  */
302 static int
303 print_address (void *cls, const struct GNUNET_HELLO_Address *address,
304                struct GNUNET_TIME_Absolute expiration)
305 {
306   struct PrintContext *pc = cls;
307   struct AddressRecord *ar;
308
309   GNUNET_assert (0 < pc->off);
310   ar = &pc->address_list[--pc->off];
311   ar->pc = pc;
312   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg, address, no_resolve,
313                                                  GNUNET_TIME_relative_multiply
314                                                  (GNUNET_TIME_UNIT_SECONDS, 10),
315                                                  &process_resolved_address, ar);
316   return GNUNET_OK;
317 }
318
319
320 /**
321  * Print information about the peer.
322  * Currently prints the GNUNET_PeerIdentity and the transport address.
323  *
324  * @param cls the 'struct PrintContext'
325  * @param peer identity of the peer 
326  * @param hello addresses of the peer
327  * @param err_msg error message
328  */
329 static void
330 print_peer_info (void *cls, const struct GNUNET_PeerIdentity *peer,
331                  const struct GNUNET_HELLO_Message *hello, const char *err_msg)
332 {
333   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
334   struct PrintContext *pc;
335   int friend_only;
336
337   if (NULL == peer)
338   {
339     pic = NULL; /* end of iteration */
340     if (NULL != err_msg)
341     {
342       FPRINTF (stderr, 
343                _("Error in communication with PEERINFO service: %s\n"),
344                err_msg);
345     }
346     if (NULL == pc_head)
347       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
348     return;
349   }
350   friend_only = GNUNET_HELLO_is_friend_only (hello);
351   if ((GNUNET_YES == be_quiet) || (NULL == hello))
352   {
353     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
354     printf ("%s%s\n",
355                 (GNUNET_YES == friend_only) ? "F2F: " : "",
356                 (const char *) &enc);
357     return;
358   }
359   pc = GNUNET_malloc (sizeof (struct PrintContext));
360   GNUNET_CONTAINER_DLL_insert (pc_head,
361                                pc_tail, 
362                                pc);
363   pc->peer = *peer;
364   pc->friend_only = friend_only;
365   GNUNET_HELLO_iterate_addresses (hello, 
366                                   GNUNET_NO, 
367                                   &count_address, 
368                                   pc);
369   if (0 == pc->off)
370   {
371     dump_pc (pc);
372     return;
373   }
374   pc->address_list_size = pc->off;
375   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
376   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, 
377                                   &print_address, pc);
378 }
379
380
381 /* ************************* GET URI ************************** */
382
383
384 /**
385  * Print URI of the peer.
386  *
387  * @param cls the 'struct GetUriContext'
388  * @param peer identity of the peer (unused)
389  * @param hello addresses of the peer
390  * @param err_msg error message
391  */
392 static void
393 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
394               const struct GNUNET_HELLO_Message *hello, 
395               const char *err_msg)
396 {
397   if (peer == NULL)
398   {
399     pic = NULL;
400     if (err_msg != NULL)
401       FPRINTF (stderr,
402                _("Error in communication with PEERINFO service: %s\n"), 
403                err_msg);
404     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
405     return;
406   }
407
408   if (NULL == hello)
409     return;
410
411   char *uri = GNUNET_HELLO_compose_uri(hello, &GPI_plugins_find);
412   if (NULL != uri) {
413     printf ("%s\n", (const char *) uri);
414     GNUNET_free (uri);
415   }
416 }
417
418
419 /* ************************* import HELLO by URI ********************* */
420
421
422 /**
423  * Continuation called from 'GNUNET_PEERINFO_add_peer'
424  *
425  * @param cls closure, NULL
426  * @param emsg error message, NULL on success
427  */
428 static void
429 add_continuation (void *cls,
430                   const char *emsg)
431 {
432   ac = NULL;
433   if (NULL != emsg)
434     fprintf (stderr,
435              _("Failure adding HELLO: %s\n"),
436              emsg);
437   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
438 }
439
440
441 /**
442  * Parse the PUT URI given at the command line and add it to our peerinfo 
443  * database.
444  *
445  * @param put_uri URI string to parse
446  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
447  */
448 static int
449 parse_hello_uri (const char *put_uri)
450 {
451   struct GNUNET_HELLO_Message *hello = NULL;
452
453   int ret = GNUNET_HELLO_parse_uri(put_uri, &my_public_key, &hello, &GPI_plugins_find);
454
455   if (NULL != hello) {
456     /* WARNING: this adds the address from URI WITHOUT verification! */
457     if (GNUNET_OK == ret)
458       ac = GNUNET_PEERINFO_add_peer (peerinfo, hello, &add_continuation, NULL);
459     else
460       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
461     GNUNET_free (hello);
462   }
463
464   /* wait 1s to give peerinfo operation a chance to succeed */
465   /* FIXME: current peerinfo API sucks to require this; not to mention
466      that we get no feedback to determine if the operation actually succeeded */
467   return ret;
468 }
469
470
471 /* ************************ Main state machine ********************* */
472
473
474 /**
475  * Main state machine that goes over all options and
476  * runs the next requested function.
477  *
478  * @param cls unused
479  * @param tc scheduler context
480  */
481 static void
482 shutdown_task (void *cls,
483                const struct GNUNET_SCHEDULER_TaskContext *tc)
484 {
485   struct PrintContext *pc;
486   struct AddressRecord *ar;
487   unsigned int i;
488
489   if (NULL != ac)
490   {
491     GNUNET_PEERINFO_add_peer_cancel (ac);
492     ac = NULL;
493   }
494   if (GNUNET_SCHEDULER_NO_TASK != tt)
495   {
496     GNUNET_SCHEDULER_cancel (tt);
497     tt = GNUNET_SCHEDULER_NO_TASK;
498   }
499   if (NULL != pic)
500   {
501     GNUNET_PEERINFO_iterate_cancel (pic);
502     pic = NULL;
503   }
504   while (NULL != (pc = pc_head))
505   {
506     GNUNET_CONTAINER_DLL_remove (pc_head,
507                                  pc_tail,
508                                  pc);
509     for (i=0;i<pc->address_list_size;i++)
510     {
511       ar = &pc->address_list[i];
512       GNUNET_free_non_null (ar->result);
513       if (NULL != ar->atsc)
514       {
515         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
516         ar->atsc = NULL;
517       }
518     }
519     GNUNET_free_non_null (pc->address_list);
520     GNUNET_free (pc);
521   }
522   GPI_plugins_unload ();
523   if (NULL != peerinfo)
524   {
525     GNUNET_PEERINFO_disconnect (peerinfo);
526     peerinfo = NULL;
527   }
528 }
529
530
531 /**
532  * Main function that will be run by the scheduler.
533  *
534  * @param cls closure
535  * @param args remaining command-line arguments
536  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
537  * @param c configuration
538  */
539 static void
540 run (void *cls, char *const *args, const char *cfgfile,
541      const struct GNUNET_CONFIGURATION_Handle *c)
542 {
543   struct GNUNET_CRYPTO_EccPrivateKey *priv;
544   char *fn;
545
546   cfg = c;
547   if ( (NULL != args[0]) &&
548        (NULL == put_uri) &&
549        (args[0] == strcasestr (args[0], "gnunet://hello/")) )
550   {
551     put_uri = GNUNET_strdup (args[0]);
552     args++;
553   }
554   if (NULL != args[0]) 
555   {
556     FPRINTF (stderr, 
557              _("Invalid command line argument `%s'\n"), 
558              args[0]);
559     return;
560   }
561   if (NULL == (peerinfo = GNUNET_PEERINFO_connect (cfg)))
562   {
563     FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
564     return;
565   }
566   if ( (GNUNET_YES == get_self) || (GNUNET_YES == get_uri) )
567   {
568     /* load private key */
569     if (GNUNET_OK !=
570         GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY",
571                                                  &fn))
572     {
573       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
574                "GNUNETD", "HOSTKEYFILE");
575       return;
576     }
577     if (NULL == (priv = GNUNET_CRYPTO_ecc_key_create_from_file (fn)))
578     {
579       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
580       GNUNET_free (fn);
581       return;
582     }
583     GNUNET_free (fn);
584     GNUNET_CRYPTO_ecc_key_get_public (priv, &my_public_key);
585     GNUNET_CRYPTO_ecc_key_free (priv);
586     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
587   }
588
589   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
590   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
591                                 &shutdown_task,
592                                 NULL);
593 }
594
595
596 /**
597  * Main state machine that goes over all options and
598  * runs the next requested function.
599  *
600  * @param cls unused
601  * @param tc scheduler context
602  */
603 static void
604 state_machine (void *cls,
605                const struct GNUNET_SCHEDULER_TaskContext *tc)
606 {
607   tt = GNUNET_SCHEDULER_NO_TASK;
608
609   if (NULL != put_uri)
610   {
611     GPI_plugins_load (cfg);
612     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
613     {
614       fprintf (stderr,
615                _("Invalid URI `%s'\n"),
616                put_uri);
617       GNUNET_SCHEDULER_shutdown ();
618     }
619     GNUNET_free (put_uri);
620     put_uri = NULL;
621     return;
622   }
623   if (GNUNET_YES == get_info)
624   {
625     get_info = GNUNET_NO;
626     GPI_plugins_load (cfg);
627     pic = GNUNET_PEERINFO_iterate (peerinfo, include_friend_only, NULL,
628                                    TIMEOUT,
629                                    &print_peer_info, NULL);
630     return;
631   }
632   if (GNUNET_YES == get_self)
633   {
634     struct GNUNET_CRYPTO_HashAsciiEncoded enc;
635
636     get_self = GNUNET_NO;
637     GNUNET_CRYPTO_hash_to_enc (&my_peer_identity.hashPubKey, &enc);
638     if (be_quiet)
639       printf ("%s\n", (char *) &enc);
640     else
641       printf (_("I am peer `%s'.\n"), (const char *) &enc);
642   }
643   if (GNUNET_YES == get_uri)
644   {
645     GPI_plugins_load (cfg);
646     pic = GNUNET_PEERINFO_iterate (peerinfo,include_friend_only, &my_peer_identity,
647                                    TIMEOUT, &print_my_uri, NULL);
648     get_uri = GNUNET_NO;
649     return;
650   }
651   GNUNET_SCHEDULER_shutdown ();
652 }
653
654
655 /**
656  * The main function to obtain peer information.
657  *
658  * @param argc number of arguments from the command line
659  * @param argv command line arguments
660  * @return 0 ok, 1 on error
661  */
662 int
663 main (int argc, char *const *argv)
664 {
665   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
666     {'n', "numeric", NULL,
667      gettext_noop ("don't resolve host names"),
668      0, &GNUNET_GETOPT_set_one, &no_resolve},
669     {'q', "quiet", NULL,
670      gettext_noop ("output only the identity strings"),
671      0, &GNUNET_GETOPT_set_one, &be_quiet},
672     {'f', "friends", NULL,
673      gettext_noop ("include friend-only information"),
674      0, &GNUNET_GETOPT_set_one, &include_friend_only},
675     {'s', "self", NULL,
676      gettext_noop ("output our own identity only"),
677      0, &GNUNET_GETOPT_set_one, &get_self},
678     {'i', "info", NULL,
679      gettext_noop ("list all known peers"),
680      0, &GNUNET_GETOPT_set_one, &get_info},
681     {'g', "get-hello", NULL,
682      gettext_noop ("also output HELLO uri(s)"),
683      0, &GNUNET_GETOPT_set_one, &get_uri},
684     {'p', "put-hello", "HELLO",
685      gettext_noop ("add given HELLO uri to the database"),
686      1, &GNUNET_GETOPT_set_string, &put_uri},
687     GNUNET_GETOPT_OPTION_END
688   };
689   int ret;
690
691   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
692     return 2;
693
694   ret = (GNUNET_OK ==
695          GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
696                              gettext_noop ("Print information about peers."),
697                              options, &run, NULL)) ? 0 : 1;
698   GNUNET_free ((void*) argv);
699   return ret;
700 }
701
702 /* end of gnunet-peerinfo.c */