-fix leaks
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
1
2 /*
3      This file is part of GNUnet.
4      (C) 2012, 2013 Christian Grothoff (and other contributing authors)
5
6      GNUnet is free software; you can redistribute it and/or modify
7      it under the terms of the GNU General Public License as published
8      by the Free Software Foundation; either version 3, or (at your
9      option) any later version.
10
11      GNUnet is distributed in the hope that it will be useful, but
12      WITHOUT ANY WARRANTY; without even the implied warranty of
13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14      General Public License for more details.
15
16      You should have received a copy of the GNU General Public License
17      along with GNUnet; see the file COPYING.  If not, write to the
18      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19      Boston, MA 02111-1307, USA.
20 */
21 /**
22  * @file gnunet-namestore.c
23  * @brief command line tool to manipulate the local zone
24  * @author Christian Grothoff
25  *
26  * TODO:
27  * - test
28  */
29 #include "platform.h"
30 #include <gnunet_util_lib.h>
31 #include <gnunet_dnsparser_lib.h>
32 #include <gnunet_identity_service.h>
33 #include <gnunet_gnsrecord_lib.h>
34 #include <gnunet_namestore_service.h>
35
36
37 /**
38  * Handle to the namestore.
39  */
40 static struct GNUNET_NAMESTORE_Handle *ns;
41
42 /**
43  * Private key for the our zone.
44  */
45 static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
46
47 /**
48  * Handle to identity lookup.
49  */
50 static struct GNUNET_IDENTITY_EgoLookup *el;
51
52 /**
53  * Name of the ego controlling the zone.
54  */
55 static char *ego_name;
56
57 /**
58  * Desired action is to add a record.
59  */
60 static int add;
61
62 /**
63  * Iterator for the 'add' operation.
64  */
65 static struct GNUNET_NAMESTORE_ZoneIterator *add_zit;
66
67 /**
68  * Queue entry for the 'add-uri' operation.
69  */
70 static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
71
72 /**
73  * Queue entry for the 'add' operation.
74  */
75 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
76
77 /**
78  * Queue entry for the 'reverse lookup' operation (in combination with a name).
79  */
80 static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe;
81
82 /**
83  * Desired action is to list records.
84  */
85 static int list;
86
87 /**
88  * List iterator for the 'list' operation.
89  */
90 static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
91
92 /**
93  * Desired action is to remove a record.
94  */
95 static int del;
96
97 /**
98  * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE)
99  */
100 static int public;
101
102 /**
103  * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW_RECORD)
104  */
105 static int shadow;
106
107 /**
108  * Queue entry for the 'del' operation.
109  */
110 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
111
112 /**
113  * Name of the records to add/list/remove.
114  */
115 static char *name;
116
117 /**
118  * Value of the record to add/remove.
119  */
120 static char *value;
121
122 /**
123  * URI to import.
124  */
125 static char *uri;
126
127 /**
128  * Reverse lookup to perform.
129  */
130 static char *reverse_pkey;
131
132 /**
133  * Type of the record to add/remove, NULL to remove all.
134  */
135 static char *typestring;
136
137 /**
138  * Desired expiration time.
139  */
140 static char *expirationstring;
141
142 /**
143  * Global return value
144  */
145 static int ret;
146
147 /**
148  * Type string converted to DNS type value.
149  */
150 static uint32_t type;
151
152 /**
153  * Value in binary format.
154  */
155 static void *data;
156
157 /**
158  * Number of bytes in 'data'.
159  */
160 static size_t data_size;
161
162 /**
163  * Expirationstring converted to relative time.
164  */
165 static struct GNUNET_TIME_Relative etime_rel;
166
167 /**
168  * Expirationstring converted to absolute time.
169  */
170 static struct GNUNET_TIME_Absolute etime_abs;
171
172 /**
173  * Is expiration time relative or absolute time?
174  */
175 static int etime_is_rel = GNUNET_SYSERR;
176
177 /**
178  * Monitor handle.
179  */
180 static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
181
182 /**
183  * Enables monitor mode.
184  */
185 static int monitor;
186
187
188 /**
189  * Task run on shutdown.  Cleans up everything.
190  *
191  * @param cls unused
192  * @param tc scheduler context
193  */
194 static void
195 do_shutdown (void *cls,
196              const struct GNUNET_SCHEDULER_TaskContext *tc)
197 {
198   if (NULL != el)
199   {
200     GNUNET_IDENTITY_ego_lookup_cancel (el);
201     el = NULL;
202   }
203   if (NULL != list_it)
204   {
205     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
206     list_it = NULL;
207   }
208   if (NULL != add_zit)
209   {
210     GNUNET_NAMESTORE_zone_iteration_stop (add_zit);
211     add_zit = NULL;
212   }
213   if (NULL != add_qe)
214   {
215     GNUNET_NAMESTORE_cancel (add_qe);
216     add_qe = NULL;
217   }
218   if (NULL != add_qe_uri)
219   {
220     GNUNET_NAMESTORE_cancel (add_qe_uri);
221     add_qe_uri = NULL;
222   }
223   if (NULL != del_qe)
224   {
225     GNUNET_NAMESTORE_cancel (del_qe);
226     del_qe = NULL;
227   }
228   if (NULL != ns)
229   {
230     GNUNET_NAMESTORE_disconnect (ns);
231     ns = NULL;
232   }
233   memset (&zone_pkey, 0, sizeof (zone_pkey));
234   if (NULL != uri)
235   {
236     GNUNET_free (uri);
237     uri = NULL;
238   }
239   if (NULL != zm)
240   {
241     GNUNET_NAMESTORE_zone_monitor_stop (zm);
242     zm = NULL;
243   }
244   if (NULL != data)
245   {
246     GNUNET_free (data);
247     data = NULL;
248   }
249 }
250
251
252 /**
253  * Check if we are finished, and if so, perform shutdown.
254  */
255 static void
256 test_finished ()
257 {
258   if ( (NULL == add_qe) &&
259        (NULL == add_qe_uri) &&
260        (NULL == del_qe) &&
261        (NULL == reverse_qe) &&
262        (NULL == list_it) )
263     GNUNET_SCHEDULER_shutdown ();
264 }
265
266
267 /**
268  * Continuation called to notify client about result of the
269  * operation.
270  *
271  * @param cls closure, location of the QueueEntry pointer to NULL out
272  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
273  *                #GNUNET_NO if content was already there
274  *                #GNUNET_YES (or other positive value) on success
275  * @param emsg NULL on success, otherwise an error message
276  */
277 static void
278 add_continuation (void *cls,
279                   int32_t success,
280                   const char *emsg)
281 {
282   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
283
284   *qe = NULL;
285   if (GNUNET_YES != success)
286   {
287     fprintf (stderr,
288              _("Adding record failed: %s\n"),
289              (GNUNET_NO == success) ? "record exists" : emsg);
290     if (GNUNET_NO != success)
291       ret = 1;
292   }
293   test_finished ();
294 }
295
296
297 /**
298  * Continuation called to notify client about result of the
299  * operation.
300  *
301  * @param cls closure, unused
302  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
303  *                #GNUNET_NO if content was already there
304  *                #GNUNET_YES (or other positive value) on success
305  * @param emsg NULL on success, otherwise an error message
306  */
307 static void
308 del_continuation (void *cls,
309                   int32_t success,
310                   const char *emsg)
311 {
312   del_qe = NULL;
313   if (success != GNUNET_YES)
314     fprintf (stderr,
315              _("Deleting record failed: %s\n"),
316              emsg);
317   test_finished ();
318 }
319
320
321 /**
322  * Process a record that was stored in the namestore.
323  *
324  * @param cls closure
325  * @param zone_key private key of the zone
326  * @param rname name that is being mapped (at most 255 characters long)
327  * @param rd_len number of entries in @a rd array
328  * @param rd array of records with data to store
329  */
330 static void
331 display_record (void *cls,
332                 const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
333                 const char *rname,
334                 unsigned int rd_len,
335                 const struct GNUNET_GNSRECORD_Data *rd)
336 {
337   const char *typestring;
338   char *s;
339   unsigned int i;
340   const char *ets;
341   struct GNUNET_TIME_Absolute at;
342   struct GNUNET_TIME_Relative rt;
343
344   if (NULL == rname)
345   {
346     list_it = NULL;
347     test_finished ();
348     return;
349   }
350   if ( (NULL != name) &&
351        (0 != strcmp (name, rname)) )
352   {
353     GNUNET_NAMESTORE_zone_iterator_next (list_it);
354     return;
355   }
356   FPRINTF (stdout,
357            "%s:\n",
358            rname);
359   for (i=0;i<rd_len;i++)
360   {
361     typestring = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
362     s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
363                                           rd[i].data,
364                                           rd[i].data_size);
365     if (NULL == s)
366     {
367       FPRINTF (stdout, _("\tCorrupt or unsupported record of type %u\n"),
368                (unsigned int) rd[i].record_type);
369       continue;
370     }
371     if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
372     {
373       rt.rel_value_us = rd[i].expiration_time;
374       ets = GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_YES);
375     }
376     else
377     {
378       at.abs_value_us = rd[i].expiration_time;
379       ets = GNUNET_STRINGS_absolute_time_to_string (at);
380     }
381     FPRINTF (stdout,
382              "\t%s: %s (%s)\n",
383              typestring,
384              s,
385              ets);
386     GNUNET_free (s);
387   }
388   FPRINTF (stdout, "%s", "\n");
389   GNUNET_NAMESTORE_zone_iterator_next (list_it);
390 }
391
392
393 /**
394  * Function called once we are in sync in monitor mode.
395  *
396  * @param cls NULL
397  */
398 static void
399 sync_cb (void *cls)
400 {
401   FPRINTF (stdout, "%s", "Monitor is now in sync.\n");
402 }
403
404
405 /**
406  * We're storing a record; this function is given the existing record
407  * so that we can merge the information.
408  *
409  * @param cls closure, unused
410  * @param zone_key private key of the zone
411  * @param rec_name name that is being mapped (at most 255 characters long)
412  * @param rd_count number of entries in @a rd array
413  * @param rd array of records with data to store
414  */
415 static void
416 get_existing_record (void *cls,
417                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
418                      const char *rec_name,
419                      unsigned int rd_count,
420                      const struct GNUNET_GNSRECORD_Data *rd)
421 {
422   struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
423   struct GNUNET_GNSRECORD_Data *rde;
424
425   if ( (NULL != zone_key) &&
426        (0 != strcmp (rec_name, name)) )
427   {
428     GNUNET_NAMESTORE_zone_iterator_next (add_zit);
429     return;
430   }
431   memset (rdn, 0, sizeof (struct GNUNET_GNSRECORD_Data));
432   memcpy (&rdn[1], rd, rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
433   /* FIXME: should add some logic to overwrite records if there
434      can only be one record of a particular type, and to check
435      if the combination of records is valid to begin with... */
436   rde = &rdn[0];
437   rde->data = data;
438   rde->data_size = data_size;
439   rde->record_type = type;
440   if (1 != shadow)
441     rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
442   if (1 != public)
443     rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
444   if (GNUNET_YES == etime_is_rel)
445   {
446     rde->expiration_time = etime_rel.rel_value_us;
447     rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
448   }
449   else if (GNUNET_NO == etime_is_rel)
450     rde->expiration_time = etime_abs.abs_value_us;
451   else
452     rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
453   GNUNET_assert (NULL != name);
454   add_qe = GNUNET_NAMESTORE_records_store (ns,
455                                            &zone_pkey,
456                                            name,
457                                            rd_count + 1,
458                                            rde,
459                                            &add_continuation,
460                                            &add_qe);
461   /* only cancel if we were not told that this
462      was the end of the iteration already */
463   if (NULL != rec_name)
464     GNUNET_NAMESTORE_zone_iteration_stop (add_zit);
465   add_zit = NULL;
466 }
467
468
469 /**
470  * Function called with the result of our attempt to obtain a name for a given
471  * public key.
472  *
473  * @param cls NULL
474  * @param zone private key of the zone; NULL on disconnect
475  * @param label label of the records; NULL on disconnect
476  * @param rd_count number of entries in @a rd array, 0 if label was deleted
477  * @param rd array of records with data to store
478  */
479 static void
480 handle_reverse_lookup (void *cls,
481                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
482                        const char *label,
483                        unsigned int rd_count,
484                        const struct GNUNET_GNSRECORD_Data *rd)
485 {
486   reverse_qe = NULL;
487   if (NULL == label)
488     FPRINTF (stdout,
489              "%s.zkey\n",
490              reverse_pkey);
491   else
492     FPRINTF (stdout,
493              "%s.gnu\n",
494              label);
495   test_finished ();
496 }
497
498
499 /**
500  * Function called with the result from the check if the namestore
501  * service is actually running.  If it is, we start the actual
502  * operation.
503  *
504  * @param cls closure with our configuration
505  * @param result #GNUNET_YES if the namestore service is running
506  */
507 static void
508 testservice_task (void *cls,
509                   int result)
510 {
511   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
512   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
513   struct GNUNET_GNSRECORD_Data rd;
514
515   if (GNUNET_YES != result)
516   {
517     FPRINTF (stderr, _("Service `%s' is not running\n"),
518              "namestore");
519     return;
520   }
521   if (! (add|del|list|(NULL != uri)|(NULL != reverse_pkey)) )
522   {
523     /* nothing more to be done */
524     fprintf (stderr,
525              _("No options given\n"));
526     GNUNET_SCHEDULER_shutdown ();
527     return;
528   }
529   GNUNET_CRYPTO_ecdsa_key_get_public (&zone_pkey,
530                                     &pub);
531
532   ns = GNUNET_NAMESTORE_connect (cfg);
533   if (NULL == ns)
534   {
535     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
536                 _("Failed to connect to namestore\n"));
537     return;
538   }
539   if (add)
540   {
541     if (NULL == name)
542     {
543       fprintf (stderr,
544                _("Missing option `%s' for operation `%s'\n"),
545                "-n", _("add"));
546       GNUNET_SCHEDULER_shutdown ();
547       ret = 1;
548       return;
549     }
550     if (NULL == typestring)
551     {
552       fprintf (stderr,
553                _("Missing option `%s' for operation `%s'\n"),
554                "-t", _("add"));
555       GNUNET_SCHEDULER_shutdown ();
556       ret = 1;
557       return;
558     }
559     type = GNUNET_GNSRECORD_typename_to_number (typestring);
560     if (UINT32_MAX == type)
561     {
562       fprintf (stderr, _("Unsupported type `%s'\n"), typestring);
563       GNUNET_SCHEDULER_shutdown ();
564       ret = 1;
565       return;
566     }
567     if (NULL == value)
568     {
569       fprintf (stderr,
570                _("Missing option `%s' for operation `%s'\n"),
571                "-V", _("add"));
572       ret = 1;
573       GNUNET_SCHEDULER_shutdown ();
574       return;
575     }
576     if (GNUNET_OK !=
577         GNUNET_GNSRECORD_string_to_value (type,
578                                           value,
579                                           &data,
580                                           &data_size))
581     {
582       fprintf (stderr, _("Value `%s' invalid for record type `%s'\n"),
583                value,
584                typestring);
585       GNUNET_SCHEDULER_shutdown ();
586       ret = 1;
587       return;
588     }
589     if (NULL == expirationstring)
590     {
591       fprintf (stderr,
592                _("Missing option `%s' for operation `%s'\n"),
593                "-e", _("add"));
594       GNUNET_SCHEDULER_shutdown ();
595       ret = 1;
596       return;
597     }
598     if (0 == strcmp (expirationstring, "never"))
599     {
600       etime_abs = GNUNET_TIME_UNIT_FOREVER_ABS;
601       etime_is_rel = GNUNET_NO;
602     }
603     else if (GNUNET_OK ==
604              GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
605                                                     &etime_rel))
606     {
607       etime_is_rel = GNUNET_YES;
608     }
609     else if (GNUNET_OK ==
610              GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
611                                                     &etime_abs))
612     {
613       etime_is_rel = GNUNET_NO;
614     }
615     else
616     {
617       fprintf (stderr,
618                _("Invalid time format `%s'\n"),
619                expirationstring);
620       GNUNET_SCHEDULER_shutdown ();
621       ret = 1;
622       return;
623     }
624     add_zit = GNUNET_NAMESTORE_zone_iteration_start (ns,
625                                                      &zone_pkey,
626                                                      &get_existing_record,
627                                                      NULL);
628   }
629   if (del)
630   {
631     if (NULL == name)
632     {
633       fprintf (stderr,
634                _("Missing option `%s' for operation `%s'\n"),
635                "-n", _("del"));
636       GNUNET_SCHEDULER_shutdown ();
637       ret = 1;
638       return;
639     }
640     del_qe = GNUNET_NAMESTORE_records_store (ns,
641                                              &zone_pkey,
642                                              name,
643                                              0, NULL,
644                                              &del_continuation,
645                                              NULL);
646   }
647   if (list)
648   {
649     list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
650                                                      &zone_pkey,
651                                                      &display_record,
652                                                      NULL);
653   }
654   if (NULL != reverse_pkey)
655   {
656     struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
657
658     if (GNUNET_OK !=
659         GNUNET_CRYPTO_ecdsa_public_key_from_string (reverse_pkey,
660                                                        strlen (reverse_pkey),
661                                                        &pubkey))
662     {
663       fprintf (stderr,
664                _("Invalid public key for reverse lookup `%s'\n"),
665                reverse_pkey);
666       GNUNET_SCHEDULER_shutdown ();
667     }
668     reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns,
669                                                 &zone_pkey,
670                                                 &pubkey,
671                                                 &handle_reverse_lookup,
672                                                 NULL);
673   }
674   if (NULL != uri)
675   {
676     char sh[105];
677     char sname[64];
678     struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
679
680     if ( (2 != (sscanf (uri,
681                         "gnunet://gns/%104s/%63s",
682                         sh,
683                         sname)) ) ||
684          (GNUNET_OK !=
685           GNUNET_CRYPTO_ecdsa_public_key_from_string (sh, strlen (sh), &pkey)) )
686     {
687       fprintf (stderr,
688                _("Invalid URI `%s'\n"),
689                uri);
690       GNUNET_SCHEDULER_shutdown ();
691       ret = 1;
692       return;
693     }
694     memset (&rd, 0, sizeof (rd));
695     rd.data = &pkey;
696     rd.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
697     rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
698     if (GNUNET_YES == etime_is_rel)
699     {
700       rd.expiration_time = etime_rel.rel_value_us;
701       rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
702     }
703     else if (GNUNET_NO == etime_is_rel)
704       rd.expiration_time = etime_abs.abs_value_us;
705     else
706       rd.expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
707     if (1 != shadow)
708       rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
709     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
710                                                  &zone_pkey,
711                                                  sname,
712                                                  1,
713                                                  &rd,
714                                                  &add_continuation,
715                                                  &add_qe_uri);
716   }
717   if (monitor)
718   {
719     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
720                                               &zone_pkey,
721                                               &display_record,
722                                               &sync_cb,
723                                               NULL);
724   }
725 }
726
727
728 /**
729  * Callback invoked from identity service with ego information.
730  * An @a ego of NULL means the ego was not found.
731  *
732  * @param cls closure with the configuration
733  * @param ego an ego known to identity service, or NULL
734  */
735 static void
736 identity_cb (void *cls,
737              const struct GNUNET_IDENTITY_Ego *ego)
738 {
739   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
740
741   el = NULL;
742   if (NULL == ego)
743   {
744     fprintf (stderr,
745              _("Ego `%s' not known to identity service\n"),
746              ego_name);
747     GNUNET_SCHEDULER_shutdown ();
748     return;
749   }
750   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
751   GNUNET_free (ego_name);
752   ego_name = NULL;
753   GNUNET_CLIENT_service_test ("namestore", cfg,
754                               GNUNET_TIME_UNIT_SECONDS,
755                               &testservice_task,
756                               (void *) cfg);
757 }
758
759
760 /**
761  * Main function that will be run.
762  *
763  * @param cls closure
764  * @param args remaining command-line arguments
765  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
766  * @param cfg configuration
767  */
768 static void
769 run (void *cls, char *const *args, const char *cfgfile,
770      const struct GNUNET_CONFIGURATION_Handle *cfg)
771 {
772   if (NULL == ego_name)
773   {
774     fprintf (stderr,
775              _("You must specify which zone should be accessed\n"));
776     return;
777   }
778   if ( (NULL != args[0]) && (NULL == uri) )
779     uri = GNUNET_strdup (args[0]);
780   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
781                                 &do_shutdown, NULL);
782   el = GNUNET_IDENTITY_ego_lookup (cfg,
783                                    ego_name,
784                                    &identity_cb,
785                                    (void *) cfg);
786 }
787
788
789 /**
790  * The main function for gnunet-namestore.
791  *
792  * @param argc number of arguments from the command line
793  * @param argv command line arguments
794  * @return 0 ok, 1 on error
795  */
796 int
797 main (int argc, char *const *argv)
798 {
799   public = -1;
800
801   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
802     {'a', "add", NULL,
803      gettext_noop ("add record"), 0,
804      &GNUNET_GETOPT_set_one, &add},
805     {'d', "delete", NULL,
806      gettext_noop ("delete record"), 0,
807      &GNUNET_GETOPT_set_one, &del},
808     {'D', "display", NULL,
809      gettext_noop ("display records"), 0,
810      &GNUNET_GETOPT_set_one, &list},
811     {'e', "expiration", "TIME",
812      gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"), 1,
813      &GNUNET_GETOPT_set_string, &expirationstring},
814     {'m', "monitor", NULL,
815      gettext_noop ("monitor changes in the namestore"), 0,
816      &GNUNET_GETOPT_set_one, &monitor},
817     {'n', "name", "NAME",
818      gettext_noop ("name of the record to add/delete/display"), 1,
819      &GNUNET_GETOPT_set_string, &name},
820     {'r', "reverse", "PKEY",
821      gettext_noop ("determine our name for the given PKEY"), 1,
822      &GNUNET_GETOPT_set_string, &reverse_pkey},
823     {'t', "type", "TYPE",
824      gettext_noop ("type of the record to add/delete/display"), 1,
825      &GNUNET_GETOPT_set_string, &typestring},
826     {'u', "uri", "URI",
827      gettext_noop ("URI to import into our zone"), 1,
828      &GNUNET_GETOPT_set_string, &uri},
829     {'V', "value", "VALUE",
830      gettext_noop ("value of the record to add/delete"), 1,
831      &GNUNET_GETOPT_set_string, &value},
832     {'p', "public", NULL,
833      gettext_noop ("create or list public record"), 0,
834      &GNUNET_GETOPT_set_one, &public},
835     {'s', "shadow", NULL,
836      gettext_noop ("create shadow record (only valid if all other records of the same type have expired"), 0,
837      &GNUNET_GETOPT_set_one, &shadow},
838     {'z', "zone", "EGO",
839      gettext_noop ("name of the ego controlling the zone"), 1,
840      &GNUNET_GETOPT_set_string, &ego_name},
841     GNUNET_GETOPT_OPTION_END
842   };
843
844   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
845     return 2;
846
847   GNUNET_log_setup ("gnunet-namestore", "WARNING", NULL);
848   if (GNUNET_OK !=
849       GNUNET_PROGRAM_run (argc, argv, "gnunet-namestore",
850                           _("GNUnet zone manipulation tool"),
851                           options,
852                           &run, NULL))
853   {
854     GNUNET_free ((void*) argv);
855     GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
856     return 1;
857   }
858   GNUNET_free ((void*) argv);
859   GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
860   return ret;
861 }
862
863 /* end of gnunet-namestore.c */