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