diff options
Diffstat (limited to 'src/fs/gnunet-publish.c')
-rw-r--r-- | src/fs/gnunet-publish.c | 556 |
1 files changed, 556 insertions, 0 deletions
diff --git a/src/fs/gnunet-publish.c b/src/fs/gnunet-publish.c new file mode 100644 index 000000000..58885c234 --- /dev/null +++ b/src/fs/gnunet-publish.c | |||
@@ -0,0 +1,556 @@ | |||
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, simulate) | ||
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 | } | ||
390 | |||
391 | |||
392 | /** | ||
393 | * gnunet-publish command line options | ||
394 | */ | ||
395 | static struct GNUNET_GETOPT_CommandLineOption options[] = { | ||
396 | {'a', "anonymity", "LEVEL", | ||
397 | gettext_noop ("set the desired LEVEL of sender-anonymity"), | ||
398 | 1, &GNUNET_GETOPT_set_uint, &anonymity}, | ||
399 | {'d', "disable-creation-time", NULL, | ||
400 | gettext_noop | ||
401 | ("disable adding the creation time to the metadata of the uploaded file"), | ||
402 | 0, &GNUNET_GETOPT_set_one, &do_disable_creation_time}, | ||
403 | {'D', "disable-extractor", NULL, | ||
404 | gettext_noop | ||
405 | ("do not use libextractor to add keywords or metadata"), | ||
406 | 0, &GNUNET_GETOPT_set_one, &disable_extractor}, | ||
407 | {'e', "extract", NULL, | ||
408 | gettext_noop | ||
409 | ("print list of extracted keywords that would be used, but do not perform upload"), | ||
410 | 0, &GNUNET_GETOPT_set_one, &extract_only}, | ||
411 | {'k', "key", "KEYWORD", | ||
412 | gettext_noop | ||
413 | ("add an additional keyword for the top-level file or directory" | ||
414 | " (this option can be specified multiple times)"), | ||
415 | 1, &GNUNET_FS_getopt_set_keywords, &topKeywords}, | ||
416 | // *: option not yet used... (can handle in a pass over FI) | ||
417 | {'m', "meta", "TYPE:VALUE", | ||
418 | gettext_noop ("set the meta-data for the given TYPE to the given VALUE"), | ||
419 | 1, &GNUNET_FS_getopt_set_metadata, &meta}, | ||
420 | {'n', "noindex", NULL, | ||
421 | gettext_noop ("do not index, perform full insertion (stores entire " | ||
422 | "file in encrypted form in GNUnet database)"), | ||
423 | 0, &GNUNET_GETOPT_set_one, &do_insert}, | ||
424 | {'N', "next", "ID", | ||
425 | gettext_noop | ||
426 | ("specify ID of an updated version to be published in the future" | ||
427 | " (for namespace insertions only)"), | ||
428 | 1, &GNUNET_GETOPT_set_string, &next_id}, | ||
429 | {'p', "priority", "PRIORITY", | ||
430 | gettext_noop ("specify the priority of the content"), | ||
431 | 1, &GNUNET_GETOPT_set_uint, &priority}, | ||
432 | {'P', "pseudonym", "NAME", | ||
433 | gettext_noop | ||
434 | ("publish the files under the pseudonym NAME (place file into namespace)"), | ||
435 | 1, &GNUNET_GETOPT_set_string, &pseudonym}, | ||
436 | // *: option not yet used... (need FS API support!) | ||
437 | {'s', "simulate-only", NULL, | ||
438 | gettext_noop ("only simulate the process but do not do any " | ||
439 | "actual publishing (useful to compute URIs)"), | ||
440 | 0, &GNUNET_GETOPT_set_one, &do_simulate}, | ||
441 | {'t', "this", "ID", | ||
442 | gettext_noop ("set the ID of this version of the publication" | ||
443 | " (for namespace insertions only)"), | ||
444 | 1, &GNUNET_GETOPT_set_string, &this_id}, | ||
445 | // *: option not yet used... (need FS API support!) | ||
446 | {'u', "uri", "URI", | ||
447 | gettext_noop ("URI to be published (can be used instead of passing a " | ||
448 | "file to add keywords to the file with the respective URI)"), | ||
449 | 1, &GNUNET_GETOPT_set_string, &uri_string}, | ||
450 | GNUNET_GETOPT_OPTION_END | ||
451 | }; | ||
452 | |||
453 | |||
454 | /** | ||
455 | * The main function to publish content to GNUnet. | ||
456 | * | ||
457 | * @param argc number of arguments from the command line | ||
458 | * @param argv command line arguments | ||
459 | * @return 0 ok, 1 on error | ||
460 | */ | ||
461 | int | ||
462 | main (int argc, char *const *argv) | ||
463 | { | ||
464 | return (GNUNET_OK == | ||
465 | GNUNET_PROGRAM_run (argc, | ||
466 | argv, | ||
467 | "gnunet-publish", | ||
468 | gettext_noop | ||
469 | ("Publish files on GNUnet."), | ||
470 | options, &run, NULL)) ? ret : 1; | ||
471 | } | ||
472 | |||
473 | /* end of gnunet-publish.c */ | ||
474 | |||
475 | //////////////////////////////////////////////////////////////// | ||
476 | |||
477 | |||
478 | /** | ||
479 | * Print progess message. | ||
480 | */ | ||
481 | static void * | ||
482 | printstatus (void *ctx, const GNUNET_FSUI_Event * event) | ||
483 | { | ||
484 | unsigned long long delta; | ||
485 | char *fstring; | ||
486 | |||
487 | switch (event->type) | ||
488 | { | ||
489 | case GNUNET_FSUI_upload_progress: | ||
490 | if (*verboselevel) | ||
491 | { | ||
492 | char *ret; | ||
493 | GNUNET_CronTime now; | ||
494 | |||
495 | now = GNUNET_get_time (); | ||
496 | delta = event->data.UploadProgress.eta - now; | ||
497 | if (event->data.UploadProgress.eta < now) | ||
498 | delta = 0; | ||
499 | ret = GNUNET_get_time_interval_as_fancy_string (delta); | ||
500 | PRINTF (_("%16llu of %16llu bytes inserted " | ||
501 | "(estimating %6s to completion) - %s\n"), | ||
502 | event->data.UploadProgress.completed, | ||
503 | event->data.UploadProgress.total, | ||
504 | ret, event->data.UploadProgress.filename); | ||
505 | GNUNET_free (ret); | ||
506 | } | ||
507 | break; | ||
508 | case GNUNET_FSUI_upload_completed: | ||
509 | if (*verboselevel) | ||
510 | { | ||
511 | delta = GNUNET_get_time () - start_time; | ||
512 | PRINTF (_("Upload of `%s' complete, " | ||
513 | "%llu bytes took %llu seconds (%8.3f KiB/s).\n"), | ||
514 | event->data.UploadCompleted.filename, | ||
515 | event->data.UploadCompleted.total, | ||
516 | delta / GNUNET_CRON_SECONDS, | ||
517 | (delta == 0) | ||
518 | ? (double) (-1.0) | ||
519 | : (double) (event->data.UploadCompleted.total | ||
520 | / 1024.0 * GNUNET_CRON_SECONDS / delta)); | ||
521 | } | ||
522 | fstring = GNUNET_ECRS_uri_to_string (event->data.UploadCompleted.uri); | ||
523 | printf (_("File `%s' has URI: %s\n"), | ||
524 | event->data.UploadCompleted.filename, fstring); | ||
525 | GNUNET_free (fstring); | ||
526 | if (ul == event->data.UploadCompleted.uc.pos) | ||
527 | { | ||
528 | postProcess (event->data.UploadCompleted.uri); | ||
529 | errorCode = 0; | ||
530 | GNUNET_shutdown_initiate (); | ||
531 | } | ||
532 | break; | ||
533 | case GNUNET_FSUI_upload_aborted: | ||
534 | printf (_("\nUpload aborted.\n")); | ||
535 | errorCode = 2; | ||
536 | GNUNET_shutdown_initiate (); | ||
537 | break; | ||
538 | case GNUNET_FSUI_upload_error: | ||
539 | printf (_("\nError uploading file: %s"), | ||
540 | event->data.UploadError.message); | ||
541 | errorCode = 3; | ||
542 | GNUNET_shutdown_initiate (); | ||
543 | break; | ||
544 | case GNUNET_FSUI_upload_started: | ||
545 | case GNUNET_FSUI_upload_stopped: | ||
546 | break; | ||
547 | default: | ||
548 | printf (_("\nUnexpected event: %d\n"), event->type); | ||
549 | GNUNET_GE_BREAK (ectx, 0); | ||
550 | break; | ||
551 | } | ||
552 | return NULL; | ||
553 | } | ||
554 | #endif | ||
555 | |||
556 | /* end of gnunet-publish.c */ | ||