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