UFO: Alien Invasion
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
http.cpp
Go to the documentation of this file.
1 /*
2 Copyright (C) 1997-2001 Id Software, Inc.
3 
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
8 
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
12 
13 See the GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, 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 
38 bool 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 
126 size_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)) {
143  dlhandle_t* dl = (dlhandle_t*)stream;
144  if (dl->file)
145  dl->fileSize = strtoul(headerBuff + 16, nullptr, 10);
146  }
147 
148  return bytes;
149 }
150 
154 size_t HTTP_Recv (void* ptr, size_t size, size_t nmemb, void* stream)
155 {
156  const size_t bytes = size * nmemb;
157  dlhandle_t* dl = (dlhandle_t*)stream;
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 
184 static 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 
206 static 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 
265 bool 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 
336 bool 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 
352 bool 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 
374 bool 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)) {
380  Mem_Free(dl.tempBuffer);
381  dl.tempBuffer = nullptr;
382  return false;
383  }
384 
385  if (callback != nullptr)
386  callback(dl.tempBuffer, userdata);
387 
388  Mem_Free(dl.tempBuffer);
389  dl.tempBuffer = nullptr;
390  return true;
391 }
392 
396 void HTTP_Cleanup (void)
397 {
398  curl_global_cleanup();
399 }
400 #else
401 void HTTP_GetURL(const char* url, http_callback_t callback) {}
402 void HTTP_PutFile(const char* formName, const char* fileName, const char* url, const upparam_t* params) {}
403 size_t HTTP_Recv(void* ptr, size_t size, size_t nmemb, void* stream) {return 0L;}
404 size_t HTTP_Header(void* ptr, size_t size, size_t nmemb, void* stream) {return 0L;}
405 void HTTP_Cleanup(void) {}
406 bool 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
bool Q_strnull(const char *string)
Definition: shared.h:138
#define Mem_AllocTypeN(type, n)
Definition: mem.h:38
char URL[576]
Definition: http.h:54
struct upparam_s * next
Definition: http.h:62
size_t HTTP_Header(void *ptr, size_t size, size_t nmemb, void *stream)
libcurl callback to update header info.
Definition: http.cpp:126
const char * value
Definition: http.h:61
bool Com_sprintf(char *dest, size_t size, const char *fmt,...)
copies formatted string with buffer-size checking
Definition: shared.cpp:494
#define FILE
Definition: test_webapi.cpp:30
void HTTP_Cleanup(void)
UFO is exiting or we're changing servers. Clean up.
Definition: http.cpp:396
static bool HTTP_GetURLInternal(dlhandle_t &dl, const char *url, FILE *file, const char *postfields)
Gets a specific url.
Definition: http.cpp:206
void Com_Printf(const char *const fmt,...)
Definition: common.cpp:386
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_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_callback_t)(const char *response, void *userdata)
Definition: http.h:65
int integer
Definition: cvar.h:81
voidpf void * buf
Definition: ioapi.h:42
char * tempBuffer
Definition: http.h:55
const char * name
Definition: http.h:60
void Com_Error(int code, const char *fmt,...)
Definition: common.cpp:417
cvar_t * http_proxy
Definition: common.cpp:47
void Q_strncpyz(char *dest, const char *src, size_t destsize)
Safe strncpy that ensures a trailing zero.
Definition: shared.cpp:457
#define UFO_VERSION
Definition: common.h:36
#define ERR_DROP
Definition: common.h:211
GLsizei size
Definition: r_gl.h:152
#define OBJZERO(obj)
Definition: shared.h:178
#define MAX_VAR
Definition: shared.h:36
#define Q_strncasecmp(s1, s2, n)
Definition: shared.h:132
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_PutFile(const char *formName, const char *fileName, const char *url, const upparam_t *params)
Definition: http.cpp:265
cvar_t * http_timeout
Definition: common.cpp:48
#define GAME_TITLE
Definition: common.h:37
bool HTTP_GetToFile(const char *url, FILE *file, const char *postfields)
Downloads the given url into the given file.
Definition: http.cpp:336
CURL * curl
Definition: http.h:47
voidpf stream
Definition: ioapi.h:42
#define Q_strneq(a, b, n)
Definition: shared.h:137
FILE * file
Definition: http.h:49
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
QGL_EXTERN GLint i
Definition: r_gl.h:113
QGL_EXTERN GLuint GLchar GLuint * len
Definition: r_gl.h:99
char * string
Definition: cvar.h:73
size_t fileSize
Definition: http.h:51
#define Mem_Free(ptr)
Definition: mem.h:35
cvar_t * port
Definition: common.cpp:58
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
Definition: http.h:59
#define Q_streq(a, b)
Definition: shared.h:136
size_t position
Definition: http.h:52
bool NET_ResolvNode(const char *node, char *buf, size_t bufLength)
Definition: net.cpp:1229