plane hacking
[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 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 #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            "\t%s - %s\n",
212            EXTRACTOR_metatype_to_string (type),
213            data);
214   return 0;
215 }
216
217
218 /**
219  * Function called on all entries before the publication.  This is
220  * where we perform modifications to the default based on command-line
221  * options.
222  *
223  * @param cls closure
224  * @param fi the entry in the publish-structure
225  * @param length length of the file or directory
226  * @param m metadata for the file or directory (can be modified)
227  * @param uri pointer to the keywords that will be used for this entry (can be modified)
228  * @param anonymity pointer to selected anonymity level (can be modified)
229  * @param priority pointer to selected priority (can be modified)
230  * @param do_index should we index?
231  * @param expirationTime pointer to selected expiration time (can be modified)
232  * @param client_info pointer to client context set upon creation (can be modified)
233  * @return GNUNET_OK to continue, GNUNET_NO to remove
234  *         this entry from the directory, GNUNET_SYSERR
235  *         to abort the iteration
236  */
237 static int
238 publish_inspector (void *cls,
239                    struct GNUNET_FS_FileInformation *fi,
240                    uint64_t length,
241                    struct GNUNET_CONTAINER_MetaData *m,
242                    struct GNUNET_FS_Uri **uri,
243                    unsigned int *anonymity,
244                    unsigned int *priority,
245                    int *do_index,
246                    struct GNUNET_TIME_Absolute *expirationTime,
247                    void **client_info)
248 {
249   char *fn;
250   char *fs;
251   struct GNUNET_FS_Uri *new_uri;
252
253   if (cls == fi)
254     return GNUNET_OK;
255   if (NULL != topKeywords)
256     {
257       if (*uri != NULL)
258         {
259           new_uri = GNUNET_FS_uri_ksk_merge (topKeywords,
260                                              *uri);
261           GNUNET_FS_uri_destroy (*uri); 
262           *uri = new_uri;
263           GNUNET_FS_uri_destroy (topKeywords);
264         }
265       else
266         {
267           *uri = topKeywords;
268         }
269       topKeywords = NULL;
270     }
271   if (NULL != meta) 
272     {
273       GNUNET_CONTAINER_meta_data_merge (m, meta);
274       GNUNET_CONTAINER_meta_data_destroy (meta);
275       meta = NULL;
276     }
277   if (! do_disable_creation_time)
278     GNUNET_CONTAINER_meta_data_add_publication_date (m);
279   if (extract_only)
280     {
281       fn = GNUNET_CONTAINER_meta_data_get_by_type (m,
282                                                    EXTRACTOR_METATYPE_FILENAME);
283       fs = GNUNET_STRINGS_byte_size_fancy (length);
284       fprintf (stdout,
285                _("Keywords for file `%s' (%s)\n"),
286                fn,
287                fs);
288       GNUNET_free (fn);
289       GNUNET_free (fs);
290       GNUNET_CONTAINER_meta_data_iterate (m,
291                                           &meta_printer,
292                                           NULL);
293       fprintf (stdout, "\n");
294     }
295   if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m))
296     GNUNET_FS_file_information_inspect (fi,
297                                         &publish_inspector,
298                                         fi);
299   return GNUNET_OK;
300 }
301
302
303 static void 
304 uri_sks_continuation (void *cls,
305                       const struct GNUNET_FS_Uri *ksk_uri,
306                       const char *emsg)
307 {
308   if (emsg != NULL)
309     {
310       fprintf (stderr,
311                "%s\n",
312                emsg);
313       ret = 1;
314     }
315   GNUNET_FS_uri_destroy (uri);
316   uri = NULL;
317   GNUNET_FS_stop (ctx);
318   ctx = NULL;
319 }
320
321
322 static void 
323 uri_ksk_continuation (void *cls,
324                       const struct GNUNET_FS_Uri *ksk_uri,
325                       const char *emsg)
326 {
327   struct GNUNET_FS_Namespace *ns;
328
329   if (emsg != NULL)
330     {
331       fprintf (stderr,
332                "%s\n",
333                emsg);
334       ret = 1;
335     }
336   if (pseudonym != NULL)
337     {
338       ns = GNUNET_FS_namespace_create (ctx,
339                                        pseudonym);
340       if (ns == NULL)
341         {
342           fprintf (stderr,
343                    _("Failed to create namespace `%s'\n"),
344                    pseudonym);
345           ret = 1;
346         }
347       else
348         {
349           GNUNET_FS_publish_sks (ctx,
350                                  ns,
351                                  this_id,
352                                  next_id,
353                                  meta,
354                                  uri,
355                                  GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION),
356                                  anonymity,
357                                  priority,
358                                  GNUNET_FS_PUBLISH_OPTION_NONE,
359                                  uri_sks_continuation,
360                                  NULL);
361           GNUNET_assert (GNUNET_OK ==
362                          GNUNET_FS_namespace_delete (ns, GNUNET_NO));
363           return;
364         }
365     }
366   GNUNET_FS_uri_destroy (uri);
367   uri = NULL;
368   GNUNET_FS_stop (ctx);
369   ctx = NULL;
370 }
371
372
373 /**
374  * Main function that will be run by the scheduler.
375  *
376  * @param cls closure
377  * @param s the scheduler to use
378  * @param args remaining command-line arguments
379  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
380  * @param c configuration
381  */
382 static void
383 run (void *cls,
384      struct GNUNET_SCHEDULER_Handle *s,
385      char *const *args,
386      const char *cfgfile,
387      const struct GNUNET_CONFIGURATION_Handle *c)
388 {
389   struct GNUNET_FS_FileInformation *fi;
390   struct GNUNET_FS_Namespace *namespace;
391   struct EXTRACTOR_PluginList *plugins;
392   struct stat sbuf;
393   char *ex;
394   char *emsg;
395   
396   sched = s;
397   /* check arguments */
398   if ((uri_string != NULL) && (extract_only))
399     {
400       printf (_("Cannot extract metadata from a URI!\n"));
401       ret = -1;
402       return;
403     }
404   if ( ( (uri_string == NULL) || (extract_only) ) 
405        && ( (args[0] == NULL) || (args[1] != NULL) ) )
406     {
407       printf (_
408               ("You must specify one and only one filename for insertion.\n"));
409       ret = -1;
410       return;
411     }
412   if ((uri_string != NULL) && (args[0] != NULL))
413     {
414       printf (_("You must NOT specify an URI and a filename.\n"));
415       ret = -1;
416       return;
417     }
418   if (pseudonym != NULL)
419     {
420       if (NULL == this_id)
421         {
422           fprintf (stderr,
423                    _("Option `%s' is required when using option `%s'.\n"),
424                    "-t", "-P");
425           ret = -1;
426           return;
427         }
428     }
429   else
430     {                           /* ordinary insertion checks */
431       if (NULL != next_id)
432         {
433           fprintf (stderr,
434                    _("Option `%s' makes no sense without option `%s'.\n"),
435                    "-N", "-P");
436           ret = -1;
437           return;
438         }
439       if (NULL != this_id)
440         {
441           fprintf (stderr,
442                    _("Option `%s' makes no sense without option `%s'.\n"),
443                    "-t", "-P");
444           ret = -1;
445           return;
446         }
447     }
448   cfg = c;
449   ctx = GNUNET_FS_start (sched,
450                          cfg,
451                          "gnunet-publish",
452                          &progress_cb,
453                          NULL,
454                          GNUNET_FS_FLAGS_NONE,
455                          GNUNET_FS_OPTIONS_END);
456   if (NULL == ctx)
457     {
458       fprintf (stderr,
459                _("Could not initialize `%s' subsystem.\n"),
460                "FS");
461       ret = 1;
462       return;
463     }
464   namespace = NULL;
465   if (NULL != pseudonym)
466     {
467       namespace = GNUNET_FS_namespace_create (ctx,
468                                               pseudonym);
469       if (NULL == namespace)
470         {
471           fprintf (stderr,
472                    _("Could not create namespace `%s'\n"),
473                    pseudonym);
474           GNUNET_FS_stop (ctx);
475           ret = 1;
476           return;
477         }
478     }
479   if (NULL != uri_string)
480     {      
481       emsg = NULL;
482       uri = GNUNET_FS_uri_parse (uri_string,
483                                  &emsg);
484       if (uri == NULL)
485         {
486           fprintf (stderr, 
487                    _("Failed to parse URI: %s\n"),
488                    emsg);
489           GNUNET_free (emsg);
490           if (namespace != NULL)
491             GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
492           GNUNET_FS_stop (ctx);
493           ret = 1;
494           return;         
495         }
496       GNUNET_FS_publish_ksk (ctx,
497                              topKeywords,
498                              meta,
499                              uri,
500                              GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION),
501                              anonymity,
502                              priority,
503                              GNUNET_FS_PUBLISH_OPTION_NONE,
504                              &uri_ksk_continuation,
505                              NULL);
506       if (namespace != NULL)
507         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
508       return;
509     }
510   plugins = NULL;
511   if (! disable_extractor)
512     {
513       plugins = EXTRACTOR_plugin_add_defaults (EXTRACTOR_OPTION_DEFAULT_POLICY);
514       if (GNUNET_OK ==
515           GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS",
516                                                  &ex))
517         {
518           if (strlen (ex) > 0)
519             plugins = EXTRACTOR_plugin_add_config (plugins, ex, EXTRACTOR_OPTION_DEFAULT_POLICY);
520           GNUNET_free (ex);
521         }
522     }
523   emsg = NULL;
524   if (0 != STAT (args[0], &sbuf))
525     {
526       GNUNET_asprintf (&emsg,
527                        _("Could not access file: %s\n"),
528                        STRERROR (errno));
529       fi = NULL;
530     }
531   else if (S_ISDIR (sbuf.st_mode))
532     {
533       fi = GNUNET_FS_file_information_create_from_directory (ctx,
534                                                              NULL,
535                                                              args[0],
536                                                              &GNUNET_FS_directory_scanner_default,
537                                                              plugins,
538                                                              !do_insert,
539                                                              anonymity,
540                                                              priority,
541                                                              GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION),
542                                                              &emsg);
543     }
544   else
545     {
546       if (meta == NULL)
547         meta = GNUNET_CONTAINER_meta_data_create ();
548       GNUNET_FS_meta_data_extract_from_file (meta,
549                                              args[0],
550                                              plugins);
551       fi = GNUNET_FS_file_information_create_from_file (ctx,
552                                                         NULL,
553                                                         args[0],
554                                                         NULL,
555                                                         NULL,
556                                                         !do_insert,
557                                                         anonymity,
558                                                         priority,
559                                                         GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
560       GNUNET_break (fi != NULL);
561     }
562   EXTRACTOR_plugin_remove_all (plugins);  
563   if (fi == NULL)
564     {
565       fprintf (stderr,
566                _("Could not publish `%s': %s\n"),
567                args[0],
568                emsg);
569       GNUNET_free (emsg);
570       if (namespace != NULL)
571         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
572       GNUNET_FS_stop (ctx);
573       ret = 1;
574       return;
575     }
576   GNUNET_FS_file_information_inspect (fi,
577                                       &publish_inspector,
578                                       NULL);
579   if (extract_only)
580     {
581       if (namespace != NULL)
582         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
583       GNUNET_FS_file_information_destroy (fi, NULL, NULL);
584       GNUNET_FS_stop (ctx);
585       return;
586     }
587   pc = GNUNET_FS_publish_start (ctx,
588                                 fi,
589                                 namespace,
590                                 this_id,
591                                 next_id,
592                                 (do_simulate) 
593                                 ? GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY
594                                 : GNUNET_FS_PUBLISH_OPTION_NONE);
595   if (NULL == pc)
596     {
597       fprintf (stderr,
598                _("Could not start publishing.\n"));
599       GNUNET_FS_stop (ctx);
600       ret = 1;
601       return;
602     }
603   kill_task = GNUNET_SCHEDULER_add_delayed (sched,
604                                             GNUNET_TIME_UNIT_FOREVER_REL,
605                                             &do_stop_task,
606                                             NULL);
607 }
608
609
610
611
612 /**
613  * The main function to publish content to GNUnet.
614  *
615  * @param argc number of arguments from the command line
616  * @param argv command line arguments
617  * @return 0 ok, 1 on error
618  */
619 int
620 main (int argc, char *const *argv)
621 {
622   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
623     {'a', "anonymity", "LEVEL",
624      gettext_noop ("set the desired LEVEL of sender-anonymity"),
625      1, &GNUNET_GETOPT_set_uint, &anonymity},
626     {'d', "disable-creation-time", NULL,
627      gettext_noop
628      ("disable adding the creation time to the metadata of the uploaded file"),
629      0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
630     {'D', "disable-extractor", NULL,
631      gettext_noop
632      ("do not use libextractor to add keywords or metadata"),
633      0, &GNUNET_GETOPT_set_one, &disable_extractor},
634     {'e', "extract", NULL,
635      gettext_noop
636      ("print list of extracted keywords that would be used, but do not perform upload"),
637      0, &GNUNET_GETOPT_set_one, &extract_only},
638     {'k', "key", "KEYWORD",
639      gettext_noop
640      ("add an additional keyword for the top-level file or directory"
641       " (this option can be specified multiple times)"),
642      1, &GNUNET_FS_getopt_set_keywords, &topKeywords},
643     {'m', "meta", "TYPE:VALUE",
644      gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
645      1, &GNUNET_FS_getopt_set_metadata, &meta},
646     {'n', "noindex", NULL,
647      gettext_noop ("do not index, perform full insertion (stores entire "
648                    "file in encrypted form in GNUnet database)"),
649      0, &GNUNET_GETOPT_set_one, &do_insert},
650     {'N', "next", "ID",
651      gettext_noop
652      ("specify ID of an updated version to be published in the future"
653       " (for namespace insertions only)"),
654      1, &GNUNET_GETOPT_set_string, &next_id},
655     {'p', "priority", "PRIORITY",
656      gettext_noop ("specify the priority of the content"),
657      1, &GNUNET_GETOPT_set_uint, &priority},
658     {'P', "pseudonym", "NAME",
659      gettext_noop
660      ("publish the files under the pseudonym NAME (place file into namespace)"),
661      1, &GNUNET_GETOPT_set_string, &pseudonym},
662     {'s', "simulate-only", NULL,
663      gettext_noop ("only simulate the process but do not do any "
664                    "actual publishing (useful to compute URIs)"),
665      0, &GNUNET_GETOPT_set_one, &do_simulate},
666     {'t', "this", "ID",
667      gettext_noop ("set the ID of this version of the publication"
668                    " (for namespace insertions only)"),
669      1, &GNUNET_GETOPT_set_string, &this_id},
670     {'u', "uri", "URI",
671      gettext_noop ("URI to be published (can be used instead of passing a "
672                    "file to add keywords to the file with the respective URI)"),
673      1, &GNUNET_GETOPT_set_string, &uri_string}, 
674     {'V', "verbose", NULL,
675      gettext_noop ("be verbose (print progress information)"),
676      0, &GNUNET_GETOPT_set_one, &verbose},
677     GNUNET_GETOPT_OPTION_END
678   };
679   return (GNUNET_OK ==
680           GNUNET_PROGRAM_run (argc,
681                               argv,
682                               "gnunet-publish",
683                               gettext_noop
684                               ("Publish files on GNUnet."),
685                               options, &run, NULL)) ? ret : 1;
686 }
687
688 /* end of gnunet-publish.c */