9 #include "../window_gui.h" |
9 #include "../window_gui.h" |
10 #include "../textbuf_gui.h" |
10 #include "../textbuf_gui.h" |
11 #include "../variables.h" |
11 #include "../variables.h" |
12 #include "../newgrf.h" |
12 #include "../newgrf.h" |
13 #include "newgrf_download.h" |
13 #include "newgrf_download.h" |
|
14 #include "core/config.h" |
14 #include "../functions.h" |
15 #include "../functions.h" |
|
16 #include "../debug.h" |
15 #include "../window_func.h" |
17 #include "../window_func.h" |
16 #include "../core/alloc_func.hpp" |
18 #include "../core/alloc_func.hpp" |
17 #include "../string_func.h" |
19 #include "../string_func.h" |
18 #include "../gfx_func.h" |
20 #include "../gfx_func.h" |
19 #include "../querystring_gui.h" |
21 #include "../querystring_gui.h" |
20 #include "../sortlist_type.h" |
22 #include "../sortlist_type.h" |
|
23 #include "../rev.h" |
|
24 |
|
25 // XXX: ... |
|
26 #include <curl/curl.h> |
21 |
27 |
22 #include "table/strings.h" |
28 #include "table/strings.h" |
23 #include "../table/sprites.h" |
29 #include "../table/sprites.h" |
|
30 |
|
31 static int _has_curl_init = 0; |
|
32 |
|
33 static CURLcode do_init_curl () { |
|
34 CURLcode err; |
|
35 |
|
36 if (!_has_curl_init) { |
|
37 // XXX: be smarter about what to initialize |
|
38 if ((err = curl_global_init(CURL_GLOBAL_ALL))) |
|
39 return err; |
|
40 |
|
41 _has_curl_init = 1; |
|
42 } |
|
43 |
|
44 return CURLE_OK; |
|
45 } |
|
46 |
|
47 struct check_write_ctx { |
|
48 char *buf; |
|
49 size_t len; |
|
50 size_t offset; |
|
51 }; |
|
52 |
|
53 #define QUERY_ITEM_SIZE ( \ |
|
54 4 /* the "grf=" prefix */ \ |
|
55 + 8 /* the GRFID, in hex */ \ |
|
56 + 1 /* the ":" */ \ |
|
57 + 32 /* the md5sum, in hex */ \ |
|
58 + 1 /* the "&" or '\0' */ \ |
|
59 ) |
|
60 |
|
61 static size_t check_available_write_cb (void *ptr, size_t size, size_t nmemb, void *arg) { |
|
62 struct check_write_ctx *ctx = (struct check_write_ctx *) arg; |
|
63 |
|
64 // total number of bytes |
|
65 size_t len = size * nmemb; |
|
66 |
|
67 // realloc if needed |
|
68 if (ctx->offset + len + 1 > ctx->len) { |
|
69 ctx->len *= 2; |
|
70 ctx->buf = ReallocT<char>(ctx->buf, ctx->len); |
|
71 } |
|
72 |
|
73 // thankfully we only have a limited amount of data.. |
|
74 memcpy(ctx->buf + ctx->offset, ptr, len); |
|
75 ctx->offset += len; |
|
76 *(ctx->buf + ctx->offset) = '\0'; |
|
77 |
|
78 return len; |
|
79 } |
|
80 |
|
81 /** Ask the master database about the availablity of the given NewGRFs, and update |
|
82 * the GRFConfigs that are avilable to GCS_AVAILABLE |
|
83 * @param config pointer to a linked-list of grfconfig's needed */ |
|
84 static void CheckAvailableNewGRFs(GRFConfig *list) |
|
85 { |
|
86 GRFConfig *c; |
|
87 int i; |
|
88 char md5buf[64]; |
|
89 struct check_write_ctx write_ctx; |
|
90 CURL *curl; |
|
91 char api_url[512], useragent[512]; |
|
92 char curl_errbuf[CURL_ERROR_SIZE]; |
|
93 long http_code; |
|
94 char *buf_ptr; |
|
95 CURLcode err; |
|
96 |
|
97 // initialize to a safe state for error handling |
|
98 curl = write_ctx.buf = NULL; |
|
99 |
|
100 // count how many NewGRFs in our list |
|
101 for (c = list, i = 0; c != NULL; c = c->next, i++) {} |
|
102 |
|
103 // how many bytes these take up... |
|
104 int query_size = i * QUERY_ITEM_SIZE; |
|
105 |
|
106 // allocate the buffer for the query data |
|
107 char *query_buf = MallocT<char>(query_size), *query_ptr = query_buf; |
|
108 |
|
109 // build the query |
|
110 for (c = list; c != NULL; c = c->next) { |
|
111 |
|
112 // the "&", except not for the first entry |
|
113 if (c != list) |
|
114 *(query_ptr++) = '&'; |
|
115 |
|
116 // format the md5sum |
|
117 md5sumToString(md5buf, lastof(md5buf), c->md5sum); |
|
118 |
|
119 // snprintf the entry into the query buffer |
|
120 assert(snprintf(query_ptr, QUERY_ITEM_SIZE, "grf=%08X:%32s", BSWAP32(c->grfid), md5buf) == QUERY_ITEM_SIZE -1); |
|
121 |
|
122 // advance the query_ptr to point at the nul byte |
|
123 query_ptr += QUERY_ITEM_SIZE - 1; |
|
124 } |
|
125 |
|
126 // buil the target URL by formatting NETWORK_NEWGRF_MASTER_API with the method as the only parameter |
|
127 assert(snprintf(api_url, sizeof(api_url), NETWORK_NEWGRF_MASTER_API, "search") < 512); |
|
128 |
|
129 // our useragent |
|
130 assert(snprintf(useragent, sizeof(useragent), NETWORK_HTTP_USER_AGENT, _openttd_revision) < 512); |
|
131 |
|
132 // receiving is so damn hard... |
|
133 write_ctx.buf = MallocT<char>(512); |
|
134 write_ctx.len = 512; |
|
135 write_ctx.offset = 0; |
|
136 |
|
137 // set up curl globally if needed |
|
138 if ((err = do_init_curl())) { |
|
139 DEBUG(grfdl, 0, "curl_global_init failed: %s", curl_easy_strerror(err)); |
|
140 goto error; |
|
141 } |
|
142 |
|
143 // allocate a new curl_easy |
|
144 if ((curl = curl_easy_init()) == NULL) { |
|
145 DEBUG(grfdl, 0, "curl_easy_init failed"); |
|
146 goto error; |
|
147 } |
|
148 |
|
149 // let's set some options... |
|
150 if ( |
|
151 (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_VERBOSE, 1))) // XXX: tie this in with OpenTTD's debug stuff |
|
152 /* |
|
153 curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, XXX) |
|
154 */ |
|
155 || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1))) // no progress meter on stdout, thx |
|
156 || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf))) // human-readable error messages |
|
157 || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_URL, api_url))) // the URL to retrieve |
|
158 || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_USERAGENT, useragent))) // the User-agent to use |
|
159 || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_POSTFIELDS, query_buf))) // the POST data |
|
160 || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &check_available_write_cb))) |
|
161 || (CURLE_OK != (err = curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_ctx))) // the write buffer |
|
162 ) { |
|
163 DEBUG(grfdl, 0, "curl_easy_setopt failed: %s", curl_easy_strerror(err)); |
|
164 goto error; |
|
165 } |
|
166 |
|
167 // XXX: currently this freezes the UI.. |
|
168 if ((err = curl_easy_perform(curl))) { |
|
169 DEBUG(grfdl, 0, "the curl request failed: %s", curl_easy_strerror(err)); |
|
170 goto error; |
|
171 } |
|
172 |
|
173 // get the HTTP return code |
|
174 if ((err = curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code))) { |
|
175 DEBUG(grfdl, 0, "curl_easy_getinfo failed: %s", curl_easy_strerror(err)); |
|
176 goto error; |
|
177 } |
|
178 |
|
179 if (http_code != 200) { |
|
180 DEBUG(grfdl, 0, "Bad HTTP response code: %d", http_code); |
|
181 goto error; |
|
182 } |
|
183 |
|
184 // start parsing... |
|
185 buf_ptr = write_ctx.buf; |
|
186 |
|
187 while (buf_ptr < write_ctx.buf + write_ctx.offset) { |
|
188 // replace the \n with a \0 |
|
189 char *nl = strchr(buf_ptr, '\n'); |
|
190 |
|
191 if (!nl) { |
|
192 DEBUG(grfdl, 0, "Incomplete line in search response: %s", buf_ptr); |
|
193 goto error; |
|
194 } |
|
195 |
|
196 *nl = '\0'; |
|
197 |
|
198 // check the format |
|
199 if (buf_ptr[8] != ':' || buf_ptr[8 + 1 + 32] != ':') { |
|
200 DEBUG(grfdl, 0, "Invalid line in search response: %s", buf_ptr); |
|
201 goto error; |
|
202 } |
|
203 |
|
204 // separate out the fields |
|
205 buf_ptr[8] = buf_ptr[8 + 1 + 32] = '\0'; |
|
206 |
|
207 // parse the GRFID... |
|
208 char *invalid; |
|
209 uint32 grfid = strtol(buf_ptr + 0, &invalid, 16); |
|
210 |
|
211 if (*invalid) { |
|
212 DEBUG(grfdl, 0, "Invalid GRFID in search response: %s", buf_ptr); |
|
213 goto error; |
|
214 } |
|
215 |
|
216 // parse the md5sum |
|
217 uint8 md5sum[16]; |
|
218 |
|
219 if (StringToMd5sum(md5sum, buf_ptr + 8 + 1)) { |
|
220 DEBUG(grfdl, 0, "Invalid md5sum in search response: %s", buf_ptr); |
|
221 goto error; |
|
222 } |
|
223 |
|
224 // copy the URL |
|
225 char *url = strdup(buf_ptr + 8 + 1 + 32 + 1); |
|
226 |
|
227 // look for the GRF |
|
228 for (c = list; c != NULL; c = c->next) { |
|
229 if (BSWAP32(c->grfid) == grfid && memcmp(c->md5sum, md5sum, 16) == 0) { |
|
230 DEBUG(grfdl, 0, "Found %08X:%s at %s", buf_ptr + 0, buf_ptr + 8 + 1, url); |
|
231 |
|
232 c->filename = url; |
|
233 c->status = GCS_AVAILABLE; |
|
234 break; |
|
235 } |
|
236 } |
|
237 |
|
238 // free the allocated memory if it wasn't stored in a GRFConfig |
|
239 if (c == NULL) { |
|
240 free(url); |
|
241 } |
|
242 |
|
243 // move on to the next line |
|
244 buf_ptr = nl + 1; |
|
245 } |
|
246 |
|
247 error : |
|
248 // cleanup |
|
249 free(write_ctx.buf); |
|
250 |
|
251 if (curl) |
|
252 curl_easy_cleanup(curl); |
|
253 } |
24 |
254 |
25 // XXX: copy-pasted from newgrf_gui.cpp |
255 // XXX: copy-pasted from newgrf_gui.cpp |
26 static void ShowDownloadNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, uint bottom, bool show_params) |
256 static void ShowDownloadNewGRFInfo(const GRFConfig *c, uint x, uint y, uint w, uint bottom, bool show_params) |
27 { |
257 { |
28 char buff[256]; |
258 char buff[256]; |