-remove trailing whitespace
[oweals/gnunet.git] / src / fs / gnunet-publish.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001-2013 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
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 = 1;
36
37 /**
38  * Command line option 'verbose' set
39  */
40 static 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 do_disable_creation_time;
126
127 /**
128  * Task run on CTRL-C to kill everything nicely.
129  */
130 static GNUNET_SCHEDULER_TaskIdentifier kill_task;
131
132 /**
133  * Handle to the directory scanner (for recursive insertions).
134  */
135 static struct GNUNET_FS_DirScanner *ds;
136
137 /**
138  * Which namespace do we publish to? NULL if we do not publish to
139  * a namespace.
140  */
141 static struct GNUNET_IDENTITY_Ego *namespace;
142
143 /**
144  * Handle to identity service.
145  */
146 static struct GNUNET_IDENTITY_Handle *identity;
147
148
149 /**
150  * We are finished with the publishing operation, clean up all
151  * FS state.
152  *
153  * @param cls NULL
154  * @param tc scheduler context
155  */
156 static void
157 do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
158 {
159   struct GNUNET_FS_PublishContext *p;
160
161   kill_task = GNUNET_SCHEDULER_NO_TASK;
162   if (NULL != pc)
163   {
164     p = pc;
165     pc = NULL;
166     GNUNET_FS_publish_stop (p);
167   }
168   if (NULL != meta)
169   {
170     GNUNET_CONTAINER_meta_data_destroy (meta);
171     meta = NULL;
172   }
173 }
174
175
176 /**
177  * Stop the directory scanner (we had an error).
178  *
179  * @param cls closure
180  * @param tc scheduler context
181  */
182 static void
183 stop_scanner_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
184 {
185   kill_task = GNUNET_SCHEDULER_NO_TASK;
186   if (NULL != ds)
187   {
188     GNUNET_FS_directory_scan_abort (ds);
189     ds = NULL;
190   }
191   if (NULL != identity)
192   {
193     GNUNET_IDENTITY_disconnect (identity);
194     identity = NULL;
195   }
196   GNUNET_FS_stop (ctx);
197   ctx = NULL;
198   ret = 1;
199 }
200
201
202 /**
203  * Called by FS client to give information about the progress of an
204  * operation.
205  *
206  * @param cls closure
207  * @param info details about the event, specifying the event type
208  *        and various bits about the event
209  * @return client-context (for the next progress call
210  *         for this operation; should be set to NULL for
211  *         SUSPEND and STOPPED events).  The value returned
212  *         will be passed to future callbacks in the respective
213  *         field in the GNUNET_FS_ProgressInfo struct.
214  */
215 static void *
216 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
217 {
218   const char *s;
219   char *suri;
220
221   switch (info->status)
222   {
223   case GNUNET_FS_STATUS_PUBLISH_START:
224     break;
225   case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
226     if (verbose)
227     {
228       s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.eta,
229                                                   GNUNET_YES);
230       FPRINTF (stdout, _("Publishing `%s' at %llu/%llu (%s remaining)\n"),
231                info->value.publish.filename,
232                (unsigned long long) info->value.publish.completed,
233                (unsigned long long) info->value.publish.size, s);
234     }
235     break;
236   case GNUNET_FS_STATUS_PUBLISH_ERROR:
237     FPRINTF (stderr, _("Error publishing: %s.\n"),
238              info->value.publish.specifics.error.message);
239     if (kill_task != GNUNET_SCHEDULER_NO_TASK)
240     {
241       GNUNET_SCHEDULER_cancel (kill_task);
242       kill_task = GNUNET_SCHEDULER_NO_TASK;
243     }
244     kill_task = GNUNET_SCHEDULER_add_now (&do_stop_task, NULL);
245     break;
246   case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
247     FPRINTF (stdout, _("Publishing `%s' done.\n"),
248              info->value.publish.filename);
249     suri = GNUNET_FS_uri_to_string (info->value.publish.specifics.
250                                  completed.chk_uri);
251     FPRINTF (stdout, _("URI is `%s'.\n"), suri);
252     GNUNET_free (suri);
253     if (NULL == info->value.publish.pctx)
254     {
255       if (GNUNET_SCHEDULER_NO_TASK != kill_task)
256       {
257         GNUNET_SCHEDULER_cancel (kill_task);
258         kill_task = GNUNET_SCHEDULER_NO_TASK;
259       }
260       kill_task = GNUNET_SCHEDULER_add_now (&do_stop_task, NULL);
261     }
262     ret = 0;
263     break;
264   case GNUNET_FS_STATUS_PUBLISH_STOPPED:
265     GNUNET_break (NULL == pc);
266     return NULL;
267   case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
268     return NULL;
269   case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
270     FPRINTF (stderr, "%s",  _("Cleanup after abort complete.\n"));
271     return NULL;
272   default:
273     FPRINTF (stderr, _("Unexpected status: %d\n"), info->status);
274     return NULL;
275   }
276   return "";                    /* non-null */
277 }
278
279
280 /**
281  * Print metadata entries (except binary
282  * metadata and the filename).
283  *
284  * @param cls closure
285  * @param plugin_name name of the plugin that generated the meta data
286  * @param type type of the meta data
287  * @param format format of data
288  * @param data_mime_type mime type of data
289  * @param data value of the meta data
290  * @param data_size number of bytes in data
291  * @return always 0
292  */
293 static int
294 meta_printer (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
295               enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
296               const char *data, size_t data_size)
297 {
298   if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
299       (EXTRACTOR_METAFORMAT_C_STRING != format))
300     return 0;
301   if (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type)
302     return 0;
303   FPRINTF (stdout, "\t%s - %s\n", EXTRACTOR_metatype_to_string (type), data);
304   return 0;
305 }
306
307
308 /**
309  * Iterator printing keywords
310  *
311  * @param cls closure
312  * @param keyword the keyword
313  * @param is_mandatory is the keyword mandatory (in a search)
314  * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort
315  */
316 static int
317 keyword_printer (void *cls, const char *keyword, int is_mandatory)
318 {
319   FPRINTF (stdout, "\t%s\n", keyword);
320   return GNUNET_OK;
321 }
322
323
324 /**
325  * Function called on all entries before the publication.  This is
326  * where we perform modifications to the default based on command-line
327  * options.
328  *
329  * @param cls closure
330  * @param fi the entry in the publish-structure
331  * @param length length of the file or directory
332  * @param m metadata for the file or directory (can be modified)
333  * @param uri pointer to the keywords that will be used for this entry (can be modified)
334  * @param bo block options
335  * @param do_index should we index?
336  * @param client_info pointer to client context set upon creation (can be modified)
337  * @return GNUNET_OK to continue, GNUNET_NO to remove
338  *         this entry from the directory, GNUNET_SYSERR
339  *         to abort the iteration
340  */
341 static int
342 publish_inspector (void *cls, struct GNUNET_FS_FileInformation *fi,
343                    uint64_t length, struct GNUNET_CONTAINER_MetaData *m,
344                    struct GNUNET_FS_Uri **uri,
345                    struct GNUNET_FS_BlockOptions *bo, int *do_index,
346                    void **client_info)
347 {
348   char *fn;
349   char *fs;
350   struct GNUNET_FS_Uri *new_uri;
351
352   if (cls == fi)
353     return GNUNET_OK;
354   if ( (disable_extractor) &&
355        (NULL != *uri) )
356   {
357     GNUNET_FS_uri_destroy (*uri);
358     *uri = NULL;
359   }
360   if (NULL != topKeywords)
361   {
362     if (NULL != *uri)
363     {
364       new_uri = GNUNET_FS_uri_ksk_merge (topKeywords, *uri);
365       GNUNET_FS_uri_destroy (*uri);
366       *uri = new_uri;
367       GNUNET_FS_uri_destroy (topKeywords);
368     }
369     else
370     {
371       *uri = topKeywords;
372     }
373     topKeywords = NULL;
374   }
375   if (NULL != meta)
376   {
377     GNUNET_CONTAINER_meta_data_merge (m, meta);
378     GNUNET_CONTAINER_meta_data_destroy (meta);
379     meta = NULL;
380   }
381   if (!do_disable_creation_time)
382     GNUNET_CONTAINER_meta_data_add_publication_date (m);
383   if (extract_only)
384   {
385     fn = GNUNET_CONTAINER_meta_data_get_by_type (m,
386                                                  EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
387     fs = GNUNET_STRINGS_byte_size_fancy (length);
388     FPRINTF (stdout, _("Meta data for file `%s' (%s)\n"), fn, fs);
389     GNUNET_CONTAINER_meta_data_iterate (m, &meta_printer, NULL);
390     FPRINTF (stdout, _("Keywords for file `%s' (%s)\n"), fn, fs);
391     GNUNET_free (fn);
392     GNUNET_free (fs);
393     if (NULL != *uri)
394       GNUNET_FS_uri_ksk_get_keywords (*uri, &keyword_printer, NULL);
395     FPRINTF (stdout, "%s",  "\n");
396   }
397   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m))
398     GNUNET_FS_file_information_inspect (fi, &publish_inspector, fi);
399   return GNUNET_OK;
400 }
401
402
403 /**
404  * Function called upon completion of the publishing
405  * of the UBLOCK for the SKS URI.  As this is the last
406  * step, stop our interaction with FS (clean up).
407  *
408  * @param cls NULL (closure)
409  * @param sks_uri URI for the block that was published
410  * @param emsg error message, NULL on success
411  */
412 static void
413 uri_sks_continuation (void *cls, const struct GNUNET_FS_Uri *sks_uri,
414                       const char *emsg)
415 {
416   if (NULL != emsg)
417   {
418     FPRINTF (stderr, "%s\n", emsg);
419     ret = 1;
420   }
421   GNUNET_FS_uri_destroy (uri);
422   uri = NULL;
423   GNUNET_FS_stop (ctx);
424   ctx = NULL;
425 }
426
427
428 /**
429  * Function called upon completion of the publishing
430  * of the UBLOCK for the KSK URI.  Continue with
431  * publishing the SKS URI (if applicable) or clean up.
432  *
433  * @param cls NULL (closure)
434  * @param ksk_uri URI for the block that was published
435  * @param emsg error message, NULL on success
436  */
437 static void
438 uri_ksk_continuation (void *cls, const struct GNUNET_FS_Uri *ksk_uri,
439                       const char *emsg)
440 {
441   const struct GNUNET_CRYPTO_EccPrivateKey *priv;
442
443   if (NULL != emsg)
444   {
445     FPRINTF (stderr, "%s\n", emsg);
446     ret = 1;
447   }
448   if (NULL != namespace)
449   {
450     priv = GNUNET_IDENTITY_ego_get_private_key (namespace);
451     GNUNET_FS_publish_sks (ctx, priv, this_id, next_id, meta, uri, &bo,
452                            GNUNET_FS_PUBLISH_OPTION_NONE,
453                            &uri_sks_continuation, NULL);
454     return;
455   }
456   GNUNET_FS_uri_destroy (uri);
457   uri = NULL;
458   GNUNET_FS_stop (ctx);
459   ctx = NULL;
460 }
461
462
463 /**
464  * Iterate over the results from the directory scan and extract
465  * the desired information for the publishing operation.
466  *
467  * @param item root with the data from the directroy scan
468  * @return handle with the information for the publishing operation
469  */
470 static struct GNUNET_FS_FileInformation *
471 get_file_information (struct GNUNET_FS_ShareTreeItem *item)
472 {
473   struct GNUNET_FS_FileInformation *fi;
474   struct GNUNET_FS_FileInformation *fic;
475   struct GNUNET_FS_ShareTreeItem *child;
476
477   if (GNUNET_YES == item->is_directory)
478   {
479     GNUNET_CONTAINER_meta_data_delete (item->meta,
480                                        EXTRACTOR_METATYPE_MIMETYPE,
481                                        NULL, 0);
482     GNUNET_FS_meta_data_make_directory (item->meta);
483     if (NULL == item->ksk_uri)
484     {
485       const char *mime = GNUNET_FS_DIRECTORY_MIME;
486       item->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1, &mime);
487     }
488     else
489       GNUNET_FS_uri_ksk_add_keyword (item->ksk_uri, GNUNET_FS_DIRECTORY_MIME,
490                                      GNUNET_NO);
491     fi = GNUNET_FS_file_information_create_empty_directory (ctx, NULL,
492                                                             item->ksk_uri,
493                                                             item->meta,
494                                                             &bo, item->filename);
495     for (child = item->children_head; child; child = child->next)
496     {
497       fic = get_file_information (child);
498       GNUNET_break (GNUNET_OK == GNUNET_FS_file_information_add (fi, fic));
499     }
500   }
501   else
502   {
503     fi = GNUNET_FS_file_information_create_from_file (ctx, NULL,
504                                                       item->filename,
505                                                       item->ksk_uri, item->meta,
506                                                       !do_insert,
507                                                       &bo);
508   }
509   return fi;
510 }
511
512
513 /**
514  * We've finished scanning the directory and optimized the meta data.
515  * Begin the publication process.
516  *
517  * @param directory_scan_result result from the directory scan, freed in this function
518  */
519 static void
520 directory_trim_complete (struct GNUNET_FS_ShareTreeItem *directory_scan_result)
521 {
522   struct GNUNET_FS_FileInformation *fi;
523   const struct GNUNET_CRYPTO_EccPrivateKey *priv;
524
525   fi = get_file_information (directory_scan_result);
526   GNUNET_FS_share_tree_free (directory_scan_result);
527   if (NULL == fi)
528   {
529     FPRINTF (stderr, "%s", _("Could not publish\n"));
530     GNUNET_SCHEDULER_shutdown ();
531     ret = 1;
532     return;
533   }
534   GNUNET_FS_file_information_inspect (fi, &publish_inspector, NULL);
535   if (extract_only)
536   {
537     GNUNET_FS_file_information_destroy (fi, NULL, NULL);
538     GNUNET_SCHEDULER_shutdown ();
539     return;
540   }
541   if (NULL == namespace)
542     priv = NULL;
543   else
544     priv = GNUNET_IDENTITY_ego_get_private_key (namespace);
545   pc = GNUNET_FS_publish_start (ctx, fi,
546                                 priv, this_id, next_id,
547                                 (do_simulate) ?
548                                 GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY :
549                                 GNUNET_FS_PUBLISH_OPTION_NONE);
550   if (NULL == pc)
551   {
552     FPRINTF (stderr, "%s",  _("Could not start publishing.\n"));
553     GNUNET_SCHEDULER_shutdown ();
554     ret = 1;
555     return;
556   }
557 }
558
559
560 /**
561  * Function called by the directory scanner as we build the tree
562  * that we will need to publish later.
563  *
564  * @param cls closure
565  * @param filename which file we are making progress on
566  * @param is_directory GNUNET_YES if this is a directory,
567  *                     GNUNET_NO if this is a file
568  *                     GNUNET_SYSERR if it is neither (or unknown)
569  * @param reason kind of progress we are making
570  */
571 static void
572 directory_scan_cb (void *cls,
573                    const char *filename,
574                    int is_directory,
575                    enum GNUNET_FS_DirScannerProgressUpdateReason reason)
576 {
577   struct GNUNET_FS_ShareTreeItem *directory_scan_result;
578
579   switch (reason)
580   {
581   case GNUNET_FS_DIRSCANNER_FILE_START:
582     if (verbose > 1)
583     {
584       if (is_directory == GNUNET_YES)
585         FPRINTF (stdout, _("Scanning directory `%s'.\n"), filename);
586       else
587         FPRINTF (stdout, _("Scanning file `%s'.\n"), filename);
588     }
589     break;
590   case GNUNET_FS_DIRSCANNER_FILE_IGNORED:
591     FPRINTF (stderr,
592              _("There was trouble processing file `%s', skipping it.\n"),
593              filename);
594     break;
595   case GNUNET_FS_DIRSCANNER_ALL_COUNTED:
596     if (verbose)
597       FPRINTF (stdout, "%s", _("Preprocessing complete.\n"));
598     break;
599   case GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED:
600     if (verbose > 2)
601       FPRINTF (stdout, _("Extracting meta data from file `%s' complete.\n"), filename);
602     break;
603   case GNUNET_FS_DIRSCANNER_FINISHED:
604     if (verbose > 1)
605       FPRINTF (stdout, "%s", _("Meta data extraction has finished.\n"));
606     directory_scan_result = GNUNET_FS_directory_scan_get_result (ds);
607     ds = NULL;
608     GNUNET_FS_share_tree_trim (directory_scan_result);
609     directory_trim_complete (directory_scan_result);
610     break;
611   case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
612     FPRINTF (stdout, "%s", _("Internal error scanning directory.\n"));
613     if (kill_task != GNUNET_SCHEDULER_NO_TASK)
614     {
615       GNUNET_SCHEDULER_cancel (kill_task);
616       kill_task = GNUNET_SCHEDULER_NO_TASK;
617     }
618     kill_task = GNUNET_SCHEDULER_add_now (&stop_scanner_task, NULL);
619     break;
620   default:
621     GNUNET_assert (0);
622     break;
623   }
624   fflush (stdout);
625 }
626
627
628 /**
629  * Continuation proceeding with initialization after identity subsystem
630  * has been initialized.
631  *
632  * @param args0 filename to publish
633  */
634 static void
635 identity_continuation (const char *args0)
636 {
637   char *ex;
638   char *emsg;
639
640   if ( (NULL != pseudonym) &&
641        (NULL == namespace) )
642   {
643     FPRINTF (stderr, _("Selected pseudonym `%s' unknown\n"), pseudonym);
644     GNUNET_SCHEDULER_shutdown ();
645     return;
646   }
647   if (NULL != uri_string)
648   {
649     emsg = NULL;
650     if (NULL == (uri = GNUNET_FS_uri_parse (uri_string, &emsg)))
651     {
652       FPRINTF (stderr, _("Failed to parse URI: %s\n"), emsg);
653       GNUNET_free (emsg);
654       GNUNET_SCHEDULER_shutdown ();
655       ret = 1;
656       return;
657     }
658     GNUNET_FS_publish_ksk (ctx, topKeywords, meta, uri, &bo,
659                            GNUNET_FS_PUBLISH_OPTION_NONE, &uri_ksk_continuation,
660                            NULL);
661     return;
662   }
663   if (GNUNET_OK !=
664       GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS", &ex))
665     ex = NULL;
666   if (0 != ACCESS (args0, R_OK))
667   {
668     FPRINTF (stderr,
669              _("Failed to access `%s': %s\n"),
670              args0,
671              STRERROR (errno));
672     GNUNET_free_non_null (ex);
673     return;
674   }
675   ds = GNUNET_FS_directory_scan_start (args0,
676                                        disable_extractor,
677                                        ex,
678                                        &directory_scan_cb, NULL);
679   if (NULL == ds)
680   {
681     FPRINTF (stderr,
682              "%s", _("Failed to start meta directory scanner.  Is gnunet-helper-publish-fs installed?\n"));
683     GNUNET_free_non_null (ex);
684     return;
685   }
686   GNUNET_free_non_null (ex);
687 }
688
689
690 /**
691  * Function called by identity service with known pseudonyms.
692  *
693  * @param cls closure with 'const char *' of filename to publish
694  * @param ego ego handle
695  * @param ctx context for application to store data for this ego
696  *                 (during the lifetime of this process, initially NULL)
697  * @param name name assigned by the user for this ego,
698  *                   NULL if the user just deleted the ego and it
699  *                   must thus no longer be used
700  */
701 static void
702 identity_cb (void *cls,
703              struct GNUNET_IDENTITY_Ego *ego,
704              void **ctx,
705              const char *name)
706 {
707   const char *args0 = cls;
708
709   if (NULL == ego)
710   {
711     identity_continuation (args0);
712     return;
713   }
714   if (0 == strcmp (name, pseudonym))
715     namespace = ego;
716 }
717
718
719 /**
720  * Main function that will be run by the scheduler.
721  *
722  * @param cls closure
723  * @param args remaining command-line arguments
724  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
725  * @param c configuration
726  */
727 static void
728 run (void *cls, char *const *args, const char *cfgfile,
729      const struct GNUNET_CONFIGURATION_Handle *c)
730 {
731   /* check arguments */
732   if ((NULL != uri_string) && (extract_only))
733   {
734     printf (_("Cannot extract metadata from a URI!\n"));
735     ret = -1;
736     return;
737   }
738   if (((NULL == uri_string) || (extract_only)) &&
739       ((NULL == args[0]) || (NULL != args[1])))
740   {
741     printf (_("You must specify one and only one filename for insertion.\n"));
742     ret = -1;
743     return;
744   }
745   if ((NULL != uri_string) && (NULL != args[0]))
746   {
747     printf (_("You must NOT specify an URI and a filename.\n"));
748     ret = -1;
749     return;
750   }
751   if (NULL != pseudonym)
752   {
753     if (NULL == this_id)
754     {
755       FPRINTF (stderr, _("Option `%s' is required when using option `%s'.\n"),
756                "-t", "-P");
757       ret = -1;
758       return;
759     }
760   }
761   else
762   {                             /* ordinary insertion checks */
763     if (NULL != next_id)
764     {
765       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
766                "-N", "-P");
767       ret = -1;
768       return;
769     }
770     if (NULL != this_id)
771     {
772       FPRINTF (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
773                "-t", "-P");
774       ret = -1;
775       return;
776     }
777   }
778   cfg = c;
779   ctx =
780       GNUNET_FS_start (cfg, "gnunet-publish", &progress_cb, NULL,
781                        GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
782   if (NULL == ctx)
783   {
784     FPRINTF (stderr, _("Could not initialize `%s' subsystem.\n"), "FS");
785     ret = 1;
786     return;
787   }
788   kill_task =
789       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
790                                     NULL);
791   if (NULL != pseudonym)
792     identity = GNUNET_IDENTITY_connect (cfg,
793                                         &identity_cb, args[0]);
794   else
795     identity_continuation (args[0]);
796 }
797
798
799 /**
800  * The main function to publish content to GNUnet.
801  *
802  * @param argc number of arguments from the command line
803  * @param argv command line arguments
804  * @return 0 ok, 1 on error
805  */
806 int
807 main (int argc, char *const *argv)
808 {
809   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
810     {'a', "anonymity", "LEVEL",
811      gettext_noop ("set the desired LEVEL of sender-anonymity"),
812      1, &GNUNET_GETOPT_set_uint, &bo.anonymity_level},
813     {'d', "disable-creation-time", NULL,
814      gettext_noop
815      ("disable adding the creation time to the metadata of the uploaded file"),
816      0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
817     {'D', "disable-extractor", NULL,
818      gettext_noop ("do not use libextractor to add keywords or metadata"),
819      0, &GNUNET_GETOPT_set_one, &disable_extractor},
820     {'e', "extract", NULL,
821      gettext_noop
822      ("print list of extracted keywords that would be used, but do not perform upload"),
823      0, &GNUNET_GETOPT_set_one, &extract_only},
824     {'k', "key", "KEYWORD",
825      gettext_noop
826      ("add an additional keyword for the top-level file or directory"
827       " (this option can be specified multiple times)"),
828      1, &GNUNET_FS_getopt_set_keywords, &topKeywords},
829     {'m', "meta", "TYPE:VALUE",
830      gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
831      1, &GNUNET_FS_getopt_set_metadata, &meta},
832     {'n', "noindex", NULL,
833      gettext_noop ("do not index, perform full insertion (stores entire "
834                    "file in encrypted form in GNUnet database)"),
835      0, &GNUNET_GETOPT_set_one, &do_insert},
836     {'N', "next", "ID",
837      gettext_noop
838      ("specify ID of an updated version to be published in the future"
839       " (for namespace insertions only)"),
840      1, &GNUNET_GETOPT_set_string, &next_id},
841     {'p', "priority", "PRIORITY",
842      gettext_noop ("specify the priority of the content"),
843      1, &GNUNET_GETOPT_set_uint, &bo.content_priority},
844     {'P', "pseudonym", "NAME",
845      gettext_noop
846      ("publish the files under the pseudonym NAME (place file into namespace)"),
847      1, &GNUNET_GETOPT_set_string, &pseudonym},
848     {'r', "replication", "LEVEL",
849      gettext_noop ("set the desired replication LEVEL"),
850      1, &GNUNET_GETOPT_set_uint, &bo.replication_level},
851     {'s', "simulate-only", NULL,
852      gettext_noop ("only simulate the process but do not do any "
853                    "actual publishing (useful to compute URIs)"),
854      0, &GNUNET_GETOPT_set_one, &do_simulate},
855     {'t', "this", "ID",
856      gettext_noop ("set the ID of this version of the publication"
857                    " (for namespace insertions only)"),
858      1, &GNUNET_GETOPT_set_string, &this_id},
859     {'u', "uri", "URI",
860      gettext_noop ("URI to be published (can be used instead of passing a "
861                    "file to add keywords to the file with the respective URI)"),
862      1, &GNUNET_GETOPT_set_string, &uri_string},
863     {'V', "verbose", NULL,
864      gettext_noop ("be verbose (print progress information)"),
865      0, &GNUNET_GETOPT_set_one, &verbose},
866     GNUNET_GETOPT_OPTION_END
867   };
868   bo.expiration_time =
869       GNUNET_TIME_year_to_time (GNUNET_TIME_get_current_year () + 2);
870
871   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
872     return 2;
873   ret = (GNUNET_OK ==
874          GNUNET_PROGRAM_run (argc, argv, "gnunet-publish [OPTIONS] FILENAME",
875                              gettext_noop
876                              ("Publish a file or directory on GNUnet"),
877                              options, &run, NULL)) ? ret : 1;
878   GNUNET_free ((void*) argv);
879   return ret;
880 }
881
882 /* end of gnunet-publish.c */