include plugin in gnunet-transport output
[oweals/gnunet.git] / src / fs / gnunet-publish.c
1 /*
2      This file is part of GNUnet.
3      (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 2010, 2011 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
31 static int ret;
32
33 static int verbose;
34
35 static const struct GNUNET_CONFIGURATION_Handle *cfg;
36
37 static struct GNUNET_FS_Handle *ctx;
38
39 static struct GNUNET_FS_PublishContext *pc;
40
41 static struct GNUNET_CONTAINER_MetaData *meta;
42
43 static struct GNUNET_FS_Uri *topKeywords;
44
45 static struct GNUNET_FS_Uri *uri;
46
47 static struct GNUNET_FS_BlockOptions bo = { {0LL}, 1, 365, 1 };
48
49 static char *uri_string;
50
51 static char *next_id;
52
53 static char *this_id;
54
55 static char *pseudonym;
56
57 static int do_insert;
58
59 static int disable_extractor;
60
61 static int do_simulate;
62
63 static int extract_only;
64
65 static int do_disable_creation_time;
66
67 static GNUNET_SCHEDULER_TaskIdentifier kill_task;
68
69
70 /**
71  * FIXME: docu
72  */
73 static void
74 do_stop_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
75 {
76   struct GNUNET_FS_PublishContext *p;
77
78   if (pc != NULL)
79   {
80     p = pc;
81     pc = NULL;
82     GNUNET_FS_publish_stop (p);
83     if (NULL != meta)
84     {
85       GNUNET_CONTAINER_meta_data_destroy (meta);
86       meta = NULL;
87     }
88   }
89 }
90
91
92 /**
93  * Called by FS client to give information about the progress of an
94  * operation.
95  *
96  * @param cls closure
97  * @param info details about the event, specifying the event type
98  *        and various bits about the event
99  * @return client-context (for the next progress call
100  *         for this operation; should be set to NULL for
101  *         SUSPEND and STOPPED events).  The value returned
102  *         will be passed to future callbacks in the respective
103  *         field in the GNUNET_FS_ProgressInfo struct.
104  */
105 static void *
106 progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
107 {
108   char *s;
109
110   switch (info->status)
111   {
112   case GNUNET_FS_STATUS_PUBLISH_START:
113     break;
114   case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
115     if (verbose)
116     {
117       s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.eta);
118       fprintf (stdout, _("Publishing `%s' at %llu/%llu (%s remaining)\n"),
119                info->value.publish.filename,
120                (unsigned long long) info->value.publish.completed,
121                (unsigned long long) info->value.publish.size, s);
122       GNUNET_free (s);
123     }
124     break;
125   case GNUNET_FS_STATUS_PUBLISH_ERROR:
126     fprintf (stderr, _("Error publishing: %s.\n"),
127              info->value.publish.specifics.error.message);
128     if (kill_task != GNUNET_SCHEDULER_NO_TASK)
129     {
130       GNUNET_SCHEDULER_cancel (kill_task);
131       kill_task = GNUNET_SCHEDULER_NO_TASK;
132     }
133     GNUNET_SCHEDULER_add_continuation (&do_stop_task, NULL,
134                                        GNUNET_SCHEDULER_REASON_PREREQ_DONE);
135     break;
136   case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
137     fprintf (stdout, _("Publishing `%s' done.\n"),
138              info->value.publish.filename);
139     s = GNUNET_FS_uri_to_string (info->value.publish.specifics.
140                                  completed.chk_uri);
141     fprintf (stdout, _("URI is `%s'.\n"), s);
142     GNUNET_free (s);
143     if (info->value.publish.pctx == NULL)
144     {
145       if (kill_task != GNUNET_SCHEDULER_NO_TASK)
146       {
147         GNUNET_SCHEDULER_cancel (kill_task);
148         kill_task = GNUNET_SCHEDULER_NO_TASK;
149       }
150       GNUNET_SCHEDULER_add_continuation (&do_stop_task, NULL,
151                                          GNUNET_SCHEDULER_REASON_PREREQ_DONE);
152     }
153     break;
154   case GNUNET_FS_STATUS_PUBLISH_STOPPED:
155     GNUNET_break (NULL == pc);
156     return NULL;
157   case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
158     return NULL;
159   case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
160     fprintf (stderr, _("Cleanup after abort complete.\n"));
161   default:
162     fprintf (stderr, _("Unexpected status: %d\n"), info->status);
163     return NULL;
164   }
165   return "";                    /* non-null */
166 }
167
168
169 /**
170  * Print metadata entries (except binary
171  * metadata and the filename).
172  *
173  * @param cls closure
174  * @param plugin_name name of the plugin that generated the meta data
175  * @param type type of the meta data
176  * @param format format of data
177  * @param data_mime_type mime type of data
178  * @param data value of the meta data
179  * @param data_size number of bytes in data
180  * @return always 0
181  */
182 static int
183 meta_printer (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
184               enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
185               const char *data, size_t data_size)
186 {
187   if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
188       (format != EXTRACTOR_METAFORMAT_C_STRING))
189     return 0;
190   if (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
191     return 0;
192   fprintf (stdout, "\t%s - %s\n", EXTRACTOR_metatype_to_string (type), data);
193   return 0;
194 }
195
196
197 /**
198  * Iterator printing keywords
199  *
200  * @param cls closure
201  * @param keyword the keyword
202  * @param is_mandatory is the keyword mandatory (in a search)
203  * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort
204  */
205 static int
206 keyword_printer (void *cls, const char *keyword, int is_mandatory)
207 {
208   fprintf (stdout, "\t%s\n", keyword);
209   return GNUNET_OK;
210 }
211
212
213 /**
214  * Function called on all entries before the publication.  This is
215  * where we perform modifications to the default based on command-line
216  * options.
217  *
218  * @param cls closure
219  * @param fi the entry in the publish-structure
220  * @param length length of the file or directory
221  * @param m metadata for the file or directory (can be modified)
222  * @param uri pointer to the keywords that will be used for this entry (can be modified)
223  * @param bo block options
224  * @param do_index should we index?
225  * @param client_info pointer to client context set upon creation (can be modified)
226  * @return GNUNET_OK to continue, GNUNET_NO to remove
227  *         this entry from the directory, GNUNET_SYSERR
228  *         to abort the iteration
229  */
230 static int
231 publish_inspector (void *cls, struct GNUNET_FS_FileInformation *fi,
232                    uint64_t length, struct GNUNET_CONTAINER_MetaData *m,
233                    struct GNUNET_FS_Uri **uri,
234                    struct GNUNET_FS_BlockOptions *bo, int *do_index,
235                    void **client_info)
236 {
237   char *fn;
238   char *fs;
239   struct GNUNET_FS_Uri *new_uri;
240
241   if (cls == fi)
242     return GNUNET_OK;
243   if (NULL != topKeywords)
244   {
245     if (*uri != NULL)
246     {
247       new_uri = GNUNET_FS_uri_ksk_merge (topKeywords, *uri);
248       GNUNET_FS_uri_destroy (*uri);
249       *uri = new_uri;
250       GNUNET_FS_uri_destroy (topKeywords);
251     }
252     else
253     {
254       *uri = topKeywords;
255     }
256     topKeywords = NULL;
257   }
258   if (NULL != meta)
259   {
260     GNUNET_CONTAINER_meta_data_merge (m, meta);
261     GNUNET_CONTAINER_meta_data_destroy (meta);
262     meta = NULL;
263   }
264   if (!do_disable_creation_time)
265     GNUNET_CONTAINER_meta_data_add_publication_date (m);
266   if (extract_only)
267   {
268     fn = GNUNET_CONTAINER_meta_data_get_by_type (m,
269                                                  EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
270     fs = GNUNET_STRINGS_byte_size_fancy (length);
271     fprintf (stdout, _("Meta data for file `%s' (%s)\n"), fn, fs);
272     GNUNET_CONTAINER_meta_data_iterate (m, &meta_printer, NULL);
273     fprintf (stdout, _("Keywords for file `%s' (%s)\n"), fn, fs);
274     GNUNET_free (fn);
275     GNUNET_free (fs);
276     if (NULL != *uri)
277       GNUNET_FS_uri_ksk_get_keywords (*uri, &keyword_printer, NULL);
278     fprintf (stdout, "\n");
279   }
280   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m))
281     GNUNET_FS_file_information_inspect (fi, &publish_inspector, fi);
282   return GNUNET_OK;
283 }
284
285
286 /**
287  * FIXME: docu
288  */
289 static void
290 uri_sks_continuation (void *cls, const struct GNUNET_FS_Uri *ksk_uri,
291                       const char *emsg)
292 {
293   if (emsg != NULL)
294   {
295     fprintf (stderr, "%s\n", emsg);
296     ret = 1;
297   }
298   GNUNET_FS_uri_destroy (uri);
299   uri = NULL;
300   GNUNET_FS_stop (ctx);
301   ctx = NULL;
302 }
303
304
305 /**
306  * FIXME: docu
307  */
308 static void
309 uri_ksk_continuation (void *cls, const struct GNUNET_FS_Uri *ksk_uri,
310                       const char *emsg)
311 {
312   struct GNUNET_FS_Namespace *ns;
313
314   if (emsg != NULL)
315   {
316     fprintf (stderr, "%s\n", emsg);
317     ret = 1;
318   }
319   if (pseudonym != NULL)
320   {
321     ns = GNUNET_FS_namespace_create (ctx, pseudonym);
322     if (ns == NULL)
323     {
324       fprintf (stderr, _("Failed to create namespace `%s'\n"), pseudonym);
325       ret = 1;
326     }
327     else
328     {
329       GNUNET_FS_publish_sks (ctx, ns, this_id, next_id, meta, uri, &bo,
330                              GNUNET_FS_PUBLISH_OPTION_NONE,
331                              uri_sks_continuation, NULL);
332       GNUNET_assert (GNUNET_OK == GNUNET_FS_namespace_delete (ns, GNUNET_NO));
333       return;
334     }
335   }
336   GNUNET_FS_uri_destroy (uri);
337   uri = NULL;
338   GNUNET_FS_stop (ctx);
339   ctx = NULL;
340 }
341
342
343 /**
344  * Main function that will be run by the scheduler.
345  *
346  * @param cls closure
347  * @param args remaining command-line arguments
348  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
349  * @param c configuration
350  */
351 static void
352 run (void *cls, char *const *args, const char *cfgfile,
353      const struct GNUNET_CONFIGURATION_Handle *c)
354 {
355   struct GNUNET_FS_FileInformation *fi;
356   struct GNUNET_FS_Namespace *namespace;
357   struct EXTRACTOR_PluginList *plugins;
358   struct GNUNET_FS_Uri *keywords;
359   struct stat sbuf;
360   char *ex;
361   char *emsg;
362
363   /* check arguments */
364   if ((uri_string != NULL) && (extract_only))
365   {
366     printf (_("Cannot extract metadata from a URI!\n"));
367     ret = -1;
368     return;
369   }
370   if (((uri_string == NULL) || (extract_only)) &&
371       ((args[0] == NULL) || (args[1] != NULL)))
372   {
373     printf (_("You must specify one and only one filename for insertion.\n"));
374     ret = -1;
375     return;
376   }
377   if ((uri_string != NULL) && (args[0] != NULL))
378   {
379     printf (_("You must NOT specify an URI and a filename.\n"));
380     ret = -1;
381     return;
382   }
383   if (pseudonym != NULL)
384   {
385     if (NULL == this_id)
386     {
387       fprintf (stderr, _("Option `%s' is required when using option `%s'.\n"),
388                "-t", "-P");
389       ret = -1;
390       return;
391     }
392   }
393   else
394   {                             /* ordinary insertion checks */
395     if (NULL != next_id)
396     {
397       fprintf (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
398                "-N", "-P");
399       ret = -1;
400       return;
401     }
402     if (NULL != this_id)
403     {
404       fprintf (stderr, _("Option `%s' makes no sense without option `%s'.\n"),
405                "-t", "-P");
406       ret = -1;
407       return;
408     }
409   }
410   cfg = c;
411   ctx =
412       GNUNET_FS_start (cfg, "gnunet-publish", &progress_cb, NULL,
413                        GNUNET_FS_FLAGS_NONE, GNUNET_FS_OPTIONS_END);
414   if (NULL == ctx)
415   {
416     fprintf (stderr, _("Could not initialize `%s' subsystem.\n"), "FS");
417     ret = 1;
418     return;
419   }
420   namespace = NULL;
421   if (NULL != pseudonym)
422   {
423     namespace = GNUNET_FS_namespace_create (ctx, pseudonym);
424     if (NULL == namespace)
425     {
426       fprintf (stderr, _("Could not create namespace `%s'\n"), pseudonym);
427       GNUNET_FS_stop (ctx);
428       ret = 1;
429       return;
430     }
431   }
432   if (NULL != uri_string)
433   {
434     emsg = NULL;
435     uri = GNUNET_FS_uri_parse (uri_string, &emsg);
436     if (uri == NULL)
437     {
438       fprintf (stderr, _("Failed to parse URI: %s\n"), emsg);
439       GNUNET_free (emsg);
440       if (namespace != NULL)
441         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
442       GNUNET_FS_stop (ctx);
443       ret = 1;
444       return;
445     }
446     GNUNET_FS_publish_ksk (ctx, topKeywords, meta, uri, &bo,
447                            GNUNET_FS_PUBLISH_OPTION_NONE, &uri_ksk_continuation,
448                            NULL);
449     if (namespace != NULL)
450       GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
451     return;
452   }
453   plugins = NULL;
454   if (!disable_extractor)
455   {
456     plugins = EXTRACTOR_plugin_add_defaults (EXTRACTOR_OPTION_DEFAULT_POLICY);
457     if (GNUNET_OK ==
458         GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS", &ex))
459     {
460       if (strlen (ex) > 0)
461         plugins =
462             EXTRACTOR_plugin_add_config (plugins, ex,
463                                          EXTRACTOR_OPTION_DEFAULT_POLICY);
464       GNUNET_free (ex);
465     }
466   }
467   emsg = NULL;
468   GNUNET_assert (NULL != args[0]);
469   if (0 != STAT (args[0], &sbuf))
470   {
471     GNUNET_asprintf (&emsg, _("Could not access file: %s\n"), STRERROR (errno));
472     fi = NULL;
473   }
474   else if (S_ISDIR (sbuf.st_mode))
475   {
476     fi = GNUNET_FS_file_information_create_from_directory (ctx, NULL, args[0],
477                                                            &GNUNET_FS_directory_scanner_default,
478                                                            plugins, !do_insert,
479                                                            &bo, &emsg);
480   }
481   else
482   {
483     if (meta == NULL)
484       meta = GNUNET_CONTAINER_meta_data_create ();
485     GNUNET_FS_meta_data_extract_from_file (meta, args[0], plugins);
486     keywords = GNUNET_FS_uri_ksk_create_from_meta_data (meta);
487     fi = GNUNET_FS_file_information_create_from_file (ctx, NULL, args[0],
488                                                       keywords, NULL,
489                                                       !do_insert, &bo);
490     GNUNET_break (fi != NULL);
491     GNUNET_FS_uri_destroy (keywords);
492   }
493   EXTRACTOR_plugin_remove_all (plugins);
494   if (fi == NULL)
495   {
496     fprintf (stderr, _("Could not publish `%s': %s\n"), args[0], emsg);
497     GNUNET_free (emsg);
498     if (namespace != NULL)
499       GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
500     GNUNET_FS_stop (ctx);
501     ret = 1;
502     return;
503   }
504   GNUNET_FS_file_information_inspect (fi, &publish_inspector, NULL);
505   if (extract_only)
506   {
507     if (namespace != NULL)
508       GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
509     GNUNET_FS_file_information_destroy (fi, NULL, NULL);
510     GNUNET_FS_stop (ctx);
511     return;
512   }
513   pc = GNUNET_FS_publish_start (ctx, fi, namespace, this_id, next_id,
514                                 (do_simulate) ?
515                                 GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY :
516                                 GNUNET_FS_PUBLISH_OPTION_NONE);
517   if (NULL == pc)
518   {
519     fprintf (stderr, _("Could not start publishing.\n"));
520     GNUNET_FS_stop (ctx);
521     ret = 1;
522     return;
523   }
524   kill_task =
525       GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &do_stop_task,
526                                     NULL);
527 }
528
529
530 /**
531  * The main function to publish content to GNUnet.
532  *
533  * @param argc number of arguments from the command line
534  * @param argv command line arguments
535  * @return 0 ok, 1 on error
536  */
537 int
538 main (int argc, char *const *argv)
539 {
540   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
541     {'a', "anonymity", "LEVEL",
542      gettext_noop ("set the desired LEVEL of sender-anonymity"),
543      1, &GNUNET_GETOPT_set_uint, &bo.anonymity_level},
544     {'d', "disable-creation-time", NULL,
545      gettext_noop
546      ("disable adding the creation time to the metadata of the uploaded file"),
547      0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
548     {'D', "disable-extractor", NULL,
549      gettext_noop ("do not use libextractor to add keywords or metadata"),
550      0, &GNUNET_GETOPT_set_one, &disable_extractor},
551     {'e', "extract", NULL,
552      gettext_noop
553      ("print list of extracted keywords that would be used, but do not perform upload"),
554      0, &GNUNET_GETOPT_set_one, &extract_only},
555     {'k', "key", "KEYWORD",
556      gettext_noop
557      ("add an additional keyword for the top-level file or directory"
558       " (this option can be specified multiple times)"),
559      1, &GNUNET_FS_getopt_set_keywords, &topKeywords},
560     {'m', "meta", "TYPE:VALUE",
561      gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
562      1, &GNUNET_FS_getopt_set_metadata, &meta},
563     {'n', "noindex", NULL,
564      gettext_noop ("do not index, perform full insertion (stores entire "
565                    "file in encrypted form in GNUnet database)"),
566      0, &GNUNET_GETOPT_set_one, &do_insert},
567     {'N', "next", "ID",
568      gettext_noop
569      ("specify ID of an updated version to be published in the future"
570       " (for namespace insertions only)"),
571      1, &GNUNET_GETOPT_set_string, &next_id},
572     {'p', "priority", "PRIORITY",
573      gettext_noop ("specify the priority of the content"),
574      1, &GNUNET_GETOPT_set_uint, &bo.content_priority},
575     {'P', "pseudonym", "NAME",
576      gettext_noop
577      ("publish the files under the pseudonym NAME (place file into namespace)"),
578      1, &GNUNET_GETOPT_set_string, &pseudonym},
579     {'r', "replication", "LEVEL",
580      gettext_noop ("set the desired replication LEVEL"),
581      1, &GNUNET_GETOPT_set_uint, &bo.replication_level},
582     {'s', "simulate-only", NULL,
583      gettext_noop ("only simulate the process but do not do any "
584                    "actual publishing (useful to compute URIs)"),
585      0, &GNUNET_GETOPT_set_one, &do_simulate},
586     {'t', "this", "ID",
587      gettext_noop ("set the ID of this version of the publication"
588                    " (for namespace insertions only)"),
589      1, &GNUNET_GETOPT_set_string, &this_id},
590     {'u', "uri", "URI",
591      gettext_noop ("URI to be published (can be used instead of passing a "
592                    "file to add keywords to the file with the respective URI)"),
593      1, &GNUNET_GETOPT_set_string, &uri_string},
594     {'V', "verbose", NULL,
595      gettext_noop ("be verbose (print progress information)"),
596      0, &GNUNET_GETOPT_set_one, &verbose},
597     GNUNET_GETOPT_OPTION_END
598   };
599   bo.expiration_time =
600       GNUNET_FS_year_to_time (GNUNET_FS_get_current_year () + 2);
601   return (GNUNET_OK ==
602           GNUNET_PROGRAM_run (argc, argv, "gnunet-publish [OPTIONS] FILENAME",
603                               gettext_noop
604                               ("Publish a file or directory on GNUnet"),
605                               options, &run, NULL)) ? ret : 1;
606 }
607
608 /* end of gnunet-publish.c */