-applying fix based on fixed #2210
[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_peerinfo_service.h"
31 #include "gnunet_transport_service.h"
32 #include "gnunet_program_lib.h"
33 #include "gnunet_transport_plugin.h"
34 #include "gnunet-peerinfo_plugins.h"
35
36 /**
37  * Prefix that every HELLO URI must start with.
38  */
39 #define HELLO_URI_PREFIX "gnunet://hello/"
40
41 /**
42  * How long until we time out during peerinfo iterations?
43  */
44 #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5)
45
46 /**
47  * Structure we use to collect printable address information.
48  */
49 struct PrintContext;
50
51 /**
52  * Record we keep for each printable address.
53  */
54 struct AddressRecord
55 {
56   /**
57    * Current address-to-string context (if active, otherwise NULL).
58    */
59   struct GNUNET_TRANSPORT_AddressToStringContext *atsc;
60
61   /**
62    * Printable address.
63    */
64   char *result;
65   
66   /**
67    * Print context this address record belongs to.
68    */
69   struct PrintContext *pc;
70 };
71
72
73 /**
74  * Structure we use to collect printable address information.
75  */
76 struct PrintContext
77 {
78
79   /**
80    * Kept in DLL.
81    */
82   struct PrintContext *next;
83
84   /**
85    * Kept in DLL.
86    */
87   struct PrintContext *prev;
88
89   /**
90    * Identity of the peer.
91    */
92   struct GNUNET_PeerIdentity peer;
93   
94   /**
95    * List of printable addresses.
96    */
97   struct AddressRecord *address_list;
98
99   /**
100    * Number of completed addresses in 'address_list'.
101    */
102   unsigned int num_addresses;
103
104   /**
105    * Number of addresses allocated in 'address_list'.
106    */
107   unsigned int address_list_size;
108
109   /**
110    * Current offset in 'address_list' (counted down).
111    */
112   unsigned int off;
113
114 };
115
116
117 /**
118  * Context used for building our own URI.
119  */
120 struct GetUriContext
121 {
122   /**
123    * Final URI.
124    */
125   char *uri;
126
127 };
128
129
130 /**
131  * Context for 'add_address_to_hello'.
132  */
133 struct GNUNET_PEERINFO_HelloAddressParsingContext
134 {
135   /**
136    * Position in the URI with the next address to parse.
137    */
138   const char *pos;
139
140   /**
141    * Set to GNUNET_SYSERR to indicate parse errors.
142    */
143   int ret;
144
145 };
146
147
148 /**
149  * Option '-n'
150  */
151 static int no_resolve;
152
153 /**
154  * Option '-q'
155  */
156 static int be_quiet;
157
158 /**
159  * Option '-s'
160  */
161 static int get_self;
162
163 /**
164  * Option 
165  */
166 static int get_uri;
167
168 /**
169  * Option '-i'
170  */
171 static int get_info;
172
173 /**
174  * Option 
175  */
176 static char *put_uri;
177
178 /**
179  * Handle to peerinfo service.
180  */
181 static struct GNUNET_PEERINFO_Handle *peerinfo;
182
183 /**
184  * Configuration handle.
185  */
186 static const struct GNUNET_CONFIGURATION_Handle *cfg;
187
188 /**
189  * Main state machine task (if active).
190  */
191 static GNUNET_SCHEDULER_TaskIdentifier tt;
192
193 /**
194  * Current iterator context (if active, otherwise NULL).
195  */
196 static struct GNUNET_PEERINFO_IteratorContext *pic;
197
198 /**
199  * My peer identity.
200  */
201 static struct GNUNET_PeerIdentity my_peer_identity;
202
203 /**
204  * My public key.
205  */
206 static struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded my_public_key;
207
208 /**
209  * Head of list of print contexts.
210  */
211 static struct PrintContext *pc_head;
212
213 /**
214  * Tail of list of print contexts.
215  */
216 static struct PrintContext *pc_tail;
217
218 /**
219  * Handle to current 'GNUNET_PEERINFO_add_peer' operation.
220  */
221 static struct GNUNET_PEERINFO_AddContext *ac;
222
223
224 /**
225  * Main state machine that goes over all options and
226  * runs the next requested function.
227  *
228  * @param cls unused
229  * @param tc unused
230  */
231 static void
232 state_machine (void *cls,
233                const struct GNUNET_SCHEDULER_TaskContext *tc);
234
235
236
237 /**
238  * Replace all characters in the input 'in' according
239  * to the mapping.  The mapping says to map each character
240  * in 'oldchars' to the corresponding character (by offset)
241  * in 'newchars'.  
242  *
243  * @param in input string to remap
244  * @param oldchars characters to replace
245  * @param newchars replacement characters, must have same length as 'oldchars'
246  * @return copy of string with replacement applied.
247  */
248 static char *
249 map_characters (const char *in,
250                 const char *oldchars,
251                 const char *newchars)
252 {
253   char *ret;
254   const char *off;
255   size_t i;
256
257   GNUNET_assert (strlen (oldchars) == strlen (newchars));
258   ret = GNUNET_strdup (in);
259   i = 0;
260   while (ret[i] != '\0')
261   {
262     off = strchr (oldchars, ret[i]);
263     if (NULL != off)
264       ret[i] = newchars[off - oldchars];
265     i++;    
266   }
267   return ret;
268 }
269
270
271
272 /* ********************* 'get_info' ******************* */
273
274 /**
275  * Print the collected address information to the console and free 'pc'.
276  *
277  * @param pc printing context
278  */
279 static void
280 dump_pc (struct PrintContext *pc)
281 {
282   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
283   unsigned int i;
284
285   GNUNET_CRYPTO_hash_to_enc (&pc->peer.hashPubKey, &enc);
286   printf (_("Peer `%s'\n"), 
287           (const char *) &enc);
288   for (i = 0; i < pc->num_addresses; i++)
289   {
290     if (NULL != pc->address_list[i].result)
291     {
292       printf ("\t%s\n", pc->address_list[i].result);
293       GNUNET_free (pc->address_list[i].result);
294     }
295   }
296   printf ("\n");
297   GNUNET_free_non_null (pc->address_list);
298   GNUNET_CONTAINER_DLL_remove (pc_head,
299                                pc_tail,
300                                pc);
301   GNUNET_free (pc);
302   if ( (NULL == pc_head) &&
303        (NULL == pic) )
304     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);  
305 }
306
307
308 /* ************************* list all known addresses **************** */
309
310
311 /**
312  * Function to call with a human-readable format of an address
313  *
314  * @param cls closure
315  * @param address NULL on error, otherwise 0-terminated printable UTF-8 string
316  */
317 static void
318 process_resolved_address (void *cls, const char *address)
319 {
320   struct AddressRecord * ar = cls;
321   struct PrintContext *pc = ar->pc;
322
323   if (NULL != address)
324   {
325     if (NULL == ar->result)
326       ar->result = GNUNET_strdup (address);
327     return;
328   }
329   ar->atsc = NULL;
330   pc->num_addresses++;
331   if (pc->num_addresses == pc->address_list_size)
332     dump_pc (pc);
333 }
334
335
336 /**
337  * Iterator callback to go over all addresses and count them.
338  *
339  * @param cls 'struct PrintContext' with 'off' to increment
340  * @param address the address
341  * @param expiration expiration time
342  * @return GNUNET_OK to keep the address and continue
343  */
344 static int
345 count_address (void *cls, const struct GNUNET_HELLO_Address *address,
346                struct GNUNET_TIME_Absolute expiration)
347 {
348   struct PrintContext *pc = cls;
349
350   pc->off++;
351   return GNUNET_OK;
352 }
353
354
355 /**
356  * Iterator callback to go over all addresses.
357  *
358  * @param cls closure
359  * @param address the address
360  * @param expiration expiration time
361  * @return GNUNET_OK to keep the address and continue
362  */
363 static int
364 print_address (void *cls, const struct GNUNET_HELLO_Address *address,
365                struct GNUNET_TIME_Absolute expiration)
366 {
367   struct PrintContext *pc = cls;
368   struct AddressRecord *ar;
369
370   GNUNET_assert (0 < pc->off);
371   ar = &pc->address_list[--pc->off];
372   ar->pc = pc;
373   ar->atsc = GNUNET_TRANSPORT_address_to_string (cfg, address, no_resolve,
374                                                  GNUNET_TIME_relative_multiply
375                                                  (GNUNET_TIME_UNIT_SECONDS, 10),
376                                                  &process_resolved_address, ar);
377   return GNUNET_OK;
378 }
379
380
381 /**
382  * Print information about the peer.
383  * Currently prints the GNUNET_PeerIdentity and the transport address.
384  *
385  * @param cls the 'struct PrintContext'
386  * @param peer identity of the peer 
387  * @param hello addresses of the peer
388  * @param err_msg error message
389  */
390 static void
391 print_peer_info (void *cls, const struct GNUNET_PeerIdentity *peer,
392                  const struct GNUNET_HELLO_Message *hello, const char *err_msg)
393 {
394   struct GNUNET_CRYPTO_HashAsciiEncoded enc;
395   struct PrintContext *pc;
396
397   if (peer == NULL)
398   {
399     pic = NULL; /* end of iteration */
400     if (err_msg != NULL)
401     {
402       FPRINTF (stderr, 
403                _("Error in communication with PEERINFO service: %s\n"),
404                err_msg);
405     }
406     if (NULL == pc_head)
407       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
408     return;
409   }
410   if ((GNUNET_YES == be_quiet) || (NULL == hello))
411   {
412     GNUNET_CRYPTO_hash_to_enc (&peer->hashPubKey, &enc);
413     printf ("%s\n", (const char *) &enc);
414     return;
415   }
416   pc = GNUNET_malloc (sizeof (struct PrintContext));
417   GNUNET_CONTAINER_DLL_insert (pc_head,
418                                pc_tail, 
419                                pc);
420   pc->peer = *peer;
421   GNUNET_HELLO_iterate_addresses (hello, 
422                                   GNUNET_NO, 
423                                   &count_address, 
424                                   pc);
425   if (0 == pc->off)
426   {
427     dump_pc (pc);
428     return;
429   }
430   pc->address_list_size = pc->off;
431   pc->address_list = GNUNET_malloc (sizeof (struct AddressRecord) * pc->off);
432   GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, 
433                                   &print_address, pc);
434 }
435
436
437 /* ************************* GET URI ************************** */
438
439
440 /**
441  * Function that is called on each address of this peer.
442  * Expands the corresponding URI string.
443  *
444  * @param cls the 'GetUriContext'
445  * @param address address to add
446  * @param expiration expiration time for the address
447  * @return GNUNET_OK (continue iteration).
448  */
449 static int
450 compose_uri (void *cls, const struct GNUNET_HELLO_Address *address,
451              struct GNUNET_TIME_Absolute expiration)
452 {
453   struct GetUriContext *guc = cls;
454   struct GNUNET_TRANSPORT_PluginFunctions *papi;
455   const char *addr;
456   char *uri_addr;
457   char *ret;
458   char tbuf[16];
459   struct tm *t;
460   time_t seconds;
461
462   papi = GPI_plugins_find (address->transport_name);
463   if (papi == NULL)
464   {
465     /* Not an error - we might just not have the right plugin. */
466     return GNUNET_OK;
467   }
468   if (NULL == papi->address_to_string)
469   {
470     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
471                 "URI conversion not implemented for plugin `%s'\n",
472                 address->transport_name);
473     return GNUNET_OK;
474   }
475   addr = papi->address_to_string (papi->cls, address->address, address->address_length);
476   if ( (addr == NULL) || (strlen(addr) == 0) )
477     return GNUNET_OK;
478    /* For URIs we use '(' and ')' instead of '[' and ']' as brackets are reserved
479       characters in URIs */
480   uri_addr = map_characters (addr, "[]", "()");
481   seconds = expiration.abs_value / 1000;
482   t = gmtime (&seconds);
483   GNUNET_assert (0 != strftime (tbuf, sizeof (tbuf),
484                                 "%Y%m%d%H%M%S",
485                                 t));
486   GNUNET_asprintf (&ret,
487                    "%s!%s!%s!%s",
488                    guc->uri,
489                    tbuf,
490                    address->transport_name, 
491                    uri_addr);
492   GNUNET_free (uri_addr);
493   GNUNET_free (guc->uri);
494   guc->uri = ret;
495   return GNUNET_OK;
496 }
497
498
499 /**
500  * Print URI of the peer.
501  *
502  * @param cls the 'struct GetUriContext'
503  * @param peer identity of the peer (unused)
504  * @param hello addresses of the peer
505  * @param err_msg error message
506  */
507 static void
508 print_my_uri (void *cls, const struct GNUNET_PeerIdentity *peer,
509               const struct GNUNET_HELLO_Message *hello, 
510               const char *err_msg)
511 {
512   struct GetUriContext *guc = cls;
513
514   if (peer == NULL)
515   {
516     pic = NULL;
517     if (err_msg != NULL)
518       FPRINTF (stderr,
519                _("Error in communication with PEERINFO service: %s\n"), 
520                err_msg);
521     GNUNET_free_non_null (guc->uri);
522     GNUNET_free (guc);  
523     tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
524     return;
525   } 
526   if (NULL != hello)
527     GNUNET_HELLO_iterate_addresses (hello, GNUNET_NO, &compose_uri, guc);   
528   printf ("%s\n", (const char *) guc->uri);
529 }
530
531
532 /* ************************* import HELLO by URI ********************* */
533
534
535 /**
536  * We're building a HELLO.  Parse the next address from the
537  * parsing context and append it.
538  *
539  * @param cls the 'struct GNUNET_PEERINFO_HelloAddressParsingContext'
540  * @param max number of bytes available for HELLO construction
541  * @param buffer where to copy the next address (in binary format)
542  * @return number of bytes added to buffer
543  */ 
544 static size_t
545 add_address_to_hello (void *cls, size_t max, void *buffer)
546 {
547   struct GNUNET_PEERINFO_HelloAddressParsingContext *ctx = cls;
548   const char *tname;
549   const char *address;
550   char *uri_address;
551   char *plugin_address;
552   const char *end;
553   char *plugin_name;
554   struct tm expiration_time;
555   time_t expiration_seconds;
556   struct GNUNET_TIME_Absolute expire;
557   struct GNUNET_TRANSPORT_PluginFunctions *papi;
558   void *addr;
559   size_t addr_len;
560   struct GNUNET_HELLO_Address haddr;
561   size_t ret;
562
563   if (NULL == ctx->pos)
564     return 0;
565   if ('!' != ctx->pos[0])
566   {
567     ctx->ret = GNUNET_SYSERR;
568     GNUNET_break (0);
569     return 0;
570   }
571   ctx->pos++;
572   memset (&expiration_time, 0, sizeof (expiration_time));
573   tname = strptime (ctx->pos,
574                     "%Y%m%d%H%M%S",
575                     &expiration_time);
576
577   if (NULL == tname)
578   {
579     ctx->ret = GNUNET_SYSERR;
580     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
581                 _("Failed to parse HELLO message: missing expiration time\n"));
582     GNUNET_break (0);
583     return 0;
584   }
585   expiration_seconds = mktime (&expiration_time);
586   if (expiration_seconds == (time_t) -1)
587   {
588     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
589                 _("Failed to parse HELLO message: invalid expiration time\n"));
590     ctx->ret = GNUNET_SYSERR;
591     GNUNET_break (0);
592     return 0;
593   }
594   expire.abs_value = expiration_seconds * 1000;
595   if ('!' != tname[0])
596   {
597     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
598                 _("Failed to parse HELLO message: malformed\n"));
599     ctx->ret = GNUNET_SYSERR;
600     GNUNET_break (0);
601     return 0;
602   }
603   tname++;
604   address = strchr (tname, (int) '!');
605   if (NULL == address)
606   {
607     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
608                 _("Failed to parse HELLO message: missing transport plugin\n"));
609     ctx->ret = GNUNET_SYSERR;
610     GNUNET_break (0);
611     return 0;
612   }
613   address++;
614   end = strchr (address, (int) '!');
615   ctx->pos = end;
616   plugin_name = GNUNET_strndup (tname, address - (tname+1));
617   papi = GPI_plugins_find (plugin_name);
618   if (NULL == papi)
619   {
620     /* Not an error - we might just not have the right plugin.
621      * Skip this part, advance to the next one and recurse.
622      * But only if this is not the end of string.
623      */
624     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
625                 _("Plugin `%s' not found\n"),
626                 plugin_name);
627     GNUNET_free (plugin_name);
628     GNUNET_break (0);
629     return 0;
630   }
631   if (NULL == papi->string_to_address)
632   {
633     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
634                 _("Plugin `%s' does not support URIs yet\n"),
635                 plugin_name);
636     GNUNET_free (plugin_name);
637     GNUNET_break (0);
638     return 0;
639   }
640   uri_address = GNUNET_strndup (address, end - address);
641   /* For URIs we use '(' and ')' instead of '[' and ']' as brackets are reserved
642      characters in URIs; need to convert back to '[]' for the plugin */
643    plugin_address = map_characters (uri_address, "()", "[]");
644   GNUNET_free (uri_address);
645   if (GNUNET_OK !=
646       papi->string_to_address (papi->cls, 
647                                plugin_address,
648                                strlen (plugin_address) + 1,
649                                &addr,
650                                &addr_len))
651   {
652     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
653                 _("Failed to parse `%s' as an address for plugin `%s'\n"),
654                 plugin_address,
655                 plugin_name);
656     GNUNET_free (plugin_name);
657     GNUNET_free (plugin_address);
658     return 0;
659   }
660   GNUNET_free (plugin_address);
661   /* address.peer is unset - not used by add_address() */
662   haddr.address_length = addr_len;
663   haddr.address = addr;
664   haddr.transport_name = plugin_name;
665   ret = GNUNET_HELLO_add_address (&haddr, expire, buffer, max);
666   GNUNET_free (addr);
667   GNUNET_free (plugin_name);
668   return ret;
669 }
670
671
672 /**
673  * Continuation called from 'GNUNET_PEERINFO_add_peer'
674  *
675  * @param cls closure, NULL
676  * @param emsg error message, NULL on success
677  */
678 static void
679 add_continuation (void *cls,
680                   const char *emsg)
681 {
682   ai = NULL;
683   if (NULL != emsg)
684     fprintf (stderr,
685              _("Failure adding HELLO: %s\n"),
686              emsg);
687   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
688 }
689
690
691 /**
692  * Parse the PUT URI given at the command line and add it to our peerinfo 
693  * database.
694  *
695  * @param put_uri URI string to parse
696  * @return GNUNET_OK on success, GNUNET_SYSERR if the URI was invalid, GNUNET_NO on other errors
697  */
698 static int
699 parse_hello_uri (const char *put_uri)
700 {
701   const char *pks;
702   const char *exc;
703   struct GNUNET_HELLO_Message *hello;
704   struct GNUNET_PEERINFO_HelloAddressParsingContext ctx;
705
706   if (0 != strncmp (put_uri,
707                     HELLO_URI_PREFIX,
708                     strlen (HELLO_URI_PREFIX)))
709     return GNUNET_SYSERR;
710   pks = &put_uri[strlen (HELLO_URI_PREFIX)];
711   exc = strstr (pks, "!");
712
713   if (GNUNET_OK != GNUNET_STRINGS_string_to_data (pks,
714                                                   (NULL == exc) ? strlen (pks) : (exc - pks),
715                                                   (unsigned char *) &my_public_key, 
716                                                   sizeof (my_public_key)))
717     return GNUNET_SYSERR;
718   ctx.pos = exc;
719   ctx.ret = GNUNET_OK;
720   hello = GNUNET_HELLO_create (&my_public_key, &add_address_to_hello, &ctx);
721
722   if (NULL != hello)
723   {
724     /* WARNING: this adds the address from URI WITHOUT verification! */
725     if (GNUNET_OK == ctx.ret)    
726       ac = GNUNET_PEERINFO_add_peer (peerinfo, hello, NULL, NULL);
727     else
728       tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
729     GNUNET_free (hello);
730   }
731
732   /* wait 1s to give peerinfo operation a chance to succeed */
733   /* FIXME: current peerinfo API sucks to require this; not to mention
734      that we get no feedback to determine if the operation actually succeeded */
735   return ctx.ret;
736 }
737
738
739 /* ************************ Main state machine ********************* */
740
741
742 /**
743  * Main state machine that goes over all options and
744  * runs the next requested function.
745  *
746  * @param cls unused
747  * @param tc scheduler context
748  */
749 static void
750 shutdown_task (void *cls,
751                const struct GNUNET_SCHEDULER_TaskContext *tc)
752 {
753   struct PrintContext *pc;
754   struct AddressRecord *ar;
755   unsigned int i;
756
757   if (NULL != ac)
758   {
759     GNUNET_PEERINFO_add_peer_cancel (ac);
760     ac = NULL;
761   }
762   if (GNUNET_SCHEDULER_NO_TASK != tt)
763   {
764     GNUNET_SCHEDULER_cancel (tt);
765     tt = GNUNET_SCHEDULER_NO_TASK;
766   }
767   if (NULL != pic)
768   {
769     GNUNET_PEERINFO_iterate_cancel (pic);
770     pic = NULL;
771   }
772   while (NULL != (pc = pc_head))
773   {
774     GNUNET_CONTAINER_DLL_remove (pc_head,
775                                  pc_tail,
776                                  pc);
777     for (i=0;i<pc->address_list_size;i++)
778     {
779       ar = &pc->address_list[i];
780       GNUNET_free_non_null (ar->result);
781       if (NULL != ar->atsc)
782       {
783         GNUNET_TRANSPORT_address_to_string_cancel (ar->atsc);
784         ar->atsc = NULL;
785       }
786     }
787     GNUNET_free_non_null (pc->address_list);
788     GNUNET_free (pc);
789   }
790   GPI_plugins_unload ();
791   if (NULL != peerinfo)
792   {
793     GNUNET_PEERINFO_disconnect (peerinfo);
794     peerinfo = NULL;
795   }
796 }
797
798
799 /**
800  * Main function that will be run by the scheduler.
801  *
802  * @param cls closure
803  * @param args remaining command-line arguments
804  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
805  * @param c configuration
806  */
807 static void
808 run (void *cls, char *const *args, const char *cfgfile,
809      const struct GNUNET_CONFIGURATION_Handle *c)
810 {
811   struct GNUNET_CRYPTO_RsaPrivateKey *priv;
812   char *fn;
813
814   cfg = c;
815   if (args[0] != NULL)
816   {
817     FPRINTF (stderr, _("Invalid command line argument `%s'\n"), args[0]);
818     return;
819   }
820   peerinfo = GNUNET_PEERINFO_connect (cfg);
821   if (peerinfo == NULL)
822   {
823     FPRINTF (stderr, "%s",  _("Could not access PEERINFO service.  Exiting.\n"));
824     return;
825   }
826   if ( (GNUNET_YES == get_self) || (GNUNET_YES == get_uri) )
827   {
828     /* load private key */
829     if (GNUNET_OK !=
830         GNUNET_CONFIGURATION_get_value_filename (cfg, "GNUNETD", "HOSTKEY",
831                                                  &fn))
832     {
833       FPRINTF (stderr, _("Could not find option `%s:%s' in configuration.\n"),
834                "GNUNETD", "HOSTKEYFILE");
835       return;
836     }
837
838     if (NULL == (priv = GNUNET_CRYPTO_rsa_key_create_from_file (fn)))
839     {
840       FPRINTF (stderr, _("Loading hostkey from `%s' failed.\n"), fn);
841       GNUNET_free (fn);
842       return;
843     }
844     GNUNET_free (fn);
845     GNUNET_CRYPTO_rsa_key_get_public (priv, &my_public_key);
846     GNUNET_CRYPTO_rsa_key_free (priv);
847     GNUNET_CRYPTO_hash (&my_public_key, sizeof (my_public_key), &my_peer_identity.hashPubKey);
848   }
849
850   tt = GNUNET_SCHEDULER_add_now (&state_machine, NULL);
851   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
852                                 &shutdown_task,
853                                 NULL);
854 }
855
856
857 /**
858  * Main state machine that goes over all options and
859  * runs the next requested function.
860  *
861  * @param cls unused
862  * @param tc scheduler context
863  */
864 static void
865 state_machine (void *cls,
866                const struct GNUNET_SCHEDULER_TaskContext *tc)
867 {
868   tt = GNUNET_SCHEDULER_NO_TASK;
869
870   if (NULL != put_uri)
871   {
872     GPI_plugins_load (cfg);
873     if (GNUNET_SYSERR == parse_hello_uri (put_uri))
874       fprintf (stderr,
875                _("Invalid URI `%s'\n"),
876                put_uri);    
877     GNUNET_free (put_uri);
878     put_uri = NULL;
879     return;
880   }
881   if (GNUNET_YES == get_info)
882   {
883     get_info = GNUNET_NO;
884     GPI_plugins_load (cfg);
885     pic = GNUNET_PEERINFO_iterate (peerinfo, NULL,
886                                    TIMEOUT,
887                                    &print_peer_info, NULL);
888     return;
889   }
890   if (GNUNET_YES == get_self)
891   {
892     struct GNUNET_CRYPTO_HashAsciiEncoded enc;
893
894     get_self = GNUNET_NO;
895     GNUNET_CRYPTO_hash_to_enc (&my_peer_identity.hashPubKey, &enc);
896     if (be_quiet)
897       printf ("%s\n", (char *) &enc);
898     else
899       printf (_("I am peer `%s'.\n"), (const char *) &enc);
900   }
901   if (GNUNET_YES == get_uri)
902   {
903     struct GetUriContext *guc;
904     char *pkey;
905
906     guc = GNUNET_malloc (sizeof (struct GetUriContext));
907     pkey = GNUNET_CRYPTO_rsa_public_key_to_string (&my_public_key);
908     GNUNET_asprintf (&guc->uri,
909                      "%s%s",
910                      HELLO_URI_PREFIX,
911                      pkey);
912     GNUNET_free (pkey);
913     GPI_plugins_load (cfg);
914     pic = GNUNET_PEERINFO_iterate (peerinfo, &my_peer_identity,
915                                    TIMEOUT,
916                                    &print_my_uri, guc);
917     get_uri = GNUNET_NO;
918     return;
919   }
920   GNUNET_SCHEDULER_shutdown ();
921 }
922
923
924 /**
925  * The main function to obtain peer information.
926  *
927  * @param argc number of arguments from the command line
928  * @param argv command line arguments
929  * @return 0 ok, 1 on error
930  */
931 int
932 main (int argc, char *const *argv)
933 {
934   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
935     {'n', "numeric", NULL,
936      gettext_noop ("don't resolve host names"),
937      0, &GNUNET_GETOPT_set_one, &no_resolve},
938     {'q', "quiet", NULL,
939      gettext_noop ("output only the identity strings"),
940      0, &GNUNET_GETOPT_set_one, &be_quiet},
941     {'s', "self", NULL,
942      gettext_noop ("output our own identity only"),
943      0, &GNUNET_GETOPT_set_one, &get_self},
944     {'i', "info", NULL,
945      gettext_noop ("list all known peers"),
946      0, &GNUNET_GETOPT_set_one, &get_info},
947     {'g', "get-hello", NULL,
948      gettext_noop ("also output HELLO uri(s)"),
949      0, &GNUNET_GETOPT_set_one, &get_uri},
950     {'p', "put-hello", "HELLO",
951      gettext_noop ("add given HELLO uri to the database"),
952      1, &GNUNET_GETOPT_set_string, &put_uri},
953     GNUNET_GETOPT_OPTION_END
954   };
955   return (GNUNET_OK ==
956           GNUNET_PROGRAM_run (argc, argv, "gnunet-peerinfo",
957                               gettext_noop ("Print information about peers."),
958                               options, &run, NULL)) ? 0 : 1;
959 }
960
961 /* end of gnunet-peerinfo.c */