fixed friend flag
[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_NO;
351   if (NULL != hello)
352         friend_only = GNUNET_HELLO_is_friend_only (hello);
353   if ((GNUNET_YES == be_quiet) || (NULL == hello))
354   {
355     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
356     printf ("%s%s\n",
357                 (GNUNET_YES == friend_only) ? "F2F: " : "",
358                 (const char *) &enc);
359     return;
360   }
361   pc = GNUNET_malloc (sizeof (struct PrintContext));
362   GNUNET_CONTAINER_DLL_insert (pc_head,
363                                pc_tail, 
364                                pc);
365   pc->peer = *peer;
366   pc->friend_only = friend_only;
367   GNUNET_HELLO_iterate_addresses (hello, 
368                                   GNUNET_NO, 
369                                   &count_address, 
370                                   pc);
371   if (0 == pc->off)
372   {
373     dump_pc (pc);
374     return;
375   }
376   pc->address_list_size = pc->off;
377   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
378   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, 
379                                   &print_address, pc);
380 }
381
382
383 /* ************************* GET URI ************************** */
384
385
386 /**
387  * Print URI of the peer.
388  *
389  * @param cls the 'struct GetUriContext'
390  * @param peer identity of the peer (unused)
391  * @param hello addresses of the peer
392  * @param err_msg error message
393  */
394 static void
395 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
396               const struct GNUNET_HELLO_Message *hello, 
397               const char *err_msg)
398 {
399   if (peer == NULL)
400   {
401     pic = NULL;
402     if (err_msg != NULL)
403       FPRINTF (stderr,
404                _("Error in communication with PEERINFO service: %s\n"), 
405                err_msg);
406     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
407     return;
408   }
409
410   if (NULL == hello)
411     return;
412   char *uri = GNUNET_HELLO_compose_uri(hello, &GPI_plugins_find);
413   if (NULL != uri) {
414     printf ("%s\n", (const char *) uri);
415     GNUNET_free (uri);
416   }
417 }
418
419
420 /* ************************* import HELLO by URI ********************* */
421
422
423 /**
424  * Continuation called from 'GNUNET_PEERINFO_add_peer'
425  *
426  * @param cls closure, NULL
427  * @param emsg error message, NULL on success
428  */
429 static void
430 add_continuation (void *cls,
431                   const char *emsg)
432 {
433   ac = NULL;
434   if (NULL != emsg)
435     fprintf (stderr,
436              _("Failure adding HELLO: %s\n"),
437              emsg);
438   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
439 }
440
441
442 /**
443  * Parse the PUT URI given at the command line and add it to our peerinfo 
444  * database.
445  *
446  * @param put_uri URI string to parse
447  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
448  */
449 static int
450 parse_hello_uri (const char *put_uri)
451 {
452   struct GNUNET_HELLO_Message *hello = NULL;
453
454   int ret = GNUNET_HELLO_parse_uri(put_uri, &my_public_key, &hello, &GPI_plugins_find);
455
456   if (NULL != hello) {
457     /* WARNING: this adds the address from URI WITHOUT verification! */
458     if (GNUNET_OK == ret)
459       ac = GNUNET_PEERINFO_add_peer (peerinfo, hello, &add_continuation, NULL);
460     else
461       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
462     GNUNET_free (hello);
463   }
464
465   /* wait 1s to give peerinfo operation a chance to succeed */
466   /* FIXME: current peerinfo API sucks to require this; not to mention
467      that we get no feedback to determine if the operation actually succeeded */
468   return ret;
469 }
470
471
472 /* ************************ Main state machine ********************* */
473
474
475 /**
476  * Main state machine that goes over all options and
477  * runs the next requested function.
478  *
479  * @param cls unused
480  * @param tc scheduler context
481  */
482 static void
483 shutdown_task (void *cls,
484                const struct GNUNET_SCHEDULER_TaskContext *tc)
485 {
486   struct PrintContext *pc;
487   struct AddressRecord *ar;
488   unsigned int i;
489
490   if (NULL != ac)
491   {
492     GNUNET_PEERINFO_add_peer_cancel (ac);
493     ac = NULL;
494   }
495   if (GNUNET_SCHEDULER_NO_TASK != tt)
496   {
497     GNUNET_SCHEDULER_cancel (tt);
498     tt = GNUNET_SCHEDULER_NO_TASK;
499   }
500   if (NULL != pic)
501   {
502     GNUNET_PEERINFO_iterate_cancel (pic);
503     pic = NULL;
504   }
505   while (NULL != (pc = pc_head))
506   {
507     GNUNET_CONTAINER_DLL_remove (pc_head,
508                                  pc_tail,
509                                  pc);
510     for (i=0;i<pc->address_list_size;i++)
511     {
512       ar = &pc->address_list[i];
513       GNUNET_free_non_null (ar->result);
514       if (NULL != ar->atsc)
515       {
516         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
517         ar->atsc = NULL;
518       }
519     }
520     GNUNET_free_non_null (pc->address_list);
521     GNUNET_free (pc);
522   }
523   GPI_plugins_unload ();
524   if (NULL != peerinfo)
525   {
526     GNUNET_PEERINFO_disconnect (peerinfo);
527     peerinfo = NULL;
528   }
529 }
530
531
532 /**
533  * Main function that will be run by the scheduler.
534  *
535  * @param cls closure
536  * @param args remaining command-line arguments
537  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
538  * @param c configuration
539  */
540 static void
541 run (void *cls, char *const *args, const char *cfgfile,
542      const struct GNUNET_CONFIGURATION_Handle *c)
543 {
544   struct GNUNET_CRYPTO_EccPrivateKey *priv;
545   char *fn;
546
547   cfg = c;
548   if ( (NULL != args[0]) &&
549        (NULL == put_uri) &&
550        (args[0] == strcasestr (args[0], "gnunet://hello/")) )
551   {
552     put_uri = GNUNET_strdup (args[0]);
553     args++;
554   }
555   if (NULL != args[0]) 
556   {
557     FPRINTF (stderr, 
558              _("Invalid command line argument `%s'\n"), 
559              args[0]);
560     return;
561   }
562   if (NULL == (peerinfo = GNUNET_PEERINFO_connect (cfg)))
563   {
564     FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
565     return;
566   }
567   if ( (GNUNET_YES == get_self) || (GNUNET_YES == get_uri) )
568   {
569     /* load private key */
570     if (GNUNET_OK !=
571         GNUNET_CONFIGURATION_get_value_filename (cfg, "PEER", "PRIVATE_KEY",
572                                                  &fn))
573     {
574       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
575                "GNUNETD", "HOSTKEYFILE");
576       return;
577     }
578     if (NULL == (priv = GNUNET_CRYPTO_ecc_key_create_from_file (fn)))
579     {
580       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
581       GNUNET_free (fn);
582       return;
583     }
584     GNUNET_free (fn);
585     GNUNET_CRYPTO_ecc_key_get_public (priv, &my_public_key);
586     GNUNET_CRYPTO_ecc_key_free (priv);
587     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
588   }
589
590   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
591   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
592                                 &shutdown_task,
593                                 NULL);
594 }
595
596
597 /**
598  * Main state machine that goes over all options and
599  * runs the next requested function.
600  *
601  * @param cls unused
602  * @param tc scheduler context
603  */
604 static void
605 state_machine (void *cls,
606                const struct GNUNET_SCHEDULER_TaskContext *tc)
607 {
608   tt = GNUNET_SCHEDULER_NO_TASK;
609
610   if (NULL != put_uri)
611   {
612     GPI_plugins_load (cfg);
613     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
614     {
615       fprintf (stderr,
616                _("Invalid URI `%s'\n"),
617                put_uri);
618       GNUNET_SCHEDULER_shutdown ();
619     }
620     GNUNET_free (put_uri);
621     put_uri = NULL;
622     return;
623   }
624   if (GNUNET_YES == get_info)
625   {
626     get_info = GNUNET_NO;
627     GPI_plugins_load (cfg);
628     pic = GNUNET_PEERINFO_iterate (peerinfo, include_friend_only, NULL,
629                                    TIMEOUT,
630                                    &print_peer_info, NULL);
631     return;
632   }
633   if (GNUNET_YES == get_self)
634   {
635     struct GNUNET_CRYPTO_HashAsciiEncoded enc;
636
637     get_self = GNUNET_NO;
638     GNUNET_CRYPTO_hash_to_enc (&my_peer_identity.hashPubKey, &enc);
639     if (be_quiet)
640       printf ("%s\n", (char *) &enc);
641     else
642       printf (_("I am peer `%s'.\n"), (const char *) &enc);
643   }
644   if (GNUNET_YES == get_uri)
645   {
646     GPI_plugins_load (cfg);
647     pic = GNUNET_PEERINFO_iterate (peerinfo, include_friend_only, &my_peer_identity,
648                                    TIMEOUT, &print_my_uri, NULL);
649     get_uri = GNUNET_NO;
650     return;
651   }
652   GNUNET_SCHEDULER_shutdown ();
653 }
654
655
656 /**
657  * The main function to obtain peer information.
658  *
659  * @param argc number of arguments from the command line
660  * @param argv command line arguments
661  * @return 0 ok, 1 on error
662  */
663 int
664 main (int argc, char *const *argv)
665 {
666   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
667     {'n', "numeric", NULL,
668      gettext_noop ("don't resolve host names"),
669      0, &GNUNET_GETOPT_set_one, &no_resolve},
670     {'q', "quiet", NULL,
671      gettext_noop ("output only the identity strings"),
672      0, &GNUNET_GETOPT_set_one, &be_quiet},
673     {'f', "friends", NULL,
674      gettext_noop ("include friend-only information"),
675      0, &GNUNET_GETOPT_set_one, &include_friend_only},
676     {'s', "self", NULL,
677      gettext_noop ("output our own identity only"),
678      0, &GNUNET_GETOPT_set_one, &get_self},
679     {'i', "info", NULL,
680      gettext_noop ("list all known peers"),
681      0, &GNUNET_GETOPT_set_one, &get_info},
682     {'g', "get-hello", NULL,
683      gettext_noop ("also output HELLO uri(s)"),
684      0, &GNUNET_GETOPT_set_one, &get_uri},
685     {'p', "put-hello", "HELLO",
686      gettext_noop ("add given HELLO uri to the database"),
687      1, &GNUNET_GETOPT_set_string, &put_uri},
688     GNUNET_GETOPT_OPTION_END
689   };
690   int ret;
691
692   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
693     return 2;
694
695   ret = (GNUNET_OK ==
696          GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
697                              gettext_noop ("Print information about peers."),
698                              options, &run, NULL)) ? 0 : 1;
699   GNUNET_free ((void*) argv);
700   return ret;
701 }
702
703 /* end of gnunet-peerinfo.c */