Beispiel #1
0
static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error)
{
	qboolean ok = false;
	if(!curl_dll)
		return;
	switch(status)
	{
		case CURL_DOWNLOAD_SUCCESS:
			ok = true;
			di->callback(CURLCBSTATUS_OK, di->bytes_received, di->buffer, di->callback_data);
			break;
		case CURL_DOWNLOAD_FAILED:
			di->callback(CURLCBSTATUS_FAILED, di->bytes_received, di->buffer, di->callback_data);
			break;
		case CURL_DOWNLOAD_ABORTED:
			di->callback(CURLCBSTATUS_ABORTED, di->bytes_received, di->buffer, di->callback_data);
			break;
		case CURL_DOWNLOAD_SERVERERROR:
			// reopen to enforce it to have zero bytes again
			if(di->stream)
			{
				FS_Close(di->stream);
				di->stream = FS_OpenRealFile(di->filename, "wb", false);
			}

			if(di->callback)
				di->callback(error ? (int) error : CURLCBSTATUS_SERVERERROR, di->bytes_received, di->buffer, di->callback_data);
			break;
		default:
			if(di->callback)
				di->callback(CURLCBSTATUS_UNKNOWN, di->bytes_received, di->buffer, di->callback_data);
			break;
	}

	if(di->curle)
	{
		qcurl_multi_remove_handle(curlm, di->curle);
		qcurl_easy_cleanup(di->curle);
		if(di->slist)
			qcurl_slist_free_all(di->slist);
	}

	if(!di->callback && ok && !di->bytes_received)
	{
		Con_Printf("ERROR: empty file\n");
		ok = false;
	}

	if(di->stream)
		FS_Close(di->stream);

	if(ok && di->ispak)
	{
		ok = FS_AddPack(di->filename, NULL, true);
		if(!ok)
		{
			// pack loading failed?
			// this is critical
			// better clear the file again...
			di->stream = FS_OpenRealFile(di->filename, "wb", false);
			FS_Close(di->stream);

			if(di->startpos && !di->callback)
			{
				// this was a resume?
				// then try to redownload it without reporting the error
				Curl_Begin(di->url, di->extraheaders, di->maxspeed, di->filename, di->ispak, di->forthismap, di->post_content_type, di->postbuf, di->postbufsize, NULL, 0, NULL, NULL);
				di->forthismap = false; // don't count the error
			}
		}
	}

	if(di->prev)
		di->prev->next = di->next;
	else
		downloads = di->next;
	if(di->next)
		di->next->prev = di->prev;

	--numdownloads;
	if(di->forthismap)
	{
		if(ok)
			++numdownloads_success;
		else
			++numdownloads_fail;
	}
	Z_Free(di);
}
Beispiel #2
0
/*
====================
Curl_Begin

Starts a download of a given URL to the file name portion of this URL (or name
if given) in the "dlcache/" folder.
====================
*/
static qboolean Curl_Begin(const char *URL, const char *extraheaders, double maxspeed, const char *name, qboolean ispak, qboolean forthismap, const char *post_content_type, const unsigned char *postbuf, size_t postbufsize, unsigned char *buf, size_t bufsize, curl_callback_t callback, void *cbdata)
{
	if(!curl_dll)
	{
		return false;
	}
	else
	{
		char fn[MAX_OSPATH];
		char urlbuf[1024];
		const char *p, *q;
		size_t length;
		downloadinfo *di;

		// if URL is protocol:///* or protocol://:port/*, insert the IP of the current server
		p = strchr(URL, ':');
		if(p)
		{
			if(!strncmp(p, ":///", 4) || !strncmp(p, "://:", 4))
			{
				char addressstring[128];
				*addressstring = 0;
				InfoString_GetValue(cls.userinfo, "*ip", addressstring, sizeof(addressstring));
				q = strchr(addressstring, ':');
				if(!q)
					q = addressstring + strlen(addressstring);
				if(*addressstring)
				{
					dpsnprintf(urlbuf, sizeof(urlbuf), "%.*s://%.*s%s", (int) (p - URL), URL, (int) (q - addressstring), addressstring, URL + (p - URL) + 3);
					URL = urlbuf;
				}
			}
		}

		// Note: This extraction of the file name portion is NOT entirely correct.
		//
		// It does the following:
		//
		//   http://host/some/script.cgi/SomeFile.pk3?uid=ABCDE -> SomeFile.pk3
		//   http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3 -> SomeFile.pk3
		//   http://host/some/script.php?uid=ABCDE&file=SomeFile.pk3 -> script.php
		//
		// However, I'd like to keep this "buggy" behavior so that PHP script
		// authors can write download scripts without having to enable
		// AcceptPathInfo on Apache. They just have to ensure that their script
		// can be called with such a "fake" path name like
		// http://host/some/script.php?uid=ABCDE&file=/SomeFile.pk3
		//
		// By the way, such PHP scripts should either send the file or a
		// "Location:" redirect; PHP code example:
		//
		//   header("Location: http://www.example.com/");
		//
		// By the way, this will set User-Agent to something like
		// "Nexuiz build 22:27:55 Mar 17 2006" (engineversion) and Referer to
		// dp://serverhost:serverport/ so you can filter on this; an example
		// httpd log file line might be:
		//
		//   141.2.16.3 - - [17/Mar/2006:22:32:43 +0100] "GET /maps/tznex07.pk3 HTTP/1.1" 200 1077455 "dp://141.2.16.7:26000/" "Nexuiz Linux 22:07:43 Mar 17 2006"

		if(!name)
			name = CleanURL(URL);

		if(!buf)
		{
			p = strrchr(name, '/');
			p = p ? (p+1) : name;
			q = strchr(p, '?');
			length = q ? (size_t)(q - p) : strlen(p);
			dpsnprintf(fn, sizeof(fn), "dlcache/%.*s", (int)length, p);

			name = fn; // make it point back

			// already downloading the file?
			{
				downloadinfo *di = Curl_Find(fn);
				if(di)
				{
					Con_Printf("Can't download %s, already getting it from %s!\n", fn, CleanURL(di->url));

					// however, if it was not for this map yet...
					if(forthismap && !di->forthismap)
					{
						di->forthismap = true;
						// this "fakes" a download attempt so the client will wait for
						// the download to finish and then reconnect
						++numdownloads_added;
					}

					return false;
				}
			}

			if(ispak && FS_FileExists(fn))
			{
				qboolean already_loaded;
				if(FS_AddPack(fn, &already_loaded, true))
				{
					Con_DPrintf("%s already exists, not downloading!\n", fn);
					if(already_loaded)
						Con_DPrintf("(pak was already loaded)\n");
					else
					{
						if(forthismap)
						{
							++numdownloads_added;
							++numdownloads_success;
						}
					}

					return false;
				}
				else
				{
					qfile_t *f = FS_OpenRealFile(fn, "rb", false);
					if(f)
					{
						char buf[4] = {0};
						FS_Read(f, buf, sizeof(buf)); // no "-1", I will use memcmp

						if(memcmp(buf, "PK\x03\x04", 4) && memcmp(buf, "PACK", 4))
						{
							Con_DPrintf("Detected non-PAK %s, clearing and NOT resuming.\n", fn);
							FS_Close(f);
							f = FS_OpenRealFile(fn, "wb", false);
							if(f)
								FS_Close(f);
						}
						else
						{
							// OK
							FS_Close(f);
						}
					}
				}
			}
		}

		// if we get here, we actually want to download... so first verify the
		// URL scheme (so one can't read local files using file://)
		if(strncmp(URL, "http://", 7) && strncmp(URL, "ftp://", 6) && strncmp(URL, "https://", 8))
		{
			Con_Printf("Curl_Begin(\"%s\"): nasty URL scheme rejected\n", URL);
			return false;
		}

		if(forthismap)
			++numdownloads_added;
		di = (downloadinfo *) Z_Malloc(sizeof(*di));
		strlcpy(di->filename, name, sizeof(di->filename));
		strlcpy(di->url, URL, sizeof(di->url));
		dpsnprintf(di->referer, sizeof(di->referer), "dp://%s/", cls.netcon ? cls.netcon->address : "notconnected.invalid");
		di->forthismap = forthismap;
		di->stream = NULL;
		di->startpos = 0;
		di->curle = NULL;
		di->started = false;
		di->ispak = (ispak && !buf);
		di->maxspeed = maxspeed;
		di->bytes_received = 0;
		di->bytes_received_curl = 0;
		di->bytes_sent_curl = 0;
		di->extraheaders = extraheaders;
		di->next = downloads;
		di->prev = NULL;
		if(di->next)
			di->next->prev = di;

		di->buffer = buf;
		di->buffersize = bufsize;
		if(callback == NULL)
		{
			di->callback = curl_default_callback;
			di->callback_data = di;
		}
		else
		{
			di->callback = callback;
			di->callback_data = cbdata;
		}

		if(post_content_type)
		{
			di->post_content_type = post_content_type;
			di->postbuf = postbuf;
			di->postbufsize = postbufsize;
		}
		else
		{
			di->post_content_type = NULL;
			di->postbuf = NULL;
			di->postbufsize = 0;
		}

		downloads = di;
		return true;
	}
}
Beispiel #3
0
static void Curl_EndDownload(downloadinfo *di, CurlStatus status, CURLcode error, const char *content_type_)
{
	char content_type[64];
	qboolean ok = false;
	if(!curl_dll)
		return;
	switch(status)
	{
		case CURL_DOWNLOAD_SUCCESS:
			ok = true;
			di->callback(CURLCBSTATUS_OK, di->bytes_received, di->buffer, di->callback_data);
			break;
		case CURL_DOWNLOAD_FAILED:
			di->callback(CURLCBSTATUS_FAILED, di->bytes_received, di->buffer, di->callback_data);
			break;
		case CURL_DOWNLOAD_ABORTED:
			di->callback(CURLCBSTATUS_ABORTED, di->bytes_received, di->buffer, di->callback_data);
			break;
		case CURL_DOWNLOAD_SERVERERROR:
			// reopen to enforce it to have zero bytes again
			if(di->stream)
			{
				FS_Close(di->stream);
				di->stream = FS_OpenRealFile(di->filename, "wb", false);
			}

			if(di->callback)
				di->callback(error ? (int) error : CURLCBSTATUS_SERVERERROR, di->bytes_received, di->buffer, di->callback_data);
			break;
		default:
			if(di->callback)
				di->callback(CURLCBSTATUS_UNKNOWN, di->bytes_received, di->buffer, di->callback_data);
			break;
	}
	if(content_type_)
		strlcpy(content_type, content_type_, sizeof(content_type));
	else
		*content_type = 0;

	if(di->curle)
	{
		qcurl_multi_remove_handle(curlm, di->curle);
		qcurl_easy_cleanup(di->curle);
		if(di->slist)
			qcurl_slist_free_all(di->slist);
	}

	if(!di->callback && ok && !di->bytes_received)
	{
		Con_Printf("ERROR: empty file\n");
		ok = false;
	}

	if(di->stream)
		FS_Close(di->stream);

#define CLEAR_AND_RETRY() \
	do \
	{ \
		di->stream = FS_OpenRealFile(di->filename, "wb", false); \
		FS_Close(di->stream); \
		if(di->startpos && !di->callback) \
		{ \
			Curl_Begin(di->url, di->extraheaders, di->maxspeed, di->filename, di->loadtype, di->forthismap, di->post_content_type, di->postbuf, di->postbufsize, NULL, 0, NULL, NULL); \
			di->forthismap = false; \
		} \
	} \
	while(0)

	if(ok && di->loadtype == LOADTYPE_PAK)
	{
		ok = FS_AddPack(di->filename, NULL, true);
		if(!ok)
			CLEAR_AND_RETRY();
	}
	else if(ok && di->loadtype == LOADTYPE_CACHEPIC)
	{
		const char *p;
		unsigned char *pixels = NULL;

		p = di->filename;
#ifdef WE_ARE_EVIL
		if(!strncmp(p, "dlcache/", 8))
			p += 8;
#endif

		pixels = decode_image(di, content_type);
		if(pixels)
			Draw_NewPic(p, image_width, image_height, true, pixels);
		else
			CLEAR_AND_RETRY();
	}
	else if(ok && di->loadtype == LOADTYPE_SKINFRAME)
	{
		const char *p;
		unsigned char *pixels = NULL;

		p = di->filename;
#ifdef WE_ARE_EVIL
		if(!strncmp(p, "dlcache/", 8))
			p += 8;
#endif

		pixels = decode_image(di, content_type);
		if(pixels)
			R_SkinFrame_LoadInternalBGRA(p, TEXF_FORCE_RELOAD | TEXF_MIPMAP | TEXF_ALPHA, pixels, image_width, image_height, false); // TODO what sRGB argument to put here?
		else
			CLEAR_AND_RETRY();
	}

	if(di->prev)
		di->prev->next = di->next;
	else
		downloads = di->next;
	if(di->next)
		di->next->prev = di->prev;

	--numdownloads;
	if(di->forthismap)
	{
		if(ok)
			++numdownloads_success;
		else
			++numdownloads_fail;
	}
	Z_Free(di);
}