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"
45 static const char *download_modpack_recursive(const char *URL
,
46 const struct fcmp_params
*fcmp
,
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
;
63 /**************************************************************************
64 Download modpack from a given URL
65 **************************************************************************/
66 const char *download_modpack(const char *URL
,
67 const struct fcmp_params
*fcmp
,
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
,
84 char local_name
[2048];
86 int filenbr
, total_files
;
87 struct section_file
*control
;
88 const char *control_capstr
;
89 const char *baseURLpart
;
90 enum modpack_type type
;
97 bool partial_failure
= FALSE
;
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
),
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] != '/';
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");
130 /* TRANS: %s is a filename with suffix '.modpack' */
131 fc_snprintf(buf
, sizeof(buf
), _("Downloading \"%s\" control file."), URL
+ start_idx
);
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");
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
);
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);
190 strncpy(baseURL
, baseURLpart
, sizeof(baseURL
));
195 dep_name
= secfile_lookup_str_default(control
, NULL
,
196 "dependencies.list%d.modpack", dep
);
197 if (dep_name
!= NULL
) {
199 const char *inst_ver
;
200 const char *dep_typestr
;
201 enum modpack_type dep_type
;
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
) {
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
)) {
232 char dep_URL_full
[2048];
234 log_debug("Dependency modpack \"%s\" needed.", dep_name
);
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);
248 strncpy(dep_URL_full
, dep_URL
, sizeof(dep_URL_full
));
251 msg
= download_modpack_recursive(dep_URL_full
, fcmp
, mcb
, pbcb
, recursion
+ 1);
261 } while (dep_name
!= NULL
);
266 src_name
= secfile_lookup_str_default(control
, NULL
,
267 "files.list%d.src", total_files
);
269 if (src_name
!= NULL
) {
272 } while (src_name
!= NULL
);
275 /* Control file already downloaded */
276 pbcb(1, total_files
+ 1);
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 */
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] == '.') {
312 fc_snprintf(buf
, sizeof(buf
), _("Illegal path for %s"),
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
;
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
--) {
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
;
355 fc_snprintf(buf
, sizeof(buf
), _("Downloading %s"), src_name
);
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
)) {
365 fc_snprintf(buf
, sizeof(buf
), _("Failed to download %s"),
369 partial_failure
= TRUE
;
372 #ifndef DIR_SEPARATOR_IS_DEFAULT
373 free(dest_name_copy
);
374 #endif /* DIR_SEPARATOR_IS_DEFAULT */
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
);
396 /**************************************************************************
397 Download modpack list
398 **************************************************************************/
399 const char *download_modpack_list(const struct fcmp_params
*fcmp
,
400 modpack_list_setup_cb cb
,
403 struct section_file
*list_file
;
404 const char *list_capstr
;
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] != '/';
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");
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",
458 mplic
= secfile_lookup_str_default(list_file
, NULL
,
459 "modpacks.list%d.license",
461 mp_type_str
= secfile_lookup_str_default(list_file
, NULL
,
462 "modpacks.list%d.type",
464 mp_subtype
= secfile_lookup_str_default(list_file
, NULL
,
465 "modpacks.list%d.subtype",
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");
482 if (mp_subtype
== NULL
) {
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);
494 strncpy(mpURL_full
, mpURL
, sizeof(mpURL_full
));
497 cb(mp_name
, mpURL_full
, mpver
, mplic
, type
, _(mp_subtype
), mp_notes
);
500 } while (mp_name
!= NULL
);