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