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