uncrustify as demanded.
[oweals/gnunet.git] / src / fs / gnunet-download.c
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 #include "gnunet_fs_service.h"
30
31 static int ret;
32
33 static unsigned int verbose;
34
35 static int delete_incomplete;
36
37 static const struct GNUNET_CONFIGURATION_Handle *cfg;
38
39 static struct GNUNET_FS_Handle *ctx;
40
41 static struct GNUNET_FS_DownloadContext *dc;
42
43 static unsigned int anonymity = 1;
44
45 static unsigned int parallelism = 16;
46
47 static unsigned int request_parallelism = 4092;
48
49 static int do_recursive;
50
51 static char *filename;
52
53 static int local_only;
54
55
56 static void
57 cleanup_task(void *cls)
58 {
59   GNUNET_FS_stop(ctx);
60   ctx = NULL;
61 }
62
63
64 static void
65 shutdown_task(void *cls)
66 {
67   if (NULL != dc)
68     {
69       GNUNET_FS_download_stop(dc, delete_incomplete);
70       dc = NULL;
71     }
72 }
73
74
75 /**
76  * Display progress bar (if tty).
77  *
78  * @param x current position in the download
79  * @param n total size of the download
80  * @param w desired number of steps in the progress bar
81  */
82 static void
83 display_bar(unsigned long long x, unsigned long long n, unsigned int w)
84 {
85   char buf[w + 20];
86   unsigned int p;
87   unsigned int endeq;
88   float ratio_complete;
89
90 #if !WINDOWS
91   if (0 == isatty(1))
92     return;
93 #else
94   if (FILE_TYPE_CHAR != GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)))
95     return;
96 #endif
97   ratio_complete = x / (float)n;
98   endeq = ratio_complete * w;
99   GNUNET_snprintf(buf, sizeof(buf), "%3d%% [", (int)(ratio_complete * 100));
100   for (p = 0; p < endeq; p++)
101     strcat(buf, "=");
102   for (p = endeq; p < w; p++)
103     strcat(buf, " ");
104   strcat(buf, "]\r");
105   printf("%s", buf);
106   fflush(stdout);
107 }
108
109
110 /**
111  * Called by FS client to give information about the progress of an
112  * operation.
113  *
114  * @param cls closure
115  * @param info details about the event, specifying the event type
116  *        and various bits about the event
117  * @return client-context (for the next progress call
118  *         for this operation; should be set to NULL for
119  *         SUSPEND and STOPPED events).  The value returned
120  *         will be passed to future callbacks in the respective
121  *         field in the `struct GNUNET_FS_ProgressInfo`
122  */
123 static void *
124 progress_cb(void *cls, const struct GNUNET_FS_ProgressInfo *info)
125 {
126   char *s;
127   const char *s2;
128   char *t;
129
130   switch (info->status)
131     {
132     case GNUNET_FS_STATUS_DOWNLOAD_START:
133       if (verbose > 1)
134         fprintf(stderr,
135                 _("Starting download `%s'.\n"),
136                 info->value.download.filename);
137       break;
138
139     case GNUNET_FS_STATUS_DOWNLOAD_PROGRESS:
140       if (verbose)
141         {
142           s = GNUNET_strdup(
143             GNUNET_STRINGS_relative_time_to_string(info->value.download.eta,
144                                                    GNUNET_YES));
145           if (info->value.download.specifics.progress.block_download_duration
146               .rel_value_us == GNUNET_TIME_UNIT_FOREVER_REL.rel_value_us)
147             s2 = _("<unknown time>");
148           else
149             s2 = GNUNET_STRINGS_relative_time_to_string(info->value.download
150                                                         .specifics.progress
151                                                         .block_download_duration,
152                                                         GNUNET_YES);
153           t = GNUNET_STRINGS_byte_size_fancy(
154             info->value.download.completed * 1000LL /
155             (info->value.download.duration.rel_value_us + 1));
156           fprintf(
157             stdout,
158             _(
159               "Downloading `%s' at %llu/%llu (%s remaining, %s/s). Block took %s to download\n"),
160             info->value.download.filename,
161             (unsigned long long)info->value.download.completed,
162             (unsigned long long)info->value.download.size,
163             s,
164             t,
165             s2);
166           GNUNET_free(s);
167           GNUNET_free(t);
168         }
169       else
170         {
171           display_bar(info->value.download.completed,
172                       info->value.download.size,
173                       60);
174         }
175       break;
176
177     case GNUNET_FS_STATUS_DOWNLOAD_ERROR:
178 #if !WINDOWS
179       if (0 != isatty(1))
180         fprintf(stdout, "\n");
181 #else
182       if (FILE_TYPE_CHAR == GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)))
183         fprintf(stdout, "\n");
184 #endif
185       fprintf(stderr,
186               _("Error downloading: %s.\n"),
187               info->value.download.specifics.error.message);
188       GNUNET_SCHEDULER_shutdown();
189       break;
190
191     case GNUNET_FS_STATUS_DOWNLOAD_COMPLETED:
192       s = GNUNET_STRINGS_byte_size_fancy(
193         info->value.download.completed * 1000 /
194         (info->value.download.duration.rel_value_us + 1));
195 #if !WINDOWS
196       if (0 != isatty(1))
197         fprintf(stdout, "\n");
198 #else
199       if (FILE_TYPE_CHAR == GetFileType(GetStdHandle(STD_OUTPUT_HANDLE)))
200         fprintf(stdout, "\n");
201 #endif
202       fprintf(stdout,
203               _("Downloading `%s' done (%s/s).\n"),
204               info->value.download.filename,
205               s);
206       GNUNET_free(s);
207       if (info->value.download.dc == dc)
208         GNUNET_SCHEDULER_shutdown();
209       break;
210
211     case GNUNET_FS_STATUS_DOWNLOAD_STOPPED:
212       if (info->value.download.dc == dc)
213         GNUNET_SCHEDULER_add_now(&cleanup_task, NULL);
214       break;
215
216     case GNUNET_FS_STATUS_DOWNLOAD_ACTIVE:
217     case GNUNET_FS_STATUS_DOWNLOAD_INACTIVE:
218       break;
219
220     default:
221       fprintf(stderr, _("Unexpected status: %d\n"), info->status);
222       break;
223     }
224   return NULL;
225 }
226
227
228 /**
229  * Main function that will be run by the scheduler.
230  *
231  * @param cls closure
232  * @param args remaining command-line arguments
233  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
234  * @param c configuration
235  */
236 static void
237 run(void *cls,
238     char *const *args,
239     const char *cfgfile,
240     const struct GNUNET_CONFIGURATION_Handle *c)
241 {
242   struct GNUNET_FS_Uri *uri;
243   char *emsg;
244   enum GNUNET_FS_DownloadOptions options;
245
246   if (NULL == args[0])
247     {
248       fprintf(stderr, "%s", _("You need to specify a URI argument.\n"));
249       return;
250     }
251   uri = GNUNET_FS_uri_parse(args[0], &emsg);
252   if (NULL == uri)
253     {
254       fprintf(stderr, _("Failed to parse URI: %s\n"), emsg);
255       GNUNET_free(emsg);
256       ret = 1;
257       return;
258     }
259   if ((!GNUNET_FS_uri_test_chk(uri)) && (!GNUNET_FS_uri_test_loc(uri)))
260     {
261       fprintf(stderr, "%s", _("Only CHK or LOC URIs supported.\n"));
262       ret = 1;
263       GNUNET_FS_uri_destroy(uri);
264       return;
265     }
266   if (NULL == filename)
267     {
268       fprintf(stderr, "%s", _("Target filename must be specified.\n"));
269       ret = 1;
270       GNUNET_FS_uri_destroy(uri);
271       return;
272     }
273   cfg = c;
274   ctx = GNUNET_FS_start(cfg,
275                         "gnunet-download",
276                         &progress_cb,
277                         NULL,
278                         GNUNET_FS_FLAGS_NONE,
279                         GNUNET_FS_OPTIONS_DOWNLOAD_PARALLELISM,
280                         parallelism,
281                         GNUNET_FS_OPTIONS_REQUEST_PARALLELISM,
282                         request_parallelism,
283                         GNUNET_FS_OPTIONS_END);
284   if (NULL == ctx)
285     {
286       fprintf(stderr, _("Could not initialize `%s' subsystem.\n"), "FS");
287       GNUNET_FS_uri_destroy(uri);
288       ret = 1;
289       return;
290     }
291   options = GNUNET_FS_DOWNLOAD_OPTION_NONE;
292   if (do_recursive)
293     options |= GNUNET_FS_DOWNLOAD_OPTION_RECURSIVE;
294   if (local_only)
295     options |= GNUNET_FS_DOWNLOAD_OPTION_LOOPBACK_ONLY;
296   dc = GNUNET_FS_download_start(ctx,
297                                 uri,
298                                 NULL,
299                                 filename,
300                                 NULL,
301                                 0,
302                                 GNUNET_FS_uri_chk_get_file_size(uri),
303                                 anonymity,
304                                 options,
305                                 NULL,
306                                 NULL);
307   GNUNET_FS_uri_destroy(uri);
308   if (dc == NULL)
309     {
310       GNUNET_FS_stop(ctx);
311       ctx = NULL;
312       return;
313     }
314   GNUNET_SCHEDULER_add_shutdown(&shutdown_task, NULL);
315 }
316
317
318 /**
319  * The main function to download GNUnet.
320  *
321  * @param argc number of arguments from the command line
322  * @param argv command line arguments
323  * @return 0 ok, 1 on error
324  */
325 int
326 main(int argc, char *const *argv)
327 {
328   struct GNUNET_GETOPT_CommandLineOption options[] =
329   { GNUNET_GETOPT_option_uint('a',
330                               "anonymity",
331                               "LEVEL",
332                               gettext_noop(
333                                 "set the desired LEVEL of receiver-anonymity"),
334                               &anonymity),
335
336     GNUNET_GETOPT_option_flag(
337       'D',
338       "delete-incomplete",
339       gettext_noop("delete incomplete downloads (when aborted with CTRL-C)"),
340       &delete_incomplete),
341
342     GNUNET_GETOPT_option_flag(
343       'n',
344       "no-network",
345       gettext_noop("only search the local peer (no P2P network search)"),
346       &local_only),
347     GNUNET_GETOPT_option_string('o',
348                                 "output",
349                                 "FILENAME",
350                                 gettext_noop("write the file to FILENAME"),
351                                 &filename),
352     GNUNET_GETOPT_option_uint(
353       'p',
354       "parallelism",
355       "DOWNLOADS",
356       gettext_noop(
357         "set the maximum number of parallel downloads that is allowed"),
358       &parallelism),
359     GNUNET_GETOPT_option_uint(
360       'r',
361       "request-parallelism",
362       "REQUESTS",
363       gettext_noop(
364         "set the maximum number of parallel requests for blocks that is allowed"),
365       &request_parallelism),
366     GNUNET_GETOPT_option_flag('R',
367                               "recursive",
368                               gettext_noop(
369                                 "download a GNUnet directory recursively"),
370                               &do_recursive),
371     GNUNET_GETOPT_option_increment_uint(
372       'V',
373       "verbose",
374       gettext_noop("be verbose (print progress information)"),
375       &verbose),
376     GNUNET_GETOPT_OPTION_END };
377
378   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args(argc, argv, &argc, &argv))
379     return 2;
380
381   ret =
382     (GNUNET_OK ==
383      GNUNET_PROGRAM_run(
384        argc,
385        argv,
386        "gnunet-download [OPTIONS] URI",
387        gettext_noop(
388          "Download files from GNUnet using a GNUnet CHK or LOC URI (gnunet://fs/chk/...)"),
389        options,
390        &run,
391        NULL))
392     ? ret
393     : 1;
394   GNUNET_free((void *)argv);
395   return ret;
396 }
397
398 /* end of gnunet-download.c */