Exemple #1
0
/*
* CL_DownloadDone
*/
void CL_DownloadDone( void )
{
	qboolean requestnext;

	Mem_ZoneFree( cls.download.requestname );
	cls.download.requestname = NULL;

	requestnext = cls.download.requestnext;
	cls.download.requestnext = qfalse;
	cls.download.requestpak = qfalse;
	cls.download.timeout = 0;
	cls.download.timestart = 0;
	cls.download.offset = cls.download.baseoffset = 0;
	cls.download.web = qfalse;
	cls.download.filenum = 0;
	cls.download.cancelled = qfalse;

	// the server has changed map during the download
	if( cls.download.pending_reconnect )
	{
		cls.download.pending_reconnect = qfalse;
		CL_FreeDownloadList();
		CL_ServerReconnect_f();
		return;
	}

	if( requestnext && cls.state > CA_DISCONNECTED )
		CL_RequestNextDownload();
}
Exemple #2
0
// Fatal HTTP error occured, remove any special entries from
// queue and fall back to UDP downloading.
static void abort_downloads (void) {
    dlqueue_t   *q;

    HTTP_CleanupDownloads();

    cls.download.current = NULL;
    cls.download.percent = 0;

    FOR_EACH_DLQ (q) {
        if (q->state != DL_DONE && q->type >= DL_LIST)
            CL_FinishDownload (q);
        else if (q->state == DL_RUNNING)
            q->state = DL_PENDING;
    }

    CL_RequestNextDownload ();
    CL_StartNextDownload ();
}
Exemple #3
0
/*
 * The server will send this command right
 * before allowing the client into the server
 */
void
CL_Precache_f(void)
{
	/* Yet another hack to let old demos work */
	if (Cmd_Argc() < 2)
	{
		unsigned map_checksum;    /* for detecting cheater maps */

		CM_LoadMap(cl.configstrings[CS_MODELS + 1], true, &map_checksum);
		CL_RegisterSounds();
		CL_PrepRefresh();
		return;
	}

	precache_check = CS_MODELS;

	precache_spawncount = (int)strtol(Cmd_Argv(1), (char **)NULL, 10);
	precache_model = 0;
	precache_model_skin = 0;

	CL_RequestNextDownload();
}
Exemple #4
0
void CL_ParseDownload (qboolean dataIsCompressed)
{
	int		size, percent;
	char	name[MAX_OSPATH];

	// read the data
	size = MSG_ReadShort (&net_message);
	percent = MSG_ReadByte (&net_message);

	if (size < 0)
	{
		if (size == -1)
			Com_Printf ("Server does not have this file.\n", LOG_CLIENT);
		else
			Com_Printf ("Bad download data from server.\n", LOG_CLIENT);

		//r1: nuke the temp filename
		cls.downloadtempname[0] = 0;
		cls.downloadname[0] = 0;
		cls.failed_download = true;

		if (cls.download)
		{
			// if here, we tried to resume a file but the server said no
			fclose (cls.download);
			cls.download = NULL;
		}

		cls.downloadpending = false;
		CL_RequestNextDownload ();
		return;
	}

	// open the file if not opened yet
	if (!cls.download)
	{
		if (!cls.downloadtempname[0])
		{
			Com_Printf ("Received download packet without request. Ignored.\n", LOG_CLIENT);
			net_message.readcount += size;
			return;
		}
		CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);

		FS_CreatePath (name);

		cls.download = fopen (name, "wb");
		if (!cls.download)
		{
			net_message.readcount += size;
			Com_Printf ("Failed to open %s\n", LOG_CLIENT, cls.downloadtempname);
			cls.downloadpending = false;
			CL_RequestNextDownload ();
			return;
		}
	}

	//r1: downloading something, drop to console to show status bar
	SCR_EndLoadingPlaque();

	//r1: if we're stuck with udp, may as well make best use of the bandwidth...
	if (dataIsCompressed)
	{
#ifndef NO_ZLIB
		uint16		uncompressedLen;
		byte		uncompressed[0xFFFF];

		uncompressedLen = (uint16)MSG_ReadShort (&net_message);

		if (!uncompressedLen)
			Com_Error (ERR_DROP, "uncompressedLen == 0");

		ZLibDecompress (net_message_buffer + net_message.readcount, size, uncompressed, uncompressedLen, -15);
		fwrite (uncompressed, 1, uncompressedLen, cls.download);
		Com_DPrintf ("svc_zdownload(%s): %d -> %d\n", cls.downloadname, size, uncompressedLen);
#else
		Com_Error (ERR_DROP, "Received a unrequested compressed download");
#endif
	}
	else
	{
		fwrite (net_message_buffer + net_message.readcount, 1, size, cls.download);
	}

	net_message.readcount += size;

	if (percent != 100)
	{
		cls.downloadpercent = percent;

		MSG_WriteByte (clc_stringcmd);
		MSG_Print ("nextdl");
		MSG_EndWriting (&cls.netchan.message);
		send_packet_now = true;
	}
	else
	{
		CL_FinishDownload ();

		// get another file if needed
		CL_RequestNextDownload ();
	}
}
Exemple #5
0
/*
===============
CL_FinishHTTPDownload

A download finished, find out what it was, whether there were any errors and
if so, how severe. If none, rename file and other such stuff.
===============
*/
static void CL_FinishHTTPDownload (void)
{
	size_t		i;
	int			msgs_in_queue;
	CURLMsg		*msg;
	CURLcode	result;
	dlhandle_t	*dl;
	CURL		*curl;
	long		responseCode;
	double		timeTaken;
	double		fileSize;
	char		tempName[MAX_OSPATH];
	qboolean	isFile;

	do
	{
		msg = curl_multi_info_read (multi, &msgs_in_queue);

		if (!msg)
		{
			Com_Printf ("CL_FinishHTTPDownload: Odd, no message for us...\n");
			return;
		}

		if (msg->msg != CURLMSG_DONE)
		{
			Com_Printf ("CL_FinishHTTPDownload: Got some weird message...\n");
			continue;
		}

		curl = msg->easy_handle;

		// curl doesn't provide reverse-lookup of the void * ptr, so search for it
		for (i = 0; i < 4; i++)
		{
			if (cls.HTTPHandles[i].curl == curl)
			{
				dl = &cls.HTTPHandles[i];
				break;
			}
		}

		if (i == 4)
			Com_Error (ERR_DROP, "CL_FinishHTTPDownload: Handle not found");

		// we mark everything as done even if it errored to prevent multiple
		// attempts.
		dl->queueEntry->state = DLQ_STATE_DONE;

		//filelist processing is done on read
		if (dl->file)
			isFile = true;
		else
			isFile = false;

		if (isFile)
		{
			fclose (dl->file);
			dl->file = NULL;
		}

		//might be aborted
		if (pendingCount)
			pendingCount--;
		handleCount--;
		//Com_Printf ("finished dl: hc = %d\n", LOG_GENERAL, handleCount);
		cls.downloadname[0] = 0;
		cls.downloadposition = 0;

		result = msg->data.result;

		switch (result)
		{
			// for some reason curl returns CURLE_OK for a 404...
			case CURLE_HTTP_RETURNED_ERROR:
			case CURLE_OK:
			
				curl_easy_getinfo (curl, CURLINFO_RESPONSE_CODE, &responseCode);
				if (responseCode == 404)
				{
					i = strlen (dl->queueEntry->quakePath);
					if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak")
						|| !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") )
						downloading_pak = false;

					if (isFile)
						remove (dl->filePath);
					Com_Printf ("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->quakePath, pendingCount);
					curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize);
					if (fileSize > 512)
					{
						// ick
						isFile = false;
						result = CURLE_FILESIZE_EXCEEDED;
						Com_Printf ("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize);
					}
					else
					{
						curl_multi_remove_handle (multi, dl->curl);
						// Knightmare- fall back to UDP download for this map if failure on .bsp
						if ( !strncmp(dl->queueEntry->quakePath, "maps/", 5) && !strcmp(dl->queueEntry->quakePath + i - 4, ".bsp") )
						{
							Com_Printf ("HTTP: failed to download %s, falling back to UDP until next map.\n", dl->queueEntry->quakePath);
							thisMapAbort = true;
							CL_CancelHTTPDownloads (false);
							CL_ResetPrecacheCheck ();
						}
						// end Knightmare
						continue;
					}
				}
				else if (responseCode == 200)
				{
					if (!isFile && !abortDownloads)
						CL_ParseFileList (dl);
					break;
				}

				//every other code is treated as fatal, fallthrough here
				Com_Printf ("Bad HTTP response code %d for %s, aborting HTTP downloading.\n", responseCode, dl->queueEntry->quakePath);

			//fatal error, disable http
			case CURLE_COULDNT_RESOLVE_HOST:
			case CURLE_COULDNT_CONNECT:
			case CURLE_COULDNT_RESOLVE_PROXY:
				if (isFile)
					remove (dl->filePath);
			//	Com_Printf ("Fatal HTTP error: %s\n", curl_easy_strerror (result));
				Com_Printf ("Fatal HTTP error: %s\n", CURL_ERROR(result));
				curl_multi_remove_handle (multi, dl->curl);
				if (abortDownloads)
					continue;
				CL_CancelHTTPDownloads (true);
				continue;
			default:
				i = strlen (dl->queueEntry->quakePath);
				if ( !strcmp (dl->queueEntry->quakePath + i - 4, ".pak") || !strcmp (dl->queueEntry->quakePath + i - 4, ".pk3") )
					downloading_pak = false;
				if (isFile)
					remove (dl->filePath);
			//	Com_Printf ("HTTP download failed: %s\n", curl_easy_strerror (result));
				Com_Printf ("HTTP download failed: %s\n", CURL_ERROR(result));
				curl_multi_remove_handle (multi, dl->curl);
				continue;
		}

		if (isFile)
		{
			//rename the temp file
			Com_sprintf (tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->quakePath);

			if (rename (dl->filePath, tempName))
				Com_Printf ("Failed to rename %s for some odd reason...", dl->filePath);

			//a pak file is very special...
			i = strlen (tempName);
			if ( !strcmp (tempName + i - 4, ".pak") || !strcmp (tempName + i - 4, ".pk3") )
			{
			//	FS_FlushCache ();
			//	FS_ReloadPAKs ();
				// Knightmare- just add the pk3/ pak file
				if (!strcmp (tempName + i - 4, ".pk3")) 
					FS_AddPK3File (tempName);
				else
					FS_AddPAKFile (tempName);

				CL_ReVerifyHTTPQueue ();
				downloading_pak = false;
			}
		}

		//show some stats
		curl_easy_getinfo (curl, CURLINFO_TOTAL_TIME, &timeTaken);
		curl_easy_getinfo (curl, CURLINFO_SIZE_DOWNLOAD, &fileSize);

		//FIXME:
		//technically i shouldn't need to do this as curl will auto reuse the
		//existing handle when you change the URL. however, the handleCount goes
		//all weird when reusing a download slot in this way. if you can figure
		//out why, please let me know.
		curl_multi_remove_handle (multi, dl->curl);

		Com_Printf ("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n", dl->queueEntry->quakePath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount);
	} while (msgs_in_queue > 0);

//	FS_FlushCache ();

	if (handleCount == 0)
	{
		if (abortDownloads == HTTPDL_ABORT_SOFT)
			abortDownloads = HTTPDL_ABORT_NONE;
		else if (abortDownloads == HTTPDL_ABORT_HARD) {
			// FS: Added because Whale's Weapons HTTP server rejects you after a lot of 404s.  Then you lose HTTP until a hard reconnect.
			Q_strncpyz(cls.downloadServerRetry, cls.downloadServer, sizeof(cls.downloadServerRetry)); 
			cls.downloadServer[0] = 0;
		}
	}

	// done current batch, see if we have more to dl - maybe a .bsp needs downloaded
	if (cls.state == ca_connected && !CL_PendingHTTPDownloads())
		CL_RequestNextDownload ();
}
Exemple #6
0
/*
=====================
CL_ParseDownload

A download message has been received from the server
=====================
*/
void CL_ParseDownload(void)
{
    int size, percent;
    char name[MAX_OSPATH];
    int r;

    // read the data
    size = MSG_ReadShort(&net_message);
    percent = MSG_ReadByte(&net_message);
    if (size == -1)
    {
        Com_Printf("Server does not have this file.\n");
        if (cls.download)
        {
            // if here, we tried to resume a file but the server said no
            fclose(cls.download);
            cls.download = NULL;
        }
        CL_RequestNextDownload();
        return;
    }

    // open the file if not opened yet
    if (!cls.download)
    {
        CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);

        FS_CreatePath(name);

        cls.download = fopen(name, "wb");
        if (!cls.download)
        {
            net_message.readcount += size;
            Com_Printf("Failed to open %s\n", cls.downloadtempname);
            CL_RequestNextDownload();
            return;
        }
    }

    fwrite(net_message.data + net_message.readcount, 1, size, cls.download);
    net_message.readcount += size;

    if (percent != 100)
    {
// request next block
// change display routines by zoid
#if 0
		Com_Printf (".");
		if (10*(percent/10) != cls.downloadpercent)
		{
			cls.downloadpercent = 10*(percent/10);
			Com_Printf ("%i%%", cls.downloadpercent);
		}
#endif
        cls.downloadpercent = percent;

        MSG_WriteByte(&cls.netchan.message, clc_stringcmd);
        SZ_Print(&cls.netchan.message, "nextdl");
    }
    else
    {
        char oldn[MAX_OSPATH];
        char newn[MAX_OSPATH];

        //		Com_Printf ("100%%\n");

        fclose(cls.download);

        // rename the temp file to it's final name
        CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
        CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
        r = rename(oldn, newn);
        if (r)
            Com_Printf("failed to rename.\n");

        cls.download = NULL;
        cls.downloadpercent = 0;

        // get another file if needed

        CL_RequestNextDownload();
    }
}
Exemple #7
0
/**
 * @brief A download finished, find out what it was, whether there were any errors and
 * if so, how severe. If none, rename file and other such stuff.
 */
static void CL_FinishHTTPDownload (void)
{
	int messagesInQueue, i;
	CURLcode result;
	CURL *curl;
	long responseCode;
	double timeTaken, fileSize;
	char tempName[MAX_OSPATH];
	bool isFile;

	do {
		CURLMsg *msg = curl_multi_info_read(multi, &messagesInQueue);
		dlhandle_t *dl = NULL;

		if (!msg) {
			Com_Printf("CL_FinishHTTPDownload: Odd, no message for us...\n");
			return;
		}

		if (msg->msg != CURLMSG_DONE) {
			Com_Printf("CL_FinishHTTPDownload: Got some weird message...\n");
			continue;
		}

		curl = msg->easy_handle;

		/* curl doesn't provide reverse-lookup of the void * ptr, so search for it */
		for (i = 0; i < 4; i++) {
			if (cls.HTTPHandles[i].curl == curl) {
				dl = &cls.HTTPHandles[i];
				break;
			}
		}

		if (!dl)
			Com_Error(ERR_DROP, "CL_FinishHTTPDownload: Handle not found");

		/* we mark everything as done even if it errored to prevent multiple attempts. */
		dl->queueEntry->state = DLQ_STATE_DONE;

		/* filelist processing is done on read */
		if (dl->file)
			isFile = true;
		else
			isFile = false;

		if (isFile) {
			fclose(dl->file);
			dl->file = NULL;
		}

		/* might be aborted */
		if (pendingCount)
			pendingCount--;
		handleCount--;
		/* Com_Printf("finished dl: hc = %d\n", handleCount); */
		cls.downloadName[0] = 0;
		cls.downloadPosition = 0;

		result = msg->data.result;

		switch (result) {
		/* for some reason curl returns CURLE_OK for a 404... */
		case CURLE_HTTP_RETURNED_ERROR:
		case CURLE_OK:
			curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &responseCode);
			if (responseCode == 404) {
				const char *extension = Com_GetExtension(dl->queueEntry->ufoPath);
				if (extension != NULL && Q_streq(extension, "pk3"))
					downloadingPK3 = false;

				if (isFile)
					FS_RemoveFile(dl->filePath);
				Com_Printf("HTTP(%s): 404 File Not Found [%d remaining files]\n", dl->queueEntry->ufoPath, pendingCount);
				curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize);
				if (fileSize > 512) {
					/* ick */
					isFile = false;
					result = CURLE_FILESIZE_EXCEEDED;
					Com_Printf("Oversized 404 body received (%d bytes), aborting HTTP downloading.\n", (int)fileSize);
				} else {
					curl_multi_remove_handle(multi, dl->curl);
					continue;
				}
			} else if (responseCode == 200) {
				if (!isFile && !abortDownloads)
					CL_ParseFileList(dl);
				break;
			}

			/* every other code is treated as fatal, fallthrough here */

		/* fatal error, disable http */
		case CURLE_COULDNT_RESOLVE_HOST:
		case CURLE_COULDNT_CONNECT:
		case CURLE_COULDNT_RESOLVE_PROXY:
			if (isFile)
				FS_RemoveFile(dl->filePath);
			Com_Printf("Fatal HTTP error: %s\n", curl_easy_strerror(result));
			curl_multi_remove_handle(multi, dl->curl);
			if (abortDownloads)
				continue;
			CL_CancelHTTPDownloads(true);
			continue;
		default:
			i = strlen(dl->queueEntry->ufoPath);
			if (Q_streq(dl->queueEntry->ufoPath + i - 4, ".pk3"))
				downloadingPK3 = false;
			if (isFile)
				FS_RemoveFile(dl->filePath);
			Com_Printf("HTTP download failed: %s\n", curl_easy_strerror(result));
			curl_multi_remove_handle(multi, dl->curl);
			continue;
		}

		if (isFile) {
			/* rename the temp file */
			Com_sprintf(tempName, sizeof(tempName), "%s/%s", FS_Gamedir(), dl->queueEntry->ufoPath);

			if (!FS_RenameFile(dl->filePath, tempName, false))
				Com_Printf("Failed to rename %s for some odd reason...", dl->filePath);

			/* a pk3 file is very special... */
			i = strlen(tempName);
			if (Q_streq(tempName + i - 4, ".pk3")) {
				FS_RestartFilesystem(NULL);
				CL_ReVerifyHTTPQueue();
				downloadingPK3 = false;
			}
		}

		/* show some stats */
		curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &timeTaken);
		curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &fileSize);

		/** @todo
		 * technically i shouldn't need to do this as curl will auto reuse the
		 * existing handle when you change the URL. however, the handleCount goes
		 * all weird when reusing a download slot in this way. if you can figure
		 * out why, please let me know. */
		curl_multi_remove_handle(multi, dl->curl);

		Com_Printf("HTTP(%s): %.f bytes, %.2fkB/sec [%d remaining files]\n",
			dl->queueEntry->ufoPath, fileSize, (fileSize / 1024.0) / timeTaken, pendingCount);
	} while (messagesInQueue > 0);

	if (handleCount == 0) {
		if (abortDownloads == HTTPDL_ABORT_SOFT)
			abortDownloads = HTTPDL_ABORT_NONE;
		else if (abortDownloads == HTTPDL_ABORT_HARD)
			cls.downloadServer[0] = 0;
	}

	/* done current batch, see if we have more to dl - maybe a .bsp needs downloaded */
	if (cls.state == ca_connected && !CL_PendingHTTPDownloads())
		CL_RequestNextDownload();
}
Exemple #8
0
// A download finished, find out what it was, whether there were any errors and
// if so, how severe. If none, rename file and other such stuff.
static qboolean finish_download(void)
{
    int         msgs_in_queue;
    CURLMsg     *msg;
    CURLcode    result;
    dlhandle_t  *dl;
    CURL        *curl;
    long        response;
    double      sec, bytes;
    char        size[16], speed[16];
    char        temp[MAX_OSPATH];
    qboolean    fatal_error = qfalse;
    const char  *err;
    print_type_t level;

    do {
        msg = curl_multi_info_read(curl_multi, &msgs_in_queue);
        if (!msg)
            break;

        if (msg->msg != CURLMSG_DONE)
            continue;

        curl = msg->easy_handle;
        dl = find_handle(curl);

        cls.download.current = NULL;
        cls.download.percent = 0;

        //filelist processing is done on read
        if (dl->file) {
            fclose(dl->file);
            dl->file = NULL;
        }

        curl_handles--;

        result = msg->data.result;

        switch (result) {
            //for some reason curl returns CURLE_OK for a 404...
        case CURLE_HTTP_RETURNED_ERROR:
        case CURLE_OK:
            curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
            if (result == CURLE_OK && response == 200) {
                //success
                break;
            }

            err = http_strerror(response);

            //404 is non-fatal
            if (response == 404) {
                level = PRINT_ALL;
                goto fail1;
            }

            //every other code is treated as fatal
            //not marking download as done since
            //we are falling back to UDP
            level = PRINT_ERROR;
            fatal_error = qtrue;
            goto fail2;

        case CURLE_COULDNT_RESOLVE_HOST:
        case CURLE_COULDNT_CONNECT:
        case CURLE_COULDNT_RESOLVE_PROXY:
            //connection problems are fatal
            err = curl_easy_strerror(result);
            level = PRINT_ERROR;
            fatal_error = qtrue;
            goto fail2;

        default:
            err = curl_easy_strerror(result);
            level = PRINT_WARNING;
fail1:
            //we mark download as done even if it errored
            //to prevent multiple attempts.
            CL_FinishDownload(dl->queue);
fail2:
            Com_LPrintf(level,
                        "[HTTP] %s [%s] [%d remaining file%s]\n",
                        dl->queue->path, err, cls.download.pending,
                        cls.download.pending == 1 ? "" : "s");
            if (dl->path[0]) {
                remove(dl->path);
                dl->path[0] = 0;
            }
            if (dl->buffer) {
                Z_Free(dl->buffer);
                dl->buffer = NULL;
            }
            curl_multi_remove_handle(curl_multi, curl);
            continue;
        }

        //mark as done
        CL_FinishDownload(dl->queue);

        //show some stats
        curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME, &sec);
        curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD, &bytes);
        if (sec < 0.001)
            sec = 0.001;
        Com_FormatSizeLong(size, sizeof(size), bytes);
        Com_FormatSizeLong(speed, sizeof(speed), bytes / sec);

        //FIXME:
        //technically i shouldn't need to do this as curl will auto reuse the
        //existing handle when you change the url. however, the curl_handles goes
        //all weird when reusing a download slot in this way. if you can figure
        //out why, please let me know.
        curl_multi_remove_handle(curl_multi, curl);

        Com_Printf("[HTTP] %s [%s, %s/sec] [%d remaining file%s]\n",
                   dl->queue->path, size, speed, cls.download.pending,
                   cls.download.pending == 1 ? "" : "s");

        if (dl->path[0]) {
            //rename the temp file
            Q_snprintf(temp, sizeof(temp), "%s/%s", fs_gamedir, dl->queue->path);

            if (rename(dl->path, temp))
                Com_EPrintf("[HTTP] Failed to rename '%s' to '%s': %s\n",
                            dl->path, dl->queue->path, strerror(errno));
            dl->path[0] = 0;

            //a pak file is very special...
            if (dl->queue->type == DL_PAK) {
                CL_RestartFilesystem(qfalse);
                rescan_queue();
            }
        } else if (!fatal_error) {
            parse_file_list(dl);
        }
    } while (msgs_in_queue > 0);

    //fatal error occured, disable HTTP
    if (fatal_error) {
        abort_downloads();
        return qfalse;
    }

    // see if we have more to dl
    CL_RequestNextDownload();
    return qtrue;
}
Exemple #9
0
// Actually starts a download by adding it to the curl multi handle.
static void start_download(dlqueue_t *entry, dlhandle_t *dl)
{
    size_t  len;
    char    temp[MAX_QPATH];
    char    escaped[MAX_QPATH * 4];
    CURLMcode ret;
    qerror_t err;

    //yet another hack to accomodate filelists, how i wish i could push :(
    //NULL file handle indicates filelist.
    if (entry->type == DL_LIST) {
        dl->file = NULL;
        dl->path[0] = 0;
        //filelist paths are absolute
        escape_path(entry->path, escaped);
    } else {
        len = Q_snprintf(dl->path, sizeof(dl->path), "%s/%s.tmp", fs_gamedir, entry->path);
        if (len >= sizeof(dl->path)) {
            Com_EPrintf("[HTTP] Refusing oversize temporary file path.\n");
            goto fail;
        }

        //prepend quake path with gamedir
        len = Q_snprintf(temp, sizeof(temp), "%s/%s", http_gamedir(), entry->path);
        if (len >= sizeof(temp)) {
            Com_EPrintf("[HTTP] Refusing oversize server file path.\n");
            goto fail;
        }
        escape_path(temp, escaped);

        err = FS_CreatePath(dl->path);
        if (err < 0) {
            Com_EPrintf("[HTTP] Couldn't create path to '%s': %s\n", dl->path, Q_ErrorString(err));
            goto fail;
        }

        //don't bother with http resume... too annoying if server doesn't support it.
        dl->file = fopen(dl->path, "wb");
        if (!dl->file) {
            Com_EPrintf("[HTTP] Couldn't open '%s' for writing: %s\n", dl->path, strerror(errno));
            goto fail;
        }
    }

    len = Q_snprintf(dl->url, sizeof(dl->url), "%s%s", download_server, escaped);
    if (len >= sizeof(dl->url)) {
        Com_EPrintf("[HTTP] Refusing oversize download URL.\n");
        goto fail;
    }

    dl->buffer = NULL;
    dl->size = 0;
    dl->position = 0;
    dl->queue = entry;
    if (!dl->curl)
        dl->curl = curl_easy_init();

    curl_easy_setopt(dl->curl, CURLOPT_ENCODING, "");
#ifdef _DEBUG
    if (cl_http_debug->integer) {
        curl_easy_setopt(dl->curl, CURLOPT_DEBUGFUNCTION, debug_func);
        curl_easy_setopt(dl->curl, CURLOPT_VERBOSE, 1);
    }
#endif
    curl_easy_setopt(dl->curl, CURLOPT_NOPROGRESS, 0);
    if (dl->file) {
        curl_easy_setopt(dl->curl, CURLOPT_WRITEDATA, dl->file);
        curl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, NULL);
    } else {
        curl_easy_setopt(dl->curl, CURLOPT_WRITEDATA, dl);
        curl_easy_setopt(dl->curl, CURLOPT_WRITEFUNCTION, recv_func);
    }
    curl_easy_setopt(dl->curl, CURLOPT_FAILONERROR, 1);
    curl_easy_setopt(dl->curl, CURLOPT_PROXY, cl_http_proxy->string);
    curl_easy_setopt(dl->curl, CURLOPT_FOLLOWLOCATION, 1);
    curl_easy_setopt(dl->curl, CURLOPT_MAXREDIRS, 5);
    curl_easy_setopt(dl->curl, CURLOPT_PROGRESSFUNCTION, progress_func);
    curl_easy_setopt(dl->curl, CURLOPT_PROGRESSDATA, dl);
    curl_easy_setopt(dl->curl, CURLOPT_USERAGENT, com_version->string);
    curl_easy_setopt(dl->curl, CURLOPT_REFERER, download_referer);
    curl_easy_setopt(dl->curl, CURLOPT_URL, dl->url);

    ret = curl_multi_add_handle(curl_multi, dl->curl);
    if (ret != CURLM_OK) {
        Com_EPrintf("[HTTP] Failed to add download handle: %s\n",
                    curl_multi_strerror(ret));
fail:
        CL_FinishDownload(entry);

        // see if we have more to dl
        CL_RequestNextDownload();
        return;
    }

    Com_DPrintf("[HTTP] Fetching %s...\n", dl->url);
    entry->state = DL_RUNNING;
    curl_handles++;
}
Exemple #10
0
/*
 * A download message has been received from the server
 */
void CL_ParseDownload (void) {
	int		size, percent;
	char	name[MAX_OSPATH];
	int		r;

	/* read the data */
	size = MSG_ReadShort (&net_message);
	percent = MSG_ReadByte (&net_message);

	if (size == -1) {
		Com_Printf ("Server does not have this file.\n");

		if (cls.download) {
			/* if here, we tried to resume a
			   file but the server said no */
			fclose (cls.download);
			cls.download = NULL;
		}

		CL_RequestNextDownload ();
		return;
	}

	/* open the file if not opened yet */
	if (!cls.download) {
		CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);

		FS_CreatePath (name);

		cls.download = fopen (name, "wb");

		if (!cls.download) {
			net_message.readcount += size;
			Com_Printf ("Failed to open %s\n", cls.downloadtempname);
			CL_RequestNextDownload ();
			return;
		}
	}

	fwrite (net_message.data + net_message.readcount, 1, size, cls.download);
	net_message.readcount += size;

	if (percent != 100) {
		/* request next block */
		cls.downloadpercent = percent;

		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
		SZ_Print (&cls.netchan.message, "nextdl");

	} else {
		char	oldn[MAX_OSPATH];
		char	newn[MAX_OSPATH];

		fclose (cls.download);

		/* rename the temp file to it's final name */
		CL_DownloadFileName(oldn, sizeof(oldn), cls.downloadtempname);
		CL_DownloadFileName(newn, sizeof(newn), cls.downloadname);
		r = rename (oldn, newn);

		if (r)
			Com_Printf ("failed to rename.\n");

		cls.download = NULL;
		cls.downloadpercent = 0;

		/* get another file if needed */
		CL_RequestNextDownload ();
	}
}
Exemple #11
0
/*
=====================
CL_ParseDownload

A download message has been received from the server
=====================
*/
void CL_ParseDownload (sizebuf_t *msg, qboolean dataIsCompressed)
{
	int		size, percent;
	char	name[MAX_OSPATH];
//	int		r;

	// read the data
	size = MSG_ReadShort (msg);
	percent = MSG_ReadByte (msg);
	if (size < 0)
	{
		if (size == -1)
			Com_Printf ("Server does not have this file.\n");
		else
			Com_Printf ("Bad download data from server.\n");

		cls.downloadtempname[0] = 0;
		cls.downloadname[0] = 0;
		cls.failed_download = true;

		if (cls.download)
		{
			// if here, we tried to resume a file but the server said no
			fclose (cls.download);
			cls.download = NULL;
		}
		CL_RequestNextDownload ();
		return;
	}

	// open the file if not opened yet
	if (!cls.download)
	{
		if (!cls.downloadtempname[0])
		{
			Com_Printf ("Received download packet without request. Ignored.\n");
			msg->readcount += size;
			return;
		}

		CL_DownloadFileName(name, sizeof(name), cls.downloadtempname);

		FS_CreatePath (name);

		cls.download = fopen (name, "wb");
		if (!cls.download)
		{
			msg->readcount += size;
			Com_Printf ("Failed to open %s\n", cls.downloadtempname);
			CL_RequestNextDownload ();
			return;
		}
	}

	if (dataIsCompressed)
	{
		uint16		uncompressedLen;
		byte		uncompressed[0xFFFF];

		uncompressedLen = MSG_ReadShort (msg);

		if (!uncompressedLen)
			Com_Error (ERR_DROP, "uncompressedLen == 0");

		ZLibDecompress (msg->data + msg->readcount, size, uncompressed, uncompressedLen, -15);
		fwrite (uncompressed, 1, uncompressedLen, cls.download);
		Com_DPrintf ("svc_zdownload(%s): %d -> %d\n", cls.downloadname, size, uncompressedLen);
	}
	else
	{
		fwrite (msg->data + msg->readcount, 1, size, cls.download);
	}

	//fwrite (net_message.data + net_message.readcount, 1, size, cls.download);
	msg->readcount += size;

	if (percent != 100)
	{
		// request next block
// change display routines by zoid

		cls.downloadpercent = percent;

		MSG_WriteByte (&cls.netchan.message, clc_stringcmd);
		SZ_Print (&cls.netchan.message, "nextdl");
	}
	else
	{
		CL_FinishDownload();

		// get another file if needed
		CL_RequestNextDownload ();
	}
}
Exemple #12
0
/**
 * @brief The server will send this command right before allowing the client into the server
 * @sa CL_StartGame
 * @sa SV_Configstrings_f
 */
static void CL_Precache_f (void)
{
	cls.downloadMaps = true;

	CL_RequestNextDownload();
}