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