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