NAMESTORE/JSON: fix parsing exp and flags
[oweals/gnunet.git] / src / namestore / gnunet-namestore.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012, 2013, 2014 GNUnet e.V.
4
5      GNUnet is free software: you can redistribute it and/or modify it
6      under the terms of the GNU Affero General Public License as published
7      by the Free Software Foundation, either version 3 of the License,
8      or (at your option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      Affero General Public License for more details.
14     
15      You should have received a copy of the GNU Affero General Public License
16      along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19 */
20 /**
21  * @file gnunet-namestore.c
22  * @brief command line tool to manipulate the local zone
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - test
27  */
28 #include "platform.h"
29 #include <gnunet_util_lib.h>
30 #include <gnunet_dnsparser_lib.h>
31 #include <gnunet_identity_service.h>
32 #include <gnunet_gnsrecord_lib.h>
33 #include <gnunet_gns_service.h>
34 #include <gnunet_namestore_service.h>
35
36
37 /**
38  * Entry in record set for bulk processing.
39  */
40 struct RecordSetEntry
41 {
42   /**
43    * Kept in a linked list.
44    */
45   struct RecordSetEntry *next;
46
47   /**
48    * The record to add/remove.
49    */
50   struct GNUNET_GNSRECORD_Data record;
51   
52 };
53
54
55 /**
56  * Handle to the namestore.
57  */
58 static struct GNUNET_NAMESTORE_Handle *ns;
59
60 /**
61  * Private key for the our zone.
62  */
63 static struct GNUNET_CRYPTO_EcdsaPrivateKey zone_pkey;
64
65 /**
66  * Handle to identity lookup.
67  */
68 static struct GNUNET_IDENTITY_EgoLookup *el;
69
70 /**
71  * Identity service handle
72  */
73 static struct GNUNET_IDENTITY_Handle *idh;
74
75 /**
76  * Obtain default ego
77  */
78 struct GNUNET_IDENTITY_Operation *get_default;
79
80 /**
81  * Name of the ego controlling the zone.
82  */
83 static char *ego_name;
84
85 /**
86  * Desired action is to add a record.
87  */
88 static int add;
89
90 /**
91  * Queue entry for the 'add-uri' operation.
92  */
93 static struct GNUNET_NAMESTORE_QueueEntry *add_qe_uri;
94
95 /**
96  * Queue entry for the 'add' operation.
97  */
98 static struct GNUNET_NAMESTORE_QueueEntry *add_qe;
99
100 /**
101  * Queue entry for the 'lookup' operation.
102  */
103 static struct GNUNET_NAMESTORE_QueueEntry *get_qe;
104
105 /**
106  * Queue entry for the 'reverse lookup' operation (in combination with a name).
107  */
108 static struct GNUNET_NAMESTORE_QueueEntry *reverse_qe;
109
110 /**
111  * Desired action is to list records.
112  */
113 static int list;
114
115 /**
116  * List iterator for the 'list' operation.
117  */
118 static struct GNUNET_NAMESTORE_ZoneIterator *list_it;
119
120 /**
121  * Desired action is to remove a record.
122  */
123 static int del;
124
125 /**
126  * Is record public (opposite of #GNUNET_GNSRECORD_RF_PRIVATE)
127  */
128 static int is_public;
129
130 /**
131  * Is record a shadow record (#GNUNET_GNSRECORD_RF_SHADOW_RECORD)
132  */
133 static int is_shadow;
134
135 /**
136  * Queue entry for the 'del' operation.
137  */
138 static struct GNUNET_NAMESTORE_QueueEntry *del_qe;
139
140 /**
141  * Queue entry for the 'set/replace' operation.
142  */
143 static struct GNUNET_NAMESTORE_QueueEntry *set_qe;
144
145 /**
146  * Name of the records to add/list/remove.
147  */
148 static char *name;
149
150 /**
151  * Value of the record to add/remove.
152  */
153 static char *value;
154
155 /**
156  * URI to import.
157  */
158 static char *uri;
159
160 /**
161  * Reverse lookup to perform.
162  */
163 static char *reverse_pkey;
164
165 /**
166  * Type of the record to add/remove, NULL to remove all.
167  */
168 static char *typestring;
169
170 /**
171  * Desired expiration time.
172  */
173 static char *expirationstring;
174
175 /**
176  * Desired nick name.
177  */
178 static char *nickstring;
179
180 /**
181  * Global return value
182  */
183 static int ret;
184
185 /**
186  * Type string converted to DNS type value.
187  */
188 static uint32_t type;
189
190 /**
191  * Value in binary format.
192  */
193 static void *data;
194
195 /**
196  * Number of bytes in #data.
197  */
198 static size_t data_size;
199
200 /**
201  * Expiration string converted to numeric value.
202  */
203 static uint64_t etime;
204
205 /**
206  * Is expiration time relative or absolute time?
207  */
208 static int etime_is_rel = GNUNET_SYSERR;
209
210 /**
211  * Monitor handle.
212  */
213 static struct GNUNET_NAMESTORE_ZoneMonitor *zm;
214
215 /**
216  * Enables monitor mode.
217  */
218 static int monitor;
219
220 /**
221  * Entry in record set for processing records in bulk.
222  */
223 static struct RecordSetEntry *recordset;
224
225
226 /**
227  * Task run on shutdown.  Cleans up everything.
228  *
229  * @param cls unused
230  */
231 static void
232 do_shutdown (void *cls)
233 {
234   (void) cls;
235   if (NULL != get_default)
236   {
237     GNUNET_IDENTITY_cancel (get_default);
238     get_default = NULL;
239   }
240   if (NULL != idh)
241   {
242     GNUNET_IDENTITY_disconnect (idh);
243     idh = NULL;
244   }
245   if (NULL != el)
246   {
247     GNUNET_IDENTITY_ego_lookup_cancel (el);
248     el = NULL;
249   }
250   if (NULL != list_it)
251   {
252     GNUNET_NAMESTORE_zone_iteration_stop (list_it);
253     list_it = NULL;
254   }
255   if (NULL != add_qe)
256   {
257     GNUNET_NAMESTORE_cancel (add_qe);
258     add_qe = NULL;
259   }
260   if (NULL != set_qe)
261   {
262     GNUNET_NAMESTORE_cancel (set_qe);
263     set_qe = NULL;
264   }
265   if (NULL != add_qe_uri)
266   {
267     GNUNET_NAMESTORE_cancel (add_qe_uri);
268     add_qe_uri = NULL;
269   }
270   if (NULL != get_qe)
271   {
272     GNUNET_NAMESTORE_cancel (get_qe);
273     get_qe = NULL;
274   }
275   if (NULL != del_qe)
276   {
277     GNUNET_NAMESTORE_cancel (del_qe);
278     del_qe = NULL;
279   }
280   if (NULL != ns)
281   {
282     GNUNET_NAMESTORE_disconnect (ns);
283     ns = NULL;
284   }
285   memset (&zone_pkey, 0, sizeof (zone_pkey));
286   if (NULL != uri)
287   {
288     GNUNET_free (uri);
289     uri = NULL;
290   }
291   if (NULL != zm)
292   {
293     GNUNET_NAMESTORE_zone_monitor_stop (zm);
294     zm = NULL;
295   }
296   if (NULL != data)
297   {
298     GNUNET_free (data);
299     data = NULL;
300   }
301 }
302
303
304 /**
305  * Check if we are finished, and if so, perform shutdown.
306  */
307 static void
308 test_finished ()
309 {
310   if ( (NULL == add_qe) &&
311        (NULL == add_qe_uri) &&
312        (NULL == get_qe) &&
313        (NULL == del_qe) &&
314        (NULL == reverse_qe) &&
315        (NULL == list_it) )
316     GNUNET_SCHEDULER_shutdown ();
317 }
318
319
320 /**
321  * Continuation called to notify client about result of the
322  * operation.
323  *
324  * @param cls closure, location of the QueueEntry pointer to NULL out
325  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
326  *                #GNUNET_NO if content was already there
327  *                #GNUNET_YES (or other positive value) on success
328  * @param emsg NULL on success, otherwise an error message
329  */
330 static void
331 add_continuation (void *cls,
332                   int32_t success,
333                   const char *emsg)
334 {
335   struct GNUNET_NAMESTORE_QueueEntry **qe = cls;
336
337   *qe = NULL;
338   if (GNUNET_YES != success)
339   {
340     fprintf (stderr,
341              _("Adding record failed: %s\n"),
342              (GNUNET_NO == success) ? "record exists" : emsg);
343     if (GNUNET_NO != success)
344       ret = 1;
345   }
346   ret = 0;
347   test_finished ();
348 }
349
350
351 /**
352  * Continuation called to notify client about result of the
353  * operation.
354  *
355  * @param cls closure, unused
356  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
357  *                #GNUNET_NO if content was already there
358  *                #GNUNET_YES (or other positive value) on success
359  * @param emsg NULL on success, otherwise an error message
360  */
361 static void
362 del_continuation (void *cls,
363                   int32_t success,
364                   const char *emsg)
365 {
366   (void) cls;
367   del_qe = NULL;
368   if (GNUNET_NO == success)
369   {
370     fprintf (stderr,
371              _("Deleting record failed, record does not exist%s%s\n"),
372              (NULL != emsg) ? ": " : "",
373              (NULL != emsg) ? emsg : "");
374   }
375   if (GNUNET_SYSERR == success)
376   {
377     fprintf (stderr,
378              _("Deleting record failed%s%s\n"),
379              (NULL != emsg) ? ": " : "",
380              (NULL != emsg) ? emsg : "");
381   }
382   test_finished ();
383 }
384
385
386 /**
387  * Function called when we are done with a zone iteration.
388  */
389 static void
390 zone_iteration_finished (void *cls)
391 {
392   (void) cls;
393   list_it = NULL;
394   test_finished ();
395 }
396
397
398 /**
399  * Function called when we encountered an error in a zone iteration.
400  */
401 static void
402 zone_iteration_error_cb (void *cls)
403 {
404   (void) cls;
405   list_it = NULL;
406   fprintf (stderr,
407            "Error iterating over zone\n");
408   ret = 1;
409   test_finished ();
410 }
411
412
413 /**
414  * Process a record that was stored in the namestore.
415  *
416  * @param rname name that is being mapped (at most 255 characters long)
417  * @param rd_len number of entries in @a rd array
418  * @param rd array of records with data to store
419  */
420 static void
421 display_record (const char *rname,
422                 unsigned int rd_len,
423                 const struct GNUNET_GNSRECORD_Data *rd)
424 {
425   const char *typestring;
426   char *s;
427   const char *ets;
428   struct GNUNET_TIME_Absolute at;
429   struct GNUNET_TIME_Relative rt;
430
431   if ( (NULL != name) &&
432        (0 != strcmp (name, rname)) )
433   {
434     GNUNET_NAMESTORE_zone_iterator_next (list_it,
435                                          1);
436     return;
437   }
438   FPRINTF (stdout,
439            "%s:\n",
440            rname);
441   for (unsigned int i=0;i<rd_len;i++)
442   {
443     if ( (GNUNET_GNSRECORD_TYPE_NICK == rd[i].record_type) &&
444          (0 != strcmp (rname,
445                        GNUNET_GNS_EMPTY_LABEL_AT)) )
446       continue;
447     typestring = GNUNET_GNSRECORD_number_to_typename (rd[i].record_type);
448     s = GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
449                                           rd[i].data,
450                                           rd[i].data_size);
451     if (NULL == s)
452     {
453       FPRINTF (stdout,
454                _("\tCorrupt or unsupported record of type %u\n"),
455                (unsigned int) rd[i].record_type);
456       continue;
457     }
458     if (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION))
459     {
460       rt.rel_value_us = rd[i].expiration_time;
461       ets = GNUNET_STRINGS_relative_time_to_string (rt, GNUNET_YES);
462     }
463     else
464     {
465       at.abs_value_us = rd[i].expiration_time;
466       ets = GNUNET_STRINGS_absolute_time_to_string (at);
467     }
468     FPRINTF (stdout,
469              "\t%s: %s (%s)\t%s\t%s\n",
470              typestring,
471              s,
472              ets,
473              (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_PRIVATE)) ? "PRIVATE" : "PUBLIC",
474              (0 != (rd[i].flags & GNUNET_GNSRECORD_RF_SHADOW_RECORD)) ? "SHADOW" : "");
475     GNUNET_free (s);
476   }
477   FPRINTF (stdout, "%s", "\n");
478 }
479
480
481 /**
482  * Process a record that was stored in the namestore.
483  *
484  * @param cls closure
485  * @param zone_key private key of the zone
486  * @param rname name that is being mapped (at most 255 characters long)
487  * @param rd_len number of entries in @a rd array
488  * @param rd array of records with data to store
489  */
490 static void
491 display_record_iterator (void *cls,
492                          const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
493                          const char *rname,
494                          unsigned int rd_len,
495                          const struct GNUNET_GNSRECORD_Data *rd)
496 {
497   (void) cls;
498   (void) zone_key;
499   display_record (rname,
500                   rd_len,
501                   rd);
502   GNUNET_NAMESTORE_zone_iterator_next (list_it,
503                                        1);
504 }
505
506
507 /**
508  * Process a record that was stored in the namestore.
509  *
510  * @param cls closure
511  * @param zone_key private key of the zone
512  * @param rname name that is being mapped (at most 255 characters long)
513  * @param rd_len number of entries in @a rd array
514  * @param rd array of records with data to store
515  */
516 static void
517 display_record_monitor (void *cls,
518                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
519                         const char *rname,
520                         unsigned int rd_len,
521                         const struct GNUNET_GNSRECORD_Data *rd)
522 {
523   (void) cls;
524   (void) zone_key;
525   display_record (rname,
526                   rd_len,
527                   rd);
528   GNUNET_NAMESTORE_zone_monitor_next (zm,
529                                       1);
530 }
531
532
533 /**
534  * Process a record that was stored in the namestore.
535  *
536  * @param cls closure
537  * @param zone_key private key of the zone
538  * @param rname name that is being mapped (at most 255 characters long)
539  * @param rd_len number of entries in @a rd array
540  * @param rd array of records with data to store
541  */
542 static void
543 display_record_lookup (void *cls,
544                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
545                        const char *rname,
546                        unsigned int rd_len,
547                        const struct GNUNET_GNSRECORD_Data *rd)
548 {
549   (void) cls;
550   (void) zone_key;
551   get_qe = NULL;
552   display_record (rname,
553                   rd_len,
554                   rd);
555   test_finished ();
556 }
557
558
559 /**
560  * Function called once we are in sync in monitor mode.
561  *
562  * @param cls NULL
563  */
564 static void
565 sync_cb (void *cls)
566 {
567   (void) cls;
568   FPRINTF (stdout,
569            "%s",
570            "Monitor is now in sync.\n");
571 }
572
573
574 /**
575  * Function called on errors while monitoring.
576  *
577  * @param cls NULL
578  */
579 static void
580 monitor_error_cb (void *cls)
581 {
582   (void) cls;
583   FPRINTF (stderr,
584            "%s",
585            "Monitor disconnected and out of sync.\n");
586 }
587
588
589 /**
590  * Function called on errors while monitoring.
591  *
592  * @param cls NULL
593  */
594 static void
595 lookup_error_cb (void *cls)
596 {
597   (void) cls;
598   get_qe = NULL;
599   FPRINTF (stderr,
600            "%s",
601            "Failed to lookup record.\n");
602   test_finished ();
603 }
604
605
606 /**
607  * Function called if lookup fails.
608  */
609 static void
610 add_error_cb (void *cls)
611 {
612   (void) cls;
613   add_qe = NULL;
614   GNUNET_break (0);
615   ret = 1;
616   test_finished ();
617 }
618
619
620 /**
621  * We're storing a record; this function is given the existing record
622  * so that we can merge the information.
623  *
624  * @param cls closure, unused
625  * @param zone_key private key of the zone
626  * @param rec_name name that is being mapped (at most 255 characters long)
627  * @param rd_count number of entries in @a rd array
628  * @param rd array of records with data to store
629  */
630 static void
631 get_existing_record (void *cls,
632                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
633                      const char *rec_name,
634                      unsigned int rd_count,
635                      const struct GNUNET_GNSRECORD_Data *rd)
636 {
637   struct GNUNET_GNSRECORD_Data rdn[rd_count + 1];
638   struct GNUNET_GNSRECORD_Data *rde;
639
640   (void) cls;
641   (void) zone_key;
642   add_qe = NULL;
643   if (0 != strcmp (rec_name, name))
644   {
645     GNUNET_break (0);
646     ret = 1;
647     test_finished ();
648     return;
649   }
650
651   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
652               "Received %u records for name `%s'\n",
653               rd_count, rec_name);
654   for (unsigned int i=0;i<rd_count;i++)
655   {
656     switch (rd[i].record_type)
657     {
658     case GNUNET_DNSPARSER_TYPE_CNAME:
659       fprintf (stderr,
660                _("A %s record exists already under `%s', no other records can be added.\n"),
661                "CNAME",
662                rec_name);
663       ret = 1;
664       test_finished ();
665       return;
666     case GNUNET_GNSRECORD_TYPE_PKEY:
667       fprintf (stderr,
668                _("A %s record exists already under `%s', no other records can be added.\n"),
669                "PKEY",
670                rec_name);
671       ret = 1;
672       test_finished ();
673       return;
674     }
675   }
676   switch (type)
677   {
678   case GNUNET_DNSPARSER_TYPE_CNAME:
679     if (0 != rd_count)
680     {
681       fprintf (stderr,
682                _("Records already exist under `%s', cannot add `%s' record.\n"),
683                rec_name,
684                "CNAME");
685       ret = 1;
686       test_finished ();
687       return;
688     }
689     break;
690   case GNUNET_GNSRECORD_TYPE_PKEY:
691     if (0 != rd_count)
692     {
693       fprintf (stderr,
694                _("Records already exist under `%s', cannot add `%s' record.\n"),
695                rec_name,
696                "PKEY");
697       ret = 1;
698       test_finished ();
699       return;
700     }
701     break;
702   case GNUNET_GNSRECORD_TYPE_GNS2DNS:
703     for (unsigned int i=0;i<rd_count;i++)
704       if (GNUNET_GNSRECORD_TYPE_GNS2DNS != rd[i].record_type)
705       {
706         fprintf (stderr,
707                  _("Non-GNS2DNS records already exist under `%s', cannot add GNS2DNS record.\n"),
708                  rec_name);
709         ret = 1;
710         test_finished ();
711         return;
712       }
713     break;
714   }
715   memset (rdn,
716           0,
717           sizeof (struct GNUNET_GNSRECORD_Data));
718   GNUNET_memcpy (&rdn[1],
719                  rd,
720                  rd_count * sizeof (struct GNUNET_GNSRECORD_Data));
721   rde = &rdn[0];
722   rde->data = data;
723   rde->data_size = data_size;
724   rde->record_type = type;
725   if (1 == is_shadow)
726     rde->flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
727   if (1 != is_public)
728     rde->flags |= GNUNET_GNSRECORD_RF_PRIVATE;
729   rde->expiration_time = etime;
730   if (GNUNET_YES == etime_is_rel)
731     rde->flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
732   else if (GNUNET_NO != etime_is_rel)
733     rde->expiration_time = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
734   GNUNET_assert (NULL != name);
735   add_qe = GNUNET_NAMESTORE_records_store (ns,
736                                            &zone_pkey,
737                                            name,
738                                            rd_count + 1,
739                                            rde,
740                                            &add_continuation,
741                                            &add_qe);
742 }
743
744
745 /**
746  * Function called if we encountered an error in zone-to-name.
747  */
748 static void
749 reverse_error_cb (void *cls)
750 {
751   (void) cls;
752   reverse_qe = NULL;
753   FPRINTF (stdout,
754            "%s.zkey\n",
755            reverse_pkey);
756 }
757
758
759 /**
760  * Function called with the result of our attempt to obtain a name for a given
761  * public key.
762  *
763  * @param cls NULL
764  * @param zone private key of the zone; NULL on disconnect
765  * @param label label of the records; NULL on disconnect
766  * @param rd_count number of entries in @a rd array, 0 if label was deleted
767  * @param rd array of records with data to store
768  */
769 static void
770 handle_reverse_lookup (void *cls,
771                        const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
772                        const char *label,
773                        unsigned int rd_count,
774                        const struct GNUNET_GNSRECORD_Data *rd)
775 {
776   (void) cls;
777   (void) zone;
778   (void) rd_count;
779   (void) rd;
780   reverse_qe = NULL;
781   if (NULL == label)
782     FPRINTF (stdout,
783              "%s\n",
784              reverse_pkey);
785   else
786     FPRINTF (stdout,
787              "%s.%s\n",
788              label,
789              ego_name);
790   test_finished ();
791 }
792
793
794 /**
795  * Function called if lookup for deletion fails.
796  */
797 static void
798 del_lookup_error_cb (void *cls)
799 {
800   (void) cls;
801   del_qe = NULL;
802   GNUNET_break (0);
803   ret = 1;
804   test_finished ();
805 }
806
807
808 /**
809  * We were asked to delete something; this function is called with
810  * the existing records. Now we should determine what should be
811  * deleted and then issue the deletion operation.
812  *
813  * @param cls NULL
814  * @param zone private key of the zone we are deleting from
815  * @param label name of the records we are editing
816  * @param rd_count size of the @a rd array
817  * @param rd existing records
818  */
819 static void
820 del_monitor (void *cls,
821              const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
822              const char *label,
823              unsigned int rd_count,
824              const struct GNUNET_GNSRECORD_Data *rd)
825 {
826   struct GNUNET_GNSRECORD_Data rdx[rd_count];
827   unsigned int rd_left;
828   uint32_t type;
829   char *vs;
830
831   (void) cls;
832   (void) zone;
833   del_qe = NULL;
834   if (0 == rd_count)
835   {
836     FPRINTF (stderr,
837              _("There are no records under label `%s' that could be deleted.\n"),
838              label);
839     ret = 1;
840     test_finished ();
841     return;
842   }
843   if ( (NULL == value) &&
844        (NULL == typestring) )
845   {
846     /* delete everything */
847     del_qe = GNUNET_NAMESTORE_records_store (ns,
848                                              &zone_pkey,
849                                              name,
850                                              0,
851                                              NULL,
852                                              &del_continuation,
853                                              NULL);
854     return;
855   }
856   rd_left = 0;
857   if (NULL != typestring)
858     type = GNUNET_GNSRECORD_typename_to_number (typestring);
859   else
860     type = GNUNET_GNSRECORD_TYPE_ANY;
861   for (unsigned int i=0;i<rd_count;i++)
862   {
863     vs = NULL;
864     if (! ( ( (GNUNET_GNSRECORD_TYPE_ANY == type) ||
865               (rd[i].record_type == type) ) &&
866             ( (NULL == value) ||
867               (NULL == (vs = (GNUNET_GNSRECORD_value_to_string (rd[i].record_type,
868                                                                 rd[i].data,
869                                                                 rd[i].data_size)))) ||
870               (0 == strcmp (vs, value)) ) ) )
871       rdx[rd_left++] = rd[i];
872     GNUNET_free_non_null (vs);
873   }
874   if (rd_count == rd_left)
875   {
876     /* nothing got deleted */
877     FPRINTF (stderr,
878              _("There are no records under label `%s' that match the request for deletion.\n"),
879              label);
880     test_finished ();
881     return;
882   }
883   /* delete everything but what we copied to 'rdx' */
884   del_qe = GNUNET_NAMESTORE_records_store (ns,
885                                            &zone_pkey,
886                                            name,
887                                            rd_left,
888                                            rdx,
889                                            &del_continuation,
890                                            NULL);
891 }
892
893
894 /**
895  * Parse expiration time.
896  *
897  * @param expirationstring text to parse
898  * @param etime_is_rel[out] set to #GNUNET_YES if time is relative
899  * @param etime[out] set to expiration time (abs or rel)
900  * @return #GNUNET_OK on success
901  */
902 static int
903 parse_expiration (const char *expirationstring,
904                   int *etime_is_rel,
905                   uint64_t *etime)
906 {
907   struct GNUNET_TIME_Relative etime_rel;
908   struct GNUNET_TIME_Absolute etime_abs;
909   
910   if (0 == strcmp (expirationstring,
911                    "never"))
912   {
913     *etime = GNUNET_TIME_UNIT_FOREVER_ABS.abs_value_us;
914     *etime_is_rel = GNUNET_NO;
915     return GNUNET_OK;
916   }
917   if (GNUNET_OK ==
918       GNUNET_STRINGS_fancy_time_to_relative (expirationstring,
919                                              &etime_rel))
920   {
921     *etime_is_rel = GNUNET_YES;
922     *etime = etime_rel.rel_value_us;
923     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
924                 "Storing record with relative expiration time of %s\n",
925                 GNUNET_STRINGS_relative_time_to_string (etime_rel,
926                                                         GNUNET_NO));
927     return GNUNET_OK;
928   }
929   if (GNUNET_OK ==
930       GNUNET_STRINGS_fancy_time_to_absolute (expirationstring,
931                                              &etime_abs))
932   {
933     *etime_is_rel = GNUNET_NO;
934     *etime = etime_abs.abs_value_us;
935     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
936                 "Storing record with absolute expiration time of %s\n",
937                 GNUNET_STRINGS_absolute_time_to_string (etime_abs));
938     return GNUNET_OK;
939   }
940   return GNUNET_SYSERR;
941 }
942
943
944 /**
945  * Function called when namestore is done with the replace
946  * operation.
947  *
948  * @param cls NULL
949  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
950  *                #GNUNET_NO if content was already there or not found
951  *                #GNUNET_YES (or other positive value) on success
952  * @param emsg NULL on success, otherwise an error message
953  */
954 static void
955 replace_cont (void *cls,
956               int success,
957               const char *emsg)
958 {
959   (void) cls;
960   
961   set_qe = NULL;
962   if (GNUNET_OK != success)
963   {
964     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
965                 _("Failed to replace records: %s\n"),
966                 emsg);
967     ret = 1; /* fail from 'main' */
968   }
969   GNUNET_SCHEDULER_shutdown ();
970 }
971
972
973 /**
974  * Callback invoked from identity service with ego information.
975  * An @a ego of NULL means the ego was not found.
976  *
977  * @param cls closure with the configuration
978  * @param ego an ego known to identity service, or NULL
979  */
980 static void
981 identity_cb (void *cls,
982              const struct GNUNET_IDENTITY_Ego *ego)
983 {
984   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
985   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
986   struct GNUNET_GNSRECORD_Data rd;
987
988   el = NULL;
989   if (NULL == ego)
990   {
991     if (NULL != ego_name)
992     {
993       fprintf (stderr,
994                _("Ego `%s' not known to identity service\n"),
995                ego_name);
996     }
997     GNUNET_SCHEDULER_shutdown ();
998     ret = -1;
999     return;
1000   }
1001   zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1002   GNUNET_free_non_null (ego_name);
1003   ego_name = NULL;
1004
1005   if (! (add|del|list|(NULL != nickstring)|(NULL != uri)|(NULL != reverse_pkey)|(NULL != recordset)) )
1006   {
1007     /* nothing more to be done */
1008     fprintf (stderr,
1009              _("No options given\n"));
1010     GNUNET_SCHEDULER_shutdown ();
1011     return;
1012   }
1013   GNUNET_CRYPTO_ecdsa_key_get_public (&zone_pkey,
1014                                       &pub);
1015   ns = GNUNET_NAMESTORE_connect (cfg);
1016   if (NULL == ns)
1017   {
1018     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1019                 _("Failed to connect to namestore\n"));
1020     return;
1021   }
1022
1023   if (NULL != recordset)
1024   {
1025     /* replace entire record set */
1026     unsigned int rd_count;
1027     struct GNUNET_GNSRECORD_Data *rd;
1028
1029     if (NULL == name)
1030     {
1031       fprintf (stderr,
1032                _("Missing option `%s' for operation `%s'\n"),
1033                "-R", _("replace"));
1034       GNUNET_SCHEDULER_shutdown ();
1035       ret = 1;
1036       return;
1037     }
1038     rd_count = 0;
1039     for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1040       rd_count++;
1041     rd = GNUNET_new_array (rd_count,
1042                            struct GNUNET_GNSRECORD_Data);
1043     rd_count = 0;
1044     for (struct RecordSetEntry *e = recordset; NULL != e; e = e->next)
1045     {
1046       rd[rd_count] = e->record;
1047       rd_count++;
1048     }
1049     set_qe = GNUNET_NAMESTORE_records_store (ns,
1050                                              &zone_pkey,
1051                                              name,
1052                                              rd_count,
1053                                              rd,
1054                                              &replace_cont,
1055                                              NULL);
1056     GNUNET_free (rd);
1057     return;
1058   }
1059   
1060   if (add)
1061   {
1062     if (NULL == name)
1063     {
1064       fprintf (stderr,
1065                _("Missing option `%s' for operation `%s'\n"),
1066                "-n", _("add"));
1067       GNUNET_SCHEDULER_shutdown ();
1068       ret = 1;
1069       return;
1070     }
1071     if (NULL == typestring)
1072     {
1073       fprintf (stderr,
1074                _("Missing option `%s' for operation `%s'\n"),
1075                "-t", _("add"));
1076       GNUNET_SCHEDULER_shutdown ();
1077       ret = 1;
1078       return;
1079     }
1080     type = GNUNET_GNSRECORD_typename_to_number (typestring);
1081     if (UINT32_MAX == type)
1082     {
1083       fprintf (stderr,
1084                _("Unsupported type `%s'\n"),
1085                typestring);
1086       GNUNET_SCHEDULER_shutdown ();
1087       ret = 1;
1088       return;
1089     }
1090     if (NULL == value)
1091     {
1092       fprintf (stderr,
1093                _("Missing option `%s' for operation `%s'\n"),
1094                "-V", _("add"));
1095       ret = 1;
1096       GNUNET_SCHEDULER_shutdown ();
1097       return;
1098     }
1099     if (GNUNET_OK !=
1100         GNUNET_GNSRECORD_string_to_value (type,
1101                                           value,
1102                                           &data,
1103                                           &data_size))
1104     {
1105       fprintf (stderr,
1106                _("Value `%s' invalid for record type `%s'\n"),
1107                value,
1108                typestring);
1109       GNUNET_SCHEDULER_shutdown ();
1110       ret = 1;
1111       return;
1112     }
1113     if (NULL == expirationstring)
1114     {
1115       fprintf (stderr,
1116                _("Missing option `%s' for operation `%s'\n"),
1117                "-e",
1118                _("add"));
1119       GNUNET_SCHEDULER_shutdown ();
1120       ret = 1;
1121       return;
1122     }
1123     if (GNUNET_OK !=
1124         parse_expiration (expirationstring,
1125                           &etime_is_rel,
1126                           &etime))
1127     {
1128       fprintf (stderr,
1129                _("Invalid time format `%s'\n"),
1130                expirationstring);
1131       GNUNET_SCHEDULER_shutdown ();
1132       ret = 1;
1133       return;
1134     }
1135     add_qe = GNUNET_NAMESTORE_records_lookup (ns,
1136                                               &zone_pkey,
1137                                               name,
1138                                               &add_error_cb,
1139                                               NULL,
1140                                               &get_existing_record,
1141                                               NULL);
1142   }
1143   if (del)
1144   {
1145     if (NULL == name)
1146     {
1147       fprintf (stderr,
1148                _("Missing option `%s' for operation `%s'\n"),
1149                "-n", _("del"));
1150       GNUNET_SCHEDULER_shutdown ();
1151       ret = 1;
1152       return;
1153     }
1154     del_qe = GNUNET_NAMESTORE_records_lookup (ns,
1155                                               &zone_pkey,
1156                                               name,
1157                                               &del_lookup_error_cb,
1158                                               NULL,
1159                                               &del_monitor,
1160                                               NULL);
1161   }
1162   if (list)
1163   {
1164     if (NULL != name)
1165       get_qe = GNUNET_NAMESTORE_records_lookup (ns,
1166                                                 &zone_pkey,
1167                                                 name,
1168                                                 &lookup_error_cb,
1169                                                 NULL,
1170                                                 &display_record_lookup,
1171                                                 NULL);
1172     else
1173       list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
1174                                                        &zone_pkey,
1175                                                        &zone_iteration_error_cb,
1176                                                        NULL,
1177                                                        &display_record_iterator,
1178                                                        NULL,
1179                                                        &zone_iteration_finished,
1180                                                        NULL);
1181   }
1182   if (NULL != reverse_pkey)
1183   {
1184     struct GNUNET_CRYPTO_EcdsaPublicKey pubkey;
1185
1186     if (GNUNET_OK !=
1187         GNUNET_CRYPTO_ecdsa_public_key_from_string (reverse_pkey,
1188                                                     strlen (reverse_pkey),
1189                                                     &pubkey))
1190     {
1191       fprintf (stderr,
1192                _("Invalid public key for reverse lookup `%s'\n"),
1193                reverse_pkey);
1194       GNUNET_SCHEDULER_shutdown ();
1195     }
1196     reverse_qe = GNUNET_NAMESTORE_zone_to_name (ns,
1197                                                 &zone_pkey,
1198                                                 &pubkey,
1199                                                 &reverse_error_cb,
1200                                                 NULL,
1201                                                 &handle_reverse_lookup,
1202                                                 NULL);
1203   }
1204   if (NULL != uri)
1205   {
1206     char sh[105];
1207     char sname[64];
1208     struct GNUNET_CRYPTO_EcdsaPublicKey pkey;
1209
1210     GNUNET_STRINGS_utf8_tolower (uri, uri);
1211     if ( (2 != (sscanf (uri,
1212                         "gnunet://gns/%52s/%63s",
1213                         sh,
1214                         sname)) ) ||
1215          (GNUNET_OK !=
1216           GNUNET_CRYPTO_ecdsa_public_key_from_string (sh,
1217                                                       strlen (sh),
1218                                                       &pkey)) )
1219     {
1220       fprintf (stderr,
1221                _("Invalid URI `%s'\n"),
1222                uri);
1223       GNUNET_SCHEDULER_shutdown ();
1224       ret = 1;
1225       return;
1226     }
1227     memset (&rd, 0, sizeof (rd));
1228     rd.data = &pkey;
1229     rd.data_size = sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey);
1230     rd.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
1231     rd.expiration_time = etime;
1232     if (GNUNET_YES == etime_is_rel)    
1233       rd.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1234     if (1 == is_shadow)
1235       rd.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
1236     add_qe_uri = GNUNET_NAMESTORE_records_store (ns,
1237                                                  &zone_pkey,
1238                                                  sname,
1239                                                  1,
1240                                                  &rd,
1241                                                  &add_continuation,
1242                                                  &add_qe_uri);
1243   }
1244   if (NULL != nickstring)
1245   {
1246     if (0 == strlen(nickstring))
1247     {
1248       fprintf (stderr,
1249                _("Invalid nick `%s'\n"),
1250                nickstring);
1251       GNUNET_SCHEDULER_shutdown ();
1252       ret = 1;
1253       return;
1254     }
1255     add_qe_uri = GNUNET_NAMESTORE_set_nick (ns,
1256                                             &zone_pkey,
1257                                             nickstring,
1258                                             &add_continuation,
1259                                             &add_qe_uri);
1260   }
1261   if (monitor)
1262   {
1263     zm = GNUNET_NAMESTORE_zone_monitor_start (cfg,
1264                                               &zone_pkey,
1265                                               GNUNET_YES,
1266                                               &monitor_error_cb,
1267                                               NULL,
1268                                               &display_record_monitor,
1269                                               NULL,
1270                                               &sync_cb,
1271                                               NULL);
1272   }
1273 }
1274
1275
1276 static void
1277 default_ego_cb (void *cls,
1278                 struct GNUNET_IDENTITY_Ego *ego,
1279                 void **ctx,
1280                 const char *name)
1281 {
1282   (void) cls;
1283   (void) ctx;
1284   (void) name;
1285   get_default = NULL;
1286   if (NULL == ego)
1287   {
1288     fprintf (stderr,
1289              _("No default ego configured in identity service\n"));
1290     GNUNET_SCHEDULER_shutdown ();
1291     ret = -1;
1292     return;
1293   }
1294   else
1295   {
1296     identity_cb (cls, ego);
1297   }
1298 }
1299
1300
1301 static void
1302 id_connect_cb (void *cls,
1303                struct GNUNET_IDENTITY_Ego *ego,
1304                void **ctx,
1305                const char *name)
1306 {
1307   const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1308
1309   (void) cls;
1310   (void) ctx;
1311   (void) name;
1312   if (NULL == ego)
1313   {
1314     get_default = GNUNET_IDENTITY_get (idh,
1315                                        "namestore",
1316                                        &default_ego_cb,
1317                                        (void *) cfg);
1318   }
1319 }
1320
1321
1322 /**
1323  * Main function that will be run.
1324  *
1325  * @param cls closure
1326  * @param args remaining command-line arguments
1327  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1328  * @param cfg configuration
1329  */
1330 static void
1331 run (void *cls,
1332      char *const *args,
1333      const char *cfgfile,
1334      const struct GNUNET_CONFIGURATION_Handle *cfg)
1335 {
1336   (void) cls;
1337   (void) args;
1338   (void) cfgfile;
1339   if (NULL != args[0])
1340     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1341                 _("Superfluous command line arguments (starting with `%s') ignored\n"),
1342                 args[0]);
1343   if ( (NULL != args[0]) &&
1344        (NULL == uri) )
1345     uri = GNUNET_strdup (args[0]);
1346
1347   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1348                                  (void *) cfg);
1349
1350   if (NULL == ego_name)
1351   {
1352     idh = GNUNET_IDENTITY_connect (cfg,
1353                                    &id_connect_cb,
1354                                    (void *) cfg);
1355     if (NULL == idh)
1356       fprintf (stderr,
1357                _("Cannot connect to identity service\n"));
1358     ret = -1;
1359     return;
1360   }
1361   el = GNUNET_IDENTITY_ego_lookup (cfg,
1362                                    ego_name,
1363                                    &identity_cb,
1364                                    (void *) cfg);
1365 }
1366
1367
1368 /**
1369  * Command-line option parser function that allows the user to specify
1370  * a complete record as one argument for adding/removing.  A pointer
1371  * to the head of the list of record sets must be passed as the "scls"
1372  * argument.
1373  *
1374  * @param ctx command line processor context
1375  * @param scls must be of type "struct GNUNET_FS_Uri **"
1376  * @param option name of the option (typically 'R')
1377  * @param value command line argument given; format is
1378  *        "TTL TYPE FLAGS VALUE" where TTL is an expiration time (rel or abs),
1379  *        always given in seconds (without the unit),
1380  *         TYPE is a DNS/GNS record type, FLAGS is either "n" for no flags or
1381  *         a combination of 's' (shadow) and 'p' (public) and VALUE is the 
1382  *         value (in human-readable format)
1383  * @return #GNUNET_OK on success
1384  */
1385 static int
1386 multirecord_process (struct GNUNET_GETOPT_CommandLineProcessorContext *ctx,
1387                      void *scls,
1388                      const char *option,
1389                      const char *value)
1390 {
1391   struct RecordSetEntry **head = scls;
1392   struct RecordSetEntry *r;
1393   struct GNUNET_GNSRECORD_Data record;
1394   char *cp;
1395   char *tok;
1396   char *saveptr;
1397   int etime_is_rel;
1398   void *raw_data;
1399
1400   (void) ctx;
1401   (void) option;
1402   cp = GNUNET_strdup (value);
1403   tok = strtok_r (cp, " ", &saveptr);
1404   if (NULL == tok)
1405   {
1406     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1407                 _("Empty record line argument is not allowed.\n"));
1408     GNUNET_free (cp);
1409     return GNUNET_SYSERR;
1410   }
1411   {
1412     char *etime_in_s;
1413
1414     GNUNET_asprintf (&etime_in_s,
1415                      "%s s",
1416                      tok);
1417     if (GNUNET_OK !=
1418         parse_expiration (etime_in_s,
1419                           &etime_is_rel,
1420                           &record.expiration_time))
1421     {
1422       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1423                   _("Invalid expiration time `%s' (must be without unit)\n"),
1424                   tok);
1425       GNUNET_free (cp);
1426       GNUNET_free (etime_in_s);
1427       return GNUNET_SYSERR;
1428     }
1429     GNUNET_free (etime_in_s);
1430   }
1431   tok = strtok_r (NULL, " ", &saveptr);
1432   if (NULL == tok)
1433   {
1434     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1435                 _("Missing entries in record line `%s'.\n"),
1436                 value);
1437     GNUNET_free (cp);
1438     return GNUNET_SYSERR;
1439   }
1440   record.record_type = GNUNET_GNSRECORD_typename_to_number (tok);
1441   if (UINT32_MAX == record.record_type)
1442   {
1443     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1444                 _("Unknown record type `%s'\n"),
1445                 tok);
1446     GNUNET_free (cp);
1447     return GNUNET_SYSERR;
1448   }
1449   tok = strtok_r (NULL, " ", &saveptr);
1450   if (NULL == tok)
1451   {
1452     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1453                 _("Missing entries in record line `%s'.\n"),
1454                 value);
1455     GNUNET_free (cp);
1456     return GNUNET_SYSERR;
1457   }
1458   record.flags = GNUNET_GNSRECORD_RF_NONE;
1459   if (etime_is_rel)
1460     record.flags |= GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
1461   if (NULL == strchr (tok, (unsigned char) 'p')) /* p = public */
1462     record.flags |= GNUNET_GNSRECORD_RF_PRIVATE; 
1463   if (NULL != strchr (tok, (unsigned char) 's'))
1464     record.flags |= GNUNET_GNSRECORD_RF_SHADOW_RECORD;
1465   /* find beginning of record value */
1466   tok = strchr (&value[tok - cp], (unsigned char) ' ');
1467   if (NULL == tok)
1468   {
1469     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1470                 _("Missing entries in record line `%s'.\n"),
1471                 value);
1472     GNUNET_free (cp);
1473     return GNUNET_SYSERR;
1474   }
1475   GNUNET_free (cp);
1476   tok++; /* skip space */
1477   if (GNUNET_OK !=
1478       GNUNET_GNSRECORD_string_to_value (record.record_type,
1479                                         tok,
1480                                         &raw_data,
1481                                         &record.data_size))
1482   {
1483     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1484                 _("Invalid record data for type %s: `%s'.\n"),
1485                 GNUNET_GNSRECORD_number_to_typename (record.record_type),               
1486                 tok);
1487     return GNUNET_SYSERR;
1488   }
1489   
1490   r = GNUNET_malloc (sizeof (struct RecordSetEntry) + record.data_size);
1491   r->next = *head;
1492   record.data = &r[1];
1493   memcpy (&r[1],          
1494           raw_data,
1495           record.data_size);
1496   GNUNET_free (raw_data);
1497   r->record = record;  
1498   *head = r;
1499   return GNUNET_OK;
1500 }
1501
1502
1503 /**
1504  * Allow user to specify keywords.
1505  *
1506  * @param shortName short name of the option
1507  * @param name long name of the option
1508  * @param argumentHelp help text for the option argument
1509  * @param description long help text for the option
1510  * @param[out] topKeywords set to the desired value
1511  */
1512 struct GNUNET_GETOPT_CommandLineOption
1513 multirecord_option (char shortName,
1514                     const char *name,
1515                     const char *argumentHelp,
1516                     const char *description,
1517                     struct RecordSetEntry **rs)
1518 {
1519   struct GNUNET_GETOPT_CommandLineOption clo = {
1520     .shortName = shortName,
1521     .name = name,
1522     .argumentHelp = argumentHelp,
1523     .description = description,
1524     .require_argument = 1,
1525     .processor = &multirecord_process,
1526     .scls = (void *) rs  
1527   };
1528
1529   return clo;
1530 }
1531
1532
1533
1534 /**
1535  * The main function for gnunet-namestore.
1536  *
1537  * @param argc number of arguments from the command line
1538  * @param argv command line arguments
1539  * @return 0 ok, 1 on error
1540  */
1541 int
1542 main (int argc,
1543       char *const *argv)
1544 {
1545   struct GNUNET_GETOPT_CommandLineOption options[] = {
1546     GNUNET_GETOPT_option_flag ('a',
1547                                "add",
1548                                gettext_noop ("add record"),
1549                                &add),
1550     GNUNET_GETOPT_option_flag ('d',
1551                                "delete",
1552                                gettext_noop ("delete record"),
1553                                &del),
1554     GNUNET_GETOPT_option_flag ('D',
1555                                "display",
1556                                gettext_noop ("display records"),
1557                                &list),
1558     GNUNET_GETOPT_option_string ('e',
1559                                  "expiration",
1560                                  "TIME",
1561                                  gettext_noop ("expiration time for record to use (for adding only), \"never\" is possible"),
1562                                  &expirationstring),
1563     GNUNET_GETOPT_option_string ('i',
1564                                  "nick",
1565                                  "NICKNAME",
1566                                  gettext_noop ("set the desired nick name for the zone"),
1567                                  &nickstring),
1568     GNUNET_GETOPT_option_flag ('m',
1569                                "monitor",
1570                                gettext_noop ("monitor changes in the namestore"),
1571                                &monitor),
1572     GNUNET_GETOPT_option_string ('n',
1573                                  "name",
1574                                  "NAME",
1575                                  gettext_noop ("name of the record to add/delete/display"),
1576                                  &name),
1577     GNUNET_GETOPT_option_string ('r',
1578                                  "reverse",
1579                                  "PKEY",
1580                                  gettext_noop ("determine our name for the given PKEY"),
1581                                  &reverse_pkey),
1582     multirecord_option ('R',
1583                         "replace",
1584                         "RECORDLINE",
1585                         gettext_noop ("set record set to values given by (possibly multiple) RECORDLINES; can be specified multiple times"),
1586                         &recordset),
1587     GNUNET_GETOPT_option_string ('t',
1588                                  "type",
1589                                  "TYPE",
1590                                  gettext_noop ("type of the record to add/delete/display"),
1591                                  &typestring),
1592     GNUNET_GETOPT_option_string ('u',
1593                                  "uri",
1594                                  "URI",
1595                                  gettext_noop ("URI to import into our zone"),
1596                                  &uri),
1597     GNUNET_GETOPT_option_string ('V',
1598                                  "value",
1599                                  "VALUE",
1600                                  gettext_noop ("value of the record to add/delete"),
1601                                  &value),
1602     GNUNET_GETOPT_option_flag ('p',
1603                                "public",
1604                                gettext_noop ("create or list public record"),
1605                                &is_public),
1606     GNUNET_GETOPT_option_flag ('s',
1607                                "shadow",
1608                                gettext_noop ("create shadow record (only valid if all other records of the same type have expired"),
1609                                &is_shadow),
1610     GNUNET_GETOPT_option_string ('z',
1611                                  "zone",
1612                                  "EGO",
1613                                  gettext_noop ("name of the ego controlling the zone"),
1614                                  &ego_name),
1615     GNUNET_GETOPT_OPTION_END
1616   };
1617
1618   if (GNUNET_OK !=
1619       GNUNET_STRINGS_get_utf8_args (argc, argv,
1620                                     &argc, &argv))
1621     return 2;
1622
1623   is_public = -1;
1624   is_shadow = -1;
1625   GNUNET_log_setup ("gnunet-namestore",
1626                     "WARNING",
1627                     NULL);
1628   if (GNUNET_OK !=
1629       GNUNET_PROGRAM_run (argc,
1630                           argv,
1631                           "gnunet-namestore",
1632                           _("GNUnet zone manipulation tool"),
1633                           options,
1634                           &run, NULL))
1635   {
1636     GNUNET_free ((void*) argv);
1637     GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
1638     return 1;
1639   }
1640   GNUNET_free ((void*) argv);
1641   GNUNET_CRYPTO_ecdsa_key_clear (&zone_pkey);
1642   return ret;
1643 }
1644
1645 /* end of gnunet-namestore.c */