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