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