e3918f6fb8a9383013a5394ab183da169a2cef98
[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 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  * TODO:
29  * - support for some options is still missing (uri argument)
30  */
31 #include "platform.h"
32 #include "gnunet_fs_service.h"
33
34 #define DEFAULT_EXPIRATION GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, 2)
35
36 static int ret;
37
38 static int verbose;
39
40 static const struct GNUNET_CONFIGURATION_Handle *cfg;
41
42 static struct GNUNET_FS_Handle *ctx;
43
44 static struct GNUNET_FS_PublishContext *pc;
45
46 static struct GNUNET_CONTAINER_MetaData *meta;
47
48 static struct GNUNET_FS_Uri *topKeywords;
49
50 static unsigned int anonymity = 1;
51
52 static unsigned int priority = 365;
53
54 static char *uri_string;
55
56 static char *next_id;
57
58 static char *this_id;
59
60 static char *pseudonym;
61
62 static int do_insert;
63
64 static int disable_extractor;
65
66 static int do_simulate;
67
68 static int extract_only;
69
70 static int do_disable_creation_time;
71
72
73 /**
74  * Called by FS client to give information about the progress of an 
75  * operation.
76  *
77  * @param cls closure
78  * @param info details about the event, specifying the event type
79  *        and various bits about the event
80  * @return client-context (for the next progress call
81  *         for this operation; should be set to NULL for
82  *         SUSPEND and STOPPED events).  The value returned
83  *         will be passed to future callbacks in the respective
84  *         field in the GNUNET_FS_ProgressInfo struct.
85  */
86 static void *
87 progress_cb (void *cls,
88              const struct GNUNET_FS_ProgressInfo *info)
89 {
90   char *s;
91
92   switch (info->status)
93     {
94     case GNUNET_FS_STATUS_PUBLISH_START:
95       break;
96     case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
97       if (verbose)
98         {
99           s = GNUNET_STRINGS_relative_time_to_string(info->value.publish.eta);
100           fprintf (stdout,
101                    _("Publishing `%s' at %llu/%llu (%s remaining)\n"),
102                    info->value.publish.filename,
103                    (unsigned long long) info->value.publish.completed,
104                    (unsigned long long) info->value.publish.size,
105                    s);
106           GNUNET_free (s);
107         }
108       break;
109     case GNUNET_FS_STATUS_PUBLISH_ERROR:
110       fprintf (stderr,
111                _("Error publishing: %s.\n"),
112                info->value.publish.specifics.error.message);
113       GNUNET_FS_publish_stop (pc);      
114       break;
115     case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
116       fprintf (stdout,
117                _("Publishing `%s' done.\n"),
118                info->value.publish.filename);
119       if (info->value.publish.pctx == NULL)
120         GNUNET_FS_publish_stop (pc);
121       break;
122     case GNUNET_FS_STATUS_PUBLISH_STOPPED: 
123       if (info->value.publish.sc == pc)
124         GNUNET_FS_stop (ctx);
125       return NULL;      
126     default:
127       fprintf (stderr,
128                _("Unexpected status: %d\n"),
129                info->status);
130       return NULL;
131     }
132   return ""; /* non-null */
133 }
134
135
136 /**
137  * Print metadata entries (except binary
138  * metadata and the filename).
139  *
140  * @param cls closure
141  * @param type type of the meta data
142  * @param data value of the meta data
143  * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort
144  */
145 static int
146 meta_printer (void *cls,
147               EXTRACTOR_KeywordType type,
148               const char *data)
149 {
150   if ( (type == EXTRACTOR_FILENAME) ||
151        (EXTRACTOR_isBinaryType (type)) )
152     return GNUNET_OK;
153   fprintf (stdout, 
154            "%s - %s",
155            EXTRACTOR_getKeywordTypeAsString (type),
156            data);
157   return GNUNET_OK;
158 }
159
160
161 /**
162  * Merge metadata entries (except binary
163  * metadata).
164  *
165  * @param cls closure, target metadata structure
166  * @param type type of the meta data
167  * @param data value of the meta data
168  * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort
169  */
170 static int
171 meta_merger (void *cls,
172              EXTRACTOR_KeywordType type,
173              const char *data)
174 {
175   struct GNUNET_CONTAINER_MetaData *m = cls;
176   GNUNET_CONTAINER_meta_data_insert (m,
177                                      type, 
178                                      data);
179   return GNUNET_OK;
180 }
181
182
183 /**
184  * Function called on all entries before the
185  * publication.  This is where we perform
186  * modifications to the default based on
187  * command-line options.
188  *
189  * @param cls closure
190  * @param fi the entry in the publish-structure
191  * @param length length of the file or directory
192  * @param m metadata for the file or directory (can be modified)
193  * @param uri pointer to the keywords that will be used for this entry (can be modified)
194  * @param anonymity pointer to selected anonymity level (can be modified)
195  * @param priority pointer to selected priority (can be modified)
196  * @param expirationTime pointer to selected expiration time (can be modified)
197  * @param client_info pointer to client context set upon creation (can be modified)
198  * @return GNUNET_OK to continue, GNUNET_NO to remove
199  *         this entry from the directory, GNUNET_SYSERR
200  *         to abort the iteration
201  */
202 static int
203 publish_inspector (void *cls,
204                    struct GNUNET_FS_FileInformation *fi,
205                    uint64_t length,
206                    struct GNUNET_CONTAINER_MetaData *m,
207                    struct GNUNET_FS_Uri **uri,
208                    unsigned int *anonymity,
209                    unsigned int *priority,
210                    struct GNUNET_TIME_Absolute *expirationTime,
211                    void **client_info)
212 {
213   char *fn;
214   char *fs;
215   struct GNUNET_FS_Uri *new_uri;
216
217   if (! do_disable_creation_time)
218     GNUNET_CONTAINER_meta_data_add_publication_date (meta);
219   if (NULL != topKeywords)
220     {
221       new_uri = GNUNET_FS_uri_ksk_merge (topKeywords,
222                                          *uri);
223       GNUNET_FS_uri_destroy (*uri);
224       *uri = new_uri;
225       GNUNET_FS_uri_destroy (topKeywords);
226       topKeywords = NULL;
227     }
228   if (NULL != meta)
229     {
230       GNUNET_CONTAINER_meta_data_get_contents (meta,
231                                                &meta_merger,
232                                                m);
233       GNUNET_CONTAINER_meta_data_destroy (meta);
234       meta = NULL;
235     }
236   if (extract_only)
237     {
238       fn = GNUNET_CONTAINER_meta_data_get_by_type (meta,
239                                                    EXTRACTOR_FILENAME);
240       fs = GNUNET_STRINGS_byte_size_fancy (length);
241       fprintf (stdout,
242                _("Keywords for file `%s' (%s)\n"),
243                fn,
244                fs);
245       GNUNET_free (fn);
246       GNUNET_free (fs);
247       GNUNET_CONTAINER_meta_data_get_contents (meta,
248                                                &meta_printer,
249                                                NULL);
250       fprintf (stdout, "\n");
251     }
252   if (GNUNET_FS_meta_data_test_for_directory (meta))
253     GNUNET_FS_file_information_inspect (fi,
254                                         &publish_inspector,
255                                         NULL);
256   return GNUNET_OK;
257 }
258
259
260 /**
261  * Main function that will be run by the scheduler.
262  *
263  * @param cls closure
264  * @param sched the scheduler to use
265  * @param args remaining command-line arguments
266  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
267  * @param c configuration
268  */
269 static void
270 run (void *cls,
271      struct GNUNET_SCHEDULER_Handle *sched,
272      char *const *args,
273      const char *cfgfile,
274      const struct GNUNET_CONFIGURATION_Handle *c)
275 {
276   struct GNUNET_FS_FileInformation *fi;
277   struct GNUNET_FS_Namespace *namespace;
278   EXTRACTOR_ExtractorList *l;
279   char *ex;
280   char *emsg;
281   
282   /* check arguments */
283   if ( ( (uri_string == NULL) || (extract_only) ) 
284        && ( (args[0] == NULL) || (args[1] != NULL) ) )
285     {
286       printf (_
287               ("You must specify one and only one filename for insertion.\n"));
288       ret = -1;
289       return;
290     }
291   if ((uri_string != NULL) && (args[0] != NULL))
292     {
293       printf (_("You must NOT specify an URI and a filename.\n"));
294       ret = -1;
295       return;
296     }
297   if ((uri_string != NULL) && (extract_only))
298     {
299       printf (_("Cannot extract metadata from a URI!\n"));
300       ret = -1;
301       return;
302     }
303   if (pseudonym != NULL)
304     {
305       if (NULL == this_id)
306         {
307           fprintf (stderr,
308                    _("Option `%s' is required when using option `%s'.\n"),
309                    "-t", "-P");
310           ret = -1;
311           return;
312         }
313     }
314   else
315     {                           /* ordinary insertion checks */
316       if (NULL != next_id)
317         {
318           fprintf (stderr,
319                    _("Option `%s' makes no sense without option `%s'.\n"),
320                    "-N", "-P");
321           ret = -1;
322           return;
323         }
324       if (NULL != this_id)
325         {
326           fprintf (stderr,
327                    _("Option `%s' makes no sense without option `%s'.\n"),
328                    "-t", "-P");
329           ret = -1;
330           return;
331         }
332     }
333   if (args[0] == NULL)
334     {
335       fprintf (stderr,
336                _("Need the name of a file to publish!\n"));
337       ret = 1;
338       return;
339     }
340   cfg = c;
341   ctx = GNUNET_FS_start (sched,
342                          cfg,
343                          "gnunet-publish",
344                          &progress_cb,
345                          NULL,
346                          GNUNET_FS_FLAGS_NONE,
347                          GNUNET_FS_OPTIONS_END);
348   if (NULL == ctx)
349     {
350       fprintf (stderr,
351                _("Could not initialize `%s' subsystem.\n"),
352                "FS");
353       ret = 1;
354       return;
355     }
356   namespace = NULL;
357   if (NULL != pseudonym)
358     {
359       namespace = GNUNET_FS_namespace_create (ctx,
360                                               pseudonym);
361       if (NULL == namespace)
362         {
363           fprintf (stderr,
364                    _("Could not create namespace `%s'\n"),
365                    pseudonym);
366           GNUNET_FS_stop (ctx);
367           ret = 1;
368           return;
369         }
370     }
371   if (NULL != uri_string)
372     {
373       // FIXME -- implement!
374       return;
375     }
376
377   l = NULL;
378   if (! disable_extractor)
379     {
380       l = EXTRACTOR_loadDefaultLibraries ();
381       if (GNUNET_OK ==
382           GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS",
383                                                  &ex))
384         {
385           if (strlen (ex) > 0)
386             l = EXTRACTOR_loadConfigLibraries (l, ex);
387           GNUNET_free (ex);
388         }
389     }
390   fi = GNUNET_FS_file_information_create_from_directory (NULL,
391                                                          args[0],
392                                                          &GNUNET_FS_directory_scanner_default,
393                                                          l,
394                                                          !do_insert,
395                                                          anonymity,
396                                                          priority,
397                                                          GNUNET_TIME_relative_to_absolute (DEFAULT_EXPIRATION),
398                                                          &emsg);
399   EXTRACTOR_removeAll (l);  
400   if (fi == NULL)
401     {
402       fprintf (stderr,
403                _("Could not publish `%s': %s\n"),
404                args[0],
405                emsg);
406       GNUNET_free (emsg);
407       if (namespace != NULL)
408         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
409       GNUNET_FS_stop (ctx);
410       ret = 1;
411       return;
412     }
413   GNUNET_FS_file_information_inspect (fi,
414                                       &publish_inspector,
415                                       NULL);
416   if (extract_only)
417     {
418       if (namespace != NULL)
419         GNUNET_FS_namespace_delete (namespace, GNUNET_NO);
420       GNUNET_FS_file_information_destroy (fi, NULL, NULL);
421       GNUNET_FS_stop (ctx);
422       return;
423     }
424   pc = GNUNET_FS_publish_start (ctx,
425                                 fi,
426                                 namespace,
427                                 this_id,
428                                 next_id,
429                                 (do_simulate) 
430                                 ? GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY
431                                 : GNUNET_FS_PUBLISH_OPTION_NONE);
432   if (NULL == pc)
433     {
434       fprintf (stderr,
435                _("Could not start publishing.\n"));
436       GNUNET_FS_stop (ctx);
437       ret = 1;
438       return;
439     }
440 }
441
442
443 /**
444  * gnunet-publish command line options
445  */
446 static struct GNUNET_GETOPT_CommandLineOption options[] = {
447   {'a', "anonymity", "LEVEL",
448    gettext_noop ("set the desired LEVEL of sender-anonymity"),
449    1, &GNUNET_GETOPT_set_uint, &anonymity},
450   {'d', "disable-creation-time", NULL,
451    gettext_noop
452    ("disable adding the creation time to the metadata of the uploaded file"),
453    0, &GNUNET_GETOPT_set_one, &do_disable_creation_time},
454   {'D', "disable-extractor", NULL,
455    gettext_noop
456    ("do not use libextractor to add keywords or metadata"),
457    0, &GNUNET_GETOPT_set_one, &disable_extractor},
458   {'e', "extract", NULL,
459    gettext_noop
460    ("print list of extracted keywords that would be used, but do not perform upload"),
461    0, &GNUNET_GETOPT_set_one, &extract_only},
462   {'k', "key", "KEYWORD",
463    gettext_noop
464    ("add an additional keyword for the top-level file or directory"
465     " (this option can be specified multiple times)"),
466    1, &GNUNET_FS_getopt_set_keywords, &topKeywords},
467   // *: option not yet used... (can handle in a pass over FI)
468   {'m', "meta", "TYPE:VALUE",
469    gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
470    1, &GNUNET_FS_getopt_set_metadata, &meta},
471   {'n', "noindex", NULL,
472    gettext_noop ("do not index, perform full insertion (stores entire "
473                  "file in encrypted form in GNUnet database)"),
474    0, &GNUNET_GETOPT_set_one, &do_insert},
475   {'N', "next", "ID",
476    gettext_noop
477    ("specify ID of an updated version to be published in the future"
478     " (for namespace insertions only)"),
479    1, &GNUNET_GETOPT_set_string, &next_id},
480   {'p', "priority", "PRIORITY",
481    gettext_noop ("specify the priority of the content"),
482    1, &GNUNET_GETOPT_set_uint, &priority},
483   {'P', "pseudonym", "NAME",
484    gettext_noop
485    ("publish the files under the pseudonym NAME (place file into namespace)"),
486    1, &GNUNET_GETOPT_set_string, &pseudonym},
487   // *: option not yet used... (need FS API support!)
488   {'s', "simulate-only", NULL,
489    gettext_noop ("only simulate the process but do not do any "
490                  "actual publishing (useful to compute URIs)"),
491    0, &GNUNET_GETOPT_set_one, &do_simulate},
492   {'t', "this", "ID",
493    gettext_noop ("set the ID of this version of the publication"
494                  " (for namespace insertions only)"),
495    1, &GNUNET_GETOPT_set_string, &this_id},
496   // *: option not yet used... (need FS API support!)
497   {'u', "uri", "URI",
498    gettext_noop ("URI to be published (can be used instead of passing a "
499                  "file to add keywords to the file with the respective URI)"),
500    1, &GNUNET_GETOPT_set_string, &uri_string}, 
501   {'V', "verbose", NULL,
502    gettext_noop ("be verbose (print progress information)"),
503    0, &GNUNET_GETOPT_set_one, &verbose},
504   GNUNET_GETOPT_OPTION_END
505 };
506
507
508 /**
509  * The main function to publish content to GNUnet.
510  *
511  * @param argc number of arguments from the command line
512  * @param argv command line arguments
513  * @return 0 ok, 1 on error
514  */
515 int
516 main (int argc, char *const *argv)
517 {
518   return (GNUNET_OK ==
519           GNUNET_PROGRAM_run (argc,
520                               argv,
521                               "gnunet-publish",
522                               gettext_noop
523                               ("Publish files on GNUnet."),
524                               options, &run, NULL)) ? ret : 1;
525 }
526
527 /* end of gnunet-publish.c */
528
529 ////////////////////////////////////////////////////////////////
530
531 #if 0
532 /**
533  * Print progess message.
534  */
535 static void *
536 printstatus (void *ctx, const GNUNET_FSUI_Event * event)
537 {
538   unsigned long long delta;
539   char *fstring;
540
541   switch (event->type)
542     {
543     case GNUNET_FSUI_upload_progress:
544       if (*verboselevel)
545         {
546           char *ret;
547           GNUNET_CronTime now;
548
549           now = GNUNET_get_time ();
550           delta = event->data.UploadProgress.eta - now;
551           if (event->data.UploadProgress.eta < now)
552             delta = 0;
553           ret = GNUNET_get_time_interval_as_fancy_string (delta);
554           PRINTF (_("%16llu of %16llu bytes inserted "
555                     "(estimating %6s to completion) - %s\n"),
556                   event->data.UploadProgress.completed,
557                   event->data.UploadProgress.total,
558                   ret, event->data.UploadProgress.filename);
559           GNUNET_free (ret);
560         }
561       break;
562     case GNUNET_FSUI_upload_completed:
563       if (*verboselevel)
564         {
565           delta = GNUNET_get_time () - start_time;
566           PRINTF (_("Upload of `%s' complete, "
567                     "%llu bytes took %llu seconds (%8.3f KiB/s).\n"),
568                   event->data.UploadCompleted.filename,
569                   event->data.UploadCompleted.total,
570                   delta / GNUNET_CRON_SECONDS,
571                   (delta == 0)
572                   ? (double) (-1.0)
573                   : (double) (event->data.UploadCompleted.total
574                               / 1024.0 * GNUNET_CRON_SECONDS / delta));
575         }
576       fstring = GNUNET_ECRS_uri_to_string (event->data.UploadCompleted.uri);
577       printf (_("File `%s' has URI: %s\n"),
578               event->data.UploadCompleted.filename, fstring);
579       GNUNET_free (fstring);
580       if (ul == event->data.UploadCompleted.uc.pos)
581         {
582           postProcess (event->data.UploadCompleted.uri);
583           errorCode = 0;
584           GNUNET_shutdown_initiate ();
585         }
586       break;
587     case GNUNET_FSUI_upload_aborted:
588       printf (_("\nUpload aborted.\n"));
589       errorCode = 2;
590       GNUNET_shutdown_initiate ();
591       break;
592     case GNUNET_FSUI_upload_error:
593       printf (_("\nError uploading file: %s"),
594               event->data.UploadError.message);
595       errorCode = 3;
596       GNUNET_shutdown_initiate ();
597       break;
598     case GNUNET_FSUI_upload_started:
599     case GNUNET_FSUI_upload_stopped:
600       break;
601     default:
602       printf (_("\nUnexpected event: %d\n"), event->type);
603       GNUNET_GE_BREAK (ectx, 0);
604       break;
605     }
606   return NULL;
607 }
608 #endif
609
610 /* end of gnunet-publish.c */