pass do_index
[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            "\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 *l;
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        && ( (args[0] == NULL) || (args[1] != NULL) ) )
400     {
401       printf (_
402               ("You must specify one and only one filename for insertion.\n"));
403       ret = -1;
404       return;
405     }
406   if ((uri_string != NULL) && (args[0] != NULL))
407     {
408       printf (_("You must NOT specify an URI and a filename.\n"));
409       ret = -1;
410       return;
411     }
412   if ((uri_string != NULL) && (extract_only))
413     {
414       printf (_("Cannot extract metadata from a URI!\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   if ( (args[0] == NULL) &&
449        (uri_string == NULL) )
450     {
451       fprintf (stderr,
452                _("Need the name of a file to publish!\n"));
453       ret = 1;
454       return;
455     }
456   cfg = c;
457   ctx = GNUNET_FS_start (sched,
458                          cfg,
459                          "gnunet-publish",
460                          &progress_cb,
461                          NULL,
462                          GNUNET_FS_FLAGS_NONE,
463                          GNUNET_FS_OPTIONS_END);
464   if (NULL == ctx)
465     {
466       fprintf (stderr,
467                _("Could not initialize `%s' subsystem.\n"),
468                "FS");
469       ret = 1;
470       return;
471     }
472   namespace = NULL;
473   if (NULL != pseudonym)
474     {
475       namespace = GNUNET_FS_namespace_create (ctx,
476                                               pseudonym);
477       if (NULL == namespace)
478         {
479           fprintf (stderr,
480                    _("Could not create namespace `%s'\n"),
481                    pseudonym);
482           GNUNET_FS_stop (ctx);
483           ret = 1;
484           return;
485         }
486     }
487   if (NULL != uri_string)
488     {      
489       emsg = NULL;
490       uri = GNUNET_FS_uri_parse (uri_string,
491                                  &emsg);
492       if (uri == NULL)
493         {
494           fprintf (stderr, 
495                    _("Failed to parse URI: %s\n"),
496                    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_publish_ksk (ctx,
505                              topKeywords,
506                              meta,
507                              uri,
508                              GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION),
509                              anonymity,
510                              priority,
511                              GNUNET_FS_PUBLISH_OPTION_NONE,
512                              &uri_ksk_continuation,
513                              NULL);
514       if (namespace != NULL)
515         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
516       return;
517     }
518   l = NULL;
519   if (! disable_extractor)
520     {
521       l = EXTRACTOR_plugin_add_defaults (EXTRACTOR_OPTION_DEFAULT_POLICY);
522       if (GNUNET_OK ==
523           GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS",
524                                                  &ex))
525         {
526           if (strlen (ex) > 0)
527             l = EXTRACTOR_plugin_add_config (l, ex, EXTRACTOR_OPTION_DEFAULT_POLICY);
528           GNUNET_free (ex);
529         }
530     }
531   emsg = NULL;
532   if (0 != STAT (args[0], &sbuf))
533     {
534       GNUNET_asprintf (&emsg,
535                        _("Could not access file: %s\n"),
536                        STRERROR (errno));
537       fi = NULL;
538     }
539   else if (S_ISDIR (sbuf.st_mode))
540     {
541       fi = GNUNET_FS_file_information_create_from_directory (ctx,
542                                                              NULL,
543                                                              args[0],
544                                                              &GNUNET_FS_directory_scanner_default,
545                                                              l,
546                                                              !do_insert,
547                                                              anonymity,
548                                                              priority,
549                                                              GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION),
550                                                              &emsg);
551     }
552   else
553     {
554       if (meta == NULL)
555         meta = GNUNET_CONTAINER_meta_data_create ();
556       GNUNET_FS_meta_data_extract_from_file (meta,
557                                              args[0],
558                                              l);
559       fi = GNUNET_FS_file_information_create_from_file (ctx,
560                                                         NULL,
561                                                         args[0],
562                                                         NULL,
563                                                         NULL,
564                                                         !do_insert,
565                                                         anonymity,
566                                                         priority,
567                                                         GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION));
568       GNUNET_break (fi != NULL);
569     }
570   EXTRACTOR_plugin_remove_all (l);  
571   if (fi == NULL)
572     {
573       fprintf (stderr,
574                _("Could not publish `%s': %s\n"),
575                args[0],
576                emsg);
577       GNUNET_free (emsg);
578       if (namespace != NULL)
579         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
580       GNUNET_FS_stop (ctx);
581       ret = 1;
582       return;
583     }
584   GNUNET_FS_file_information_inspect (fi,
585                                       &publish_inspector,
586                                       NULL);
587   if (extract_only)
588     {
589       if (namespace != NULL)
590         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
591       GNUNET_FS_file_information_destroy (fi, NULL, NULL);
592       GNUNET_FS_stop (ctx);
593       return;
594     }
595   pc = GNUNET_FS_publish_start (ctx,
596                                 fi,
597                                 namespace,
598                                 this_id,
599                                 next_id,
600                                 (do_simulate) 
601                                 ? GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY
602                                 : GNUNET_FS_PUBLISH_OPTION_NONE);
603   if (NULL == pc)
604     {
605       fprintf (stderr,
606                _("Could not start publishing.\n"));
607       GNUNET_FS_stop (ctx);
608       ret = 1;
609       return;
610     }
611   kill_task = GNUNET_SCHEDULER_add_delayed (sched,
612                                             GNUNET_TIME_UNIT_FOREVER_REL,
613                                             &do_stop_task,
614                                             NULL);
615 }
616
617
618 /**
619  * gnunet-publish command line options
620  */
621 static struct GNUNET_GETOPT_CommandLineOption options[] = {
622   {'a', "anonymity", "LEVEL",
623    gettext_noop ("set the desired LEVEL of sender-anonymity"),
624    1, &GNUNET_GETOPT_set_uint, &anonymity},
625   {'d', "disable-creation-time", NULL,
626    gettext_noop
627    ("disable adding the creation time to the metadata of the uploaded file"),
628    0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
629   {'D', "disable-extractor", NULL,
630    gettext_noop
631    ("do not use libextractor to add keywords or metadata"),
632    0, &GNUNET_GETOPT_set_one, &disable_extractor},
633   {'e', "extract", NULL,
634    gettext_noop
635    ("print list of extracted keywords that would be used, but do not perform upload"),
636    0, &GNUNET_GETOPT_set_one, &extract_only},
637   {'k', "key", "KEYWORD",
638    gettext_noop
639    ("add an additional keyword for the top-level file or directory"
640     " (this option can be specified multiple times)"),
641    1, &GNUNET_FS_getopt_set_keywords, &topKeywords},
642   {'m', "meta", "TYPE:VALUE",
643    gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
644    1, &GNUNET_FS_getopt_set_metadata, &meta},
645   {'n', "noindex", NULL,
646    gettext_noop ("do not index, perform full insertion (stores entire "
647                  "file in encrypted form in GNUnet database)"),
648    0, &GNUNET_GETOPT_set_one, &do_insert},
649   {'N', "next", "ID",
650    gettext_noop
651    ("specify ID of an updated version to be published in the future"
652     " (for namespace insertions only)"),
653    1, &GNUNET_GETOPT_set_string, &next_id},
654   {'p', "priority", "PRIORITY",
655    gettext_noop ("specify the priority of the content"),
656    1, &GNUNET_GETOPT_set_uint, &priority},
657   {'P', "pseudonym", "NAME",
658    gettext_noop
659    ("publish the files under the pseudonym NAME (place file into namespace)"),
660    1, &GNUNET_GETOPT_set_string, &pseudonym},
661   {'s', "simulate-only", NULL,
662    gettext_noop ("only simulate the process but do not do any "
663                  "actual publishing (useful to compute URIs)"),
664    0, &GNUNET_GETOPT_set_one, &do_simulate},
665   {'t', "this", "ID",
666    gettext_noop ("set the ID of this version of the publication"
667                  " (for namespace insertions only)"),
668    1, &GNUNET_GETOPT_set_string, &this_id},
669   {'u', "uri", "URI",
670    gettext_noop ("URI to be published (can be used instead of passing a "
671                  "file to add keywords to the file with the respective URI)"),
672    1, &GNUNET_GETOPT_set_string, &uri_string}, 
673   {'V', "verbose", NULL,
674    gettext_noop ("be verbose (print progress information)"),
675    0, &GNUNET_GETOPT_set_one, &verbose},
676   GNUNET_GETOPT_OPTION_END
677 };
678
679
680 /**
681  * The main function to publish content to GNUnet.
682  *
683  * @param argc number of arguments from the command line
684  * @param argv command line arguments
685  * @return 0 ok, 1 on error
686  */
687 int
688 main (int argc, char *const *argv)
689 {
690   return (GNUNET_OK ==
691           GNUNET_PROGRAM_run (argc,
692                               argv,
693                               "gnunet-publish",
694                               gettext_noop
695                               ("Publish files on GNUnet."),
696                               options, &run, NULL)) ? ret : 1;
697 }
698
699 /* end of gnunet-publish.c */