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)
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 ***********************************************************************/
15 #include <fc_config.h>
18 #include "fc_prehdrs.h"
20 #ifdef FREECIV_HAVE_SYS_TYPES_H
21 #include <sys/types.h>
23 #ifdef HAVE_SYS_SOCKET_H
24 #include <sys/socket.h>
33 #include "capability.h"
43 static const char *download_modpack_recursive(const char *URL
,
44 const struct fcmp_params
*fcmp
,
49 /**************************************************************************
50 Message callback called by netfile module when downloading files.
51 **************************************************************************/
52 static void nf_cb(const char *msg
, void *data
)
54 dl_msg_callback mcb
= (dl_msg_callback
) data
;
61 /**************************************************************************
62 Download modpack from a given URL
63 **************************************************************************/
64 const char *download_modpack(const char *URL
,
65 const struct fcmp_params
*fcmp
,
69 return download_modpack_recursive(URL
, fcmp
, mcb
, pbcb
, 0);
72 /**************************************************************************
73 Download modpack and its recursive dependencies.
74 **************************************************************************/
75 static const char *download_modpack_recursive(const char *URL
,
76 const struct fcmp_params
*fcmp
,
82 char local_name
[2048];
84 int filenbr
, total_files
;
85 struct section_file
*control
;
86 const char *control_capstr
;
87 const char *baseURLpart
;
88 enum modpack_type type
;
95 bool partial_failure
= FALSE
;
100 return _("Recursive dependencies too deep");
103 if (URL
== NULL
|| URL
[0] == '\0') {
104 return _("No URL given");
107 if (strlen(URL
) < strlen(MODPACK_SUFFIX
)
108 || strcmp(URL
+ strlen(URL
) - strlen(MODPACK_SUFFIX
), MODPACK_SUFFIX
)) {
109 return _("This does not look like modpack URL");
112 for (start_idx
= strlen(URL
) - strlen(MODPACK_SUFFIX
);
113 start_idx
> 0 && URL
[start_idx
- 1] != '/';
118 log_normal(_("Installing modpack %s from %s"), URL
+ start_idx
, URL
);
120 if (fcmp
->inst_prefix
== NULL
) {
121 return _("Cannot install to given directory hierarchy");
127 /* TRANS: %s is a filename with suffix '.modpack' */
128 fc_snprintf(buf
, sizeof(buf
), _("Downloading \"%s\" control file."), URL
+ start_idx
);
132 control
= netfile_get_section_file(URL
, nf_cb
, mcb
);
134 if (control
== NULL
) {
135 return _("Failed to get and parse modpack control file");
138 control_capstr
= secfile_lookup_str(control
, "info.options");
139 if (control_capstr
== NULL
) {
140 secfile_destroy(control
);
141 return _("Modpack control file has no capability string");
144 if (!has_capabilities(MODPACK_CAPSTR
, control_capstr
)) {
145 log_error("Incompatible control file:");
146 log_error(" control file options: %s", control_capstr
);
147 log_error(" supported options: %s", MODPACK_CAPSTR
);
149 secfile_destroy(control
);
151 return _("Modpack control file is incompatible");
154 mpname
= secfile_lookup_str(control
, "info.name");
155 if (mpname
== NULL
) {
156 return _("Modpack name not defined in control file");
158 mpver
= secfile_lookup_str(control
, "info.version");
160 return _("Modpack version not defined in control file");
163 typestr
= secfile_lookup_str(control
, "info.type");
164 type
= modpack_type_by_name(typestr
, fc_strcasecmp
);
165 if (!modpack_type_is_valid(type
)) {
166 return _("Illegal modpack type");
169 if (type
== MPT_SCENARIO
) {
170 fc_snprintf(local_dir
, sizeof(local_dir
),
171 "%s" DIR_SEPARATOR
"scenarios", fcmp
->inst_prefix
);
173 fc_snprintf(local_dir
, sizeof(local_dir
),
174 "%s" DIR_SEPARATOR DATASUBDIR
, fcmp
->inst_prefix
);
177 baseURLpart
= secfile_lookup_str(control
, "info.baseURL");
179 if (baseURLpart
[0] == '.') {
180 char URLstart
[start_idx
];
182 strncpy(URLstart
, URL
, start_idx
- 1);
183 URLstart
[start_idx
- 1] = '\0';
184 fc_snprintf(baseURL
, sizeof(baseURL
), "%s%s",
185 URLstart
, baseURLpart
+ 1);
187 strncpy(baseURL
, baseURLpart
, sizeof(baseURL
));
192 dep_name
= secfile_lookup_str_default(control
, NULL
,
193 "dependencies.list%d.modpack", dep
);
194 if (dep_name
!= NULL
) {
196 const char *inst_ver
;
197 const char *dep_typestr
;
198 enum modpack_type dep_type
;
201 dep_URL
= secfile_lookup_str_default(control
, NULL
,
202 "dependencies.list%d.URL", dep
);
204 if (dep_URL
== NULL
) {
205 return _("Dependency has no download URL");
208 dep_typestr
= secfile_lookup_str(control
, "dependencies.list%d.type", dep
);
209 dep_type
= modpack_type_by_name(dep_typestr
, fc_strcasecmp
);
210 if (!modpack_type_is_valid(dep_type
)) {
211 return _("Illegal dependency modpack type");
214 inst_ver
= get_installed_version(dep_name
, type
);
216 if (inst_ver
!= NULL
) {
219 dep_ver
= secfile_lookup_str_default(control
, NULL
,
220 "dependencies.list%d.version", dep
);
222 if (dep_ver
!= NULL
&& cvercmp_max(dep_ver
, inst_ver
)) {
229 char dep_URL_full
[2048];
231 log_debug("Dependency modpack \"%s\" needed.", dep_name
);
234 mcb(_("Download dependency modpack"));
237 if (dep_URL
[0] == '.') {
238 char URLstart
[start_idx
];
240 strncpy(URLstart
, URL
, start_idx
- 1);
241 URLstart
[start_idx
- 1] = '\0';
242 fc_snprintf(dep_URL_full
, sizeof(dep_URL_full
), "%s%s",
243 URLstart
, dep_URL
+ 1);
245 strncpy(dep_URL_full
, dep_URL
, sizeof(dep_URL_full
));
248 msg
= download_modpack_recursive(dep_URL_full
, fcmp
, mcb
, pbcb
, recursion
+ 1);
258 } while (dep_name
!= NULL
);
263 src_name
= secfile_lookup_str_default(control
, NULL
,
264 "files.list%d.src", total_files
);
266 if (src_name
!= NULL
) {
269 } while (src_name
!= NULL
);
272 /* Control file already downloaded */
273 pbcb(1, total_files
+ 1);
277 for (filenbr
= 0; filenbr
< total_files
; filenbr
++) {
278 const char *dest_name
;
280 #ifndef DIR_SEPARATOR_IS_DEFAULT
281 char *dest_name_copy
;
282 #else /* DIR_SEPARATOR_IS_DEFAULT */
283 #define dest_name_copy dest_name
284 #endif /* DIR_SEPARATOR_IS_DEFAULT */
287 bool illegal_filename
= FALSE
;
289 src_name
= secfile_lookup_str_default(control
, NULL
,
290 "files.list%d.src", filenbr
);
292 dest_name
= secfile_lookup_str_default(control
, NULL
,
293 "files.list%d.dest", filenbr
);
295 if (dest_name
== NULL
|| dest_name
[0] == '\0') {
296 /* Missing dest name is ok, we just default to src_name */
297 dest_name
= src_name
;
300 #ifndef DIR_SEPARATOR_IS_DEFAULT
301 dest_name_copy
= fc_malloc(strlen(dest_name
) + 1);
302 #endif /* DIR_SEPARATOR_IS_DEFAULT */
304 for (i
= 0; dest_name
[i
] != '\0'; i
++) {
305 if (dest_name
[i
] == '.' && dest_name
[i
+1] == '.') {
309 fc_snprintf(buf
, sizeof(buf
), _("Illegal path for %s"),
313 partial_failure
= TRUE
;
314 illegal_filename
= TRUE
;
317 #ifndef DIR_SEPARATOR_IS_DEFAULT
318 if (dest_name
[i
] == '/') {
319 dest_name_copy
[i
] = DIR_SEPARATOR_CHAR
;
321 dest_name_copy
[i
] = dest_name
[i
];
323 #endif /* DIR_SEPARATOR_IS_DEFAULT */
326 #ifndef DIR_SEPARATOR_IS_DEFAULT
327 dest_name_copy
[i
] = '\0';
328 #endif /* DIR_SEPARATOR_IS_DEFAULT */
330 if (!illegal_filename
) {
331 fc_snprintf(local_name
, sizeof(local_name
),
332 "%s" DIR_SEPARATOR
"%s", local_dir
, dest_name_copy
);
334 #ifndef DIR_SEPARATOR_IS_DEFAULT
335 free(dest_name_copy
);
336 #endif /* DIR_SEPARATOR_IS_DEFAULT */
338 for (i
= strlen(local_name
) - 1 ; local_name
[i
] != DIR_SEPARATOR_CHAR
; i
--) {
341 local_name
[i
] = '\0';
342 log_debug("Create directory \"%s\"", local_name
);
343 if (!make_dir(local_name
)) {
344 secfile_destroy(control
);
345 return _("Cannot create required directories");
347 local_name
[i
] = DIR_SEPARATOR_CHAR
;
352 fc_snprintf(buf
, sizeof(buf
), _("Downloading %s"), src_name
);
356 fc_snprintf(fileURL
, sizeof(fileURL
), "%s/%s", baseURL
, src_name
);
357 log_debug("Download \"%s\" as \"%s\".", fileURL
, local_name
);
358 if (!netfile_download_file(fileURL
, local_name
, nf_cb
, mcb
)) {
362 fc_snprintf(buf
, sizeof(buf
), _("Failed to download %s"),
366 partial_failure
= TRUE
;
369 #ifndef DIR_SEPARATOR_IS_DEFAULT
370 free(dest_name_copy
);
371 #endif /* DIR_SEPARATOR_IS_DEFAULT */
375 /* Count download of control file also */
376 pbcb(filenbr
+ 2, total_files
+ 1);
380 if (partial_failure
) {
381 secfile_destroy(control
);
383 return _("Some parts of the modpack failed to install.");
386 update_install_info_lists(mpname
, type
, mpver
);
388 secfile_destroy(control
);
393 /**************************************************************************
394 Download modpack list
395 **************************************************************************/
396 const char *download_modpack_list(const struct fcmp_params
*fcmp
,
397 modpack_list_setup_cb cb
,
400 struct section_file
*list_file
;
401 const char *list_capstr
;
407 list_file
= netfile_get_section_file(fcmp
->list_url
, nf_cb
, mcb
);
409 if (list_file
== NULL
) {
410 return _("Cannot fetch and parse modpack list");
413 for (start_idx
= strlen(fcmp
->list_url
);
414 start_idx
> 0 && fcmp
->list_url
[start_idx
- 1] != '/';
419 list_capstr
= secfile_lookup_str(list_file
, "info.options");
420 if (list_capstr
== NULL
) {
421 secfile_destroy(list_file
);
422 return _("Modpack list has no capability string");
425 if (!has_capabilities(MODLIST_CAPSTR
, list_capstr
)) {
426 log_error("Incompatible modpack list file:");
427 log_error(" list file options: %s", list_capstr
);
428 log_error(" supported options: %s", MODLIST_CAPSTR
);
430 secfile_destroy(list_file
);
432 return _("Modpack list is incompatible");
435 msg
= secfile_lookup_str_default(list_file
, NULL
, "info.message");
446 const char *mp_type_str
;
447 const char *mp_subtype
;
448 const char *mp_notes
;
450 mp_name
= secfile_lookup_str_default(list_file
, NULL
,
451 "modpacks.list%d.name", modpack_count
);
452 mpver
= secfile_lookup_str_default(list_file
, NULL
,
453 "modpacks.list%d.version",
455 mplic
= secfile_lookup_str_default(list_file
, NULL
,
456 "modpacks.list%d.license",
458 mp_type_str
= secfile_lookup_str_default(list_file
, NULL
,
459 "modpacks.list%d.type",
461 mp_subtype
= secfile_lookup_str_default(list_file
, NULL
,
462 "modpacks.list%d.subtype",
464 mpURL
= secfile_lookup_str_default(list_file
, NULL
,
465 "modpacks.list%d.URL", modpack_count
);
466 mp_notes
= secfile_lookup_str_default(list_file
, NULL
,
467 "modpacks.list%d.notes", modpack_count
);
469 if (mp_name
!= NULL
&& mpURL
!= NULL
) {
470 char mpURL_full
[2048];
471 enum modpack_type type
= modpack_type_by_name(mp_type_str
, fc_strcasecmp
);
473 if (!modpack_type_is_valid(type
)) {
474 log_error("Illegal modpack type \"%s\"", mp_type_str
? mp_type_str
: "NULL");
479 if (mp_subtype
== NULL
) {
483 if (mpURL
[0] == '.') {
484 char URLstart
[start_idx
];
486 strncpy(URLstart
, fcmp
->list_url
, start_idx
- 1);
487 URLstart
[start_idx
- 1] = '\0';
488 fc_snprintf(mpURL_full
, sizeof(mpURL_full
), "%s%s",
489 URLstart
, mpURL
+ 1);
491 strncpy(mpURL_full
, mpURL
, sizeof(mpURL_full
));
494 cb(mp_name
, mpURL_full
, mpver
, mplic
, type
, _(mp_subtype
), mp_notes
);
497 } while (mp_name
!= NULL
);