diff options
Diffstat (limited to 'src/cli/fs')
-rw-r--r-- | src/cli/fs/.gitignore | 8 | ||||
-rw-r--r-- | src/cli/fs/Makefile.am | 107 | ||||
-rw-r--r-- | src/cli/fs/gnunet-auto-share.c | 791 | ||||
-rw-r--r-- | src/cli/fs/gnunet-directory.c | 212 | ||||
-rw-r--r-- | src/cli/fs/gnunet-download.c | 385 | ||||
-rw-r--r-- | src/cli/fs/gnunet-fs.c | 191 | ||||
-rw-r--r-- | src/cli/fs/gnunet-publish.c | 1009 | ||||
-rw-r--r-- | src/cli/fs/gnunet-search.c | 801 | ||||
-rw-r--r-- | src/cli/fs/gnunet-unindex.c | 206 | ||||
-rw-r--r-- | src/cli/fs/meson.build | 51 |
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 @@ | |||
1 | gnunet-unindex | ||
2 | gnunet-auto-share | ||
3 | gnunet-directory | ||
4 | gnunet-download | ||
5 | gnunet-fs | ||
6 | gnunet-publish | ||
7 | gnunet-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 | ||
2 | AM_CPPFLAGS = -I$(top_srcdir)/src/include | ||
3 | |||
4 | if USE_COVERAGE | ||
5 | AM_CFLAGS = --coverage -O0 | ||
6 | XLIB = -lgcov | ||
7 | endif | ||
8 | |||
9 | pkgcfgdir= $(pkgdatadir)/config.d/ | ||
10 | |||
11 | libexecdir= $(pkglibdir)/libexec/ | ||
12 | |||
13 | bin_PROGRAMS = \ | ||
14 | gnunet-auto-share \ | ||
15 | gnunet-directory \ | ||
16 | gnunet-download \ | ||
17 | gnunet-publish \ | ||
18 | gnunet-search \ | ||
19 | gnunet-fs \ | ||
20 | gnunet-unindex | ||
21 | |||
22 | gnunet_directory_SOURCES = \ | ||
23 | gnunet-directory.c | ||
24 | gnunet_directory_LDADD = \ | ||
25 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
26 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
27 | $(GN_LIBINTL) | ||
28 | |||
29 | if HAVE_LIBEXTRACTOR | ||
30 | gnunet_directory_LDADD += \ | ||
31 | -lextractor | ||
32 | endif | ||
33 | |||
34 | gnunet_fs_SOURCES = \ | ||
35 | gnunet-fs.c | ||
36 | gnunet_fs_LDADD = \ | ||
37 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
38 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
39 | $(GN_LIBINTL) | ||
40 | |||
41 | if HAVE_LIBEXTRACTOR | ||
42 | gnunet_fs_LDADD += \ | ||
43 | -lextractor | ||
44 | endif | ||
45 | |||
46 | gnunet_download_SOURCES = \ | ||
47 | gnunet-download.c | ||
48 | gnunet_download_LDADD = \ | ||
49 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
50 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
51 | $(GN_LIBINTL) | ||
52 | |||
53 | gnunet_publish_SOURCES = \ | ||
54 | gnunet-publish.c | ||
55 | gnunet_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 | |||
61 | if HAVE_LIBEXTRACTOR | ||
62 | gnunet_publish_LDADD += \ | ||
63 | -lextractor | ||
64 | endif | ||
65 | |||
66 | gnunet_auto_share_SOURCES = \ | ||
67 | gnunet-auto-share.c | ||
68 | gnunet_auto_share_LDADD = \ | ||
69 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
70 | $(GN_LIBINTL) | ||
71 | |||
72 | if HAVE_LIBEXTRACTOR | ||
73 | gnunet_auto_share_LDADD += \ | ||
74 | -lextractor | ||
75 | endif | ||
76 | |||
77 | gnunet_helper_fs_publish_SOURCES = \ | ||
78 | gnunet-helper-fs-publish.c | ||
79 | gnunet_helper_fs_publish_LDADD = \ | ||
80 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
81 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
82 | $(GN_LIBINTL) | ||
83 | |||
84 | if HAVE_LIBEXTRACTOR | ||
85 | gnunet_helper_fs_publish_LDADD += \ | ||
86 | -lextractor | ||
87 | endif | ||
88 | |||
89 | gnunet_search_SOURCES = \ | ||
90 | gnunet-search.c | ||
91 | gnunet_search_LDADD = \ | ||
92 | $(top_builddir)/src/service/fs/libgnunetfs.la \ | ||
93 | $(top_builddir)/src/lib/util/libgnunetutil.la \ | ||
94 | $(GN_LIBINTL) | ||
95 | |||
96 | if HAVE_LIBEXTRACTOR | ||
97 | gnunet_search_LDADD += \ | ||
98 | -lextractor | ||
99 | endif | ||
100 | |||
101 | |||
102 | gnunet_unindex_SOURCES = \ | ||
103 | gnunet-unindex.c | ||
104 | gnunet_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 | */ | ||
41 | struct 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 | */ | ||
69 | static int ret; | ||
70 | |||
71 | /** | ||
72 | * Are we running 'verbosely'? | ||
73 | */ | ||
74 | static unsigned int verbose; | ||
75 | |||
76 | /** | ||
77 | * Configuration to use. | ||
78 | */ | ||
79 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
80 | |||
81 | /** | ||
82 | * Name of the configuration file. | ||
83 | */ | ||
84 | static char *cfg_filename; | ||
85 | |||
86 | /** | ||
87 | * Disable extractor option to use for publishing. | ||
88 | */ | ||
89 | static int disable_extractor; | ||
90 | |||
91 | /** | ||
92 | * Disable creation time option to use for publishing. | ||
93 | */ | ||
94 | static int do_disable_creation_time; | ||
95 | |||
96 | /** | ||
97 | * Handle for the main task that does scanning and working. | ||
98 | */ | ||
99 | static struct GNUNET_SCHEDULER_Task *run_task; | ||
100 | |||
101 | /** | ||
102 | * Anonymity level option to use for publishing. | ||
103 | */ | ||
104 | static unsigned int anonymity_level = 1; | ||
105 | |||
106 | /** | ||
107 | * Content priority option to use for publishing. | ||
108 | */ | ||
109 | static unsigned int content_priority = 365; | ||
110 | |||
111 | /** | ||
112 | * Replication level option to use for publishing. | ||
113 | */ | ||
114 | static unsigned int replication_level = 1; | ||
115 | |||
116 | /** | ||
117 | * Top-level directory we monitor to auto-publish. | ||
118 | */ | ||
119 | static const char *dir_name; | ||
120 | |||
121 | /** | ||
122 | * Head of linked list of files still to publish. | ||
123 | */ | ||
124 | static struct WorkItem *work_head; | ||
125 | |||
126 | /** | ||
127 | * Tail of linked list of files still to publish. | ||
128 | */ | ||
129 | static struct WorkItem *work_tail; | ||
130 | |||
131 | /** | ||
132 | * Map from the hash of the filename (!) to a `struct WorkItem` | ||
133 | * that was finished. | ||
134 | */ | ||
135 | static struct GNUNET_CONTAINER_MultiHashMap *work_finished; | ||
136 | |||
137 | /** | ||
138 | * Set to #GNUNET_YES if we are shutting down. | ||
139 | */ | ||
140 | static 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 | */ | ||
147 | static struct GNUNET_TIME_Absolute start_time; | ||
148 | |||
149 | /** | ||
150 | * Pipe used to communicate 'gnunet-publish' completion (SIGCHLD) via signal. | ||
151 | */ | ||
152 | static struct GNUNET_DISK_PipeHandle *sigpipe; | ||
153 | |||
154 | /** | ||
155 | * Handle to the 'gnunet-publish' process that we executed. | ||
156 | */ | ||
157 | static struct GNUNET_OS_Process *publish_proc; | ||
158 | |||
159 | |||
160 | /** | ||
161 | * Compute the name of the state database file we will use. | ||
162 | */ | ||
163 | static char * | ||
164 | get_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 | */ | ||
181 | static void | ||
182 | load_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; | ||
229 | error: | ||
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 | */ | ||
248 | static int | ||
249 | write_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 | */ | ||
274 | static void | ||
275 | save_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 | */ | ||
315 | static void | ||
316 | do_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 | */ | ||
335 | static void | ||
336 | schedule_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 | */ | ||
345 | static void | ||
346 | maint_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 | */ | ||
424 | static void | ||
425 | sighandler_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 | */ | ||
445 | static void | ||
446 | work (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 | */ | ||
514 | static int | ||
515 | determine_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 | */ | ||
561 | static int | ||
562 | add_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 | */ | ||
604 | static void | ||
605 | scan (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 | */ | ||
617 | static void | ||
618 | schedule_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 | */ | ||
650 | static void | ||
651 | run (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 | */ | ||
685 | static int | ||
686 | free_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 | */ | ||
703 | int | ||
704 | main (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 | |||
29 | static 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 | */ | ||
43 | static int | ||
44 | item_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 | */ | ||
90 | static void | ||
91 | print_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 | */ | ||
130 | static void | ||
131 | run (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 | */ | ||
186 | int | ||
187 | main (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 | |||
32 | static int ret; | ||
33 | |||
34 | static unsigned int verbose; | ||
35 | |||
36 | static int delete_incomplete; | ||
37 | |||
38 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
39 | |||
40 | static struct GNUNET_FS_Handle *ctx; | ||
41 | |||
42 | static struct GNUNET_FS_DownloadContext *dc; | ||
43 | |||
44 | static unsigned int anonymity = 1; | ||
45 | |||
46 | static unsigned int parallelism = 16; | ||
47 | |||
48 | static unsigned int request_parallelism = 4092; | ||
49 | |||
50 | static int do_recursive; | ||
51 | |||
52 | static char *filename; | ||
53 | |||
54 | static int local_only; | ||
55 | |||
56 | |||
57 | static void | ||
58 | cleanup_task (void *cls) | ||
59 | { | ||
60 | GNUNET_FS_stop (ctx); | ||
61 | ctx = NULL; | ||
62 | } | ||
63 | |||
64 | |||
65 | static void | ||
66 | shutdown_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 | */ | ||
83 | static void | ||
84 | display_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 | */ | ||
119 | static void * | ||
120 | progress_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 | */ | ||
222 | static void | ||
223 | run (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 | */ | ||
311 | int | ||
312 | main (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 | ¶llelism), | ||
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 | */ | ||
32 | static int ret; | ||
33 | |||
34 | /** | ||
35 | * Handle to FS service. | ||
36 | */ | ||
37 | static struct GNUNET_FS_Handle *fs; | ||
38 | |||
39 | /** | ||
40 | * Handle for the index listing operation. | ||
41 | */ | ||
42 | static struct GNUNET_FS_GetIndexedContext *gic; | ||
43 | |||
44 | /** | ||
45 | * Option -i given? | ||
46 | */ | ||
47 | static int list_indexed_files; | ||
48 | |||
49 | /** | ||
50 | * Option -v given? | ||
51 | */ | ||
52 | static 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 | */ | ||
63 | static enum GNUNET_GenericReturnValue | ||
64 | print_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 | */ | ||
92 | static void | ||
93 | do_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 | */ | ||
117 | static void | ||
118 | run (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 | */ | ||
157 | int | ||
158 | main (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 | */ | ||
36 | static int ret; | ||
37 | |||
38 | /** | ||
39 | * Command line option 'verbose' set | ||
40 | */ | ||
41 | static unsigned int verbose; | ||
42 | |||
43 | /** | ||
44 | * Handle to our configuration. | ||
45 | */ | ||
46 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
47 | |||
48 | /** | ||
49 | * Handle for interaction with file-sharing service. | ||
50 | */ | ||
51 | static struct GNUNET_FS_Handle *ctx; | ||
52 | |||
53 | /** | ||
54 | * Handle to FS-publishing operation. | ||
55 | */ | ||
56 | static struct GNUNET_FS_PublishContext *pc; | ||
57 | |||
58 | /** | ||
59 | * Meta-data provided via command-line option. | ||
60 | */ | ||
61 | static struct GNUNET_FS_MetaData *meta; | ||
62 | |||
63 | /** | ||
64 | * Keywords provided via command-line option. | ||
65 | */ | ||
66 | static struct GNUNET_FS_Uri *topKeywords; | ||
67 | |||
68 | /** | ||
69 | * Options we set for published blocks. | ||
70 | */ | ||
71 | static 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 | */ | ||
77 | static 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 | */ | ||
84 | static struct GNUNET_FS_Uri *uri; | ||
85 | |||
86 | /** | ||
87 | * Command-line option for namespace publishing: identifier for updates | ||
88 | * to this publication. | ||
89 | */ | ||
90 | static char *next_id; | ||
91 | |||
92 | /** | ||
93 | * Command-line option for namespace publishing: identifier for this | ||
94 | * publication. | ||
95 | */ | ||
96 | static char *this_id; | ||
97 | |||
98 | /** | ||
99 | * Command-line option identifying the pseudonym to use for the publication. | ||
100 | */ | ||
101 | static char *pseudonym; | ||
102 | |||
103 | /** | ||
104 | * Command-line option for 'inserting' | ||
105 | */ | ||
106 | static int do_insert; | ||
107 | |||
108 | /** | ||
109 | * Command-line option to disable meta data extraction. | ||
110 | */ | ||
111 | static int disable_extractor; | ||
112 | |||
113 | /** | ||
114 | * Command-line option to merely simulate publishing operation. | ||
115 | */ | ||
116 | static int do_simulate; | ||
117 | |||
118 | /** | ||
119 | * Command-line option to only perform meta data extraction, but not publish. | ||
120 | */ | ||
121 | static int extract_only; | ||
122 | |||
123 | /** | ||
124 | * Command-line option to disable adding creation time. | ||
125 | */ | ||
126 | static int enable_creation_time; | ||
127 | |||
128 | /** | ||
129 | * Handle to the directory scanner (for recursive insertions). | ||
130 | */ | ||
131 | static 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 | */ | ||
137 | static struct GNUNET_IDENTITY_Ego *namespace; | ||
138 | |||
139 | /** | ||
140 | * Handle to identity service. | ||
141 | */ | ||
142 | static 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 | */ | ||
151 | static void | ||
152 | do_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 | */ | ||
203 | static void * | ||
204 | progress_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 | */ | ||
320 | static int | ||
321 | meta_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 | */ | ||
351 | static int | ||
352 | keyword_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 | */ | ||
376 | static int | ||
377 | publish_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 | */ | ||
450 | static void | ||
451 | uri_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 | */ | ||
473 | static void | ||
474 | uri_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 | */ | ||
515 | static struct GNUNET_FS_FileInformation * | ||
516 | get_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 | */ | ||
572 | static void | ||
573 | directory_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 | */ | ||
631 | static void | ||
632 | directory_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 | */ | ||
698 | static void | ||
699 | identity_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 | */ | ||
774 | static void | ||
775 | identity_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 | */ | ||
802 | static void | ||
803 | run (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 | */ | ||
889 | int | ||
890 | main (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 | |||
65 | enum 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 | |||
73 | struct GNUNET_SEARCH_MetadataPrinterInfo | ||
74 | { | ||
75 | unsigned int counter; | ||
76 | unsigned int flags; | ||
77 | int type; | ||
78 | }; | ||
79 | |||
80 | |||
81 | static int ret; | ||
82 | |||
83 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
84 | |||
85 | static struct GNUNET_FS_Handle *ctx; | ||
86 | |||
87 | static struct GNUNET_FS_SearchContext *sc; | ||
88 | |||
89 | static char *output_filename; | ||
90 | |||
91 | static char *format_string; | ||
92 | |||
93 | static char *dir_format_string; | ||
94 | |||
95 | static char *meta_format_string; | ||
96 | |||
97 | static struct GNUNET_FS_DirectoryBuilder *db; | ||
98 | |||
99 | static unsigned int anonymity = 1; | ||
100 | |||
101 | /** | ||
102 | * Timeout for the search, 0 means to wait for CTRL-C. | ||
103 | */ | ||
104 | static struct GNUNET_TIME_Relative timeout; | ||
105 | |||
106 | static unsigned int results_limit; | ||
107 | |||
108 | static unsigned int results; | ||
109 | |||
110 | static unsigned int verbose; | ||
111 | |||
112 | static int bookmark_only; | ||
113 | |||
114 | static int local_only; | ||
115 | |||
116 | static int silent_mode; | ||
117 | |||
118 | static struct GNUNET_SCHEDULER_Task *tt; | ||
119 | |||
120 | static 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 | */ | ||
134 | static const char * | ||
135 | print_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. '<zlib>' 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 | */ | ||
199 | static int | ||
200 | item_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 | */ | ||
282 | static void | ||
283 | print_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 | |||
420 | static void | ||
421 | clean_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 | */ | ||
468 | static void * | ||
469 | progress_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 | |||
554 | static void | ||
555 | shutdown_task (void *const cls) | ||
556 | { | ||
557 | if (sc != NULL) | ||
558 | { | ||
559 | GNUNET_FS_search_stop (sc); | ||
560 | sc = NULL; | ||
561 | } | ||
562 | } | ||
563 | |||
564 | |||
565 | static void | ||
566 | timeout_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 | */ | ||
582 | static void | ||
583 | run (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 | */ | ||
694 | int | ||
695 | main (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 | |||
32 | static int ret; | ||
33 | |||
34 | static unsigned int verbose; | ||
35 | |||
36 | static const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
37 | |||
38 | static struct GNUNET_FS_Handle *ctx; | ||
39 | |||
40 | static struct GNUNET_FS_UnindexContext *uc; | ||
41 | |||
42 | |||
43 | static void | ||
44 | cleanup_task (void *cls) | ||
45 | { | ||
46 | GNUNET_FS_stop (ctx); | ||
47 | ctx = NULL; | ||
48 | } | ||
49 | |||
50 | |||
51 | static void | ||
52 | shutdown_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 | */ | ||
78 | static void * | ||
79 | progress_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 | */ | ||
133 | static void | ||
134 | run (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 | */ | ||
177 | int | ||
178 | main (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 @@ | |||
1 | executable ('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')) | ||
8 | executable ('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')) | ||
15 | executable ('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')) | ||
22 | executable ('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')) | ||
29 | executable ('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')) | ||
36 | executable ('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')) | ||
43 | executable ('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 | |||