Esempio n. 1
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", 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"))
                    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);
                    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)
                remove (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->quakePath);
            if (!strcmp (dl->queueEntry->quakePath + i - 4, ".pak"))
                downloading_pak = false;
            if (isFile)
                remove (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->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"))
            {
                CL_RestartFilesystem(true);
                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)
            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 ();
}
Esempio n. 2
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;
}
Esempio n. 3
0
/*
==================
CL_ParseServerData
==================
*/
qboolean CL_ParseServerData (sizebuf_t *msg)
{
	char	*str;
	int		i;
	
	Com_DPrintf ("Serverdata packet received.\n");
//
// wipe the client_state_t struct
//
	CL_ClearState ();
	cls.state = ca_connected;

// parse protocol version number
	i = MSG_ReadLong (msg);
	cls.serverProtocol = i;

	cl.servercount = MSG_ReadLong (msg);
	cl.attractloop = MSG_ReadByte (msg);

	if (cl.attractloop) {
		//cls.serverProtocol = PROTOCOL_VERSION_DEFAULT;
	}
	else if (i != PROTOCOL_VERSION_DEFAULT && i != PROTOCOL_VERSION_R1Q2) {
		Com_Error (ERR_DROP, "Server is using unknown protocol %d.", i);
	}

	// game directory
	str = MSG_ReadString (msg);
	Q_strncpyz (cl.gamedir, str, sizeof(cl.gamedir));
	str = cl.gamedir;

	// set gamedir
	if (!Com_ServerState()) {
		Cvar_SetLatched("game", str);
		if( FS_NeedRestart() ) {
			CL_RestartFilesystem(true);
		}
	}

	// parse player entity number
	cl.playernum = MSG_ReadShort (msg);

	// get the full level name
	str = MSG_ReadString (msg);

	cl.pmp.strafeHack = false;
	cl.pmp.speedMultiplier = 1;
	cl.pmp.airaccelerate = 0;
	cls.protocolVersion = 0;

	if (cls.serverProtocol == PROTOCOL_VERSION_R1Q2)
	{
		i = MSG_ReadByte(msg);
		if( i ) {
			Com_Printf("'Enhanced' R1Q2 servers are not supported, falling back to protocol 34.\n" );
			CL_Disconnect();		
			cls.serverProtocol = PROTOCOL_VERSION_DEFAULT;
			CL_Reconnect_f ();
			return false;
		}
		i = MSG_ReadShort(msg);
		if (i < PROTOCOL_VERSION_R1Q2_MINIMUM || i > PROTOCOL_VERSION_R1Q2_CURRENT)
		{
			if (cl.attractloop)
			{
				if ( i < PROTOCOL_VERSION_R1Q2_MINIMUM )
					Com_Printf("This demo was recorded with an earlier version of the R1Q2 protocol. It may not play back properly.\n");
				else
					Com_Printf("This demo was recorded with a later version of the R1Q2 protocol. It may not play back properly.\n");
			}
			else
			{
				if( i < PROTOCOL_VERSION_R1Q2_MINIMUM ) {
					Com_Printf("Server uses OLDER minor R1Q2 protocol version than minimum supported (%i < %i), falling back to protocol 34.\n", i, PROTOCOL_VERSION_R1Q2_MINIMUM);
					CL_Disconnect();
					cls.serverProtocol = PROTOCOL_VERSION_DEFAULT;
					CL_Reconnect_f ();
					return false;
				}
				Com_Printf("Server uses NEWER minor R1Q2 protocol version (%i > %i), some features will be unavailable.\n", i, PROTOCOL_VERSION_R1Q2_CURRENT);
			}
		}

		if (i >= 1903) {
			MSG_ReadByte(msg);
			cl.pmp.strafeHack = MSG_ReadByte(msg);
		}

		cl.pmp.speedMultiplier = 2;
		cls.protocolVersion = i;
	}


	if (cl.playernum == -1)
	{	// playing a cinematic or showing a pic, not a level
		SCR_PlayCinematic (str);
	}
	else
	{
		// seperate the printfs so the server message can have a color
		Com_Printf("\n\n\35\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\36\37\n\n");
		Com_Printf ("%c%s\n", 2, str);

		// need to prep refresh at next oportunity
		cl.refresh_prepped = false;

        if((unsigned)cl.playernum >= MAX_CLIENTS) {
            cl.playernum = ( MAX_CLIENTS - 1 );
        }
	}

	return true;
}