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 <curl/curl.h>
19
20 #ifdef WIN32_NATIVE
21 #include <windows.h>
22 #endif
23
24 /* utility */
25 #include "fcintl.h"
26 #include "ioz.h"
27 #include "mem.h"
28 #include "rand.h"
29 #include "registry.h"
30
31 #include "netfile.h"
32
33 struct netfile_post {
34 struct curl_httppost *first;
35 struct curl_httppost *last;
36 };
37
38 typedef size_t (*netfile_write_cb)(char *ptr, size_t size, size_t nmemb, void *userdata);
39
40 static char error_buf_curl[CURL_ERROR_SIZE];
41
42 /**********************************************************************
43 Set handle to usable state.
44 ***********************************************************************/
netfile_init_handle(void)45 static CURL *netfile_init_handle(void)
46 {
47 /* Consecutive transfers can use same handle for better performance */
48 static CURL *handle = NULL;
49
50 if (handle == NULL) {
51 handle = curl_easy_init();
52 } else {
53 curl_easy_reset(handle);
54 }
55
56 error_buf_curl[0] = '\0';
57 curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, error_buf_curl);
58
59 #ifdef CUSTOM_CACERT_PATH
60 curl_easy_setopt(handle, CURLOPT_CAINFO, CUSTOM_CACERT_PATH);
61 #endif /* CUSTOM_CERT_PATH */
62
63 return handle;
64 }
65
66 /**********************************************************************
67 curl write callback to store received file to memory.
68 ***********************************************************************/
netfile_memwrite_cb(char * ptr,size_t size,size_t nmemb,void * userdata)69 static size_t netfile_memwrite_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
70 {
71 struct netfile_write_cb_data *data = (struct netfile_write_cb_data *)userdata;
72
73 if (size > 0) {
74 data->mem = fc_realloc(data->mem, data->size + size * nmemb);
75 memcpy(data->mem + data->size, ptr, size * nmemb);
76 data->size += size * nmemb;
77 }
78
79 return size * nmemb;
80 }
81
82 /**********************************************************************
83 Fetch file from given URL to given file stream. This is core
84 function of netfile module.
85 ***********************************************************************/
netfile_download_file_core(const char * URL,FILE * fp,struct netfile_write_cb_data * mem_data,nf_errmsg cb,void * data)86 static bool netfile_download_file_core(const char *URL, FILE *fp,
87 struct netfile_write_cb_data *mem_data,
88 nf_errmsg cb, void *data)
89 {
90 CURLcode curlret;
91 struct curl_slist *headers = NULL;
92 static CURL *handle;
93 bool ret = TRUE;
94
95 handle = netfile_init_handle();
96
97 headers = curl_slist_append(headers,"User-Agent: Freeciv/" VERSION_STRING);
98
99 curl_easy_setopt(handle, CURLOPT_URL, URL);
100 if (mem_data != NULL) {
101 mem_data->mem = NULL;
102 mem_data->size = 0;
103 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, netfile_memwrite_cb);
104 curl_easy_setopt(handle, CURLOPT_WRITEDATA, mem_data);
105 } else {
106 curl_easy_setopt(handle, CURLOPT_WRITEDATA, fp);
107 }
108 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
109 curl_easy_setopt(handle, CURLOPT_FAILONERROR, 1);
110
111 curlret = curl_easy_perform(handle);
112
113 curl_slist_free_all(headers);
114
115 if (curlret != CURLE_OK) {
116 if (cb != NULL) {
117 char buf[2048 + CURL_ERROR_SIZE];
118
119 fc_snprintf(buf, sizeof(buf),
120 /* TRANS: first %s is URL, second is Curl error message
121 * (not in Freeciv translation domain) */
122 _("Failed to fetch %s: %s"), URL,
123 error_buf_curl[0] != '\0' ? error_buf_curl : curl_easy_strerror(curlret));
124 cb(buf, data);
125 }
126
127 ret = FALSE;
128 }
129
130 return ret;
131 }
132
133 /**********************************************************************
134 Fetch section file from net
135 ***********************************************************************/
netfile_get_section_file(const char * URL,nf_errmsg cb,void * data)136 struct section_file *netfile_get_section_file(const char *URL,
137 nf_errmsg cb, void *data)
138 {
139 bool success;
140 struct section_file *out = NULL;
141 struct netfile_write_cb_data mem_data;
142 fz_FILE *file;
143
144 success = netfile_download_file_core(URL, NULL, &mem_data, cb, data);
145
146 if (success) {
147 file = fz_from_memory(mem_data.mem, mem_data.size, TRUE);
148
149 out = secfile_from_stream(file, TRUE);
150 }
151
152 return out;
153 }
154
155 /**********************************************************************
156 Fetch file from given URL and save as given filename.
157 ***********************************************************************/
netfile_download_file(const char * URL,const char * filename,nf_errmsg cb,void * data)158 bool netfile_download_file(const char *URL, const char *filename,
159 nf_errmsg cb, void *data)
160 {
161 bool success;
162 FILE *fp;
163
164 fp = fc_fopen(filename, "w+b");
165
166 if (fp == NULL) {
167 if (cb != NULL) {
168 char buf[2048];
169
170 fc_snprintf(buf, sizeof(buf),
171 _("Could not open %s for writing"), filename);
172 cb(buf, data);
173 }
174 return FALSE;
175 }
176
177 success = netfile_download_file_core(URL, fp, NULL, cb, data);
178
179 fclose(fp);
180
181 return success;
182 }
183
184 /**********************************************************************
185 Allocate netfile_post
186 ***********************************************************************/
netfile_start_post(void)187 struct netfile_post *netfile_start_post(void)
188 {
189 return fc_calloc(1, sizeof(struct netfile_post));
190 }
191
192 /**********************************************************************
193 Add one entry to netfile post form
194 ***********************************************************************/
netfile_add_form_str(struct netfile_post * post,const char * name,const char * val)195 void netfile_add_form_str(struct netfile_post *post,
196 const char *name, const char *val)
197 {
198 curl_formadd(&post->first, &post->last,
199 CURLFORM_COPYNAME, name,
200 CURLFORM_COPYCONTENTS, val,
201 CURLFORM_END);
202 }
203
204 /**********************************************************************
205 Add one integer entry to netfile post form
206 ***********************************************************************/
netfile_add_form_int(struct netfile_post * post,const char * name,const int val)207 void netfile_add_form_int(struct netfile_post *post,
208 const char *name, const int val)
209 {
210 char buf[50];
211
212 fc_snprintf(buf, sizeof(buf), "%d", val);
213 netfile_add_form_str(post, name, buf);
214 }
215
216 /**********************************************************************
217 Free netfile_post resources
218 ***********************************************************************/
netfile_close_post(struct netfile_post * post)219 void netfile_close_post(struct netfile_post *post)
220 {
221 curl_formfree(post->first);
222 FC_FREE(post);
223 }
224
225 /**********************************************************************
226 Dummy write callback used only to make sure curl's default write
227 function does not get used as we don't want reply to stdout
228 ***********************************************************************/
dummy_write(void * buffer,size_t size,size_t nmemb,void * userp)229 static size_t dummy_write(void *buffer, size_t size, size_t nmemb, void *userp)
230 {
231 return size * nmemb;
232 }
233
234 /**********************************************************************
235 Send HTTP POST
236 ***********************************************************************/
netfile_send_post(const char * URL,struct netfile_post * post,FILE * reply_fp,struct netfile_write_cb_data * mem_data,const char * addr)237 bool netfile_send_post(const char *URL, struct netfile_post *post,
238 FILE *reply_fp, struct netfile_write_cb_data *mem_data,
239 const char *addr)
240 {
241 CURLcode curlret;
242 long http_resp;
243 struct curl_slist *headers = NULL;
244 static CURL *handle;
245
246 handle = netfile_init_handle();
247
248 headers = curl_slist_append(headers,"User-Agent: Freeciv/" VERSION_STRING);
249
250 curl_easy_setopt(handle, CURLOPT_URL, URL);
251 curl_easy_setopt(handle, CURLOPT_HTTPPOST, post->first);
252 if (mem_data != NULL) {
253 mem_data->mem = NULL;
254 mem_data->size = 0;
255 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, netfile_memwrite_cb);
256 curl_easy_setopt(handle, CURLOPT_WRITEDATA, mem_data);
257 } else if (reply_fp == NULL) {
258 curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, dummy_write);
259 } else {
260 curl_easy_setopt(handle, CURLOPT_WRITEDATA, reply_fp);
261 }
262 if (addr != NULL) {
263 curl_easy_setopt(handle, CURLOPT_INTERFACE, addr);
264 }
265 curl_easy_setopt(handle, CURLOPT_HTTPHEADER, headers);
266
267 curlret = curl_easy_perform(handle);
268
269 curl_slist_free_all(headers);
270
271 if (curlret != CURLE_OK) {
272 return FALSE;
273 }
274
275 curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &http_resp);
276
277 if (http_resp != 200) {
278 return FALSE;
279 }
280
281 return TRUE;
282 }
283