Freeciv-3.1
Loading...
Searching...
No Matches
download.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include "fc_prehdrs.h"
19
20#ifdef FREECIV_HAVE_SYS_TYPES_H
21#include <sys/types.h>
22#endif
23#ifdef HAVE_SYS_SOCKET_H
24#include <sys/socket.h>
25#endif
26#include <unistd.h>
27#include <errno.h>
28
29/* dependencies */
30#include "cvercmp.h"
31
32/* utility */
33#include "capability.h"
34#include "fcintl.h"
35#include "log.h"
36#include "mem.h"
37#include "netfile.h"
38#include "registry.h"
39
40/* tools */
41#include "mpdb.h"
42
43#include "download.h"
44
45static const char *download_modpack_recursive(const char *URL,
46 const struct fcmp_params *fcmp,
48 dl_pb_callback pbcb,
49 int recursion);
50
51/**********************************************************************/
54static void nf_cb(const char *msg, void *data)
55{
57
58 if (mcb != NULL) {
59 mcb(msg);
60 }
61}
62
63/**********************************************************************/
66const char *download_modpack(const char *URL,
67 const struct fcmp_params *fcmp,
69 dl_pb_callback pbcb)
70{
71 return download_modpack_recursive(URL, fcmp, mcb, pbcb, 0);
72}
73
74/**********************************************************************/
77static const char *download_modpack_recursive(const char *URL,
78 const struct fcmp_params *fcmp,
80 dl_pb_callback pbcb,
81 int recursion)
82{
83 char local_dir[2048];
84 char local_name[2048];
85 int start_idx;
86 int filenbr, total_files;
87 struct section_file *control;
88 const char *control_capstr;
89 const char *baseURLpart;
90 enum modpack_type type;
91 const char *typestr;
92 const char *mpname;
93 const char *mpver;
94 char baseURL[2048];
95 char fileURL[2048];
96 const char *src_name;
97 bool partial_failure = FALSE;
98 int dep;
99 const char *dep_name;
100 int URL_len;
101
102 if (recursion > 5) {
103 return _("Recursive dependencies too deep");
104 }
105
106 if (URL == NULL || URL[0] == '\0') {
107 return _("No URL given");
108 }
109
110 if (strlen(URL) < strlen(MODPACKDL_SUFFIX)
111 || strcmp(URL + strlen(URL) - strlen(MODPACKDL_SUFFIX),
113 return _("This does not look like modpack URL");
114 }
115
116 for (start_idx = strlen(URL) - strlen(MODPACKDL_SUFFIX);
117 start_idx > 0 && URL[start_idx - 1] != '/';
118 start_idx--) {
119 /* Nothing */
120 }
121
122 if (start_idx <= 0) {
123 return _("This does not look like modpack URL");
124 }
125
126 log_normal(_("Installing modpack %s from %s"), URL + start_idx, URL);
127
128 if (fcmp->inst_prefix == NULL) {
129 return _("Cannot install to given directory hierarchy");
130 }
131
132 if (mcb != NULL) {
133 char buf[2048];
134
135 /* TRANS: %s is a filename with suffix '.mpdl' */
136 fc_snprintf(buf, sizeof(buf), _("Downloading \"%s\" control file."), URL + start_idx);
137 mcb(buf);
138 }
139
140 control = netfile_get_section_file(URL, nf_cb, mcb);
141
142 if (control == NULL) {
143 return _("Failed to get and parse modpack control file");
144 }
145
146 control_capstr = secfile_lookup_str(control, "info.options");
147 if (control_capstr == NULL) {
148 secfile_destroy(control);
149 return _("Modpack control file has no capability string");
150 }
151
152 if (!has_capabilities(MODPACK_CAPSTR, control_capstr)) {
153 log_error("Incompatible control file:");
154 log_error(" control file options: %s", control_capstr);
155 log_error(" supported options: %s", MODPACK_CAPSTR);
156
157 secfile_destroy(control);
158
159 return _("Modpack control file is incompatible");
160 }
161
162 mpname = secfile_lookup_str(control, "info.name");
163 if (mpname == NULL) {
164 return _("Modpack name not defined in control file");
165 }
166 mpver = secfile_lookup_str(control, "info.version");
167 if (mpver == NULL) {
168 return _("Modpack version not defined in control file");
169 }
170
171 typestr = secfile_lookup_str(control, "info.type");
172 type = modpack_type_by_name(typestr, fc_strcasecmp);
173 if (!modpack_type_is_valid(type)) {
174 return _("Illegal modpack type");
175 }
176
177 if (type == MPT_SCENARIO) {
178 fc_snprintf(local_dir, sizeof(local_dir),
179 "%s" DIR_SEPARATOR "scenarios", fcmp->inst_prefix);
180 } else {
181 fc_snprintf(local_dir, sizeof(local_dir),
182 "%s" DIR_SEPARATOR DATASUBDIR, fcmp->inst_prefix);
183 }
184
185 baseURLpart = secfile_lookup_str(control, "info.baseURL");
186
187 if (baseURLpart[0] == '.') {
188 char URLstart[start_idx];
189
190 strncpy(URLstart, URL, start_idx - 1);
191 URLstart[start_idx - 1] = '\0';
192 fc_snprintf(baseURL, sizeof(baseURL), "%s%s",
193 URLstart, baseURLpart + 1);
194 } else {
195 sz_strlcpy(baseURL, baseURLpart);
196 }
197
198 /* Remove potential ending '/' as one will get added later. */
199 URL_len = strlen(baseURL);
200 if (baseURL[URL_len - 1] == '/') {
201 baseURL[URL_len - 1] = '\0';
202 }
203
204 dep = 0;
205 do {
206 dep_name = secfile_lookup_str_default(control, NULL,
207 "dependencies.list%d.modpack", dep);
208 if (dep_name != NULL) {
209 const char *dep_URL;
210 const char *inst_ver;
211 const char *dep_typestr;
212 enum modpack_type dep_type;
213 bool needed = TRUE;
214
215 dep_URL = secfile_lookup_str_default(control, NULL,
216 "dependencies.list%d.URL", dep);
217
218 if (dep_URL == NULL) {
219 return _("Dependency has no download URL");
220 }
221
222 dep_typestr = secfile_lookup_str(control, "dependencies.list%d.type", dep);
223 dep_type = modpack_type_by_name(dep_typestr, fc_strcasecmp);
224 if (!modpack_type_is_valid(dep_type)) {
225 return _("Illegal dependency modpack type");
226 }
227
228 inst_ver = mpdb_installed_version(dep_name, type);
229
230 if (inst_ver != NULL) {
231 const char *dep_ver;
232
233 dep_ver = secfile_lookup_str_default(control, NULL,
234 "dependencies.list%d.version", dep);
235
236 if (dep_ver != NULL && cvercmp_max(dep_ver, inst_ver)) {
237 needed = FALSE;
238 }
239 }
240
241 if (needed) {
242 const char *msg;
243 char dep_URL_full[2048];
244
245 log_debug("Dependency modpack \"%s\" needed.", dep_name);
246
247 if (mcb != NULL) {
248 mcb(_("Download dependency modpack"));
249 }
250
251 if (dep_URL[0] == '.') {
252 char URLstart[start_idx];
253
254 strncpy(URLstart, URL, start_idx - 1);
255 URLstart[start_idx - 1] = '\0';
256 fc_snprintf(dep_URL_full, sizeof(dep_URL_full), "%s%s",
257 URLstart, dep_URL + 1);
258 } else {
259 sz_strlcpy(dep_URL_full, dep_URL);
260 }
261
262 msg = download_modpack_recursive(dep_URL_full, fcmp, mcb, pbcb, recursion + 1);
263
264 if (msg != NULL) {
265 return msg;
266 }
267 }
268 }
269
270 dep++;
271
272 } while (dep_name != NULL);
273
274
275 total_files = 0;
276 do {
277 src_name = secfile_lookup_str_default(control, NULL,
278 "files.list%d.src", total_files);
279
280 if (src_name != NULL) {
281 total_files++;
282 }
283 } while (src_name != NULL);
284
285 if (pbcb != NULL) {
286 /* Control file already downloaded */
287 pbcb(1, total_files + 1);
288 }
289
290 for (filenbr = 0; filenbr < total_files; filenbr++) {
291 const char *dest_name;
292
293#ifndef DIR_SEPARATOR_IS_DEFAULT
294 char *dest_name_copy;
295#else /* DIR_SEPARATOR_IS_DEFAULT */
296#define dest_name_copy dest_name
297#endif /* DIR_SEPARATOR_IS_DEFAULT */
298
299 int i;
300 bool illegal_filename = FALSE;
301
302 src_name = secfile_lookup_str_default(control, NULL,
303 "files.list%d.src", filenbr);
304
305 dest_name = secfile_lookup_str_default(control, NULL,
306 "files.list%d.dest", filenbr);
307
308 if (dest_name == NULL || dest_name[0] == '\0') {
309 /* Missing dest name is ok, we just default to src_name */
310 dest_name = src_name;
311 }
312
313#ifndef DIR_SEPARATOR_IS_DEFAULT
314 dest_name_copy = fc_malloc(strlen(dest_name) + 1);
315#endif /* DIR_SEPARATOR_IS_DEFAULT */
316
317 for (i = 0; dest_name[i] != '\0'; i++) {
318 if (dest_name[i] == '.' && dest_name[i + 1] == '.') {
319 if (mcb != NULL) {
320 char buf[2048];
321
322 fc_snprintf(buf, sizeof(buf), _("Illegal path for %s"),
323 dest_name);
324 mcb(buf);
325 }
326 partial_failure = TRUE;
327 illegal_filename = TRUE;
328 }
329
330#ifndef DIR_SEPARATOR_IS_DEFAULT
331 if (dest_name[i] == '/') {
332 dest_name_copy[i] = DIR_SEPARATOR_CHAR;
333 } else {
334 dest_name_copy[i] = dest_name[i];
335 }
336#endif /* DIR_SEPARATOR_IS_DEFAULT */
337 }
338
339#ifndef DIR_SEPARATOR_IS_DEFAULT
340 dest_name_copy[i] = '\0';
341#endif /* DIR_SEPARATOR_IS_DEFAULT */
342
343 if (!illegal_filename) {
344 fc_snprintf(local_name, sizeof(local_name),
345 "%s" DIR_SEPARATOR "%s", local_dir, dest_name_copy);
346
347#ifndef DIR_SEPARATOR_IS_DEFAULT
348 free(dest_name_copy);
349#endif /* DIR_SEPARATOR_IS_DEFAULT */
350
351 if (!make_dir_for_file(local_name)) {
352 secfile_destroy(control);
353 return _("Cannot create required directories");
354 }
355
356 if (mcb != NULL) {
357 char buf[2048];
358
359 fc_snprintf(buf, sizeof(buf), _("Downloading %s"), src_name);
360 mcb(buf);
361 }
362
363 fc_snprintf(fileURL, sizeof(fileURL), "%s/%s", baseURL, src_name);
364 log_debug("Download \"%s\" as \"%s\".", fileURL, local_name);
365 if (!netfile_download_file(fileURL, local_name, nf_cb, mcb)) {
366 if (mcb != NULL) {
367 char buf[2048];
368
369 fc_snprintf(buf, sizeof(buf), _("Failed to download %s"),
370 src_name);
371 mcb(buf);
372 }
373 partial_failure = TRUE;
374 }
375 } else {
376#ifndef DIR_SEPARATOR_IS_DEFAULT
377 free(dest_name_copy);
378#endif /* DIR_SEPARATOR_IS_DEFAULT */
379 }
380
381 if (pbcb != NULL) {
382 /* Count download of control file also */
383 pbcb(filenbr + 2, total_files + 1);
384 }
385 }
386
387 if (partial_failure) {
388 secfile_destroy(control);
389
390 return _("Some parts of the modpack failed to install.");
391 }
392
393 mpdb_update_modpack(mpname, type, mpver);
394
395 secfile_destroy(control);
396
397 return NULL;
398}
399
400/**********************************************************************/
403const char *download_modpack_list(const struct fcmp_params *fcmp,
405 dl_msg_callback mcb)
406{
407 struct section_file *list_file;
408 const char *list_capstr;
409 int modpack_count;
410 const char *msg;
411 const char *mp_name;
412 int start_idx;
413
414 list_file = netfile_get_section_file(fcmp->list_url, nf_cb, mcb);
415
416 if (list_file == NULL) {
417 return _("Cannot fetch and parse modpack list");
418 }
419
420 for (start_idx = strlen(fcmp->list_url);
421 start_idx > 0 && fcmp->list_url[start_idx - 1] != '/';
422 start_idx--) {
423 /* Nothing */
424 }
425
426 if (start_idx <= 0) {
427 return _("Invalid modpack list URL");
428 }
429
430 list_capstr = secfile_lookup_str(list_file, "info.options");
431 if (list_capstr == NULL) {
432 secfile_destroy(list_file);
433 return _("Modpack list has no capability string");
434 }
435
436 if (!has_capabilities(MODLIST_CAPSTR, list_capstr)) {
437 log_error("Incompatible modpack list file:");
438 log_error(" list file options: %s", list_capstr);
439 log_error(" supported options: %s", MODLIST_CAPSTR);
440
441 secfile_destroy(list_file);
442
443 return _("Modpack list is incompatible");
444 }
445
446 msg = secfile_lookup_str_default(list_file, NULL, "info.message");
447
448 if (msg != NULL) {
449 mcb(msg);
450 }
451
452 modpack_count = 0;
453 do {
454 const char *mpURL;
455 const char *mpver;
456 const char *mplic;
457 const char *mp_type_str;
458 const char *mp_subtype;
459 const char *mp_notes;
460
461 mp_name = secfile_lookup_str_default(list_file, NULL,
462 "modpacks.list%d.name", modpack_count);
463 mpver = secfile_lookup_str_default(list_file, NULL,
464 "modpacks.list%d.version",
465 modpack_count);
466 mplic = secfile_lookup_str_default(list_file, NULL,
467 "modpacks.list%d.license",
468 modpack_count);
469 mp_type_str = secfile_lookup_str_default(list_file, NULL,
470 "modpacks.list%d.type",
471 modpack_count);
472 mp_subtype = secfile_lookup_str_default(list_file, NULL,
473 "modpacks.list%d.subtype",
474 modpack_count);
475 mpURL = secfile_lookup_str_default(list_file, NULL,
476 "modpacks.list%d.URL", modpack_count);
477 mp_notes = secfile_lookup_str_default(list_file, NULL,
478 "modpacks.list%d.notes", modpack_count);
479
480 if (mp_name != NULL && mpURL != NULL) {
481 char mpURL_full[2048];
482 enum modpack_type type = modpack_type_by_name(mp_type_str, fc_strcasecmp);
483
484 if (!modpack_type_is_valid(type)) {
485 log_error("Illegal modpack type \"%s\"", mp_type_str ? mp_type_str : "NULL");
486 }
487 if (mpver == NULL) {
488 mpver = "-";
489 }
490 if (mp_subtype == NULL) {
491 mp_subtype = "-";
492 }
493
494 if (mpURL[0] == '.') {
495 char URLstart[start_idx];
496
497 strncpy(URLstart, fcmp->list_url, start_idx - 1);
498 URLstart[start_idx - 1] = '\0';
499 fc_snprintf(mpURL_full, sizeof(mpURL_full), "%s%s",
500 URLstart, mpURL + 1);
501 } else {
502 sz_strlcpy(mpURL_full, mpURL);
503 }
504
505 cb(mp_name, mpURL_full, mpver, mplic, type, _(mp_subtype), mp_notes);
506 }
507 modpack_count++;
508 } while (mp_name != NULL);
509
510 return NULL;
511}
bool has_capabilities(const char *us, const char *them)
Definition capability.c:86
static const char * download_modpack_recursive(const char *URL, const struct fcmp_params *fcmp, dl_msg_callback mcb, dl_pb_callback pbcb, int recursion)
Definition download.c:77
const char * download_modpack(const char *URL, const struct fcmp_params *fcmp, dl_msg_callback mcb, dl_pb_callback pbcb)
Definition download.c:66
static void nf_cb(const char *msg, void *data)
Definition download.c:54
const char * download_modpack_list(const struct fcmp_params *fcmp, modpack_list_setup_cb cb, dl_msg_callback mcb)
Definition download.c:403
#define MODLIST_CAPSTR
Definition download.h:26
void(* modpack_list_setup_cb)(const char *name, const char *URL, const char *version, const char *license, enum modpack_type type, const char *subtype, const char *notes)
Definition download.h:38
void(* dl_msg_callback)(const char *msg)
Definition download.h:30
void(* dl_pb_callback)(int downloaded, int max)
Definition download.h:31
#define MODPACKDL_SUFFIX
Definition download.h:23
#define MODPACK_CAPSTR
Definition download.h:25
#define _(String)
Definition fcintl.h:67
GType type
Definition repodlgs.c:1312
#define log_debug(message,...)
Definition log.h:115
#define log_normal(message,...)
Definition log.h:107
#define log_error(message,...)
Definition log.h:103
#define fc_malloc(sz)
Definition mem.h:34
struct fcmp_params fcmp
Definition mpcli.c:37
const char * mpdb_installed_version(const char *name, enum modpack_type type)
Definition mpdb.c:253
bool mpdb_update_modpack(const char *name, enum modpack_type type, const char *version)
Definition mpdb.c:213
struct section_file * netfile_get_section_file(const char *URL, nf_errmsg cb, void *data)
Definition netfile.c:146
bool netfile_download_file(const char *URL, const char *filename, nf_errmsg cb, void *data)
Definition netfile.c:168
void secfile_destroy(struct section_file *secfile)
const char * secfile_lookup_str(const struct section_file *secfile, const char *path,...)
const char * secfile_lookup_str_default(const struct section_file *secfile, const char *def, const char *path,...)
bool make_dir_for_file(char *filename)
Definition shared.c:1848
#define DIR_SEPARATOR
Definition shared.h:127
#define DIR_SEPARATOR_CHAR
Definition shared.h:128
static int recursion[AIT_LAST]
Definition srv_log.c:44
const char * inst_prefix
Definition modinst.h:19
const char * list_url
Definition modinst.h:18
int fc_snprintf(char *str, size_t n, const char *format,...)
Definition support.c:969
int fc_strcasecmp(const char *str0, const char *str1)
Definition support.c:189
#define sz_strlcpy(dest, src)
Definition support.h:161
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47