uncrustify as demanded.
[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 /* end of gnunet-publish.c */