webperimental: killstack decides stack protects.
[freeciv.git] / tools / download.c
blob35e0df82c8304094a8205a97ead2e421ae5de2eb
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.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include "fc_prehdrs.h"
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>
29 /* dependencies */
30 #include "cvercmp.h"
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"
40 /* tools */
41 #include "mpdb.h"
43 #include "download.h"
45 static const char *download_modpack_recursive(const char *URL,
46 const struct fcmp_params *fcmp,
47 dl_msg_callback mcb,
48 dl_pb_callback pbcb,
49 int recursion);
51 /**************************************************************************
52 Message callback called by netfile module when downloading files.
53 **************************************************************************/
54 static void nf_cb(const char *msg, void *data)
56 dl_msg_callback mcb = (dl_msg_callback) data;
58 if (mcb != NULL) {
59 mcb(msg);
63 /**************************************************************************
64 Download modpack from a given URL
65 **************************************************************************/
66 const char *download_modpack(const char *URL,
67 const struct fcmp_params *fcmp,
68 dl_msg_callback mcb,
69 dl_pb_callback pbcb)
71 return download_modpack_recursive(URL, fcmp, mcb, pbcb, 0);
74 /**************************************************************************
75 Download modpack and its recursive dependencies.
76 **************************************************************************/
77 static const char *download_modpack_recursive(const char *URL,
78 const struct fcmp_params *fcmp,
79 dl_msg_callback mcb,
80 dl_pb_callback pbcb,
81 int recursion)
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;
101 if (recursion > 5) {
102 return _("Recursive dependencies too deep");
105 if (URL == NULL || URL[0] == '\0') {
106 return _("No URL given");
109 if (strlen(URL) < strlen(MODPACKDL_SUFFIX)
110 || strcmp(URL + strlen(URL) - strlen(MODPACKDL_SUFFIX),
111 MODPACKDL_SUFFIX)) {
112 return _("This does not look like modpack URL");
115 for (start_idx = strlen(URL) - strlen(MODPACKDL_SUFFIX);
116 start_idx > 0 && URL[start_idx - 1] != '/';
117 start_idx--) {
118 /* Nothing */
121 log_normal(_("Installing modpack %s from %s"), URL + start_idx, URL);
123 if (fcmp->inst_prefix == NULL) {
124 return _("Cannot install to given directory hierarchy");
127 if (mcb != NULL) {
128 char buf[2048];
130 /* TRANS: %s is a filename with suffix '.modpack' */
131 fc_snprintf(buf, sizeof(buf), _("Downloading \"%s\" control file."), URL + start_idx);
132 mcb(buf);
135 control = netfile_get_section_file(URL, nf_cb, mcb);
137 if (control == NULL) {
138 return _("Failed to get and parse modpack control file");
141 control_capstr = secfile_lookup_str(control, "info.options");
142 if (control_capstr == NULL) {
143 secfile_destroy(control);
144 return _("Modpack control file has no capability string");
147 if (!has_capabilities(MODPACK_CAPSTR, control_capstr)) {
148 log_error("Incompatible control file:");
149 log_error(" control file options: %s", control_capstr);
150 log_error(" supported options: %s", MODPACK_CAPSTR);
152 secfile_destroy(control);
154 return _("Modpack control file is incompatible");
157 mpname = secfile_lookup_str(control, "info.name");
158 if (mpname == NULL) {
159 return _("Modpack name not defined in control file");
161 mpver = secfile_lookup_str(control, "info.version");
162 if (mpver == NULL) {
163 return _("Modpack version not defined in control file");
166 typestr = secfile_lookup_str(control, "info.type");
167 type = modpack_type_by_name(typestr, fc_strcasecmp);
168 if (!modpack_type_is_valid(type)) {
169 return _("Illegal modpack type");
172 if (type == MPT_SCENARIO) {
173 fc_snprintf(local_dir, sizeof(local_dir),
174 "%s" DIR_SEPARATOR "scenarios", fcmp->inst_prefix);
175 } else {
176 fc_snprintf(local_dir, sizeof(local_dir),
177 "%s" DIR_SEPARATOR DATASUBDIR, fcmp->inst_prefix);
180 baseURLpart = secfile_lookup_str(control, "info.baseURL");
182 if (baseURLpart[0] == '.') {
183 char URLstart[start_idx];
185 strncpy(URLstart, URL, start_idx - 1);
186 URLstart[start_idx - 1] = '\0';
187 fc_snprintf(baseURL, sizeof(baseURL), "%s%s",
188 URLstart, baseURLpart + 1);
189 } else {
190 strncpy(baseURL, baseURLpart, sizeof(baseURL));
193 dep = 0;
194 do {
195 dep_name = secfile_lookup_str_default(control, NULL,
196 "dependencies.list%d.modpack", dep);
197 if (dep_name != NULL) {
198 const char *dep_URL;
199 const char *inst_ver;
200 const char *dep_typestr;
201 enum modpack_type dep_type;
202 bool needed = TRUE;
204 dep_URL = secfile_lookup_str_default(control, NULL,
205 "dependencies.list%d.URL", dep);
207 if (dep_URL == NULL) {
208 return _("Dependency has no download URL");
211 dep_typestr = secfile_lookup_str(control, "dependencies.list%d.type", dep);
212 dep_type = modpack_type_by_name(dep_typestr, fc_strcasecmp);
213 if (!modpack_type_is_valid(dep_type)) {
214 return _("Illegal dependency modpack type");
217 inst_ver = mpdb_installed_version(dep_name, type);
219 if (inst_ver != NULL) {
220 const char *dep_ver;
222 dep_ver = secfile_lookup_str_default(control, NULL,
223 "dependencies.list%d.version", dep);
225 if (dep_ver != NULL && cvercmp_max(dep_ver, inst_ver)) {
226 needed = FALSE;
230 if (needed) {
231 const char *msg;
232 char dep_URL_full[2048];
234 log_debug("Dependency modpack \"%s\" needed.", dep_name);
236 if (mcb != NULL) {
237 mcb(_("Download dependency modpack"));
240 if (dep_URL[0] == '.') {
241 char URLstart[start_idx];
243 strncpy(URLstart, URL, start_idx - 1);
244 URLstart[start_idx - 1] = '\0';
245 fc_snprintf(dep_URL_full, sizeof(dep_URL_full), "%s%s",
246 URLstart, dep_URL + 1);
247 } else {
248 strncpy(dep_URL_full, dep_URL, sizeof(dep_URL_full));
251 msg = download_modpack_recursive(dep_URL_full, fcmp, mcb, pbcb, recursion + 1);
253 if (msg != NULL) {
254 return msg;
259 dep++;
261 } while (dep_name != NULL);
264 total_files = 0;
265 do {
266 src_name = secfile_lookup_str_default(control, NULL,
267 "files.list%d.src", total_files);
269 if (src_name != NULL) {
270 total_files++;
272 } while (src_name != NULL);
274 if (pbcb != NULL) {
275 /* Control file already downloaded */
276 pbcb(1, total_files + 1);
279 filenbr = 0;
280 for (filenbr = 0; filenbr < total_files; filenbr++) {
281 const char *dest_name;
283 #ifndef DIR_SEPARATOR_IS_DEFAULT
284 char *dest_name_copy;
285 #else /* DIR_SEPARATOR_IS_DEFAULT */
286 #define dest_name_copy dest_name
287 #endif /* DIR_SEPARATOR_IS_DEFAULT */
289 int i;
290 bool illegal_filename = FALSE;
292 src_name = secfile_lookup_str_default(control, NULL,
293 "files.list%d.src", filenbr);
295 dest_name = secfile_lookup_str_default(control, NULL,
296 "files.list%d.dest", filenbr);
298 if (dest_name == NULL || dest_name[0] == '\0') {
299 /* Missing dest name is ok, we just default to src_name */
300 dest_name = src_name;
303 #ifndef DIR_SEPARATOR_IS_DEFAULT
304 dest_name_copy = fc_malloc(strlen(dest_name) + 1);
305 #endif /* DIR_SEPARATOR_IS_DEFAULT */
307 for (i = 0; dest_name[i] != '\0'; i++) {
308 if (dest_name[i] == '.' && dest_name[i+1] == '.') {
309 if (mcb != NULL) {
310 char buf[2048];
312 fc_snprintf(buf, sizeof(buf), _("Illegal path for %s"),
313 dest_name);
314 mcb(buf);
316 partial_failure = TRUE;
317 illegal_filename = TRUE;
320 #ifndef DIR_SEPARATOR_IS_DEFAULT
321 if (dest_name[i] == '/') {
322 dest_name_copy[i] = DIR_SEPARATOR_CHAR;
323 } else {
324 dest_name_copy[i] = dest_name[i];
326 #endif /* DIR_SEPARATOR_IS_DEFAULT */
329 #ifndef DIR_SEPARATOR_IS_DEFAULT
330 dest_name_copy[i] = '\0';
331 #endif /* DIR_SEPARATOR_IS_DEFAULT */
333 if (!illegal_filename) {
334 fc_snprintf(local_name, sizeof(local_name),
335 "%s" DIR_SEPARATOR "%s", local_dir, dest_name_copy);
337 #ifndef DIR_SEPARATOR_IS_DEFAULT
338 free(dest_name_copy);
339 #endif /* DIR_SEPARATOR_IS_DEFAULT */
341 for (i = strlen(local_name) - 1 ; local_name[i] != DIR_SEPARATOR_CHAR ; i--) {
342 /* Nothing */
344 local_name[i] = '\0';
345 log_debug("Create directory \"%s\"", local_name);
346 if (!make_dir(local_name)) {
347 secfile_destroy(control);
348 return _("Cannot create required directories");
350 local_name[i] = DIR_SEPARATOR_CHAR;
352 if (mcb != NULL) {
353 char buf[2048];
355 fc_snprintf(buf, sizeof(buf), _("Downloading %s"), src_name);
356 mcb(buf);
359 fc_snprintf(fileURL, sizeof(fileURL), "%s/%s", baseURL, src_name);
360 log_debug("Download \"%s\" as \"%s\".", fileURL, local_name);
361 if (!netfile_download_file(fileURL, local_name, nf_cb, mcb)) {
362 if (mcb != NULL) {
363 char buf[2048];
365 fc_snprintf(buf, sizeof(buf), _("Failed to download %s"),
366 src_name);
367 mcb(buf);
369 partial_failure = TRUE;
371 } else {
372 #ifndef DIR_SEPARATOR_IS_DEFAULT
373 free(dest_name_copy);
374 #endif /* DIR_SEPARATOR_IS_DEFAULT */
377 if (pbcb != NULL) {
378 /* Count download of control file also */
379 pbcb(filenbr + 2, total_files + 1);
383 if (partial_failure) {
384 secfile_destroy(control);
386 return _("Some parts of the modpack failed to install.");
389 mpdb_update_modpack(mpname, type, mpver);
391 secfile_destroy(control);
393 return NULL;
396 /**************************************************************************
397 Download modpack list
398 **************************************************************************/
399 const char *download_modpack_list(const struct fcmp_params *fcmp,
400 modpack_list_setup_cb cb,
401 dl_msg_callback mcb)
403 struct section_file *list_file;
404 const char *list_capstr;
405 int modpack_count;
406 const char *msg;
407 const char *mp_name;
408 int start_idx;
410 list_file = netfile_get_section_file(fcmp->list_url, nf_cb, mcb);
412 if (list_file == NULL) {
413 return _("Cannot fetch and parse modpack list");
416 for (start_idx = strlen(fcmp->list_url);
417 start_idx > 0 && fcmp->list_url[start_idx - 1] != '/';
418 start_idx--) {
419 /* Nothing */
422 list_capstr = secfile_lookup_str(list_file, "info.options");
423 if (list_capstr == NULL) {
424 secfile_destroy(list_file);
425 return _("Modpack list has no capability string");
428 if (!has_capabilities(MODLIST_CAPSTR, list_capstr)) {
429 log_error("Incompatible modpack list file:");
430 log_error(" list file options: %s", list_capstr);
431 log_error(" supported options: %s", MODLIST_CAPSTR);
433 secfile_destroy(list_file);
435 return _("Modpack list is incompatible");
438 msg = secfile_lookup_str_default(list_file, NULL, "info.message");
440 if (msg != NULL) {
441 mcb(msg);
444 modpack_count = 0;
445 do {
446 const char *mpURL;
447 const char *mpver;
448 const char *mplic;
449 const char *mp_type_str;
450 const char *mp_subtype;
451 const char *mp_notes;
453 mp_name = secfile_lookup_str_default(list_file, NULL,
454 "modpacks.list%d.name", modpack_count);
455 mpver = secfile_lookup_str_default(list_file, NULL,
456 "modpacks.list%d.version",
457 modpack_count);
458 mplic = secfile_lookup_str_default(list_file, NULL,
459 "modpacks.list%d.license",
460 modpack_count);
461 mp_type_str = secfile_lookup_str_default(list_file, NULL,
462 "modpacks.list%d.type",
463 modpack_count);
464 mp_subtype = secfile_lookup_str_default(list_file, NULL,
465 "modpacks.list%d.subtype",
466 modpack_count);
467 mpURL = secfile_lookup_str_default(list_file, NULL,
468 "modpacks.list%d.URL", modpack_count);
469 mp_notes = secfile_lookup_str_default(list_file, NULL,
470 "modpacks.list%d.notes", modpack_count);
472 if (mp_name != NULL && mpURL != NULL) {
473 char mpURL_full[2048];
474 enum modpack_type type = modpack_type_by_name(mp_type_str, fc_strcasecmp);
476 if (!modpack_type_is_valid(type)) {
477 log_error("Illegal modpack type \"%s\"", mp_type_str ? mp_type_str : "NULL");
479 if (mpver == NULL) {
480 mpver = "-";
482 if (mp_subtype == NULL) {
483 mp_subtype = "-";
486 if (mpURL[0] == '.') {
487 char URLstart[start_idx];
489 strncpy(URLstart, fcmp->list_url, start_idx - 1);
490 URLstart[start_idx - 1] = '\0';
491 fc_snprintf(mpURL_full, sizeof(mpURL_full), "%s%s",
492 URLstart, mpURL + 1);
493 } else {
494 strncpy(mpURL_full, mpURL, sizeof(mpURL_full));
497 cb(mp_name, mpURL_full, mpver, mplic, type, _(mp_subtype), mp_notes);
499 modpack_count++;
500 } while (mp_name != NULL);
502 return NULL;