UFO: Alien Invasion
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 struct curl_httppost* post = nullptr;
296 struct curl_httppost* last = nullptr;
297 while (params) {
298 curl_formadd(&post, &last, CURLFORM_PTRNAME, params->name, CURLFORM_PTRCONTENTS, params->value, CURLFORM_END);
299 params = params->next;
300 }
301
302 curl_formadd(&post, &last, CURLFORM_PTRNAME, formName, CURLFORM_FILE, fileName, CURLFORM_END);
303
304 curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, http_timeout->integer);
305 curl_easy_setopt(curl, CURLOPT_TIMEOUT, http_timeout->integer);
306 curl_easy_setopt(curl, CURLOPT_HTTPPOST, post);
307 curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
308 curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
309 curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
310 curl_easy_setopt(curl, CURLOPT_USERAGENT, GAME_TITLE " " UFO_VERSION);
311 curl_easy_setopt(curl, CURLOPT_URL, url);
312 curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
313 const CURLcode result = curl_easy_perform(curl);
314 if (result != CURLE_OK) {
315 if (result == CURLE_HTTP_RETURNED_ERROR) {
316 long httpCode = 0;
317 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &httpCode);
318 Com_Printf("failed to upload file '%s': %s (%i)\n", fileName, curl_easy_strerror(result), (int)httpCode);
319 } else {
320 Com_Printf("failed to upload file '%s': %s\n", fileName, curl_easy_strerror(result));
321 }
322 curl_easy_cleanup(curl);
323 return false;
324 }
325
326 curl_easy_cleanup(curl);
327 return true;
328}
329
336bool HTTP_GetToFile (const char* url, FILE* file, const char* postfields)
337{
338 if (!file)
339 return false;
340 dlhandle_t dl;
341 OBJZERO(dl);
342
343 return HTTP_GetURLInternal(dl, url, file, postfields);
344}
345
352bool HTTP_Encode (const char* url, char* out, size_t outLength)
353{
354 CURL* curl = curl_easy_init();
355 char* encoded = curl_easy_escape(curl, url, 0);
356 if (encoded == nullptr) {
357 curl_easy_cleanup(curl);
358 return false;
359 }
360 Q_strncpyz(out, encoded, outLength);
361 const bool success = strlen(encoded) < outLength;
362 curl_free(encoded);
363 curl_easy_cleanup(curl);
364 return success;
365}
366
374bool HTTP_GetURL (const char* url, http_callback_t callback, void* userdata, const char* postfields)
375{
376 dlhandle_t dl;
377 OBJZERO(dl);
378
379 if (!HTTP_GetURLInternal(dl, url, nullptr, postfields)) {
381 dl.tempBuffer = nullptr;
382 return false;
383 }
384
385 if (callback != nullptr)
386 callback(dl.tempBuffer, userdata);
387
389 dl.tempBuffer = nullptr;
390 return true;
391}
392
396void HTTP_Cleanup (void)
397{
398 curl_global_cleanup();
399}
400#else
401void HTTP_GetURL(const char* url, http_callback_t callback) {}
402void HTTP_PutFile(const char* formName, const char* fileName, const char* url, const upparam_t* params) {}
403size_t HTTP_Recv(void* ptr, size_t size, size_t nmemb, void* stream) {return 0L;}
404size_t HTTP_Header(void* ptr, size_t size, size_t nmemb, void* stream) {return 0L;}
405void HTTP_Cleanup(void) {}
406bool 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;}
407#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:417
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
#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:374
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:396
bool HTTP_GetToFile(const char *url, FILE *file, const char *postfields)
Downloads the given url into the given file.
Definition: http.cpp:336
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:352
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
int integer
Definition: cvar.h:81
char * string
Definition: cvar.h:73
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
Definition: http.h:59
struct upparam_s * next
Definition: http.h:62
const char * name
Definition: http.h:60
const char * value
Definition: http.h:61
#define FILE
Definition: test_webapi.cpp:30