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