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