aboutsummaryrefslogtreecommitdiff
path: root/src/cli/fs
diff options
context:
space:
mode:
Diffstat (limited to 'src/cli/fs')
-rw-r--r--src/cli/fs/.gitignore8
-rw-r--r--src/cli/fs/Makefile.am107
-rw-r--r--src/cli/fs/gnunet-auto-share.c791
-rw-r--r--src/cli/fs/gnunet-directory.c212
-rw-r--r--src/cli/fs/gnunet-download.c385
-rw-r--r--src/cli/fs/gnunet-fs.c191
-rw-r--r--src/cli/fs/gnunet-publish.c1009
-rw-r--r--src/cli/fs/gnunet-search.c801
-rw-r--r--src/cli/fs/gnunet-unindex.c206
-rw-r--r--src/cli/fs/meson.build51
10 files changed, 3761 insertions, 0 deletions
diff --git a/src/cli/fs/.gitignore b/src/cli/fs/.gitignore
new file mode 100644
index 000000000..3ca8908d0
--- /dev/null
+++ b/src/cli/fs/.gitignore
@@ -0,0 +1,8 @@
1gnunet-unindex
2gnunet-auto-share
3gnunet-directory
4gnunet-download
5gnunet-fs
6gnunet-publish
7gnunet-search
8
diff --git a/src/cli/fs/Makefile.am b/src/cli/fs/Makefile.am
new file mode 100644
index 000000000..0d489dbe6
--- /dev/null
+++ b/src/cli/fs/Makefile.am
@@ -0,0 +1,107 @@
1# This Makefile.am is in the public domain
2AM_CPPFLAGS = -I$(top_srcdir)/src/include
3
4if USE_COVERAGE
5 AM_CFLAGS = --coverage -O0
6 XLIB = -lgcov
7endif
8
9pkgcfgdir= $(pkgdatadir)/config.d/
10
11libexecdir= $(pkglibdir)/libexec/
12
13bin_PROGRAMS = \
14 gnunet-auto-share \
15 gnunet-directory \
16 gnunet-download \
17 gnunet-publish \
18 gnunet-search \
19 gnunet-fs \
20 gnunet-unindex
21
22gnunet_directory_SOURCES = \
23 gnunet-directory.c
24gnunet_directory_LDADD = \
25 $(top_builddir)/src/service/fs/libgnunetfs.la \
26 $(top_builddir)/src/lib/util/libgnunetutil.la \
27 $(GN_LIBINTL)
28
29if HAVE_LIBEXTRACTOR
30gnunet_directory_LDADD += \
31 -lextractor
32endif
33
34gnunet_fs_SOURCES = \
35 gnunet-fs.c
36gnunet_fs_LDADD = \
37 $(top_builddir)/src/service/fs/libgnunetfs.la \
38 $(top_builddir)/src/lib/util/libgnunetutil.la \
39 $(GN_LIBINTL)
40
41if HAVE_LIBEXTRACTOR
42gnunet_fs_LDADD += \
43 -lextractor
44endif
45
46gnunet_download_SOURCES = \
47 gnunet-download.c
48gnunet_download_LDADD = \
49 $(top_builddir)/src/service/fs/libgnunetfs.la \
50 $(top_builddir)/src/lib/util/libgnunetutil.la \
51 $(GN_LIBINTL)
52
53gnunet_publish_SOURCES = \
54 gnunet-publish.c
55gnunet_publish_LDADD = \
56 $(top_builddir)/src/service/identity/libgnunetidentity.la \
57 $(top_builddir)/src/service/fs/libgnunetfs.la \
58 $(top_builddir)/src/lib/util/libgnunetutil.la \
59 $(GN_LIBINTL)
60
61if HAVE_LIBEXTRACTOR
62gnunet_publish_LDADD += \
63 -lextractor
64endif
65
66gnunet_auto_share_SOURCES = \
67 gnunet-auto-share.c
68gnunet_auto_share_LDADD = \
69 $(top_builddir)/src/lib/util/libgnunetutil.la \
70 $(GN_LIBINTL)
71
72if HAVE_LIBEXTRACTOR
73gnunet_auto_share_LDADD += \
74 -lextractor
75endif
76
77gnunet_helper_fs_publish_SOURCES = \
78 gnunet-helper-fs-publish.c
79gnunet_helper_fs_publish_LDADD = \
80 $(top_builddir)/src/service/fs/libgnunetfs.la \
81 $(top_builddir)/src/lib/util/libgnunetutil.la \
82 $(GN_LIBINTL)
83
84if HAVE_LIBEXTRACTOR
85gnunet_helper_fs_publish_LDADD += \
86 -lextractor
87endif
88
89gnunet_search_SOURCES = \
90 gnunet-search.c
91gnunet_search_LDADD = \
92 $(top_builddir)/src/service/fs/libgnunetfs.la \
93 $(top_builddir)/src/lib/util/libgnunetutil.la \
94 $(GN_LIBINTL)
95
96if HAVE_LIBEXTRACTOR
97gnunet_search_LDADD += \
98 -lextractor
99endif
100
101
102gnunet_unindex_SOURCES = \
103 gnunet-unindex.c
104gnunet_unindex_LDADD = \
105 $(top_builddir)/src/service/fs/libgnunetfs.la \
106 $(top_builddir)/src/lib/util/libgnunetutil.la \
107 $(GN_LIBINTL)
diff --git a/src/cli/fs/gnunet-auto-share.c b/src/cli/fs/gnunet-auto-share.c
new file mode 100644
index 000000000..f91e9d00d
--- /dev/null
+++ b/src/cli/fs/gnunet-auto-share.c
@@ -0,0 +1,791 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001--2012 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-auto-share.c
22 * @brief automatically publish files on GNUnet
23 * @author Christian Grothoff
24 *
25 * TODO:
26 * - support loading meta data / keywords from resource file
27 * - add stability timer (a la buildbot)
28 */
29#include "platform.h"
30#include "gnunet_util_lib.h"
31
32#define MAX_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_HOURS, 4)
33
34#define MIN_DELAY GNUNET_TIME_UNIT_MINUTES
35
36
37/**
38 * Item in our work queue (or in the set of files/directories
39 * we have successfully published).
40 */
41struct WorkItem
42{
43 /**
44 * PENDING Work is kept in a linked list.
45 */
46 struct WorkItem *prev;
47
48 /**
49 * PENDING Work is kept in a linked list.
50 */
51 struct WorkItem *next;
52
53 /**
54 * Filename of the work item.
55 */
56 char *filename;
57
58 /**
59 * Unique identity for this work item (used to detect
60 * if we need to do the work again).
61 */
62 struct GNUNET_HashCode id;
63};
64
65
66/**
67 * Global return value from 'main'.
68 */
69static int ret;
70
71/**
72 * Are we running 'verbosely'?
73 */
74static unsigned int verbose;
75
76/**
77 * Configuration to use.
78 */
79static const struct GNUNET_CONFIGURATION_Handle *cfg;
80
81/**
82 * Name of the configuration file.
83 */
84static char *cfg_filename;
85
86/**
87 * Disable extractor option to use for publishing.
88 */
89static int disable_extractor;
90
91/**
92 * Disable creation time option to use for publishing.
93 */
94static int do_disable_creation_time;
95
96/**
97 * Handle for the main task that does scanning and working.
98 */
99static struct GNUNET_SCHEDULER_Task *run_task;
100
101/**
102 * Anonymity level option to use for publishing.
103 */
104static unsigned int anonymity_level = 1;
105
106/**
107 * Content priority option to use for publishing.
108 */
109static unsigned int content_priority = 365;
110
111/**
112 * Replication level option to use for publishing.
113 */
114static unsigned int replication_level = 1;
115
116/**
117 * Top-level directory we monitor to auto-publish.
118 */
119static const char *dir_name;
120
121/**
122 * Head of linked list of files still to publish.
123 */
124static struct WorkItem *work_head;
125
126/**
127 * Tail of linked list of files still to publish.
128 */
129static struct WorkItem *work_tail;
130
131/**
132 * Map from the hash of the filename (!) to a `struct WorkItem`
133 * that was finished.
134 */
135static struct GNUNET_CONTAINER_MultiHashMap *work_finished;
136
137/**
138 * Set to #GNUNET_YES if we are shutting down.
139 */
140static int do_shutdown;
141
142/**
143 * Start time of the current round; used to determine how long
144 * one iteration takes (which influences how fast we schedule
145 * the next one).
146 */
147static struct GNUNET_TIME_Absolute start_time;
148
149/**
150 * Pipe used to communicate 'gnunet-publish' completion (SIGCHLD) via signal.
151 */
152static struct GNUNET_DISK_PipeHandle *sigpipe;
153
154/**
155 * Handle to the 'gnunet-publish' process that we executed.
156 */
157static struct GNUNET_OS_Process *publish_proc;
158
159
160/**
161 * Compute the name of the state database file we will use.
162 */
163static char *
164get_state_file ()
165{
166 char *ret;
167
168 GNUNET_asprintf (&ret,
169 "%s%s.auto-share",
170 dir_name,
171 (DIR_SEPARATOR == dir_name[strlen (dir_name) - 1])
172 ? ""
173 : DIR_SEPARATOR_STR);
174 return ret;
175}
176
177
178/**
179 * Load the set of #work_finished items from disk.
180 */
181static void
182load_state ()
183{
184 char *fn;
185 struct GNUNET_BIO_ReadHandle *rh;
186 uint32_t n;
187 struct GNUNET_HashCode id;
188 struct WorkItem *wi;
189 char *emsg;
190
191 emsg = NULL;
192 fn = get_state_file ();
193 rh = GNUNET_BIO_read_open_file (fn);
194 GNUNET_free (fn);
195 if (NULL == rh)
196 return;
197 fn = NULL;
198 if (GNUNET_OK != GNUNET_BIO_read_int32 (rh, "number of files",
199 (int32_t *) &n))
200 goto error;
201 while (n-- > 0)
202 {
203 struct GNUNET_BIO_ReadSpec rs[] = {
204 GNUNET_BIO_read_spec_string ("filename", &fn, 1024),
205 GNUNET_BIO_read_spec_object ("id", &id, sizeof(struct GNUNET_HashCode)),
206 GNUNET_BIO_read_spec_end (),
207 };
208 if (GNUNET_OK != GNUNET_BIO_read_spec_commit (rh, rs))
209 goto error;
210 wi = GNUNET_new (struct WorkItem);
211 wi->id = id;
212 wi->filename = fn;
213 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
214 "Loaded serialization ID for `%s' is `%s'\n",
215 wi->filename,
216 GNUNET_h2s (&id));
217 fn = NULL;
218 GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &id);
219 GNUNET_break (GNUNET_OK ==
220 GNUNET_CONTAINER_multihashmap_put (
221 work_finished,
222 &id,
223 wi,
224 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
225 }
226 if (GNUNET_OK == GNUNET_BIO_read_close (rh, &emsg))
227 return;
228 rh = NULL;
229error:
230 GNUNET_free (fn);
231 if (NULL != rh)
232 (void) GNUNET_BIO_read_close (rh, &emsg);
233 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
234 _ ("Failed to load state: %s\n"),
235 emsg);
236 GNUNET_free (emsg);
237}
238
239
240/**
241 * Write work item from the #work_finished map to the given write handle.
242 *
243 * @param cls the `struct GNUNET_BIO_WriteHandle *`
244 * @param key key of the item in the map (unused)
245 * @param value the `struct WorkItem` to write
246 * @return #GNUNET_OK to continue to iterate (if write worked)
247 */
248static int
249write_item (void *cls, const struct GNUNET_HashCode *key, void *value)
250{
251 struct GNUNET_BIO_WriteHandle *wh = cls;
252 struct WorkItem *wi = value;
253
254 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
255 "Saving serialization ID of file `%s' with value `%s'\n",
256 wi->filename,
257 GNUNET_h2s (&wi->id));
258 struct GNUNET_BIO_WriteSpec ws[] = {
259 GNUNET_BIO_write_spec_string ("auto-share-write-item-filename",
260 wi->filename),
261 GNUNET_BIO_write_spec_object ("id", &wi->id, sizeof(struct
262 GNUNET_HashCode)),
263 GNUNET_BIO_write_spec_end (),
264 };
265 if (GNUNET_OK != GNUNET_BIO_write_spec_commit (wh, ws))
266 return GNUNET_SYSERR; /* write error, abort iteration */
267 return GNUNET_OK;
268}
269
270
271/**
272 * Save the set of #work_finished items on disk.
273 */
274static void
275save_state ()
276{
277 uint32_t n;
278 struct GNUNET_BIO_WriteHandle *wh;
279 char *fn;
280
281 n = GNUNET_CONTAINER_multihashmap_size (work_finished);
282 fn = get_state_file ();
283 wh = GNUNET_BIO_write_open_file (fn);
284 if (NULL == wh)
285 {
286 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
287 _ ("Failed to save state to file %s\n"),
288 fn);
289 GNUNET_free (fn);
290 return;
291 }
292 if (GNUNET_OK != GNUNET_BIO_write_int32 (wh, "size of state", n))
293 {
294 (void) GNUNET_BIO_write_close (wh, NULL);
295 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
296 _ ("Failed to save state to file %s\n"),
297 fn);
298 GNUNET_free (fn);
299 return;
300 }
301 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished, &write_item, wh);
302 if (GNUNET_OK != GNUNET_BIO_write_close (wh, NULL))
303 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
304 _ ("Failed to save state to file %s\n"),
305 fn);
306 GNUNET_free (fn);
307}
308
309
310/**
311 * Task run on shutdown. Serializes our current state to disk.
312 *
313 * @param cls closure, unused
314 */
315static void
316do_stop_task (void *cls)
317{
318 do_shutdown = GNUNET_YES;
319 if (NULL != publish_proc)
320 {
321 GNUNET_OS_process_kill (publish_proc, SIGKILL);
322 return;
323 }
324 if (NULL != run_task)
325 {
326 GNUNET_SCHEDULER_cancel (run_task);
327 run_task = NULL;
328 }
329}
330
331
332/**
333 * Decide what the next task is (working or scanning) and schedule it.
334 */
335static void
336schedule_next_task (void);
337
338
339/**
340 * Task triggered whenever we receive a SIGCHLD (child
341 * process died).
342 *
343 * @param cls the `struct WorkItem` we were working on
344 */
345static void
346maint_child_death (void *cls)
347{
348 struct WorkItem *wi = cls;
349 struct GNUNET_HashCode key;
350 enum GNUNET_OS_ProcessStatusType type;
351 unsigned long code;
352 int ret;
353 char c;
354 const struct GNUNET_DISK_FileHandle *pr;
355 const struct GNUNET_SCHEDULER_TaskContext *tc;
356
357 run_task = NULL;
358 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
359 tc = GNUNET_SCHEDULER_get_task_context ();
360 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY))
361 {
362 /* shutdown scheduled us, someone else will kill child,
363 we should just try again */
364 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
365 pr,
366 &maint_child_death,
367 wi);
368 return;
369 }
370 /* consume the signal */
371 GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof(c)));
372
373 ret = GNUNET_OS_process_status (publish_proc, &type, &code);
374 GNUNET_assert (GNUNET_SYSERR != ret);
375 if (GNUNET_NO == ret)
376 {
377 /* process still running? Then where did the SIGCHLD come from?
378 Well, let's declare it spurious (kernel bug?) and keep rolling.
379 */
380 GNUNET_break (0);
381 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
382 pr,
383 &maint_child_death,
384 wi);
385 return;
386 }
387 GNUNET_assert (GNUNET_OK == ret);
388
389 GNUNET_OS_process_destroy (publish_proc);
390 publish_proc = NULL;
391
392 if (GNUNET_YES == do_shutdown)
393 {
394 GNUNET_free (wi->filename);
395 GNUNET_free (wi);
396 return;
397 }
398 if ((GNUNET_OS_PROCESS_EXITED == type) && (0 == code))
399 {
400 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
401 _ ("Publication of `%s' done\n"),
402 wi->filename);
403 GNUNET_CRYPTO_hash (wi->filename, strlen (wi->filename), &key);
404 GNUNET_break (GNUNET_OK ==
405 GNUNET_CONTAINER_multihashmap_put (
406 work_finished,
407 &key,
408 wi,
409 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
410 }
411 else
412 {
413 GNUNET_CONTAINER_DLL_insert_tail (work_head, work_tail, wi);
414 }
415 save_state ();
416 schedule_next_task ();
417}
418
419
420/**
421 * Signal handler called for SIGCHLD. Triggers the
422 * respective handler by writing to the trigger pipe.
423 */
424static void
425sighandler_child_death ()
426{
427 static char c;
428 int old_errno = errno; /* back-up errno */
429
430 GNUNET_break (
431 1 ==
432 GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle (sigpipe,
433 GNUNET_DISK_PIPE_END_WRITE),
434 &c,
435 sizeof(c)));
436 errno = old_errno; /* restore errno */
437}
438
439
440/**
441 * Function called to process work items.
442 *
443 * @param cls closure, NULL
444 */
445static void
446work (void *cls)
447{
448 static char *argv[14];
449 static char anon_level[20];
450 static char content_prio[20];
451 static char repl_level[20];
452 struct WorkItem *wi;
453 const struct GNUNET_DISK_FileHandle *pr;
454 int argc;
455
456 run_task = NULL;
457 wi = work_head;
458 GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
459 argc = 0;
460 argv[argc++] = "gnunet-publish";
461 if (verbose)
462 argv[argc++] = "-V";
463 if (disable_extractor)
464 argv[argc++] = "-D";
465 if (do_disable_creation_time)
466 argv[argc++] = "-d";
467 argv[argc++] = "-c";
468 argv[argc++] = cfg_filename;
469 GNUNET_snprintf (anon_level, sizeof(anon_level), "%u", anonymity_level);
470 argv[argc++] = "-a";
471 argv[argc++] = anon_level;
472 GNUNET_snprintf (content_prio, sizeof(content_prio), "%u", content_priority);
473 argv[argc++] = "-p";
474 argv[argc++] = content_prio;
475 GNUNET_snprintf (repl_level, sizeof(repl_level), "%u", replication_level);
476 argv[argc++] = "-r";
477 argv[argc++] = repl_level;
478 argv[argc++] = wi->filename;
479 argv[argc] = NULL;
480 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _ ("Publishing `%s'\n"), wi->filename);
481 GNUNET_assert (NULL == publish_proc);
482 publish_proc = GNUNET_OS_start_process_vap (GNUNET_OS_USE_PIPE_CONTROL,
483 NULL,
484 NULL,
485 NULL,
486 "gnunet-publish",
487 argv);
488 if (NULL == publish_proc)
489 {
490 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
491 _ ("Failed to run `%s'\n"),
492 "gnunet-publish");
493 GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi);
494 run_task =
495 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES, &work, NULL);
496 return;
497 }
498 pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
499 run_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
500 pr,
501 &maint_child_death,
502 wi);
503}
504
505
506/**
507 * Recursively scan the given file/directory structure to determine
508 * a unique ID that represents the current state of the hierarchy.
509 *
510 * @param cls where to store the unique ID we are computing
511 * @param filename file to scan
512 * @return #GNUNET_OK (always)
513 */
514static int
515determine_id (void *cls, const char *filename)
516{
517 struct GNUNET_HashCode *id = cls;
518 struct stat sbuf;
519 struct GNUNET_HashCode fx[2];
520 struct GNUNET_HashCode ft;
521
522 if (0 != stat (filename, &sbuf))
523 {
524 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
525 return GNUNET_OK;
526 }
527 GNUNET_CRYPTO_hash (filename, strlen (filename), &fx[0]);
528 if (! S_ISDIR (sbuf.st_mode))
529 {
530 uint64_t fattr[2];
531
532 fattr[0] = GNUNET_htonll (sbuf.st_size);
533 fattr[0] = GNUNET_htonll (sbuf.st_mtime);
534
535 GNUNET_CRYPTO_hash (fattr, sizeof(fattr), &fx[1]);
536 }
537 else
538 {
539 memset (&fx[1], 1, sizeof(struct GNUNET_HashCode));
540 GNUNET_DISK_directory_scan (filename, &determine_id, &fx[1]);
541 }
542 /* use hash here to make hierarchical structure distinct from
543 all files on the same level */
544 GNUNET_CRYPTO_hash (fx, sizeof(fx), &ft);
545 /* use XOR here so that order of the files in the directory
546 does not matter! */
547 GNUNET_CRYPTO_hash_xor (&ft, id, id);
548 return GNUNET_OK;
549}
550
551
552/**
553 * Function called with a filename (or directory name) to publish
554 * (if it has changed since the last time we published it). This function
555 * is called for the top-level files only.
556 *
557 * @param cls closure, NULL
558 * @param filename complete filename (absolute path)
559 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR during shutdown
560 */
561static int
562add_file (void *cls, const char *filename)
563{
564 struct WorkItem *wi;
565 struct GNUNET_HashCode key;
566 struct GNUNET_HashCode id;
567
568 if (GNUNET_YES == do_shutdown)
569 return GNUNET_SYSERR;
570 if ((NULL != strstr (filename, "/.auto-share")) ||
571 (NULL != strstr (filename, "\\.auto-share")))
572 return GNUNET_OK; /* skip internal file */
573 GNUNET_CRYPTO_hash (filename, strlen (filename), &key);
574 wi = GNUNET_CONTAINER_multihashmap_get (work_finished, &key);
575 memset (&id, 0, sizeof(struct GNUNET_HashCode));
576 determine_id (&id, filename);
577 if (NULL != wi)
578 {
579 if (0 == memcmp (&id, &wi->id, sizeof(struct GNUNET_HashCode)))
580 return GNUNET_OK; /* skip: we did this one already */
581 /* contents changed, need to re-do the directory... */
582 GNUNET_assert (
583 GNUNET_YES ==
584 GNUNET_CONTAINER_multihashmap_remove (work_finished, &key, wi));
585 }
586 else
587 {
588 wi = GNUNET_new (struct WorkItem);
589 wi->filename = GNUNET_strdup (filename);
590 }
591 wi->id = id;
592 GNUNET_CONTAINER_DLL_insert (work_head, work_tail, wi);
593 if (GNUNET_YES == do_shutdown)
594 return GNUNET_SYSERR;
595 return GNUNET_OK;
596}
597
598
599/**
600 * Periodically run task to update our view of the directory to share.
601 *
602 * @param cls NULL
603 */
604static void
605scan (void *cls)
606{
607 run_task = NULL;
608 start_time = GNUNET_TIME_absolute_get ();
609 (void) GNUNET_DISK_directory_scan (dir_name, &add_file, NULL);
610 schedule_next_task ();
611}
612
613
614/**
615 * Decide what the next task is (working or scanning) and schedule it.
616 */
617static void
618schedule_next_task ()
619{
620 struct GNUNET_TIME_Relative delay;
621
622 if (GNUNET_YES == do_shutdown)
623 return;
624 GNUNET_assert (NULL == run_task);
625 if (NULL == work_head)
626 {
627 /* delay by at most 4h, at least 1s, and otherwise in between depending
628 on how long it took to scan */
629 delay = GNUNET_TIME_absolute_get_duration (start_time);
630 delay = GNUNET_TIME_relative_saturating_multiply (delay, 100);
631 delay = GNUNET_TIME_relative_min (delay, MAX_DELAY);
632 delay = GNUNET_TIME_relative_max (delay, MIN_DELAY);
633 run_task = GNUNET_SCHEDULER_add_delayed (delay, &scan, NULL);
634 }
635 else
636 {
637 run_task = GNUNET_SCHEDULER_add_now (&work, NULL);
638 }
639}
640
641
642/**
643 * Main function that will be run by the scheduler.
644 *
645 * @param cls closure
646 * @param args remaining command-line arguments
647 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
648 * @param c configuration
649 */
650static void
651run (void *cls,
652 char *const *args,
653 const char *cfgfile,
654 const struct GNUNET_CONFIGURATION_Handle *c)
655{
656 /* check arguments */
657 if ((NULL == args[0]) || (NULL != args[1]) ||
658 (GNUNET_YES != GNUNET_DISK_directory_test (args[0], GNUNET_YES)))
659 {
660 printf (_ (
661 "You must specify one and only one directory name for automatic publication.\n"));
662 ret = -1;
663 return;
664 }
665 cfg_filename = GNUNET_strdup (cfgfile);
666 cfg = c;
667 dir_name = args[0];
668 work_finished = GNUNET_CONTAINER_multihashmap_create (1024, GNUNET_NO);
669 load_state ();
670 run_task = GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE,
671 &scan,
672 NULL);
673 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
674}
675
676
677/**
678 * Free memory associated with the work item from the work_finished map.
679 *
680 * @param cls NULL (unused)
681 * @param key key of the item in the map (unused)
682 * @param value the `struct WorkItem` to free
683 * @return #GNUNET_OK to continue to iterate
684 */
685static int
686free_item (void *cls, const struct GNUNET_HashCode *key, void *value)
687{
688 struct WorkItem *wi = value;
689
690 GNUNET_free (wi->filename);
691 GNUNET_free (wi);
692 return GNUNET_OK;
693}
694
695
696/**
697 * The main function to automatically publish content to GNUnet.
698 *
699 * @param argc number of arguments from the command line
700 * @param argv command line arguments
701 * @return 0 ok, 1 on error
702 */
703int
704main (int argc, char *const *argv)
705{
706 struct GNUNET_GETOPT_CommandLineOption options[] = {
707 GNUNET_GETOPT_option_uint ('a',
708 "anonymity",
709 "LEVEL",
710 gettext_noop (
711 "set the desired LEVEL of sender-anonymity"),
712 &anonymity_level),
713
714 GNUNET_GETOPT_option_flag (
715 'd',
716 "disable-creation-time",
717 gettext_noop (
718 "disable adding the creation time to the metadata of the uploaded file"),
719 &do_disable_creation_time),
720
721 GNUNET_GETOPT_option_flag (
722 'D',
723 "disable-extractor",
724 gettext_noop ("do not use libextractor to add keywords or metadata"),
725 &disable_extractor),
726
727 GNUNET_GETOPT_option_uint ('p',
728 "priority",
729 "PRIORITY",
730 gettext_noop (
731 "specify the priority of the content"),
732 &content_priority),
733
734 GNUNET_GETOPT_option_uint ('r',
735 "replication",
736 "LEVEL",
737 gettext_noop (
738 "set the desired replication LEVEL"),
739 &replication_level),
740
741 GNUNET_GETOPT_option_verbose (&verbose),
742
743 GNUNET_GETOPT_OPTION_END
744 };
745 struct WorkItem *wi;
746 int ok;
747 struct GNUNET_SIGNAL_Context *shc_chld;
748
749 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
750 return 2;
751 sigpipe = GNUNET_DISK_pipe (GNUNET_DISK_PF_NONE);
752 GNUNET_assert (NULL != sigpipe);
753 shc_chld =
754 GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death);
755 ok =
756 (GNUNET_OK ==
757 GNUNET_PROGRAM_run (
758 argc,
759 argv,
760 "gnunet-auto-share [OPTIONS] FILENAME",
761 gettext_noop ("Automatically publish files from a directory on GNUnet"),
762 options,
763 &run,
764 NULL))
765 ? ret
766 : 1;
767 if (NULL != work_finished)
768 {
769 (void) GNUNET_CONTAINER_multihashmap_iterate (work_finished,
770 &free_item,
771 NULL);
772 GNUNET_CONTAINER_multihashmap_destroy (work_finished);
773 }
774 while (NULL != (wi = work_head))
775 {
776 GNUNET_CONTAINER_DLL_remove (work_head, work_tail, wi);
777 GNUNET_free (wi->filename);
778 GNUNET_free (wi);
779 }
780 GNUNET_SIGNAL_handler_uninstall (shc_chld);
781 shc_chld = NULL;
782 GNUNET_DISK_pipe_close (sigpipe);
783 sigpipe = NULL;
784 GNUNET_free (cfg_filename);
785 cfg_filename = NULL;
786 GNUNET_free_nz ((void *) argv);
787 return ok;
788}
789
790
791/* end of gnunet-auto-share.c */
diff --git a/src/cli/fs/gnunet-directory.c b/src/cli/fs/gnunet-directory.c
new file mode 100644
index 000000000..ab9f2905a
--- /dev/null
+++ b/src/cli/fs/gnunet-directory.c
@@ -0,0 +1,212 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-directory.c
22 * @brief display content of GNUnet directories
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26
27#include "gnunet_fs_service.h"
28
29static int ret;
30
31/**
32 * Print a meta data entry.
33 *
34 * @param cls closure (unused)
35 * @param plugin_name name of the plugin that generated the meta data
36 * @param type type of the keyword
37 * @param format format of data
38 * @param data_mime_type mime type of data
39 * @param data value of the meta data
40 * @param data_size number of bytes in @a data
41 * @return always 0 (to continue iterating)
42 */
43static int
44item_printer (void *cls,
45 const char *plugin_name,
46 enum EXTRACTOR_MetaType type,
47 enum EXTRACTOR_MetaFormat format,
48 const char *data_mime_type,
49 const char *data,
50 size_t data_size)
51{
52 if (type == EXTRACTOR_METATYPE_GNUNET_FULL_DATA)
53 {
54 printf (_ ("\t<original file embedded in %u bytes of meta data>\n"),
55 (unsigned int) data_size);
56 return 0;
57 }
58 if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
59 (format != EXTRACTOR_METAFORMAT_C_STRING))
60 return 0;
61 if (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
62 return 0;
63#if HAVE_LIBEXTRACTOR
64 printf ("\t%20s: %s\n",
65 dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
66 EXTRACTOR_metatype_to_string (type)),
67 data);
68#else
69 printf ("\t%20d: %s\n", type, data);
70#endif
71 return 0;
72}
73
74
75/**
76 * Print an entry in a directory.
77 *
78 * @param cls closure (not used)
79 * @param filename name of the file in the directory
80 * @param uri URI of the file
81 * @param meta metadata for the file; metadata for
82 * the directory if everything else is NULL/zero
83 * @param length length of the available data for the file
84 * (of type size_t since data must certainly fit
85 * into memory; if files are larger than size_t
86 * permits, then they will certainly not be
87 * embedded with the directory itself).
88 * @param data data available for the file (length bytes)
89 */
90static void
91print_entry (void *cls,
92 const char *filename,
93 const struct GNUNET_FS_Uri *uri,
94 const struct GNUNET_FS_MetaData *meta,
95 size_t length,
96 const void *data)
97{
98 char *string;
99 char *name;
100
101 name = GNUNET_FS_meta_data_get_by_type (
102 meta,
103 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
104 if (uri == NULL)
105 {
106 printf (_ ("Directory `%s' meta data:\n"), name ? name : "");
107 GNUNET_FS_meta_data_iterate (meta, &item_printer, NULL);
108 printf ("\n");
109 printf (_ ("Directory `%s' contents:\n"), name ? name : "");
110 GNUNET_free (name);
111 return;
112 }
113 string = GNUNET_FS_uri_to_string (uri);
114 printf ("%s (%s):\n", name ? name : "", string);
115 GNUNET_free (string);
116 GNUNET_FS_meta_data_iterate (meta, &item_printer, NULL);
117 printf ("\n");
118 GNUNET_free (name);
119}
120
121
122/**
123 * Main function that will be run by the scheduler.
124 *
125 * @param cls closure
126 * @param args remaining command-line arguments
127 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
128 * @param cfg configuration
129 */
130static void
131run (void *cls,
132 char *const *args,
133 const char *cfgfile,
134 const struct GNUNET_CONFIGURATION_Handle *cfg)
135{
136 struct GNUNET_DISK_MapHandle *map;
137 struct GNUNET_DISK_FileHandle *h;
138 void *data;
139 size_t len;
140 uint64_t size;
141 const char *filename;
142 int i;
143
144 if (NULL == args[0])
145 {
146 fprintf (stderr, "%s", _ ("You must specify a filename to inspect.\n"));
147 ret = 1;
148 return;
149 }
150 i = 0;
151 while (NULL != (filename = args[i++]))
152 {
153 if ((GNUNET_OK !=
154 GNUNET_DISK_file_size (filename, &size, GNUNET_YES, GNUNET_YES)) ||
155 (NULL == (h = GNUNET_DISK_file_open (filename,
156 GNUNET_DISK_OPEN_READ,
157 GNUNET_DISK_PERM_NONE))))
158 {
159 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
160 _ ("Failed to read directory `%s'\n"),
161 filename);
162 ret = 1;
163 continue;
164 }
165 len = (size_t) size;
166 data = GNUNET_DISK_file_map (h, &map, GNUNET_DISK_MAP_TYPE_READ, len);
167 GNUNET_assert (NULL != data);
168 if (GNUNET_OK !=
169 GNUNET_FS_directory_list_contents (len, data, 0, &print_entry, NULL))
170 fprintf (stdout, _ ("`%s' is not a GNUnet directory\n"), filename);
171 else
172 printf ("\n");
173 GNUNET_DISK_file_unmap (map);
174 GNUNET_DISK_file_close (h);
175 }
176}
177
178
179/**
180 * The main function to inspect GNUnet directories.
181 *
182 * @param argc number of arguments from the command line
183 * @param argv command line arguments
184 * @return 0 ok, 1 on error
185 */
186int
187main (int argc, char *const *argv)
188{
189 static struct GNUNET_GETOPT_CommandLineOption options[] = {
190 GNUNET_GETOPT_OPTION_END
191 };
192
193 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
194 return 2;
195
196 ret = (GNUNET_OK ==
197 GNUNET_PROGRAM_run (argc,
198 argv,
199 "gnunet-directory [OPTIONS] FILENAME",
200 gettext_noop (
201 "Display contents of a GNUnet directory"),
202 options,
203 &run,
204 NULL))
205 ? ret
206 : 1;
207 GNUNET_free_nz ((void *) argv);
208 return ret;
209}
210
211
212/* end of gnunet-directory.c */
diff --git a/src/cli/fs/gnunet-download.c b/src/cli/fs/gnunet-download.c
new file mode 100644
index 000000000..4694077e9
--- /dev/null
+++ b/src/cli/fs/gnunet-download.c
@@ -0,0 +1,385 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-download.c
22 * @brief downloading for 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
30#include "gnunet_fs_service.h"
31
32static int ret;
33
34static unsigned int verbose;
35
36static int delete_incomplete;
37
38static const struct GNUNET_CONFIGURATION_Handle *cfg;
39
40static struct GNUNET_FS_Handle *ctx;
41
42static struct GNUNET_FS_DownloadContext *dc;
43
44static unsigned int anonymity = 1;
45
46static unsigned int parallelism = 16;
47
48static unsigned int request_parallelism = 4092;
49
50static int do_recursive;
51
52static char *filename;
53
54static int local_only;
55
56
57static void
58cleanup_task (void *cls)
59{
60 GNUNET_FS_stop (ctx);
61 ctx = NULL;
62}
63
64
65static void
66shutdown_task (void *cls)
67{
68 if (NULL != dc)
69 {
70 GNUNET_FS_download_stop (dc, delete_incomplete);
71 dc = NULL;
72 }
73}
74
75
76/**
77 * Display progress bar (if tty).
78 *
79 * @param x current position in the download
80 * @param n total size of the download
81 * @param w desired number of steps in the progress bar
82 */
83static void
84display_bar (unsigned long long x, unsigned long long n, unsigned int w)
85{
86 char buf[w + 20];
87 unsigned int p;
88 unsigned int endeq;
89 float ratio_complete;
90
91 if (0 == isatty (1))
92 return;
93 ratio_complete = x / (float) n;
94 endeq = ratio_complete * w;
95 GNUNET_snprintf (buf, sizeof(buf), "%3d%% [", (int) (ratio_complete * 100));
96 for (p = 0; p < endeq; p++)
97 strcat (buf, "=");
98 for (p = endeq; p < w; p++)
99 strcat (buf, " ");
100 strcat (buf, "]\r");
101 printf ("%s", buf);
102 fflush (stdout);
103}
104
105
106/**
107 * Called by FS client to give information about the progress of an
108 * operation.
109 *
110 * @param cls closure
111 * @param info details about the event, specifying the event type
112 * and various bits about the event
113 * @return client-context (for the next progress call
114 * for this operation; should be set to NULL for
115 * SUSPEND and STOPPED events). The value returned
116 * will be passed to future callbacks in the respective
117 * field in the `struct GNUNET_FS_ProgressInfo`
118 */
119static void *
120progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
121{
122 char *s;
123 const char *s2;
124 char *t;
125
126 switch (info->status)
127 {
128 case GNUNET_FS_STATUS_DOWNLOAD_START:
129 if (verbose > 1)
130 fprintf (stderr,
131 _ ("Starting download `%s'.\n"),
132 info->value.download.filename);
133 break;
134
135 case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
136 if (verbose)
137 {
138 s = GNUNET_strdup (
139 GNUNET_STRINGS_relative_time_to_string (info->value.download.eta,
140 GNUNET_YES));
141 if (info->value.download.specifics.progress.block_download_duration
142 .rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
143 s2 = _ ("<unknown time>");
144 else
145 s2 = GNUNET_STRINGS_relative_time_to_string (info->value.download
146 .specifics.progress
147 .block_download_duration,
148 GNUNET_YES);
149 t = GNUNET_STRINGS_byte_size_fancy (
150 info->value.download.completed * 1000LL
151 / (info->value.download.duration.rel_value_us + 1));
152 fprintf (
153 stdout,
154 _ (
155 "Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"),
156 info->value.download.filename,
157 (unsigned long long) info->value.download.completed,
158 (unsigned long long) info->value.download.size,
159 s,
160 t,
161 s2);
162 GNUNET_free (s);
163 GNUNET_free (t);
164 }
165 else
166 {
167 display_bar (info->value.download.completed,
168 info->value.download.size,
169 60);
170 }
171 break;
172
173 case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
174 if (0 != isatty (1))
175 fprintf (stdout, "\n");
176 fprintf (stderr,
177 _ ("Error downloading: %s.\n"),
178 info->value.download.specifics.error.message);
179 GNUNET_SCHEDULER_shutdown ();
180 break;
181
182 case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
183 s = GNUNET_STRINGS_byte_size_fancy (
184 info->value.download.completed * 1000
185 / (info->value.download.duration.rel_value_us + 1));
186 if (0 != isatty (1))
187 fprintf (stdout, "\n");
188 fprintf (stdout,
189 _ ("Downloading `%s' done (%s/s).\n"),
190 info->value.download.filename,
191 s);
192 GNUNET_free (s);
193 if (info->value.download.dc == dc)
194 GNUNET_SCHEDULER_shutdown ();
195 break;
196
197 case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
198 if (info->value.download.dc == dc)
199 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
200 break;
201
202 case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
203 case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
204 break;
205
206 default:
207 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status);
208 break;
209 }
210 return NULL;
211}
212
213
214/**
215 * Main function that will be run by the scheduler.
216 *
217 * @param cls closure
218 * @param args remaining command-line arguments
219 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
220 * @param c configuration
221 */
222static void
223run (void *cls,
224 char *const *args,
225 const char *cfgfile,
226 const struct GNUNET_CONFIGURATION_Handle *c)
227{
228 struct GNUNET_FS_Uri *uri;
229 char *emsg;
230 enum GNUNET_FS_DownloadOptions options;
231
232 if (NULL == args[0])
233 {
234 fprintf (stderr, "%s", _ ("You need to specify a URI argument.\n"));
235 return;
236 }
237 uri = GNUNET_FS_uri_parse (args[0], &emsg);
238 if (NULL == uri)
239 {
240 fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg);
241 GNUNET_free (emsg);
242 ret = 1;
243 return;
244 }
245 if ((! GNUNET_FS_uri_test_chk (uri)) && (! GNUNET_FS_uri_test_loc (uri)))
246 {
247 fprintf (stderr, "%s", _ ("Only CHK or LOC URIs supported.\n"));
248 ret = 1;
249 GNUNET_FS_uri_destroy (uri);
250 return;
251 }
252 if (NULL == filename)
253 {
254 fprintf (stderr, "%s", _ ("Target filename must be specified.\n"));
255 ret = 1;
256 GNUNET_FS_uri_destroy (uri);
257 return;
258 }
259 cfg = c;
260 ctx = GNUNET_FS_start (cfg,
261 "gnunet-download",
262 &progress_cb,
263 NULL,
264 GNUNET_FS_FLAGS_NONE,
265 GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM,
266 parallelism,
267 GNUNET_FS_OPTIONS_REQUEST_PARALLELISM,
268 request_parallelism,
269 GNUNET_FS_OPTIONS_END);
270 if (NULL == ctx)
271 {
272 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS");
273 GNUNET_FS_uri_destroy (uri);
274 ret = 1;
275 return;
276 }
277 options = GNUNET_FS_DOWNLOAD_OPTION_NONE;
278 if (do_recursive)
279 options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
280 if (local_only)
281 options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY;
282 dc = GNUNET_FS_download_start (ctx,
283 uri,
284 NULL,
285 filename,
286 NULL,
287 0,
288 GNUNET_FS_uri_chk_get_file_size (uri),
289 anonymity,
290 options,
291 NULL,
292 NULL);
293 GNUNET_FS_uri_destroy (uri);
294 if (dc == NULL)
295 {
296 GNUNET_FS_stop (ctx);
297 ctx = NULL;
298 return;
299 }
300 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
301}
302
303
304/**
305 * The main function to download GNUnet.
306 *
307 * @param argc number of arguments from the command line
308 * @param argv command line arguments
309 * @return 0 ok, 1 on error
310 */
311int
312main (int argc, char *const *argv)
313{
314 struct GNUNET_GETOPT_CommandLineOption options[] =
315 { GNUNET_GETOPT_option_uint ('a',
316 "anonymity",
317 "LEVEL",
318 gettext_noop (
319 "set the desired LEVEL of receiver-anonymity"),
320 &anonymity),
321
322 GNUNET_GETOPT_option_flag (
323 'D',
324 "delete-incomplete",
325 gettext_noop ("delete incomplete downloads (when aborted with CTRL-C)"),
326 &delete_incomplete),
327
328 GNUNET_GETOPT_option_flag (
329 'n',
330 "no-network",
331 gettext_noop ("only search the local peer (no P2P network search)"),
332 &local_only),
333 GNUNET_GETOPT_option_string ('o',
334 "output",
335 "FILENAME",
336 gettext_noop ("write the file to FILENAME"),
337 &filename),
338 GNUNET_GETOPT_option_uint (
339 'p',
340 "parallelism",
341 "DOWNLOADS",
342 gettext_noop (
343 "set the maximum number of parallel downloads that is allowed"),
344 &parallelism),
345 GNUNET_GETOPT_option_uint (
346 'r',
347 "request-parallelism",
348 "REQUESTS",
349 gettext_noop (
350 "set the maximum number of parallel requests for blocks that is allowed"),
351 &request_parallelism),
352 GNUNET_GETOPT_option_flag ('R',
353 "recursive",
354 gettext_noop (
355 "download a GNUnet directory recursively"),
356 &do_recursive),
357 GNUNET_GETOPT_option_increment_uint (
358 'V',
359 "verbose",
360 gettext_noop ("be verbose (print progress information)"),
361 &verbose),
362 GNUNET_GETOPT_OPTION_END };
363
364 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
365 return 2;
366
367 ret =
368 (GNUNET_OK ==
369 GNUNET_PROGRAM_run (
370 argc,
371 argv,
372 "gnunet-download [OPTIONS] URI",
373 gettext_noop (
374 "Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"),
375 options,
376 &run,
377 NULL))
378 ? ret
379 : 1;
380 GNUNET_free_nz ((void *) argv);
381 return ret;
382}
383
384
385/* end of gnunet-download.c */
diff --git a/src/cli/fs/gnunet-fs.c b/src/cli/fs/gnunet-fs.c
new file mode 100644
index 000000000..21e3c4a40
--- /dev/null
+++ b/src/cli/fs/gnunet-fs.c
@@ -0,0 +1,191 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2011 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-fs.c
22 * @brief special file-sharing functions
23 * @author Christian Grothoff
24 */
25#include "platform.h"
26
27#include "gnunet_fs_service.h"
28
29/**
30 * Return value.
31 */
32static int ret;
33
34/**
35 * Handle to FS service.
36 */
37static struct GNUNET_FS_Handle *fs;
38
39/**
40 * Handle for the index listing operation.
41 */
42static struct GNUNET_FS_GetIndexedContext *gic;
43
44/**
45 * Option -i given?
46 */
47static int list_indexed_files;
48
49/**
50 * Option -v given?
51 */
52static unsigned int verbose;
53
54
55/**
56 * Print indexed filenames to stdout.
57 *
58 * @param cls closure
59 * @param filename the name of the file
60 * @param file_id hash of the contents of the indexed file
61 * @return #GNUNET_OK to continue iteration
62 */
63static enum GNUNET_GenericReturnValue
64print_indexed (void *cls,
65 const char *filename,
66 const struct GNUNET_HashCode *file_id)
67{
68 if (NULL == filename)
69 {
70 gic = NULL;
71 GNUNET_SCHEDULER_shutdown ();
72 return GNUNET_OK;
73 }
74 if (verbose)
75 fprintf (stdout,
76 "%s: %s\n",
77 GNUNET_h2s (file_id),
78 filename);
79 else
80 fprintf (stdout,
81 "%s\n",
82 filename);
83 return GNUNET_OK;
84}
85
86
87/**
88 * Function run on shutdown.
89 *
90 * @param cls NULL
91 */
92static void
93do_shutdown (void *cls)
94{
95 (void) cls;
96 if (NULL != gic)
97 {
98 GNUNET_FS_get_indexed_files_cancel (gic);
99 gic = NULL;
100 }
101 if (NULL != fs)
102 {
103 GNUNET_FS_stop (fs);
104 fs = NULL;
105 }
106}
107
108
109/**
110 * Main function that will be run by the scheduler.
111 *
112 * @param cls closure
113 * @param args remaining command-line arguments
114 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
115 * @param cfg configuration
116 */
117static void
118run (void *cls,
119 char *const *args,
120 const char *cfgfile,
121 const struct GNUNET_CONFIGURATION_Handle *cfg)
122{
123 if (! list_indexed_files)
124 return;
125 GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
126 NULL);
127 fs = GNUNET_FS_start (cfg,
128 "gnunet-fs",
129 NULL,
130 NULL,
131 GNUNET_FS_FLAGS_NONE,
132 GNUNET_FS_OPTIONS_END);
133 if (NULL == fs)
134 {
135 ret = 1;
136 return;
137 }
138 gic = GNUNET_FS_get_indexed_files (fs,
139 &print_indexed,
140 NULL);
141 if (NULL == gic)
142 {
143 ret = 2;
144 GNUNET_SCHEDULER_shutdown ();
145 return;
146 }
147}
148
149
150/**
151 * The main function to access special file-sharing functions.
152 *
153 * @param argc number of arguments from the command line
154 * @param argv command line arguments
155 * @return 0 ok, 1 on error
156 */
157int
158main (int argc,
159 char *const *argv)
160{
161 struct GNUNET_GETOPT_CommandLineOption options[] = {
162 GNUNET_GETOPT_option_flag ('i',
163 "list-indexed",
164 gettext_noop (
165 "print a list of all indexed files"),
166 &list_indexed_files),
167
168 GNUNET_GETOPT_option_verbose (&verbose),
169 GNUNET_GETOPT_OPTION_END
170 };
171
172 if (GNUNET_OK !=
173 GNUNET_STRINGS_get_utf8_args (argc, argv,
174 &argc, &argv))
175 return 2;
176 ret = (GNUNET_OK ==
177 GNUNET_PROGRAM_run (argc,
178 argv,
179 "gnunet-fs [OPTIONS]",
180 gettext_noop ("Special file-sharing operations"),
181 options,
182 &run,
183 NULL))
184 ? ret
185 : 1;
186 GNUNET_free_nz ((void *) argv);
187 return ret;
188}
189
190
191/* end of gnunet-fs.c */
diff --git a/src/cli/fs/gnunet-publish.c b/src/cli/fs/gnunet-publish.c
new file mode 100644
index 000000000..7a87130de
--- /dev/null
+++ b/src/cli/fs/gnunet-publish.c
@@ -0,0 +1,1009 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001-2013 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
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
30#include "gnunet_fs_service.h"
31#include "gnunet_identity_service.h"
32
33/**
34 * Global return value from #main().
35 */
36static int ret;
37
38/**
39 * Command line option 'verbose' set
40 */
41static unsigned int verbose;
42
43/**
44 * Handle to our configuration.
45 */
46static const struct GNUNET_CONFIGURATION_Handle *cfg;
47
48/**
49 * Handle for interaction with file-sharing service.
50 */
51static struct GNUNET_FS_Handle *ctx;
52
53/**
54 * Handle to FS-publishing operation.
55 */
56static struct GNUNET_FS_PublishContext *pc;
57
58/**
59 * Meta-data provided via command-line option.
60 */
61static struct GNUNET_FS_MetaData *meta;
62
63/**
64 * Keywords provided via command-line option.
65 */
66static struct GNUNET_FS_Uri *topKeywords;
67
68/**
69 * Options we set for published blocks.
70 */
71static struct GNUNET_FS_BlockOptions bo = { { 0LL }, 1, 365, 1 };
72
73/**
74 * Value of URI provided on command-line (when not publishing
75 * a file but just creating UBlocks to refer to an existing URI).
76 */
77static char *uri_string;
78
79/**
80 * Value of URI provided on command-line (when not publishing
81 * a file but just creating UBlocks to refer to an existing URI);
82 * parsed version of 'uri_string'.
83 */
84static struct GNUNET_FS_Uri *uri;
85
86/**
87 * Command-line option for namespace publishing: identifier for updates
88 * to this publication.
89 */
90static char *next_id;
91
92/**
93 * Command-line option for namespace publishing: identifier for this
94 * publication.
95 */
96static char *this_id;
97
98/**
99 * Command-line option identifying the pseudonym to use for the publication.
100 */
101static char *pseudonym;
102
103/**
104 * Command-line option for 'inserting'
105 */
106static int do_insert;
107
108/**
109 * Command-line option to disable meta data extraction.
110 */
111static int disable_extractor;
112
113/**
114 * Command-line option to merely simulate publishing operation.
115 */
116static int do_simulate;
117
118/**
119 * Command-line option to only perform meta data extraction, but not publish.
120 */
121static int extract_only;
122
123/**
124 * Command-line option to disable adding creation time.
125 */
126static int enable_creation_time;
127
128/**
129 * Handle to the directory scanner (for recursive insertions).
130 */
131static struct GNUNET_FS_DirScanner *ds;
132
133/**
134 * Which namespace do we publish to? NULL if we do not publish to
135 * a namespace.
136 */
137static struct GNUNET_IDENTITY_Ego *namespace;
138
139/**
140 * Handle to identity service.
141 */
142static struct GNUNET_IDENTITY_Handle *identity;
143
144
145/**
146 * We are finished with the publishing operation, clean up all
147 * FS state.
148 *
149 * @param cls NULL
150 */
151static void
152do_stop_task (void *cls)
153{
154 struct GNUNET_FS_PublishContext *p;
155
156 if (NULL != ds)
157 {
158 GNUNET_FS_directory_scan_abort (ds);
159 ds = NULL;
160 }
161 if (NULL != identity)
162 {
163 GNUNET_IDENTITY_disconnect (identity);
164 identity = NULL;
165 }
166 if (NULL != pc)
167 {
168 p = pc;
169 pc = NULL;
170 GNUNET_FS_publish_stop (p);
171 }
172 if (NULL != ctx)
173 {
174 GNUNET_FS_stop (ctx);
175 ctx = NULL;
176 }
177 if (NULL != meta)
178 {
179 GNUNET_FS_meta_data_destroy (meta);
180 meta = NULL;
181 }
182 if (NULL != uri)
183 {
184 GNUNET_FS_uri_destroy (uri);
185 uri = NULL;
186 }
187}
188
189
190/**
191 * Called by FS client to give information about the progress of an
192 * operation.
193 *
194 * @param cls closure
195 * @param info details about the event, specifying the event type
196 * and various bits about the event
197 * @return client-context (for the next progress call
198 * for this operation; should be set to NULL for
199 * SUSPEND and STOPPED events). The value returned
200 * will be passed to future callbacks in the respective
201 * field in the GNUNET_FS_ProgressInfo struct.
202 */
203static void *
204progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
205{
206 const char *s;
207 char *suri;
208
209 switch (info->status)
210 {
211 case GNUNET_FS_STATUS_PUBLISH_START:
212 break;
213
214 case GNUNET_FS_STATUS_PUBLISH_PROGRESS:
215 if (verbose)
216 {
217 s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.eta,
218 GNUNET_YES);
219 fprintf (stdout,
220 _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"),
221 info->value.publish.filename,
222 (unsigned long long) info->value.publish.completed,
223 (unsigned long long) info->value.publish.size,
224 s);
225 }
226 break;
227
228 case GNUNET_FS_STATUS_PUBLISH_PROGRESS_DIRECTORY:
229 if (verbose)
230 {
231 s = GNUNET_STRINGS_relative_time_to_string (info->value.publish.specifics
232 .progress_directory.eta,
233 GNUNET_YES);
234 fprintf (stdout,
235 _ ("Publishing `%s' at %llu/%llu (%s remaining)\n"),
236 info->value.publish.filename,
237 (unsigned long long)
238 info->value.publish.specifics.progress_directory.completed,
239 (unsigned long long)
240 info->value.publish.specifics.progress_directory.total,
241 s);
242 }
243 break;
244
245 case GNUNET_FS_STATUS_PUBLISH_ERROR:
246 fprintf (stderr,
247 _ ("Error publishing: %s.\n"),
248 info->value.publish.specifics.error.message);
249 ret = 1;
250 GNUNET_SCHEDULER_shutdown ();
251 break;
252
253 case GNUNET_FS_STATUS_PUBLISH_COMPLETED:
254 fprintf (stdout,
255 _ ("Publishing `%s' done.\n"),
256 info->value.publish.filename);
257 suri =
258 GNUNET_FS_uri_to_string (info->value.publish.specifics.completed.chk_uri);
259 fprintf (stdout, _ ("URI is `%s'.\n"), suri);
260 GNUNET_free (suri);
261 if (NULL != info->value.publish.specifics.completed.sks_uri)
262 {
263 suri = GNUNET_FS_uri_to_string (
264 info->value.publish.specifics.completed.sks_uri);
265 fprintf (stdout, _ ("Namespace URI is `%s'.\n"), suri);
266 GNUNET_free (suri);
267 }
268 if (NULL == info->value.publish.pctx)
269 {
270 ret = 0;
271 GNUNET_SCHEDULER_shutdown ();
272 }
273 break;
274
275 case GNUNET_FS_STATUS_PUBLISH_STOPPED:
276 GNUNET_break (NULL == pc);
277 return NULL;
278
279 case GNUNET_FS_STATUS_UNINDEX_START:
280 fprintf (stderr, "%s", _ ("Starting cleanup after abort\n"));
281 return NULL;
282
283 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
284 return NULL;
285
286 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
287 fprintf (stderr, "%s", _ ("Cleanup after abort completed.\n"));
288 GNUNET_FS_unindex_stop (info->value.unindex.uc);
289 return NULL;
290
291 case GNUNET_FS_STATUS_UNINDEX_ERROR:
292 fprintf (stderr, "%s", _ ("Cleanup after abort failed.\n"));
293 GNUNET_FS_unindex_stop (info->value.unindex.uc);
294 return NULL;
295
296 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
297 return NULL;
298
299 default:
300 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status);
301 return NULL;
302 }
303 return ""; /* non-null */
304}
305
306
307/**
308 * Print metadata entries (except binary
309 * metadata and the filename).
310 *
311 * @param cls closure
312 * @param plugin_name name of the plugin that generated the meta data
313 * @param type type of the meta data
314 * @param format format of data
315 * @param data_mime_type mime type of @a data
316 * @param data value of the meta data
317 * @param data_size number of bytes in @a data
318 * @return always 0
319 */
320static int
321meta_printer (void *cls,
322 const char *plugin_name,
323 enum EXTRACTOR_MetaType type,
324 enum EXTRACTOR_MetaFormat format,
325 const char *data_mime_type,
326 const char *data,
327 size_t data_size)
328{
329 if ((EXTRACTOR_METAFORMAT_UTF8 != format) &&
330 (EXTRACTOR_METAFORMAT_C_STRING != format))
331 return 0;
332 if (EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME == type)
333 return 0;
334#if HAVE_LIBEXTRACTOR
335 fprintf (stdout, "\t%s - %s\n", EXTRACTOR_metatype_to_string (type), data);
336#else
337 fprintf (stdout, "\t%d - %s\n", type, data);
338#endif
339 return 0;
340}
341
342
343/**
344 * Iterator printing keywords
345 *
346 * @param cls closure
347 * @param keyword the keyword
348 * @param is_mandatory is the keyword mandatory (in a search)
349 * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to abort
350 */
351static int
352keyword_printer (void *cls, const char *keyword, int is_mandatory)
353{
354 fprintf (stdout, "\t%s\n", keyword);
355 return GNUNET_OK;
356}
357
358
359/**
360 * Function called on all entries before the publication. This is
361 * where we perform modifications to the default based on command-line
362 * options.
363 *
364 * @param cls closure
365 * @param fi the entry in the publish-structure
366 * @param length length of the file or directory
367 * @param m metadata for the file or directory (can be modified)
368 * @param uri pointer to the keywords that will be used for this entry (can be modified)
369 * @param bo block options
370 * @param do_index should we index?
371 * @param client_info pointer to client context set upon creation (can be modified)
372 * @return #GNUNET_OK to continue, #GNUNET_NO to remove
373 * this entry from the directory, #GNUNET_SYSERR
374 * to abort the iteration
375 */
376static int
377publish_inspector (void *cls,
378 struct GNUNET_FS_FileInformation *fi,
379 uint64_t length,
380 struct GNUNET_FS_MetaData *m,
381 struct GNUNET_FS_Uri **uri,
382 struct GNUNET_FS_BlockOptions *bo,
383 int *do_index,
384 void **client_info)
385{
386 char *fn;
387 char *fs;
388 struct GNUNET_FS_Uri *new_uri;
389
390 if (cls == fi)
391 return GNUNET_OK;
392 if ((disable_extractor) && (NULL != *uri))
393 {
394 GNUNET_FS_uri_destroy (*uri);
395 *uri = NULL;
396 }
397 if (NULL != topKeywords)
398 {
399 if (NULL != *uri)
400 {
401 new_uri = GNUNET_FS_uri_ksk_merge (topKeywords, *uri);
402 GNUNET_FS_uri_destroy (*uri);
403 *uri = new_uri;
404 GNUNET_FS_uri_destroy (topKeywords);
405 }
406 else
407 {
408 *uri = topKeywords;
409 }
410 topKeywords = NULL;
411 }
412 if (NULL != meta)
413 {
414 GNUNET_FS_meta_data_merge (m, meta);
415 GNUNET_FS_meta_data_destroy (meta);
416 meta = NULL;
417 }
418 if (enable_creation_time)
419 GNUNET_FS_meta_data_add_publication_date (m);
420 if (extract_only)
421 {
422 fn = GNUNET_FS_meta_data_get_by_type (
423 m,
424 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
425 fs = GNUNET_STRINGS_byte_size_fancy (length);
426 fprintf (stdout, _ ("Meta data for file `%s' (%s)\n"), fn, fs);
427 GNUNET_FS_meta_data_iterate (m, &meta_printer, NULL);
428 fprintf (stdout, _ ("Keywords for file `%s' (%s)\n"), fn, fs);
429 GNUNET_free (fn);
430 GNUNET_free (fs);
431 if (NULL != *uri)
432 GNUNET_FS_uri_ksk_get_keywords (*uri, &keyword_printer, NULL);
433 fprintf (stdout, "%s", "\n");
434 }
435 if (GNUNET_YES == GNUNET_FS_meta_data_test_for_directory (m))
436 GNUNET_FS_file_information_inspect (fi, &publish_inspector, fi);
437 return GNUNET_OK;
438}
439
440
441/**
442 * Function called upon completion of the publishing
443 * of the UBLOCK for the SKS URI. As this is the last
444 * step, stop our interaction with FS (clean up).
445 *
446 * @param cls NULL (closure)
447 * @param sks_uri URI for the block that was published
448 * @param emsg error message, NULL on success
449 */
450static void
451uri_sks_continuation (void *cls,
452 const struct GNUNET_FS_Uri *sks_uri,
453 const char *emsg)
454{
455 if (NULL != emsg)
456 {
457 fprintf (stderr, "%s\n", emsg);
458 ret = 1;
459 }
460 GNUNET_SCHEDULER_shutdown ();
461}
462
463
464/**
465 * Function called upon completion of the publishing
466 * of the UBLOCK for the KSK URI. Continue with
467 * publishing the SKS URI (if applicable) or clean up.
468 *
469 * @param cls NULL (closure)
470 * @param ksk_uri URI for the block that was published
471 * @param emsg error message, NULL on success
472 */
473static void
474uri_ksk_continuation (void *cls,
475 const struct GNUNET_FS_Uri *ksk_uri,
476 const char *emsg)
477{
478 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
479 const struct GNUNET_CRYPTO_PrivateKey *pk;
480
481 if (NULL != emsg)
482 {
483 fprintf (stderr, "%s\n", emsg);
484 ret = 1;
485 }
486 if (NULL == namespace)
487 {
488 GNUNET_SCHEDULER_shutdown ();
489 return;
490 }
491 pk = GNUNET_IDENTITY_ego_get_private_key (namespace);
492 if (GNUNET_PUBLIC_KEY_TYPE_ECDSA != ntohl (pk->type))
493 return;
494 priv = &pk->ecdsa_key;
495 GNUNET_FS_publish_sks (ctx,
496 priv,
497 this_id,
498 next_id,
499 meta,
500 uri,
501 &bo,
502 GNUNET_FS_PUBLISH_OPTION_NONE,
503 &uri_sks_continuation,
504 NULL);
505}
506
507
508/**
509 * Iterate over the results from the directory scan and extract
510 * the desired information for the publishing operation.
511 *
512 * @param item root with the data from the directory scan
513 * @return handle with the information for the publishing operation
514 */
515static struct GNUNET_FS_FileInformation *
516get_file_information (struct GNUNET_FS_ShareTreeItem *item)
517{
518 struct GNUNET_FS_FileInformation *fi;
519 struct GNUNET_FS_FileInformation *fic;
520 struct GNUNET_FS_ShareTreeItem *child;
521
522 if (GNUNET_YES == item->is_directory)
523 {
524 if (NULL == item->meta)
525 item->meta = GNUNET_FS_meta_data_create ();
526 GNUNET_FS_meta_data_delete (item->meta,
527 EXTRACTOR_METATYPE_MIMETYPE,
528 NULL,
529 0);
530 GNUNET_FS_meta_data_make_directory (item->meta);
531 if (NULL == item->ksk_uri)
532 {
533 const char *mime = GNUNET_FS_DIRECTORY_MIME;
534 item->ksk_uri = GNUNET_FS_uri_ksk_create_from_args (1, &mime);
535 }
536 else
537 GNUNET_FS_uri_ksk_add_keyword (item->ksk_uri,
538 GNUNET_FS_DIRECTORY_MIME,
539 GNUNET_NO);
540 fi = GNUNET_FS_file_information_create_empty_directory (ctx,
541 NULL,
542 item->ksk_uri,
543 item->meta,
544 &bo,
545 item->filename);
546 for (child = item->children_head; child; child = child->next)
547 {
548 fic = get_file_information (child);
549 GNUNET_break (GNUNET_OK == GNUNET_FS_file_information_add (fi, fic));
550 }
551 }
552 else
553 {
554 fi = GNUNET_FS_file_information_create_from_file (ctx,
555 NULL,
556 item->filename,
557 item->ksk_uri,
558 item->meta,
559 ! do_insert,
560 &bo);
561 }
562 return fi;
563}
564
565
566/**
567 * We've finished scanning the directory and optimized the meta data.
568 * Begin the publication process.
569 *
570 * @param directory_scan_result result from the directory scan, freed in this function
571 */
572static void
573directory_trim_complete (struct GNUNET_FS_ShareTreeItem *directory_scan_result)
574{
575 struct GNUNET_FS_FileInformation *fi;
576 const struct GNUNET_CRYPTO_EcdsaPrivateKey *priv;
577 const struct GNUNET_CRYPTO_PrivateKey *pk;
578
579 fi = get_file_information (directory_scan_result);
580 GNUNET_FS_share_tree_free (directory_scan_result);
581 if (NULL == fi)
582 {
583 fprintf (stderr, "%s", _ ("Could not publish\n"));
584 ret = 1;
585 GNUNET_SCHEDULER_shutdown ();
586 return;
587 }
588 GNUNET_FS_file_information_inspect (fi, &publish_inspector, NULL);
589 if (extract_only)
590 {
591 GNUNET_FS_file_information_destroy (fi, NULL, NULL);
592 GNUNET_SCHEDULER_shutdown ();
593 return;
594 }
595 priv = NULL;
596 if (NULL != namespace)
597 {
598 pk = GNUNET_IDENTITY_ego_get_private_key (namespace);
599 GNUNET_assert (GNUNET_PUBLIC_KEY_TYPE_ECDSA == ntohl (pk->type));
600 priv = &pk->ecdsa_key;
601 }
602 pc = GNUNET_FS_publish_start (ctx,
603 fi,
604 priv,
605 this_id,
606 next_id,
607 (do_simulate)
608 ? GNUNET_FS_PUBLISH_OPTION_SIMULATE_ONLY
609 : GNUNET_FS_PUBLISH_OPTION_NONE);
610 if (NULL == pc)
611 {
612 fprintf (stderr, "%s", _ ("Could not start publishing.\n"));
613 ret = 1;
614 GNUNET_SCHEDULER_shutdown ();
615 return;
616 }
617}
618
619
620/**
621 * Function called by the directory scanner as we build the tree
622 * that we will need to publish later.
623 *
624 * @param cls closure
625 * @param filename which file we are making progress on
626 * @param is_directory #GNUNET_YES if this is a directory,
627 * #GNUNET_NO if this is a file
628 * #GNUNET_SYSERR if it is neither (or unknown)
629 * @param reason kind of progress we are making
630 */
631static void
632directory_scan_cb (void *cls,
633 const char *filename,
634 int is_directory,
635 enum GNUNET_FS_DirScannerProgressUpdateReason reason)
636{
637 struct GNUNET_FS_ShareTreeItem *directory_scan_result;
638
639 switch (reason)
640 {
641 case GNUNET_FS_DIRSCANNER_FILE_START:
642 if (verbose > 1)
643 {
644 if (is_directory == GNUNET_YES)
645 fprintf (stdout, _ ("Scanning directory `%s'.\n"), filename);
646 else
647 fprintf (stdout, _ ("Scanning file `%s'.\n"), filename);
648 }
649 break;
650
651 case GNUNET_FS_DIRSCANNER_FILE_IGNORED:
652 fprintf (stderr,
653 _ ("There was trouble processing file `%s', skipping it.\n"),
654 filename);
655 break;
656
657 case GNUNET_FS_DIRSCANNER_ALL_COUNTED:
658 if (verbose)
659 fprintf (stdout, "%s", _ ("Preprocessing complete.\n"));
660 break;
661
662 case GNUNET_FS_DIRSCANNER_EXTRACT_FINISHED:
663 if (verbose > 2)
664 fprintf (stdout,
665 _ ("Extracting meta data from file `%s' complete.\n"),
666 filename);
667 break;
668
669 case GNUNET_FS_DIRSCANNER_FINISHED:
670 if (verbose > 1)
671 fprintf (stdout, "%s", _ ("Meta data extraction has finished.\n"));
672 directory_scan_result = GNUNET_FS_directory_scan_get_result (ds);
673 ds = NULL;
674 GNUNET_FS_share_tree_trim (directory_scan_result);
675 directory_trim_complete (directory_scan_result);
676 break;
677
678 case GNUNET_FS_DIRSCANNER_INTERNAL_ERROR:
679 fprintf (stdout, "%s", _ ("Error scanning directory.\n"));
680 ret = 1;
681 GNUNET_SCHEDULER_shutdown ();
682 break;
683
684 default:
685 GNUNET_assert (0);
686 break;
687 }
688 fflush (stdout);
689}
690
691
692/**
693 * Continuation proceeding with initialization after identity subsystem
694 * has been initialized.
695 *
696 * @param args0 filename to publish
697 */
698static void
699identity_continuation (const char *args0)
700{
701 char *ex;
702 char *emsg;
703
704 if ((NULL != pseudonym) && (NULL == namespace))
705 {
706 fprintf (stderr, _ ("Selected pseudonym `%s' unknown\n"), pseudonym);
707 ret = 1;
708 GNUNET_SCHEDULER_shutdown ();
709 return;
710 }
711 if (NULL != uri_string)
712 {
713 emsg = NULL;
714 if (NULL == (uri = GNUNET_FS_uri_parse (uri_string, &emsg)))
715 {
716 fprintf (stderr, _ ("Failed to parse URI: %s\n"), emsg);
717 GNUNET_free (emsg);
718 ret = 1;
719 GNUNET_SCHEDULER_shutdown ();
720 return;
721 }
722 GNUNET_FS_publish_ksk (ctx,
723 topKeywords,
724 meta,
725 uri,
726 &bo,
727 GNUNET_FS_PUBLISH_OPTION_NONE,
728 &uri_ksk_continuation,
729 NULL);
730 return;
731 }
732 if (GNUNET_OK !=
733 GNUNET_CONFIGURATION_get_value_string (cfg, "FS", "EXTRACTORS", &ex))
734 ex = NULL;
735 if (0 != access (args0, R_OK))
736 {
737 fprintf (stderr,
738 _ ("Failed to access `%s': %s\n"),
739 args0,
740 strerror (errno));
741 GNUNET_free (ex);
742 return;
743 }
744 ds = GNUNET_FS_directory_scan_start (args0,
745 disable_extractor,
746 ex,
747 &directory_scan_cb,
748 NULL);
749 if (NULL == ds)
750 {
751 fprintf (
752 stderr,
753 "%s",
754 _ (
755 "Failed to start meta directory scanner. Is gnunet-helper-publish-fs installed?\n"));
756 GNUNET_free (ex);
757 return;
758 }
759 GNUNET_free (ex);
760}
761
762
763/**
764 * Function called by identity service with known pseudonyms.
765 *
766 * @param cls closure with 'const char *' of filename to publish
767 * @param ego ego handle
768 * @param ctx context for application to store data for this ego
769 * (during the lifetime of this process, initially NULL)
770 * @param name name assigned by the user for this ego,
771 * NULL if the user just deleted the ego and it
772 * must thus no longer be used
773 */
774static void
775identity_cb (void *cls,
776 struct GNUNET_IDENTITY_Ego *ego,
777 void **ctx,
778 const char *name)
779{
780 const char *args0 = cls;
781
782 if (NULL == ego)
783 {
784 identity_continuation (args0);
785 return;
786 }
787 if (NULL == name)
788 return;
789 if (0 == strcmp (name, pseudonym))
790 namespace = ego;
791}
792
793
794/**
795 * Main function that will be run by the scheduler.
796 *
797 * @param cls closure
798 * @param args remaining command-line arguments
799 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
800 * @param c configuration
801 */
802static void
803run (void *cls,
804 char *const *args,
805 const char *cfgfile,
806 const struct GNUNET_CONFIGURATION_Handle *c)
807{
808 /* check arguments */
809 if ((NULL != uri_string) && (extract_only))
810 {
811 printf (_ ("Cannot extract metadata from a URI!\n"));
812 ret = -1;
813 return;
814 }
815 if (((NULL == uri_string) || (extract_only)) &&
816 ((NULL == args[0]) || (NULL != args[1])))
817 {
818 printf (_ ("You must specify one and only one filename for insertion.\n"));
819 ret = -1;
820 return;
821 }
822 if ((NULL != uri_string) && (NULL != args[0]))
823 {
824 printf (_ ("You must NOT specify an URI and a filename.\n"));
825 ret = -1;
826 return;
827 }
828 if (NULL != pseudonym)
829 {
830 if (NULL == this_id)
831 {
832 fprintf (stderr,
833 _ ("Option `%s' is required when using option `%s'.\n"),
834 "-t",
835 "-P");
836 ret = -1;
837 return;
838 }
839 }
840 else
841 { /* ordinary insertion checks */
842 if (NULL != next_id)
843 {
844 fprintf (stderr,
845 _ ("Option `%s' makes no sense without option `%s'.\n"),
846 "-N",
847 "-P");
848 ret = -1;
849 return;
850 }
851 if (NULL != this_id)
852 {
853 fprintf (stderr,
854 _ ("Option `%s' makes no sense without option `%s'.\n"),
855 "-t",
856 "-P");
857 ret = -1;
858 return;
859 }
860 }
861 cfg = c;
862 ctx = GNUNET_FS_start (cfg,
863 "gnunet-publish",
864 &progress_cb,
865 NULL,
866 GNUNET_FS_FLAGS_NONE,
867 GNUNET_FS_OPTIONS_END);
868 if (NULL == ctx)
869 {
870 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS");
871 ret = 1;
872 return;
873 }
874 GNUNET_SCHEDULER_add_shutdown (&do_stop_task, NULL);
875 if (NULL != pseudonym)
876 identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, args[0]);
877 else
878 identity_continuation (args[0]);
879}
880
881
882/**
883 * The main function to publish content to GNUnet.
884 *
885 * @param argc number of arguments from the command line
886 * @param argv command line arguments
887 * @return 0 ok, 1 on error
888 */
889int
890main (int argc, char *const *argv)
891{
892 struct GNUNET_GETOPT_CommandLineOption options[] =
893 { GNUNET_GETOPT_option_uint ('a',
894 "anonymity",
895 "LEVEL",
896 gettext_noop (
897 "set the desired LEVEL of sender-anonymity"),
898 &bo.anonymity_level),
899 GNUNET_GETOPT_option_flag (
900 'D',
901 "disable-extractor",
902 gettext_noop ("do not use libextractor to add keywords or metadata"),
903 &disable_extractor),
904 GNUNET_GETOPT_option_flag ('E',
905 "enable-creation-time",
906 gettext_noop (
907 "enable adding the creation time to the "
908 "metadata of the uploaded file"),
909 &enable_creation_time),
910 GNUNET_GETOPT_option_flag ('e',
911 "extract",
912 gettext_noop (
913 "print list of extracted keywords that would "
914 "be used, but do not perform upload"),
915 &extract_only),
916 GNUNET_FS_GETOPT_KEYWORDS (
917 'k',
918 "key",
919 "KEYWORD",
920 gettext_noop (
921 "add an additional keyword for the top-level "
922 "file or directory (this option can be specified multiple times)"),
923 &topKeywords),
924 GNUNET_FS_GETOPT_METADATA (
925 'm',
926 "meta",
927 "TYPE:VALUE",
928 gettext_noop ("set the meta-data for the given TYPE to the given VALUE"),
929 &meta),
930 GNUNET_GETOPT_option_flag (
931 'n',
932 "noindex",
933 gettext_noop ("do not index, perform full insertion (stores "
934 "entire file in encrypted form in GNUnet database)"),
935 &do_insert),
936 GNUNET_GETOPT_option_string (
937 'N',
938 "next",
939 "ID",
940 gettext_noop ("specify ID of an updated version to be "
941 "published in the future (for namespace insertions only)"),
942 &next_id),
943 GNUNET_GETOPT_option_uint ('p',
944 "priority",
945 "PRIORITY",
946 gettext_noop (
947 "specify the priority of the content"),
948 &bo.content_priority),
949 GNUNET_GETOPT_option_string ('P',
950 "pseudonym",
951 "NAME",
952 gettext_noop (
953 "publish the files under the pseudonym "
954 "NAME (place file into namespace)"),
955 &pseudonym),
956 GNUNET_GETOPT_option_uint ('r',
957 "replication",
958 "LEVEL",
959 gettext_noop (
960 "set the desired replication LEVEL"),
961 &bo.replication_level),
962 GNUNET_GETOPT_option_flag ('s',
963 "simulate-only",
964 gettext_noop (
965 "only simulate the process but do not do "
966 "any actual publishing (useful to compute URIs)"),
967 &do_simulate),
968 GNUNET_GETOPT_option_string ('t',
969 "this",
970 "ID",
971 gettext_noop (
972 "set the ID of this version of the publication "
973 "(for namespace insertions only)"),
974 &this_id),
975 GNUNET_GETOPT_option_string (
976 'u',
977 "uri",
978 "URI",
979 gettext_noop (
980 "URI to be published (can be used instead of passing a "
981 "file to add keywords to the file with the respective URI)"),
982 &uri_string),
983
984 GNUNET_GETOPT_option_verbose (&verbose),
985
986 GNUNET_GETOPT_OPTION_END };
987
988 bo.expiration_time =
989 GNUNET_TIME_year_to_time (GNUNET_TIME_get_current_year () + 2);
990
991 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
992 return 2;
993 ret =
994 (GNUNET_OK ==
995 GNUNET_PROGRAM_run (argc,
996 argv,
997 "gnunet-publish [OPTIONS] FILENAME",
998 gettext_noop ("Publish a file or directory on GNUnet"),
999 options,
1000 &run,
1001 NULL))
1002 ? ret
1003 : 1;
1004 GNUNET_free_nz ((void *) argv);
1005 return ret;
1006}
1007
1008
1009/* end of gnunet-publish.c */
diff --git a/src/cli/fs/gnunet-search.c b/src/cli/fs/gnunet-search.c
new file mode 100644
index 000000000..a72cf97cc
--- /dev/null
+++ b/src/cli/fs/gnunet-search.c
@@ -0,0 +1,801 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009, 2022 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-search.c
22 * @brief searching for files on GNUnet
23 * @author Christian Grothoff
24 * @author Krista Bennett
25 * @author James Blackwell
26 * @author Igor Wronsky
27 * @author madmurphy
28 */
29#include "platform.h"
30#include <ctype.h>
31#include <inttypes.h>
32#include <limits.h>
33
34#include "gnunet_fs_service.h"
35
36
37#define GNUNET_SEARCH_log(kind, ...) \
38 GNUNET_log_from (kind, "gnunet-search", __VA_ARGS__)
39
40
41/* The default settings that we use for the printed output */
42
43#define DEFAULT_DIR_FORMAT "#%n:\ngnunet-download -o \"%f\" -R %u\n\n"
44#define HELP_DEFAULT_DIR_FORMAT "#%n:\\ngnunet-download -o \"%f\" -R %u\\n\\n"
45#define DEFAULT_FILE_FORMAT "#%n:\ngnunet-download -o \"%f\" %u\n\n"
46#define HELP_DEFAULT_FILE_FORMAT "#%n:\\ngnunet-download -o \"%f\" %u\\n\\n"
47#define VERB_DEFAULT_DIR_FORMAT DEFAULT_DIR_FORMAT "%a\n"
48#define VERB_DEFAULT_FILE_FORMAT DEFAULT_FILE_FORMAT "%a\n"
49
50#if HAVE_LIBEXTRACTOR
51#define DEFAULT_META_FORMAT " %t: %p\n"
52#define HELP_DEFAULT_META_FORMAT " %t: %p\\n"
53#define HELP_EXTRACTOR_TEXTADD ", %t"
54#else
55#define DEFAULT_META_FORMAT " MetaType #%i: %p\n"
56#define HELP_DEFAULT_META_FORMAT " MetaType #%i: %p\\n"
57#define HELP_EXTRACTOR_TEXTADD ""
58#endif
59
60#define GENERIC_DIRECTORY_NAME "collection"
61#define GENERIC_FILE_NAME "no-name"
62#define GENERIC_FILE_MIMETYPE "application/octet-stream"
63
64
65enum GNUNET_SEARCH_MetadataPrinterFlags
66{
67 METADATA_PRINTER_FLAG_NONE = 0,
68 METADATA_PRINTER_FLAG_ONE_RUN = 1,
69 METADATA_PRINTER_FLAG_HAVE_TYPE = 2
70};
71
72
73struct GNUNET_SEARCH_MetadataPrinterInfo
74{
75 unsigned int counter;
76 unsigned int flags;
77 int type;
78};
79
80
81static int ret;
82
83static const struct GNUNET_CONFIGURATION_Handle *cfg;
84
85static struct GNUNET_FS_Handle *ctx;
86
87static struct GNUNET_FS_SearchContext *sc;
88
89static char *output_filename;
90
91static char *format_string;
92
93static char *dir_format_string;
94
95static char *meta_format_string;
96
97static struct GNUNET_FS_DirectoryBuilder *db;
98
99static unsigned int anonymity = 1;
100
101/**
102 * Timeout for the search, 0 means to wait for CTRL-C.
103 */
104static struct GNUNET_TIME_Relative timeout;
105
106static unsigned int results_limit;
107
108static unsigned int results;
109
110static unsigned int verbose;
111
112static int bookmark_only;
113
114static int local_only;
115
116static int silent_mode;
117
118static struct GNUNET_SCHEDULER_Task *tt;
119
120static int stop_searching;
121
122
123/**
124 * Print the escape sequence at the beginning of a string.
125 *
126 * @param esc a string that **must** begin with a backslash (the function only
127 * assumes that it does, but does not check)
128 * @return the fragment that follows what has been printed
129 * @author madmurphy
130 *
131 * If `"\\nfoo"` is passed as argument, this function prints a new line and
132 * returns `"foo"`
133 */
134static const char *
135print_escape_sequence (const char *const esc)
136{
137 unsigned int probe;
138 const char *cursor = esc + 1;
139 char tmp;
140 switch (*cursor)
141 {
142 /* Trivia */
143 case '\\': putchar ('\\'); return cursor + 1;
144 case 'a': putchar ('\a'); return cursor + 1;
145 case 'b': putchar ('\b'); return cursor + 1;
146 case 'e': putchar ('\x1B'); return cursor + 1;
147 case 'f': putchar ('\f'); return cursor + 1;
148 case 'n': putchar ('\n'); return cursor + 1;
149 case 'r': putchar ('\r'); return cursor + 1;
150 case 't': putchar ('\t'); return cursor + 1;
151 case 'v': putchar ('\v'); return cursor + 1;
152
153 /* Possibly hexadecimal code point */
154 case 'x':
155 probe = 0;
156 while (probe < 256 && isxdigit ((tmp = *++cursor)))
157 probe = (probe << 4) + tmp - (tmp > 96 ? 87 : tmp > 64 ? 55 : 48);
158 goto maybe_codepoint;
159
160 /* Possibly octal code point */
161 case '0': case '1': case '2': case '3':
162 case '4': case '5': case '6': case '7':
163 probe = *cursor++ - 48;
164 do probe = (probe << 3) + *cursor++ - 48;
165 while (probe < 256 && cursor < esc + 4 && *cursor > 47 && *cursor < 56);
166 goto maybe_codepoint;
167
168 /* Boredom */
169 case '\0': putchar ('\\'); return cursor;
170 default: printf ("\\%c", *cursor); return cursor + 1;
171 }
172
173 maybe_codepoint:
174 if (probe < 256)
175 putchar (probe);
176 else
177 fwrite (esc, 1, cursor - esc, stdout);
178 return cursor;
179}
180
181
182/**
183 * Type of a function that libextractor calls for each
184 * meta data item found.
185 *
186 * @param cls closure (user-defined, used for the iteration info)
187 * @param plugin_name name of the plugin that produced this value;
188 * special values can be used (e.g. '&lt;zlib&gt;' for zlib being
189 * used in the main libextractor library and yielding
190 * meta data).
191 * @param type libextractor-type describing the meta data
192 * @param format basic format information about data
193 * @param data_mime_type mime-type of data (not of the original file);
194 * can be NULL (if mime-type is not known)
195 * @param data actual meta-data found
196 * @param data_size number of bytes in @a data
197 * @return 0 to continue extracting, 1 to abort
198 */
199static int
200item_printer (void *const cls,
201 const char *const plugin_name,
202 const enum EXTRACTOR_MetaType type,
203 const enum EXTRACTOR_MetaFormat format,
204 const char *const data_mime_type,
205 const char *const data,
206 const size_t data_size)
207{
208#define info ((struct GNUNET_SEARCH_MetadataPrinterInfo *) cls)
209 if ((format != EXTRACTOR_METAFORMAT_UTF8 &&
210 format != EXTRACTOR_METAFORMAT_C_STRING) ||
211 type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME)
212 return 0;
213 info->counter++;
214 if ((info->flags & METADATA_PRINTER_FLAG_HAVE_TYPE) && type != info->type)
215 return 0;
216
217 const char *cursor = meta_format_string;
218 const char *next_spec = strchr (cursor, '%');
219 const char *next_esc = strchr (cursor, '\\');
220
221 parse_format:
222
223 /* If an escape sequence exists before the next format specifier... */
224 if (next_esc && (! next_spec || next_esc < next_spec))
225 {
226 if (next_esc > cursor)
227 fwrite (cursor, 1, next_esc - cursor, stdout);
228
229 cursor = print_escape_sequence (next_esc);
230 next_esc = strchr (cursor, '\\');
231 goto parse_format;
232 }
233
234 /* If a format specifier exists before the next escape sequence... */
235 if (next_spec && (! next_esc || next_spec < next_esc))
236 {
237 if (next_spec > cursor)
238 fwrite (cursor, 1, next_spec - cursor, stdout);
239
240 switch (*++next_spec)
241 {
242 case '%': putchar ('%'); break;
243 case 'i': printf ("%d", type); break;
244 case 'l': printf ("%lu", (long unsigned int) data_size); break;
245 case 'n': printf ("%u", info->counter); break;
246 case 'p': printf ("%s", data); break;
247#if HAVE_LIBEXTRACTOR
248 case 't':
249 printf ("%s",
250 dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN,
251 EXTRACTOR_metatype_to_string (type)));
252 break;
253#endif
254 case 'w': printf ("%s", plugin_name); break;
255 case '\0': putchar ('%'); return 0;
256 default: printf ("%%%c", *next_spec); break;
257 }
258 cursor = next_spec + 1;
259 next_spec = strchr (cursor, '%');
260 goto parse_format;
261 }
262
263 if (*cursor)
264 printf ("%s", cursor);
265
266 return info->flags & METADATA_PRINTER_FLAG_ONE_RUN;
267#undef info
268}
269
270
271/**
272 * Print a search result according to the current formats
273 *
274 * @param filename the filename for this result
275 * @param uri the `struct GNUNET_FS_Uri` this result refers to
276 * @param metadata the `struct GNUNET_FS_MetaData` associated with this
277 result
278 * @param resultnum the result number
279 * @param is_directory GNUNET_YES if this is a directory, otherwise GNUNET_NO
280 * @author madmurphy
281 */
282static void
283print_search_result (const char *const filename,
284 const struct GNUNET_FS_Uri *const uri,
285 const struct GNUNET_FS_MetaData *const metadata,
286 const unsigned int resultnum,
287 const int is_directory)
288{
289
290 const char *cursor = GNUNET_YES == is_directory ?
291 dir_format_string
292 : format_string;
293
294 const char *next_spec = strchr (cursor, '%');
295 const char *next_esc = strchr (cursor, '\\');
296 char *placeholder;
297 struct GNUNET_SEARCH_MetadataPrinterInfo info;
298
299 parse_format:
300 /* If an escape sequence exists before the next format specifier... */
301 if (next_esc && (! next_spec || next_esc < next_spec))
302 {
303 if (next_esc > cursor)
304 fwrite (cursor, 1, next_esc - cursor, stdout);
305
306 cursor = print_escape_sequence (next_esc);
307 next_esc = strchr (cursor, '\\');
308 goto parse_format;
309 }
310
311 /* If a format specifier exists before the next escape sequence... */
312 if (next_spec && (! next_esc || next_spec < next_esc))
313 {
314 if (next_spec > cursor)
315 fwrite (cursor, 1, next_spec - cursor, stdout);
316
317 switch (*++next_spec)
318 {
319 /* All metadata fields */
320 case 'a':
321 info.flags = METADATA_PRINTER_FLAG_NONE;
322
323 iterate_meta:
324 info.counter = 0;
325 GNUNET_FS_meta_data_iterate (metadata, &item_printer, &info);
326 break;
327 /* File's name */
328 case 'f':
329 if (GNUNET_YES == is_directory)
330 {
331 printf ("%s%s", filename, GNUNET_FS_DIRECTORY_EXT);
332 break;
333 }
334 printf ("%s", filename);
335 break;
336 /* Only the first metadata field */
337 case 'j':
338 info.flags = METADATA_PRINTER_FLAG_ONE_RUN;
339 goto iterate_meta;
340 /* File name's length */
341 case 'l':
342 printf ("%lu",
343 (long unsigned int) (GNUNET_YES == is_directory ?
344 strlen (filename)
345 + (sizeof(GNUNET_FS_DIRECTORY_EXT) - 1)
346 :
347 strlen (filename)));
348 break;
349 /* File's mime type */
350 case 'm':
351 if (GNUNET_YES == is_directory)
352 {
353 printf ("%s", GNUNET_FS_DIRECTORY_MIME);
354 break;
355 }
356 placeholder = GNUNET_FS_meta_data_get_by_type (
357 metadata,
358 EXTRACTOR_METATYPE_MIMETYPE);
359 printf ("%s", placeholder ? placeholder : GENERIC_FILE_MIMETYPE);
360 GNUNET_free (placeholder);
361 break;
362 /* Result number */
363 case 'n': printf ("%u", resultnum); break;
364 /* File's size */
365 case 's':
366 printf ("%" PRIu64, GNUNET_FS_uri_chk_get_file_size (uri));
367 break;
368 /* File's URI */
369 case 'u':
370 placeholder = GNUNET_FS_uri_to_string (uri);
371 printf ("%s", placeholder);
372 GNUNET_free (placeholder);
373 break;
374
375 /* We can add as many cases as we want here... */
376
377 /* Handle `%123#a` and `%123#j` (e.g. `%5#j` is a book title) */
378 case '0': case '1': case '2': case '3': case '4':
379 case '5': case '6': case '7': case '8': case '9':
380 cursor = next_spec;
381 info.type = *cursor - 48;
382 while (isdigit (*++cursor) && info.type < (INT_MAX - *cursor + 48) / 10)
383 info.type = info.type * 10 + *cursor - 48;
384 if (info.type == 0 || *cursor != '#')
385 goto not_a_specifier;
386 switch (*++cursor)
387 {
388 /* All metadata fields of type `info.type` */
389 case 'a':
390 next_spec = cursor;
391 info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE;
392 goto iterate_meta;
393
394 /* Only the first metadata field of type `info.type` */
395 case 'j':
396 next_spec = cursor;
397 info.flags = METADATA_PRINTER_FLAG_HAVE_TYPE
398 | METADATA_PRINTER_FLAG_ONE_RUN;
399 goto iterate_meta;
400 }
401 goto not_a_specifier;
402
403 /* All other cases */
404 case '%': putchar ('%'); break;
405 case '\0': putchar ('%'); return;
406
407 not_a_specifier:
408 default: printf ("%%%c", *next_spec); break;
409 }
410 cursor = next_spec + 1;
411 next_spec = strchr (cursor, '%');
412 goto parse_format;
413 }
414
415 if (*cursor)
416 printf ("%s", cursor);
417}
418
419
420static void
421clean_task (void *const cls)
422{
423 size_t dsize;
424 void *ddata;
425
426 GNUNET_FS_stop (ctx);
427 ctx = NULL;
428 if (output_filename == NULL)
429 return;
430 if (GNUNET_OK !=
431 GNUNET_FS_directory_builder_finish (db, &dsize, &ddata))
432 {
433 GNUNET_break (0);
434 GNUNET_free (output_filename);
435 return;
436 }
437 (void) GNUNET_DISK_directory_remove (output_filename);
438 if (GNUNET_OK !=
439 GNUNET_DISK_fn_write (output_filename,
440 ddata,
441 dsize,
442 GNUNET_DISK_PERM_USER_READ
443 | GNUNET_DISK_PERM_USER_WRITE))
444 {
445 GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR,
446 _ ("Failed to write directory with search results to "
447 "`%s'\n"),
448 output_filename);
449 }
450 GNUNET_free (ddata);
451 GNUNET_free (output_filename);
452}
453
454
455/**
456 * Called by FS client to give information about the progress of an
457 * operation.
458 *
459 * @param cls closure
460 * @param info details about the event, specifying the event type
461 * and various bits about the event
462 * @return client-context (for the next progress call
463 * for this operation; should be set to NULL for
464 * SUSPEND and STOPPED events). The value returned
465 * will be passed to future callbacks in the respective
466 * field in the GNUNET_FS_ProgressInfo struct.
467 */
468static void *
469progress_cb (void *const cls,
470 const struct GNUNET_FS_ProgressInfo *const info)
471{
472 static unsigned int cnt;
473 int is_directory;
474 char *filename;
475
476 switch (info->status)
477 {
478 case GNUNET_FS_STATUS_SEARCH_START:
479 break;
480
481 case GNUNET_FS_STATUS_SEARCH_RESULT:
482 if (stop_searching)
483 break;
484
485 if (db != NULL)
486 GNUNET_FS_directory_builder_add (
487 db,
488 info->value.search.specifics.result.uri,
489 info->value.search.specifics.result.meta,
490 NULL);
491
492 if (silent_mode)
493 break;
494
495 cnt++;
496 filename = GNUNET_FS_meta_data_get_by_type (
497 info->value.search.specifics.result.meta,
498 EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME);
499 is_directory = GNUNET_FS_meta_data_test_for_directory (
500 info->value.search.specifics.result.meta);
501 if (NULL != filename)
502 {
503 while ((filename[0] != '\0') && ('/' == filename[strlen (filename) - 1]))
504 filename[strlen (filename) - 1] = '\0';
505 GNUNET_DISK_filename_canonicalize (filename);
506 }
507 print_search_result (filename ?
508 filename
509 : is_directory ?
510 GENERIC_DIRECTORY_NAME
511 :
512 GENERIC_FILE_NAME,
513 info->value.search.specifics.result.uri,
514 info->value.search.specifics.result.meta,
515 cnt,
516 is_directory);
517 fflush (stdout);
518 GNUNET_free (filename);
519 results++;
520 if ((results_limit > 0) && (results >= results_limit))
521 {
522 GNUNET_SCHEDULER_shutdown ();
523 /* otherwise the function might keep printing results for a while... */
524 stop_searching = GNUNET_YES;
525 }
526 break;
527
528 case GNUNET_FS_STATUS_SEARCH_UPDATE:
529 case GNUNET_FS_STATUS_SEARCH_RESULT_STOPPED:
530 /* ignore */
531 break;
532
533 case GNUNET_FS_STATUS_SEARCH_ERROR:
534 GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR,
535 _ ("Error searching: %s.\n"),
536 info->value.search.specifics.error.message);
537 GNUNET_SCHEDULER_shutdown ();
538 break;
539
540 case GNUNET_FS_STATUS_SEARCH_STOPPED:
541 GNUNET_SCHEDULER_add_now (&clean_task, NULL);
542 break;
543
544 default:
545 GNUNET_SEARCH_log (GNUNET_ERROR_TYPE_ERROR,
546 _ ("Unexpected status: %d\n"),
547 info->status);
548 break;
549 }
550 return NULL;
551}
552
553
554static void
555shutdown_task (void *const cls)
556{
557 if (sc != NULL)
558 {
559 GNUNET_FS_search_stop (sc);
560 sc = NULL;
561 }
562}
563
564
565static void
566timeout_task (void *const cls)
567{
568 tt = NULL;
569 stop_searching = GNUNET_YES;
570 GNUNET_SCHEDULER_shutdown ();
571}
572
573
574/**
575 * Main function that will be run by the scheduler.
576 *
577 * @param cls closure
578 * @param args remaining command-line arguments
579 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
580 * @param cfgarg configuration
581 */
582static void
583run (void *const cls,
584 char *const *const args,
585 const char *const cfgfile,
586 const struct GNUNET_CONFIGURATION_Handle *const cfgarg)
587{
588 struct GNUNET_FS_Uri *uri;
589 unsigned int argc;
590 enum GNUNET_FS_SearchOptions options;
591
592 if (silent_mode && bookmark_only)
593 {
594 fprintf (stderr,
595 _ ("Conflicting options --bookmark-only and --silent.\n"));
596 ret = 1;
597 return;
598 }
599 if (bookmark_only && output_filename)
600 {
601 fprintf (stderr,
602 _ ("Conflicting options --bookmark-only and --output.\n"));
603 ret = 1;
604 return;
605 }
606 if (silent_mode && ! output_filename)
607 {
608 fprintf (stderr, _ ("An output file is mandatory for silent mode.\n"));
609 ret = 1;
610 return;
611 }
612 if (NULL == dir_format_string)
613 dir_format_string = format_string ? format_string
614 : verbose ? VERB_DEFAULT_DIR_FORMAT
615 : DEFAULT_DIR_FORMAT;
616 if (NULL == format_string)
617 format_string = verbose ? VERB_DEFAULT_FILE_FORMAT
618 : DEFAULT_FILE_FORMAT;
619 if (NULL == meta_format_string)
620 meta_format_string = DEFAULT_META_FORMAT;
621 argc = 0;
622 while (NULL != args[argc])
623 argc++;
624 uri = GNUNET_FS_uri_ksk_create_from_args (argc, (const char **) args);
625 if (NULL == uri)
626 {
627 fprintf (stderr,
628 "%s",
629 _ ("Could not create keyword URI from arguments.\n"));
630 ret = 1;
631 return;
632 }
633 if (! GNUNET_FS_uri_test_ksk (uri) && ! GNUNET_FS_uri_test_sks (uri))
634 {
635 fprintf (stderr,
636 "%s",
637 _ ("Invalid URI. Valid URIs for searching are keyword query "
638 "URIs\n(\"gnunet://fs/ksk/...\") and namespace content URIs "
639 "(\"gnunet://fs/sks/...\").\n"));
640 GNUNET_FS_uri_destroy (uri);
641 ret = 1;
642 return;
643 }
644 if (bookmark_only)
645 {
646 char *bmstr = GNUNET_FS_uri_to_string (uri);
647 printf ("%s\n", bmstr);
648 GNUNET_free (bmstr);
649 GNUNET_FS_uri_destroy (uri);
650 ret = 0;
651 return;
652 }
653 cfg = cfgarg;
654 ctx = GNUNET_FS_start (cfg,
655 "gnunet-search",
656 &progress_cb,
657 NULL,
658 GNUNET_FS_FLAGS_NONE,
659 GNUNET_FS_OPTIONS_END);
660 if (NULL == ctx)
661 {
662 fprintf (stderr, _ ("Could not initialize the `%s` subsystem.\n"), "FS");
663 GNUNET_FS_uri_destroy (uri);
664 ret = 1;
665 return;
666 }
667 if (output_filename != NULL)
668 db = GNUNET_FS_directory_builder_create (NULL);
669 options = GNUNET_FS_SEARCH_OPTION_NONE;
670 if (local_only)
671 options |= GNUNET_FS_SEARCH_OPTION_LOOPBACK_ONLY;
672 sc = GNUNET_FS_search_start (ctx, uri, anonymity, options, NULL);
673 GNUNET_FS_uri_destroy (uri);
674 if (NULL == sc)
675 {
676 fprintf (stderr, "%s", _ ("Could not start searching.\n"));
677 GNUNET_FS_stop (ctx);
678 ret = 1;
679 return;
680 }
681 if (0 != timeout.rel_value_us)
682 tt = GNUNET_SCHEDULER_add_delayed (timeout, &timeout_task, NULL);
683 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
684}
685
686
687/**
688 * The main function to search GNUnet.
689 *
690 * @param argc number of arguments from the command line
691 * @param argv command line arguments
692 * @return 0 ok, an error number on error
693 */
694int
695main (int argc, char *const *argv)
696{
697 struct GNUNET_GETOPT_CommandLineOption options[] =
698 { GNUNET_GETOPT_option_uint (
699 'a',
700 "anonymity",
701 "LEVEL",
702 gettext_noop ("set the desired LEVEL of receiver-anonymity (default: "
703 "1)"),
704 &anonymity),
705 GNUNET_GETOPT_option_flag (
706 'b',
707 "bookmark-only",
708 gettext_noop ("do not search, print only the URI that points to this "
709 "search"),
710 &bookmark_only),
711 GNUNET_GETOPT_option_string (
712 'F',
713 "dir-printf",
714 "FORMAT",
715 gettext_noop ("write search results for directories according to "
716 "FORMAT; accepted placeholders are: %a, %f, %j, %l, %m, "
717 "%n, %s; defaults to the value of --printf when omitted "
718 "or to `" HELP_DEFAULT_DIR_FORMAT "` if --printf is "
719 "omitted too"),
720 &dir_format_string),
721 GNUNET_GETOPT_option_string (
722 'f',
723 "printf",
724 "FORMAT",
725 gettext_noop ("write search results according to FORMAT; accepted "
726 "placeholders are: %a, %f, %j, %l, %m, %n, %s; defaults "
727 "to `" HELP_DEFAULT_FILE_FORMAT "` when omitted"),
728 &format_string),
729 GNUNET_GETOPT_option_string (
730 'i',
731 "iter-printf",
732 "FORMAT",
733 gettext_noop ("when the %a or %j placeholders appear in --printf or "
734 "--dir-printf, list each metadata property according to "
735 "FORMAT; accepted placeholders are: %i, %l, %n, %p"
736 HELP_EXTRACTOR_TEXTADD ", %w; defaults to `"
737 HELP_DEFAULT_META_FORMAT "` when omitted"),
738 &meta_format_string),
739 GNUNET_GETOPT_option_uint ('N',
740 "results",
741 "VALUE",
742 gettext_noop ("automatically terminate search "
743 "after VALUE results are found"),
744 &results_limit),
745 GNUNET_GETOPT_option_flag (
746 'n',
747 "no-network",
748 gettext_noop ("only search the local peer (no P2P network search)"),
749 &local_only),
750 GNUNET_GETOPT_option_string (
751 'o',
752 "output",
753 "FILENAME",
754 gettext_noop ("create a GNUnet directory with search results at "
755 "FILENAME (e.g. `gnunet-search --output=commons"
756 GNUNET_FS_DIRECTORY_EXT " commons`)"),
757 &output_filename),
758 GNUNET_GETOPT_option_flag (
759 's',
760 "silent",
761 gettext_noop ("silent mode (requires the --output argument)"),
762 &silent_mode),
763 GNUNET_GETOPT_option_relative_time (
764 't',
765 "timeout",
766 "DELAY",
767 gettext_noop ("automatically terminate search after DELAY; the value "
768 "given must be a number followed by a space and a time "
769 "unit, for example \"500 ms\"; without a unit it defaults "
770 "to microseconds - 1000000 = 1 second; if 0 or omitted "
771 "it means to wait for CTRL-C"),
772 &timeout),
773 GNUNET_GETOPT_option_increment_uint (
774 'V',
775 "verbose",
776 gettext_noop ("be verbose (append \"%a\\n\" to the default --printf and "
777 "--dir-printf arguments - ignored when these are provided "
778 "by the user)"),
779 &verbose),
780 GNUNET_GETOPT_OPTION_END };
781
782 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
783 return 12;
784
785 if (GNUNET_SYSERR ==
786 GNUNET_PROGRAM_run (argc,
787 argv,
788 "gnunet-search [OPTIONS] KEYWORD1 KEYWORD2 ...",
789 gettext_noop ("Search for files that have been "
790 "published on GNUnet\n"),
791 options,
792 &run,
793 NULL))
794 ret = 1;
795
796 GNUNET_free_nz ((void *) argv);
797 return ret;
798}
799
800
801/* end of gnunet-search.c */
diff --git a/src/cli/fs/gnunet-unindex.c b/src/cli/fs/gnunet-unindex.c
new file mode 100644
index 000000000..326f75a63
--- /dev/null
+++ b/src/cli/fs/gnunet-unindex.c
@@ -0,0 +1,206 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2001, 2002, 2004, 2005, 2006, 2007, 2009 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file fs/gnunet-unindex.c
22 * @brief unindex files published on GNUnet
23 * @author Christian Grothoff
24 * @author Krista Bennett
25 * @author James Blackwell
26 * @author Igor Wronsky
27 */
28#include "platform.h"
29
30#include "gnunet_fs_service.h"
31
32static int ret;
33
34static unsigned int verbose;
35
36static const struct GNUNET_CONFIGURATION_Handle *cfg;
37
38static struct GNUNET_FS_Handle *ctx;
39
40static struct GNUNET_FS_UnindexContext *uc;
41
42
43static void
44cleanup_task (void *cls)
45{
46 GNUNET_FS_stop (ctx);
47 ctx = NULL;
48}
49
50
51static void
52shutdown_task (void *cls)
53{
54 struct GNUNET_FS_UnindexContext *u;
55
56 if (uc != NULL)
57 {
58 u = uc;
59 uc = NULL;
60 GNUNET_FS_unindex_stop (u);
61 }
62}
63
64
65/**
66 * Called by FS client to give information about the progress of an
67 * operation.
68 *
69 * @param cls closure
70 * @param info details about the event, specifying the event type
71 * and various bits about the event
72 * @return client-context (for the next progress call
73 * for this operation; should be set to NULL for
74 * SUSPEND and STOPPED events). The value returned
75 * will be passed to future callbacks in the respective
76 * field in the GNUNET_FS_ProgressInfo struct.
77 */
78static void *
79progress_cb (void *cls, const struct GNUNET_FS_ProgressInfo *info)
80{
81 const char *s;
82
83 switch (info->status)
84 {
85 case GNUNET_FS_STATUS_UNINDEX_START:
86 break;
87
88 case GNUNET_FS_STATUS_UNINDEX_PROGRESS:
89 if (verbose)
90 {
91 s = GNUNET_STRINGS_relative_time_to_string (info->value.unindex.eta,
92 GNUNET_YES);
93 fprintf (stdout,
94 _ ("Unindexing at %llu/%llu (%s remaining)\n"),
95 (unsigned long long) info->value.unindex.completed,
96 (unsigned long long) info->value.unindex.size,
97 s);
98 }
99 break;
100
101 case GNUNET_FS_STATUS_UNINDEX_ERROR:
102 fprintf (stderr,
103 _ ("Error unindexing: %s.\n"),
104 info->value.unindex.specifics.error.message);
105 GNUNET_SCHEDULER_shutdown ();
106 break;
107
108 case GNUNET_FS_STATUS_UNINDEX_COMPLETED:
109 fprintf (stdout, "%s", _ ("Unindexing done.\n"));
110 GNUNET_SCHEDULER_shutdown ();
111 break;
112
113 case GNUNET_FS_STATUS_UNINDEX_STOPPED:
114 GNUNET_SCHEDULER_add_now (&cleanup_task, NULL);
115 break;
116
117 default:
118 fprintf (stderr, _ ("Unexpected status: %d\n"), info->status);
119 break;
120 }
121 return NULL;
122}
123
124
125/**
126 * Main function that will be run by the scheduler.
127 *
128 * @param cls closure
129 * @param args remaining command-line arguments
130 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
131 * @param c configuration
132 */
133static void
134run (void *cls,
135 char *const *args,
136 const char *cfgfile,
137 const struct GNUNET_CONFIGURATION_Handle *c)
138{
139 /* check arguments */
140 if ((args[0] == NULL) || (args[1] != NULL))
141 {
142 printf (_ ("You must specify one and only one filename for unindexing.\n"));
143 ret = -1;
144 return;
145 }
146 cfg = c;
147 ctx = GNUNET_FS_start (cfg,
148 "gnunet-unindex",
149 &progress_cb,
150 NULL,
151 GNUNET_FS_FLAGS_NONE,
152 GNUNET_FS_OPTIONS_END);
153 if (NULL == ctx)
154 {
155 fprintf (stderr, _ ("Could not initialize `%s' subsystem.\n"), "FS");
156 ret = 1;
157 return;
158 }
159 uc = GNUNET_FS_unindex_start (ctx, args[0], NULL);
160 if (NULL == uc)
161 {
162 fprintf (stderr, "%s", _ ("Could not start unindex operation.\n"));
163 GNUNET_FS_stop (ctx);
164 return;
165 }
166 GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL);
167}
168
169
170/**
171 * The main function to unindex content.
172 *
173 * @param argc number of arguments from the command line
174 * @param argv command line arguments
175 * @return 0 ok, 1 on error
176 */
177int
178main (int argc, char *const *argv)
179{
180 struct GNUNET_GETOPT_CommandLineOption options[] = {
181 GNUNET_GETOPT_option_verbose (&verbose),
182
183 GNUNET_GETOPT_OPTION_END
184 };
185
186 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
187 return 2;
188
189 ret = (GNUNET_OK ==
190 GNUNET_PROGRAM_run (
191 argc,
192 argv,
193 "gnunet-unindex [OPTIONS] FILENAME",
194 gettext_noop (
195 "Unindex a file that was previously indexed with gnunet-publish."),
196 options,
197 &run,
198 NULL))
199 ? ret
200 : 1;
201 GNUNET_free_nz ((void *) argv);
202 return ret;
203}
204
205
206/* end of gnunet-unindex.c */
diff --git a/src/cli/fs/meson.build b/src/cli/fs/meson.build
new file mode 100644
index 000000000..1b29dd56d
--- /dev/null
+++ b/src/cli/fs/meson.build
@@ -0,0 +1,51 @@
1executable ('gnunet-search',
2 'gnunet-search.c',
3 dependencies: [libgnunetfs_dep,
4 libgnunetutil_dep],
5 include_directories: [incdir, configuration_inc],
6 install: true,
7 install_dir: get_option('bindir'))
8executable ('gnunet-unindex',
9 'gnunet-unindex.c',
10 dependencies: [libgnunetfs_dep,
11 libgnunetutil_dep],
12 include_directories: [incdir, configuration_inc],
13 install: true,
14 install_dir: get_option('bindir'))
15executable ('gnunet-auto-share',
16 'gnunet-auto-share.c',
17 dependencies: [libgnunetfs_dep,
18 libgnunetutil_dep],
19 include_directories: [incdir, configuration_inc],
20 install: true,
21 install_dir: get_option('bindir'))
22executable ('gnunet-directory',
23 'gnunet-directory.c',
24 dependencies: [libgnunetfs_dep,
25 libgnunetutil_dep],
26 include_directories: [incdir, configuration_inc],
27 install: true,
28 install_dir: get_option('bindir'))
29executable ('gnunet-download',
30 'gnunet-download.c',
31 dependencies: [libgnunetfs_dep,
32 libgnunetutil_dep],
33 include_directories: [incdir, configuration_inc],
34 install: true,
35 install_dir: get_option('bindir'))
36executable ('gnunet-fs',
37 'gnunet-fs.c',
38 dependencies: [libgnunetfs_dep,
39 libgnunetutil_dep],
40 include_directories: [incdir, configuration_inc],
41 install: true,
42 install_dir: get_option('bindir'))
43executable ('gnunet-publish',
44 'gnunet-publish.c',
45 dependencies: [libgnunetfs_dep,
46 libgnunetidentity_dep,
47 libgnunetutil_dep],
48 include_directories: [incdir, configuration_inc],
49 install: true,
50 install_dir: get_option('bindir'))
51