UFO: Alien Invasion
Loading...
Searching...
No Matches
http.cpp
Go to the documentation of this file.
1/*
2Copyright (C) 1997-2001 Id Software, Inc.
3
4This program is free software; you can redistribute it and/or
5modify it under the terms of the GNU General Public License
6as published by the Free Software Foundation; either version 2
7of the License, or (at your option) any later version.
8
9This program is distributed in the hope that it will be useful,
10but WITHOUT ANY WARRANTY; without even the implied warranty of
11MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12
13See the GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18
19*/
20
21#include "http.h"
22#ifndef NO_HTTP
23#include "../shared/shared.h"
24#include <SDL_thread.h>
25
38bool HTTP_ExtractComponents (const char* url, char* scheme, size_t schemeLength, char* host, size_t hostLength, char* path, size_t pathLength, int* port)
39{
40 char buffer[4096];
41 int i;
42 char* c;
43 char* buf;
44
45 Q_strncpyz(buffer, url, sizeof(buffer));
46
47 /* Parse the scheme */
48 for (buf = buffer, c = scheme, i = 0; *buf != '\0' && *buf != ':';) {
49 if (i >= schemeLength - 1) {
50 Com_Printf("HTTP_ExtractComponents: Scheme is too long\n");
51 return false;
52 }
53 i++;
54 *c = tolower(*buf);
55 c++;
56 buf++;
57 }
58 *c = '\0';
59
60 int defaultPort;
61 if (Q_streq("http", scheme)) {
62 defaultPort = 80;
63 } else if (Q_streq("https", scheme)) {
64 defaultPort = 443;
65 } else {
66 Com_Printf("HTTP_ExtractComponents: Not supported scheme: %s\n", scheme);
67 return false;
68 }
69 if (!Q_strneq("://", buf, 3)) {
70 Com_Printf("HTTP_ExtractComponents: Not supported scheme\n");
71 return false;
72 }
73 buf += 3;
74
75 /* parse the host */
76 for (c = host, i = 0; *buf != '\0' && *buf != ':' && *buf != '/';) {
77 if (i >= hostLength - 1) {
78 Com_Printf("HTTP_ExtractComponents: Host name is too long\n");
79 return false;
80 }
81 i++;
82 *c = tolower(*buf);
83 c++;
84 buf++;
85 }
86 *c = '\0';
87 if (Q_strnull(host)) {
88 Com_Printf("HTTP_ExtractComponents: Host name is missing\n");
89 return false;
90 }
91
92 /* parse port */
93 if (*buf == ':') {
94 buf++;
95 char portString[6];
96 for (c = portString, i = 0; *buf != '\0' && *buf != '/';) {
97 if (i >= sizeof(portString) - 1) {
98 Com_Printf("HTTP_ExtractComponents: Port specification is too long\n");
99 return false;
100 }
101 i++;
102 if (*buf < 0x30 || *buf > 0x39) {
103 Com_Printf("HTTP_ExtractComponents: Invalid characters in port specification\n");
104 return false;
105 }
106 *c++ = *buf++;
107 }
108 *c = '\0';
109 *port = atoi(portString);
110 if (*port <= 0 || *port >= 65536) {
111 Com_Printf("HTTP_ExtractComponents: Port out of bounds\n");
112 return false;
113 }
114 } else {
115 *port = defaultPort;
116 }
117
118 Q_strncpyz(path, buf, pathLength);
119
120 return true;
121}
122
126size_t HTTP_Header (void* ptr, size_t size, size_t nmemb, void* stream)
127{
128 char headerBuff[1024];
129 const size_t bytes = size * nmemb;
130 size_t len;
131
132 if (bytes <= 16)
133 return bytes;
134
135 if (bytes < sizeof(headerBuff))
136 len = bytes + 1;
137 else
138 len = sizeof(headerBuff);
139
140 Q_strncpyz(headerBuff, (const char*)ptr, len);
141
142 if (!Q_strncasecmp(headerBuff, "Content-Length: ", 16)) {
144 if (dl->file)
145 dl->fileSize = strtoul(headerBuff + 16, nullptr, 10);
146 }
147
148 return bytes;
149}
150
154size_t HTTP_Recv (void* ptr, size_t size, size_t nmemb, void* stream)
155{
156 const size_t bytes = size * nmemb;
158
159 if (!dl->fileSize) {
160 dl->fileSize = bytes > 131072 ? bytes : 131072;
161 dl->tempBuffer = Mem_AllocTypeN(char, dl->fileSize);
162 } else if (dl->position + bytes >= dl->fileSize - 1) {
163 char* tmp = dl->tempBuffer;
164 dl->tempBuffer = Mem_AllocTypeN(char, dl->fileSize * 2);
165 memcpy(dl->tempBuffer, tmp, dl->fileSize);
166 Mem_Free(tmp);
167 dl->fileSize *= 2;
168 }
169
170 memcpy(dl->tempBuffer + dl->position, ptr, bytes);
171 dl->position += bytes;
172 dl->tempBuffer[dl->position] = 0;
173
174 return bytes;
175}
176
184static void HTTP_ResolvURL (const char* url, char* buf, size_t size)
185{
186 char scheme[6];
187 char server[512];
188 char ipServer[MAX_VAR];
189 int port;
190 char uriPath[512];
191
192 buf[0] = '\0';
193
194 if (!HTTP_ExtractComponents(url, scheme, sizeof(scheme), server, sizeof(server), uriPath, sizeof(uriPath), &port))
195 Com_Error(ERR_DROP, "invalid url given: %s", url);
196
197 NET_ResolvNode(server, ipServer, sizeof(ipServer));
198 if (ipServer[0] != '\0')
199 Com_sprintf(buf, size, "%s://%s:%i%s", scheme, ipServer, port, uriPath);
200}
201
206static bool HTTP_GetURLInternal (dlhandle_t& dl, const char* url, FILE* file, const char* postfields)
207{
208 if (Q_strnull(url)) {
209 Com_Printf("invalid url given\n");
210 return false;
211 }
212
213 char buf[576];
214 HTTP_ResolvURL(url, buf, sizeof(buf));
215 if (buf[0] == '\0') {
216 Com_Printf("could not resolve '%s'\n", url);
217 return false;
218 }
219 Q_strncpyz(dl.URL, url, sizeof(dl.URL));
220
221 dl.curl = curl_easy_init();
222 curl_easy_setopt(dl.curl, CURLOPT_CONNECTTIMEOUT, http_timeout->integer);
223 curl_easy_setopt(dl.curl, CURLOPT_TIMEOUT, http_timeout->integer);
224 curl_easy_setopt(dl.curl, CURLOPT_ENCODING, "");
225 curl_easy_setopt(dl.curl, CURLOPT_NOPROGRESS, 1);
226 curl_easy_setopt(dl.curl, CURLOPT_FAILONERROR, 1);
227 if (file) {
228 curl_easy_setopt(dl.curl, CURLOPT_WRITEDATA, file);
229 curl_easy_setopt(dl.curl, CURLOPT_WRITEFUNCTION, nullptr);
230 } else {
231 curl_easy_setopt(dl.curl, CURLOPT_WRITEDATA, &dl);
232 curl_easy_setopt(dl.curl, CURLOPT_WRITEFUNCTION, HTTP_Recv);
233 }
234 curl_easy_setopt(dl.curl, CURLOPT_PROXY, http_proxy->string);
235 curl_easy_setopt(dl.curl, CURLOPT_FOLLOWLOCATION, 1);
236 curl_easy_setopt(dl.curl, CURLOPT_MAXREDIRS, 5);
237 curl_easy_setopt(dl.curl, CURLOPT_WRITEHEADER, &dl);
238 if (postfields != nullptr)
239 curl_easy_setopt(dl.curl, CURLOPT_POSTFIELDS, postfields);
240 curl_easy_setopt(dl.curl, CURLOPT_HEADERFUNCTION, HTTP_Header);
241 curl_easy_setopt(dl.curl, CURLOPT_USERAGENT, GAME_TITLE " " UFO_VERSION);
242 curl_easy_setopt(dl.curl, CURLOPT_URL, dl.URL);
243 curl_easy_setopt(dl.curl, CURLOPT_NOSIGNAL, 1);
244
245 /* get it */
246 const CURLcode result = curl_easy_perform(dl.curl);
247 if (result != CURLE_OK) {
248 if (result == CURLE_HTTP_RETURNED_ERROR) {
249 long httpCode = 0;
250 curl_easy_getinfo(dl.curl, CURLINFO_RESPONSE_CODE, &httpCode);
251 Com_Printf("failed to fetch '%s': %s (%i)\n", url, curl_easy_strerror(result), (int)httpCode);
252 } else {
253 Com_Printf("failed to fetch '%s': %s\n", url, curl_easy_strerror(result));
254 }
255 curl_easy_cleanup(dl.curl);
256 return false;
257 }
258
259 /* clean up */
260 curl_easy_cleanup(dl.curl);
261
262 return true;
263}
264
265bool HTTP_PutFile (const char* formName, const char* fileName, const char* url, const upparam_t* params)
266{
267 if (Q_strnull(url)) {
268 Com_Printf("no upload url given\n");
269 return false;
270 }
271
272 if (Q_strnull(fileName)) {
273 Com_Printf("no upload fileName given\n");
274 return false;
275 }
276
277 if (Q_strnull(formName)) {
278 Com_Printf("no upload formName given\n");
279 return false;
280 }
281
282 char buf[576];
283 HTTP_ResolvURL(url, buf, sizeof(buf));
284 if (buf[0] == '\0') {
285 Com_Printf("could not resolve '%s'\n", url);
286 return false;
287 }
288
289 CURL* curl = curl_easy_init();
290 if (curl == nullptr) {
291 Com_Printf("could not init curl\n");
292 return false;
293 }
294
295 curl_mime *mime = curl_mime_init(curl);
296 if (mime == nullptr) {
297 Com_Printf("could not init MIME for curl\n");
298 return false;
299 }
300
301 while (params) {
302 curl_mimepart *part;
303 part = curl_mime_addpart(mime);
304 curl_mime_data(part, params->value, CURL_ZERO_TERMINATED);
305 curl_mime_name(part, params->name);
306 params = params->next;
307 }
308
309 curl_mimepart *filePart;
310 filePart = curl_mime_addpart(mime);
311 curl_mime_filedata(filePart, fileName);
312 curl_mime_name(filePart, formName);
313
314 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, http_timeout->integer);
315 curl_easy_setopt(curl, CURLOPT_TIMEOUT, http_timeout->integer);
316 curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime);
317 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
318 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
319 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
320 curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_TITLE " " UFO_VERSION);
321 curl_easy_setopt(curl, CURLOPT_URL, url);
322 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
323 const CURLcode result = curl_easy_perform(curl);
324 if (result != CURLE_OK) {
325 if (result == CURLE_HTTP_RETURNED_ERROR) {
326 long httpCode = 0;
327 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
328 Com_Printf("failed to upload file '%s': %s (%i)\n", fileName, curl_easy_strerror(result), (int)httpCode);
329 } else {
330 Com_Printf("failed to upload file '%s': %s\n", fileName, curl_easy_strerror(result));
331 }
332 curl_easy_cleanup(curl);
333 return false;
334 }
335
336 curl_easy_cleanup(curl);
337 return true;
338}
339
346bool HTTP_GetToFile (const char* url, FILE* file, const char* postfields)
347{
348 if (!file)
349 return false;
350 dlhandle_t dl;
351 OBJZERO(dl);
352
353 return HTTP_GetURLInternal(dl, url, file, postfields);
354}
355
362bool HTTP_Encode (const char* url, char* out, size_t outLength)
363{
364 CURL* curl = curl_easy_init();
365 char* encoded = curl_easy_escape(curl, url, 0);
366 if (encoded == nullptr) {
367 curl_easy_cleanup(curl);
368 return false;
369 }
370 Q_strncpyz(out, encoded, outLength);
371 const bool success = strlen(encoded) < outLength;
372 curl_free(encoded);
373 curl_easy_cleanup(curl);
374 return success;
375}
376
384bool HTTP_GetURL (const char* url, http_callback_t callback, void* userdata, const char* postfields)
385{
386 dlhandle_t dl;
387 OBJZERO(dl);
388
389 if (!HTTP_GetURLInternal(dl, url, nullptr, postfields)) {
391 dl.tempBuffer = nullptr;
392 return false;
393 }
394
395 if (callback != nullptr)
396 callback(dl.tempBuffer, userdata);
397
399 dl.tempBuffer = nullptr;
400 return true;
401}
402
406void HTTP_Cleanup (void)
407{
408 curl_global_cleanup();
409}
410#else
411void HTTP_GetURL(const char* url, http_callback_t callback) {}
412void HTTP_PutFile(const char* formName, const char* fileName, const char* url, const upparam_t* params) {}
413size_t HTTP_Recv(void* ptr, size_t size, size_t nmemb, void* stream) {return 0L;}
414size_t HTTP_Header(void* ptr, size_t size, size_t nmemb, void* stream) {return 0L;}
415void HTTP_Cleanup(void) {}
416bool bool HTTP_ExtractComponents(const char* url, char* scheme, size_t schemeLength, char* host, size_t hostLength, char* path, size_t pathLength, int* port) {return false;}
417#endif
cvar_t * http_proxy
Definition common.cpp:47
cvar_t * port
Definition common.cpp:58
cvar_t * http_timeout
Definition common.cpp:48
void Com_Error(int code, const char *fmt,...)
Definition common.cpp:459
void Com_Printf(const char *const fmt,...)
Definition common.cpp:428
#define GAME_TITLE
Definition common.h:37
#define ERR_DROP
Definition common.h:211
#define UFO_VERSION
Definition common.h:36
bool HTTP_PutFile(const char *formName, const char *fileName, const char *url, const upparam_t *params)
Definition http.cpp:265
size_t HTTP_Recv(void *ptr, size_t size, size_t nmemb, void *stream)
libcurl callback for HTTP_GetURL
Definition http.cpp:154
bool HTTP_GetURL(const char *url, http_callback_t callback, void *userdata, const char *postfields)
Downloads the given url and return the data to the callback (optional).
Definition http.cpp:384
bool HTTP_ExtractComponents(const char *url, char *scheme, size_t schemeLength, char *host, size_t hostLength, char *path, size_t pathLength, int *port)
Extract the servername, the port and the path part of the given url.
Definition http.cpp:38
void HTTP_Cleanup(void)
UFO is exiting or we're changing servers. Clean up.
Definition http.cpp:406
bool HTTP_GetToFile(const char *url, FILE *file, const char *postfields)
Downloads the given url into the given file.
Definition http.cpp:346
static bool HTTP_GetURLInternal(dlhandle_t &dl, const char *url, FILE *file, const char *postfields)
Gets a specific url.
Definition http.cpp:206
size_t HTTP_Header(void *ptr, size_t size, size_t nmemb, void *stream)
libcurl callback to update header info.
Definition http.cpp:126
static void HTTP_ResolvURL(const char *url, char *buf, size_t size)
Converts the hostname into an ip to work around a bug in libcurl (resp. the resolver) that uses alarm...
Definition http.cpp:184
bool HTTP_Encode(const char *url, char *out, size_t outLength)
This function converts the given url to an URL encoded string. All input characters that are not a-z,...
Definition http.cpp:362
void(* http_callback_t)(const char *response, void *userdata)
Definition http.h:65
voidpf void uLong size
Definition ioapi.h:42
voidpf stream
Definition ioapi.h:42
voidpf void * buf
Definition ioapi.h:42
#define Mem_Free(ptr)
Definition mem.h:35
#define Mem_AllocTypeN(type, n)
Definition mem.h:38
bool NET_ResolvNode(const char *node, char *buf, size_t bufLength)
Definition net.cpp:1229
QGL_EXTERN GLuint GLchar GLuint * len
Definition r_gl.h:99
QGL_EXTERN GLint i
Definition r_gl.h:113
#define Q_streq(a, b)
Definition shared.h:136
#define Q_strneq(a, b, n)
Definition shared.h:137
bool Q_strnull(const char *string)
Definition shared.h:138
#define Q_strncasecmp(s1, s2, n)
Definition shared.h:132
#define OBJZERO(obj)
Definition shared.h:178
#define MAX_VAR
Definition shared.h:36
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition shared.cpp:457
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition shared.cpp:494
size_t fileSize
Definition http.h:51
char URL[576]
Definition http.h:54
FILE * file
Definition http.h:49
char * tempBuffer
Definition http.h:55
size_t position
Definition http.h:52
CURL * curl
Definition http.h:47
struct upparam_s * next
Definition http.h:62
const char * name
Definition http.h:60
const char * value
Definition http.h:61
#define FILE