make no creation time the default
[oweals/gnunet.git] / src / fs / gnunet-publish.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2001-2013 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 fs/gnunet-publish.c
22  * @brief publishing files on GNUnet
23  * @author Christian Grothoff
24  * @author Krista Bennett
25  * @author James Blackwell
26  * @author Igor Wronsky
27  */
28 #include "platform.h"
29 #include "gnunet_fs_service.h"
30 #include "gnunet_identity_service.h"
31
32 /**
33  * Global return value from #main().
34  */
35 static int ret;
36
37 /**
38  * Command line option 'verbose' set
39  */
40 static unsigned int verbose;
41
42 /**
43  * Handle to our configuration.
44  */
45 static const struct GNUNET_CONFIGURATION_Handle *cfg;
46
47 /**
48  * Handle for interaction with file-sharing service.
49  */
50 static struct GNUNET_FS_Handle *ctx;
51
52 /**
53  * Handle to FS-publishing operation.
54  */
55 static struct GNUNET_FS_PublishContext *pc;
56
57 /**
58  * Meta-data provided via command-line option.
59  */
60 static struct GNUNET_CONTAINER_MetaData *meta;
61
62 /**
63  * Keywords provided via command-line option.
64  */
65 static struct GNUNET_FS_Uri *topKeywords;
66
67 /**
68  * Options we set for published blocks.
69  */
70 static struct GNUNET_FS_BlockOptions bo = { {0LL}, 1, 365, 1 };
71
72 /**
73  * Value of URI provided on command-line (when not publishing
74  * a file but just creating UBlocks to refer to an existing URI).
75  */
76 static char *uri_string;
77
78 /**
79  * Value of URI provided on command-line (when not publishing
80  * a file but just creating UBlocks to refer to an existing URI);
81  * parsed version of 'uri_string'.
82  */
83 static struct GNUNET_FS_Uri *uri;
84
85 /**
86  * Command-line option for namespace publishing: identifier for updates
87  * to this publication.
88  */
89 static char *next_id;
90
91 /**
92  * Command-line option for namespace publishing: identifier for this
93  * publication.
94  */
95 static char *this_id;
96
97 /**
98  * Command-line option identifying the pseudonym to use for the publication.
99  */
100 static char *pseudonym;
101
102 /**
103  * Command-line option for 'inserting'
104  */
105 static int do_insert;
106
107 /**
108  * Command-line option to disable meta data extraction.
109  */
110 static int disable_extractor;
111
112 /**
113  * Command-line option to merely simulate publishing operation.
114  */
115 static int do_simulate;
116
117 /**
118  * Command-line option to only perform meta data extraction, but not publish.
119  */
120 static int extract_only;
121
122 /**
123  * Command-line option to disable adding creation time.
124  */
125 static int enable_creation_time;
126
127 /**
128  * Handle to the directory scanner (for recursive insertions).
129  */
130 static struct GNUNET_FS_DirScanner *ds;
131
132 /**
133  * Which namespace do we publish to? NULL if we do not publish to
134  * a namespace.
135  */
136 static struct GNUNET_IDENTITY_Ego *namespace;
137
138 /**
139  * Handle to identity service.
140  */
141 static struct GNUNET_IDENTITY_Handle *identity;
142
143
144 /**
145  * We are finished with the publishing operation, clean up all
146  * FS state.
147  *
148  * @param cls NULL
149  */
150 static void
151 do_stop_task (void *cls)
152 {
153   struct GNUNET_FS_PublishContext *p;
154
155   if (NULL != ds)
156   {
157     GNUNET_FS_directory_scan_abort (ds);
158     ds = NULL;
159   }
160   if (NULL != identity)
161   {
162     GNUNET_IDENTITY_disconnect (identity);
163     identity = NULL;
164   }
165   if (NULL != pc)
166   {
167     p = pc;
168     pc = NULL;
169     GNUNET_FS_publish_stop (p);
170   }
171   if (NULL != ctx)
172   {
173     GNUNET_FS_stop (ctx);
174     ctx = NULL;
175   }
176   if (NULL != meta)
177   {
178     GNUNET_CONTAINER_meta_data_destroy (meta);
179     meta = NULL;
180   }
181   if (NULL != uri)
182   {
183     GNUNET_FS_uri_destroy (uri);
184     uri = NULL;
185   }
186 }
187
188
189 /**
190  * Called by FS client to give information about the progress of an
191  * operation.
192  *
193  * @param cls closure
194  * @param info details about the event, specifying the event type
195  *        and various bits about the event
196  * @return client-context (for the next progress call
197  *         for this operation; should be set to NULL for
198  *         SUSPEND and STOPPED events).  The value returned
199  *         will be passed to future callbacks in the respective
200  *         field in the GNUNET_FS_ProgressInfo struct.
201  */
202 static void *
203 progress_cb (void *cls,
204              const struct GNUNET_FS_ProgressInfo *info)
205 {
206   const char *s;
207   char *suri;
208
209   switch (info->status)
210   {
211   case GNUNET_FS_STATUS_PUBLISH_START:
212     break;
213   case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
214     if (verbose)
215     {
216       s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.eta,
217                                                   GNUNET_YES);
218       FPRINTF (stdout,
219                _("Publishing `%s' at %llu/%llu (%s remaining)\n"),
220                info->value.publish.filename,
221                (unsigned long long) info->value.publish.completed,
222                (unsigned long long) info->value.publish.size, s);
223     }
224     break;
225   case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
226     if (verbose)
227     {
228       s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.specifics.progress_directory.eta,
229                                                   GNUNET_YES);
230       FPRINTF (stdout,
231                _("Publishing `%s' at %llu/%llu (%s remaining)\n"),
232                info->value.publish.filename,
233                (unsigned long long) info->value.publish.specifics.progress_directory.completed,
234                (unsigned long long) info->value.publish.specifics.progress_directory.total, s);
235     }
236     break;
237   case GNUNET_FS_STATUS_PUBLISH_ERROR:
238     FPRINTF (stderr,
239              _("Error publishing: %s.\n"),
240              info->value.publish.specifics.error.message);
241     ret = 1;
242     GNUNET_SCHEDULER_shutdown ();
243     break;
244   case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
245     FPRINTF (stdout,
246              _("Publishing `%s' done.\n"),
247              info->value.publish.filename);
248     suri = GNUNET_FS_uri_to_string (info->value.publish.specifics.
249                                     completed.chk_uri);
250     FPRINTF (stdout,
251              _("URI is `%s'.\n"),
252              suri);
253     GNUNET_free (suri);
254     if (NULL != info->value.publish.specifics.completed.sks_uri)
255     {
256       suri = GNUNET_FS_uri_to_string (info->value.publish.specifics.
257                                       completed.sks_uri);
258       FPRINTF (stdout,
259                _("Namespace URI is `%s'.\n"),
260                suri);
261       GNUNET_free (suri);
262     }
263     if (NULL == info->value.publish.pctx)
264     {
265       ret = 0;
266       GNUNET_SCHEDULER_shutdown ();
267     }
268     break;
269   case GNUNET_FS_STATUS_PUBLISH_STOPPED:
270     GNUNET_break (NULL == pc);
271     return NULL;
272   case GNUNET_FS_STATUS_UNINDEX_START:
273     FPRINTF (stderr,
274              "%s",
275              _("Starting cleanup after abort\n"));
276     return NULL;
277   case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
278     return NULL;
279   case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
280     FPRINTF (stderr,
281              "%s",
282              _("Cleanup after abort completed.\n"));
283     GNUNET_FS_unindex_stop (info->value.unindex.uc);
284     return NULL;
285   case GNUNET_FS_STATUS_UNINDEX_ERROR:
286     FPRINTF (stderr,
287              "%s",
288              _("Cleanup after abort failed.\n"));
289     GNUNET_FS_unindex_stop (info->value.unindex.uc);
290     return NULL;
291   case GNUNET_FS_STATUS_UNINDEX_STOPPED:
292     return NULL;
293   default:
294     FPRINTF (stderr,
295              _("Unexpected status: %d\n"),
296              info->status);
297     return NULL;
298   }
299   return "";                    /* non-null */
300 }
301
302
303 /**
304  * Print metadata entries (except binary
305  * metadata and the filename).
306  *
307  * @param cls closure
308  * @param plugin_name name of the plugin that generated the meta data
309  * @param type type of the meta data
310  * @param format format of data
311  * @param data_mime_type mime type of @a data
312  * @param data value of the meta data
313  * @param data_size number of bytes in @a data
314  * @return always 0
315  */
316 static int
317 meta_printer (void *cls,
318               const char *plugin_name,
319               enum EXTRACTOR_MetaType type,
320               enum EXTRACTOR_MetaFormat format,
321               const char *data_mime_type,
322               const char *data, size_t data_size)
323 {
324   if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
325       (EXTRACTOR_METAFORMAT_C_STRING != format))
326     return 0;
327   if (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type)
328     return 0;
329 #if HAVE_LIBEXTRACTOR
330   FPRINTF (stdout,
331            "\t%s - %s\n",
332            EXTRACTOR_metatype_to_string (type),
333            data);
334 #else
335   FPRINTF (stdout,
336            "\t%d - %s\n",
337            type,
338            data);
339 #endif
340   return 0;
341 }
342
343
344 /**
345  * Iterator printing keywords
346  *
347  * @param cls closure
348  * @param keyword the keyword
349  * @param is_mandatory is the keyword mandatory (in a search)
350  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to abort
351  */
352 static int
353 keyword_printer (void *cls,
354                  const char *keyword,
355                  int is_mandatory)
356 {
357   FPRINTF (stdout, "\t%s\n", keyword);
358   return GNUNET_OK;
359 }
360
361
362 /**
363  * Function called on all entries before the publication.  This is
364  * where we perform modifications to the default based on command-line
365  * options.
366  *
367  * @param cls closure
368  * @param fi the entry in the publish-structure
369  * @param length length of the file or directory
370  * @param m metadata for the file or directory (can be modified)
371  * @param uri pointer to the keywords that will be used for this entry (can be modified)
372  * @param bo block options
373  * @param do_index should we index?
374  * @param client_info pointer to client context set upon creation (can be modified)
375  * @return #GNUNET_OK to continue, #GNUNET_NO to remove
376  *         this entry from the directory, #GNUNET_SYSERR
377  *         to abort the iteration
378  */
379 static int
380 publish_inspector (void *cls,
381                    struct GNUNET_FS_FileInformation *fi,
382                    uint64_t length,
383                    struct GNUNET_CONTAINER_MetaData *m,
384                    struct GNUNET_FS_Uri **uri,
385                    struct GNUNET_FS_BlockOptions *bo,
386                    int *do_index,
387                    void **client_info)
388 {
389   char *fn;
390   char *fs;
391   struct GNUNET_FS_Uri *new_uri;
392
393   if (cls == fi)
394     return GNUNET_OK;
395   if ( (disable_extractor) &&
396        (NULL != *uri) )
397   {
398     GNUNET_FS_uri_destroy (*uri);
399     *uri = NULL;
400   }
401   if (NULL != topKeywords)
402   {
403     if (NULL != *uri)
404     {
405       new_uri = GNUNET_FS_uri_ksk_merge (topKeywords, *uri);
406       GNUNET_FS_uri_destroy (*uri);
407       *uri = new_uri;
408       GNUNET_FS_uri_destroy (topKeywords);
409     }
410     else
411     {
412       *uri = topKeywords;
413     }
414     topKeywords = NULL;
415   }
416   if (NULL != meta)
417   {
418     GNUNET_CONTAINER_meta_data_merge (m, meta);
419     GNUNET_CONTAINER_meta_data_destroy (meta);
420     meta = NULL;
421   }
422   if (enable_creation_time)
423     GNUNET_CONTAINER_meta_data_add_publication_date (m);
424   if (extract_only)
425   {
426     fn = GNUNET_CONTAINER_meta_data_get_by_type (m,
427                                                  EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
428     fs = GNUNET_STRINGS_byte_size_fancy (length);
429     FPRINTF (stdout,
430              _("Meta data for file `%s' (%s)\n"),
431              fn,
432              fs);
433     GNUNET_CONTAINER_meta_data_iterate (m, &meta_printer, NULL);
434     FPRINTF (stdout,
435              _("Keywords for file `%s' (%s)\n"),
436              fn,
437              fs);
438     GNUNET_free (fn);
439     GNUNET_free (fs);
440     if (NULL != *uri)
441       GNUNET_FS_uri_ksk_get_keywords (*uri, &keyword_printer, NULL);
442     FPRINTF (stdout,
443              "%s",
444              "\n");
445   }
446   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m))
447     GNUNET_FS_file_information_inspect (fi,
448                                         &publish_inspector, fi);
449   return GNUNET_OK;
450 }
451
452
453 /**
454  * Function called upon completion of the publishing
455  * of the UBLOCK for the SKS URI.  As this is the last
456  * step, stop our interaction with FS (clean up).
457  *
458  * @param cls NULL (closure)
459  * @param sks_uri URI for the block that was published
460  * @param emsg error message, NULL on success
461  */
462 static void
463 uri_sks_continuation (void *cls,
464                       const struct GNUNET_FS_Uri *sks_uri,
465                       const char *emsg)
466 {
467   if (NULL != emsg)
468   {
469     FPRINTF (stderr,
470              "%s\n",
471              emsg);
472     ret = 1;
473   }
474   GNUNET_SCHEDULER_shutdown ();
475 }
476
477
478 /**
479  * Function called upon completion of the publishing
480  * of the UBLOCK for the KSK URI.  Continue with
481  * publishing the SKS URI (if applicable) or clean up.
482  *
483  * @param cls NULL (closure)
484  * @param ksk_uri URI for the block that was published
485  * @param emsg error message, NULL on success
486  */
487 static void
488 uri_ksk_continuation (void *cls,
489                       const struct GNUNET_FS_Uri *ksk_uri,
490                       const char *emsg)
491 {
492   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
493
494   if (NULL != emsg)
495   {
496     FPRINTF (stderr,
497              "%s\n",
498              emsg);
499     ret = 1;
500   }
501   if (NULL == namespace)
502   {
503     GNUNET_SCHEDULER_shutdown ();
504     return;
505   }
506   priv = GNUNET_IDENTITY_ego_get_private_key (namespace);
507   GNUNET_FS_publish_sks (ctx,
508                          priv,
509                          this_id,
510                          next_id,
511                          meta,
512                          uri,
513                          &bo,
514                          GNUNET_FS_PUBLISH_OPTION_NONE,
515                          &uri_sks_continuation,
516                          NULL);
517 }
518
519
520 /**
521  * Iterate over the results from the directory scan and extract
522  * the desired information for the publishing operation.
523  *
524  * @param item root with the data from the directroy scan
525  * @return handle with the information for the publishing operation
526  */
527 static struct GNUNET_FS_FileInformation *
528 get_file_information (struct GNUNET_FS_ShareTreeItem *item)
529 {
530   struct GNUNET_FS_FileInformation *fi;
531   struct GNUNET_FS_FileInformation *fic;
532   struct GNUNET_FS_ShareTreeItem *child;
533
534   if (GNUNET_YES == item->is_directory)
535   {
536     if (NULL == item->meta)
537       item->meta = GNUNET_CONTAINER_meta_data_create ();
538     GNUNET_CONTAINER_meta_data_delete (item->meta,
539                                        EXTRACTOR_METATYPE_MIMETYPE,
540                                        NULL, 0);
541     GNUNET_FS_meta_data_make_directory (item->meta);
542     if (NULL == item->ksk_uri)
543     {
544       const char *mime = GNUNET_FS_DIRECTORY_MIME;
545       item->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1, &mime);
546     }
547     else
548       GNUNET_FS_uri_ksk_add_keyword (item->ksk_uri, GNUNET_FS_DIRECTORY_MIME,
549                                      GNUNET_NO);
550     fi = GNUNET_FS_file_information_create_empty_directory (ctx, NULL,
551                                                             item->ksk_uri,
552                                                             item->meta,
553                                                             &bo, item->filename);
554     for (child = item->children_head; child; child = child->next)
555     {
556       fic = get_file_information (child);
557       GNUNET_break (GNUNET_OK == GNUNET_FS_file_information_add (fi, fic));
558     }
559   }
560   else
561   {
562     fi = GNUNET_FS_file_information_create_from_file (ctx, NULL,
563                                                       item->filename,
564                                                       item->ksk_uri, item->meta,
565                                                       !do_insert,
566                                                       &bo);
567   }
568   return fi;
569 }
570
571
572 /**
573  * We've finished scanning the directory and optimized the meta data.
574  * Begin the publication process.
575  *
576  * @param directory_scan_result result from the directory scan, freed in this function
577  */
578 static void
579 directory_trim_complete (struct GNUNET_FS_ShareTreeItem *directory_scan_result)
580 {
581   struct GNUNET_FS_FileInformation *fi;
582   const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
583
584   fi = get_file_information (directory_scan_result);
585   GNUNET_FS_share_tree_free (directory_scan_result);
586   if (NULL == fi)
587   {
588     FPRINTF (stderr,
589              "%s",
590              _("Could not publish\n"));
591     ret = 1;
592     GNUNET_SCHEDULER_shutdown ();
593     return;
594   }
595   GNUNET_FS_file_information_inspect (fi, &publish_inspector, NULL);
596   if (extract_only)
597   {
598     GNUNET_FS_file_information_destroy (fi, NULL, NULL);
599     GNUNET_SCHEDULER_shutdown ();
600     return;
601   }
602   if (NULL == namespace)
603     priv = NULL;
604   else
605     priv = GNUNET_IDENTITY_ego_get_private_key (namespace);
606   pc = GNUNET_FS_publish_start (ctx, fi,
607                                 priv, this_id, next_id,
608                                 (do_simulate) ?
609                                 GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY :
610                                 GNUNET_FS_PUBLISH_OPTION_NONE);
611   if (NULL == pc)
612   {
613     FPRINTF (stderr,
614              "%s",
615              _("Could not start publishing.\n"));
616     ret = 1;
617     GNUNET_SCHEDULER_shutdown ();
618     return;
619   }
620 }
621
622
623 /**
624  * Function called by the directory scanner as we build the tree
625  * that we will need to publish later.
626  *
627  * @param cls closure
628  * @param filename which file we are making progress on
629  * @param is_directory #GNUNET_YES if this is a directory,
630  *                     #GNUNET_NO if this is a file
631  *                     #GNUNET_SYSERR if it is neither (or unknown)
632  * @param reason kind of progress we are making
633  */
634 static void
635 directory_scan_cb (void *cls,
636                    const char *filename,
637                    int is_directory,
638                    enum GNUNET_FS_DirScannerProgressUpdateReason reason)
639 {
640   struct GNUNET_FS_ShareTreeItem *directory_scan_result;
641
642   switch (reason)
643   {
644   case GNUNET_FS_DIRSCANNER_FILE_START:
645     if (verbose > 1)
646     {
647       if (is_directory == GNUNET_YES)
648         FPRINTF (stdout,
649                  _("Scanning directory `%s'.\n"),
650                  filename);
651       else
652         FPRINTF (stdout,
653                  _("Scanning file `%s'.\n"),
654                  filename);
655     }
656     break;
657   case GNUNET_FS_DIRSCANNER_FILE_IGNORED:
658     FPRINTF (stderr,
659              _("There was trouble processing file `%s', skipping it.\n"),
660              filename);
661     break;
662   case GNUNET_FS_DIRSCANNER_ALL_COUNTED:
663     if (verbose)
664       FPRINTF (stdout,
665                "%s",
666                _("Preprocessing complete.\n"));
667     break;
668   case GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED:
669     if (verbose > 2)
670       FPRINTF (stdout,
671                _("Extracting meta data from file `%s' complete.\n"),
672                filename);
673     break;
674   case GNUNET_FS_DIRSCANNER_FINISHED:
675     if (verbose > 1)
676       FPRINTF (stdout,
677                "%s",
678                _("Meta data extraction has finished.\n"));
679     directory_scan_result = GNUNET_FS_directory_scan_get_result (ds);
680     ds = NULL;
681     GNUNET_FS_share_tree_trim (directory_scan_result);
682     directory_trim_complete (directory_scan_result);
683     break;
684   case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
685     FPRINTF (stdout,
686              "%s",
687              _("Error scanning directory.\n"));
688     ret = 1;
689     GNUNET_SCHEDULER_shutdown ();
690     break;
691   default:
692     GNUNET_assert (0);
693     break;
694   }
695   fflush (stdout);
696 }
697
698
699 /**
700  * Continuation proceeding with initialization after identity subsystem
701  * has been initialized.
702  *
703  * @param args0 filename to publish
704  */
705 static void
706 identity_continuation (const char *args0)
707 {
708   char *ex;
709   char *emsg;
710
711   if ( (NULL != pseudonym) &&
712        (NULL == namespace) )
713   {
714     FPRINTF (stderr,
715              _("Selected pseudonym `%s' unknown\n"),
716              pseudonym);
717     ret = 1;
718     GNUNET_SCHEDULER_shutdown ();
719     return;
720   }
721   if (NULL != uri_string)
722   {
723     emsg = NULL;
724     if (NULL == (uri = GNUNET_FS_uri_parse (uri_string, &emsg)))
725     {
726       FPRINTF (stderr,
727                _("Failed to parse URI: %s\n"),
728                emsg);
729       GNUNET_free (emsg);
730       ret = 1;
731       GNUNET_SCHEDULER_shutdown ();
732       return;
733     }
734     GNUNET_FS_publish_ksk (ctx,
735                            topKeywords,
736                            meta, uri,
737                            &bo,
738                            GNUNET_FS_PUBLISH_OPTION_NONE,
739                            &uri_ksk_continuation,
740                            NULL);
741     return;
742   }
743   if (GNUNET_OK !=
744       GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS", &ex))
745     ex = NULL;
746   if (0 != ACCESS (args0, R_OK))
747   {
748     FPRINTF (stderr,
749              _("Failed to access `%s': %s\n"),
750              args0,
751              STRERROR (errno));
752     GNUNET_free_non_null (ex);
753     return;
754   }
755   ds = GNUNET_FS_directory_scan_start (args0,
756                                        disable_extractor,
757                                        ex,
758                                        &directory_scan_cb, NULL);
759   if (NULL == ds)
760   {
761     FPRINTF (stderr,
762              "%s",
763              _("Failed to start meta directory scanner.  Is gnunet-helper-publish-fs installed?\n"));
764     GNUNET_free_non_null (ex);
765     return;
766   }
767   GNUNET_free_non_null (ex);
768 }
769
770
771 /**
772  * Function called by identity service with known pseudonyms.
773  *
774  * @param cls closure with 'const char *' of filename to publish
775  * @param ego ego handle
776  * @param ctx context for application to store data for this ego
777  *                 (during the lifetime of this process, initially NULL)
778  * @param name name assigned by the user for this ego,
779  *                   NULL if the user just deleted the ego and it
780  *                   must thus no longer be used
781  */
782 static void
783 identity_cb (void *cls,
784              struct GNUNET_IDENTITY_Ego *ego,
785              void **ctx,
786              const char *name)
787 {
788   const char *args0 = cls;
789
790   if (NULL == ego)
791   {
792     identity_continuation (args0);
793     return;
794   }
795   if (NULL == name)
796     return;
797   if (0 == strcmp (name, pseudonym))
798     namespace = ego;
799 }
800
801
802 /**
803  * Main function that will be run by the scheduler.
804  *
805  * @param cls closure
806  * @param args remaining command-line arguments
807  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
808  * @param c configuration
809  */
810 static void
811 run (void *cls,
812      char *const *args,
813      const char *cfgfile,
814      const struct GNUNET_CONFIGURATION_Handle *c)
815 {
816   /* check arguments */
817   if ((NULL != uri_string) && (extract_only))
818   {
819     printf (_("Cannot extract metadata from a URI!\n"));
820     ret = -1;
821     return;
822   }
823   if (((NULL == uri_string) || (extract_only)) &&
824       ((NULL == args[0]) || (NULL != args[1])))
825   {
826     printf (_("You must specify one and only one filename for insertion.\n"));
827     ret = -1;
828     return;
829   }
830   if ((NULL != uri_string) && (NULL != args[0]))
831   {
832     printf (_("You must NOT specify an URI and a filename.\n"));
833     ret = -1;
834     return;
835   }
836   if (NULL != pseudonym)
837   {
838     if (NULL == this_id)
839     {
840       FPRINTF (stderr, _("Option `%s' is required when using option `%s'.\n"),
841                "-t", "-P");
842       ret = -1;
843       return;
844     }
845   }
846   else
847   {                             /* ordinary insertion checks */
848     if (NULL != next_id)
849     {
850       FPRINTF (stderr,
851                _("Option `%s' makes no sense without option `%s'.\n"),
852                "-N", "-P");
853       ret = -1;
854       return;
855     }
856     if (NULL != this_id)
857     {
858       FPRINTF (stderr,
859                _("Option `%s' makes no sense without option `%s'.\n"),
860                "-t", "-P");
861       ret = -1;
862       return;
863     }
864   }
865   cfg = c;
866   ctx =
867       GNUNET_FS_start (cfg, "gnunet-publish", &progress_cb, NULL,
868                        GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
869   if (NULL == ctx)
870   {
871     FPRINTF (stderr,
872              _("Could not initialize `%s' subsystem.\n"),
873              "FS");
874     ret = 1;
875     return;
876   }
877   GNUNET_SCHEDULER_add_shutdown (&do_stop_task,
878                                  NULL);
879   if (NULL != pseudonym)
880     identity = GNUNET_IDENTITY_connect (cfg,
881                                         &identity_cb,
882                                         args[0]);
883   else
884     identity_continuation (args[0]);
885 }
886
887
888 /**
889  * The main function to publish content to GNUnet.
890  *
891  * @param argc number of arguments from the command line
892  * @param argv command line arguments
893  * @return 0 ok, 1 on error
894  */
895 int
896 main (int argc,
897       char *const *argv)
898 {
899   struct GNUNET_GETOPT_CommandLineOption options[] = {
900     GNUNET_GETOPT_option_uint ('a',
901                                "anonymity",
902                                "LEVEL",
903                                gettext_noop ("set the desired LEVEL of sender-anonymity"),
904                                &bo.anonymity_level),
905     GNUNET_GETOPT_option_flag ('D',
906                                "disable-extractor",
907                                gettext_noop ("do not use libextractor to add keywords or metadata"),
908                                &disable_extractor),
909     GNUNET_GETOPT_option_flag ('E',
910                                "enable-creation-time",
911                                gettext_noop ("enable adding the creation time to the "
912                                              "metadata of the uploaded file"),
913                                &enable_creation_time),
914     GNUNET_GETOPT_option_flag ('e',
915                                "extract",
916                                gettext_noop ("print list of extracted keywords that would "
917                                              "be used, but do not perform upload"),
918                                &extract_only),
919     GNUNET_FS_GETOPT_KEYWORDS ('k',
920                                "key",
921                                "KEYWORD",
922                                gettext_noop ("add an additional keyword for the top-level "
923                                              "file or directory (this option can be specified multiple times)"),
924                                &topKeywords),
925     GNUNET_FS_GETOPT_METADATA ('m',
926                                "meta",
927                                "TYPE:VALUE",
928                                gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
929                                &meta),
930     GNUNET_GETOPT_option_flag ('n',
931                                "noindex",
932                                gettext_noop ("do not index, perform full insertion (stores "
933                                              "entire file in encrypted form in GNUnet database)"),
934                                &do_insert),
935     GNUNET_GETOPT_option_string ('N',
936                                  "next",
937                                  "ID",
938                                  gettext_noop ("specify ID of an updated version to be "
939                                                "published in the future (for namespace insertions only)"),
940                                  &next_id),
941     GNUNET_GETOPT_option_uint ('p',
942                                "priority",
943                                "PRIORITY",
944                                gettext_noop ("specify the priority of the content"),
945                                &bo.content_priority),
946     GNUNET_GETOPT_option_string ('P',
947                                  "pseudonym",
948                                  "NAME",
949                                  gettext_noop ("publish the files under the pseudonym "
950                                                "NAME (place file into namespace)"),
951                                  &pseudonym),
952     GNUNET_GETOPT_option_uint ('r',
953                                "replication",
954                                "LEVEL",
955                                gettext_noop ("set the desired replication LEVEL"),
956                                &bo.replication_level),
957     GNUNET_GETOPT_option_flag ('s',
958                                "simulate-only",
959                                gettext_noop ("only simulate the process but do not do "
960                                              "any actual publishing (useful to compute URIs)"),
961                                &do_simulate),
962     GNUNET_GETOPT_option_string ('t',
963                                  "this",
964                                  "ID",
965                                  gettext_noop ("set the ID of this version of the publication "
966                                                "(for namespace insertions only)"),
967                                  &this_id),
968     GNUNET_GETOPT_option_string ('u',
969                                  "uri",
970                                  "URI",
971                                  gettext_noop ("URI to be published (can be used instead of passing a "
972                                                "file to add keywords to the file with the respective URI)"),
973                                  &uri_string),
974
975     GNUNET_GETOPT_option_verbose (&verbose),
976
977     GNUNET_GETOPT_OPTION_END
978   };
979   bo.expiration_time =
980       GNUNET_TIME_year_to_time (GNUNET_TIME_get_current_year () + 2);
981
982   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
983     return 2;
984   ret = (GNUNET_OK ==
985          GNUNET_PROGRAM_run (argc,
986                              argv,
987                              "gnunet-publish [OPTIONS] FILENAME",
988                              gettext_noop
989                              ("Publish a file or directory on GNUnet"),
990                              options,
991                              &run,
992                              NULL)) ? ret : 1;
993   GNUNET_free ((void*) argv);
994   return ret;
995 }
996
997 /* end of gnunet-publish.c */