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