コード例 #1
0
void LLUpdateDownloader::Implementation::throwOnCurlError(CURLcode code)
{
	if(code != CURLE_OK) {
		const char * errorString = curl_easy_strerror(code);
		if(errorString != 0) {
			throw DownloadError(curl_easy_strerror(code));
		} else {
			throw DownloadError("unknown curl error");
		}
	} else {
		; // No op.
	}
}
コード例 #2
0
void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & url, bool processHeader)
{
	if(mCurl == 0) {
		mCurl = curl_easy_init();
	} else {
		curl_easy_reset(mCurl);
	}
	
	if(mCurl == 0) throw DownloadError("failed to initialize curl");
	
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOSIGNAL, true));
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_FOLLOWLOCATION, true));
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEFUNCTION, &write_function));
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_WRITEDATA, this));
	if(processHeader) {
	   throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERFUNCTION, &header_function));
	   throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HEADERDATA, this));
	}
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPGET, true));
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_URL, url.c_str()));
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSFUNCTION, &progress_callback));
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_PROGRESSDATA, this));
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_NOPROGRESS, false));
	// if it's a required update set the bandwidth limit to 0 (unlimited)
	curl_off_t limit = mDownloadData["required"].asBoolean() ? 0 : mBandwidthLimit;
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_MAX_RECV_SPEED_LARGE, limit));
	
	mDownloadPercent = 0;
}
コード例 #3
0
void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte)
{
	LL_INFOS("UpdateDownload") << "resuming download from " << mDownloadData["url"].asString()
		<< " at byte " << startByte << LL_ENDL;

	initializeCurlGet(mDownloadData["url"].asString(), false);
	
	// The header 'Range: bytes n-' will request the bytes remaining in the
	// source begining with byte n and ending with the last byte.
	boost::format rangeHeaderFormat("Range: bytes=%u-");
	rangeHeaderFormat % startByte;
	mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str());
	if(mHeaderList == 0) throw DownloadError("cannot add Range header");
	throwOnCurlError(curl_easy_setopt(mCurl, CURLOPT_HTTPHEADER, mHeaderList));
	
	mDownloadStream.open(mDownloadData["path"].asString(),
						 std::ios_base::out | std::ios_base::binary | std::ios_base::app);
	start();
}
コード例 #4
0
void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std::string const & hash)
{
	mDownloadData["url"] = uri.asString();
	mDownloadData["hash"] = hash;
	mDownloadData["current_version"] = ll_get_version();
	LLSD path = uri.pathArray();
	if(path.size() == 0) throw DownloadError("no file path");
	std::string fileName = path[path.size() - 1].asString();
	std::string filePath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, fileName);
	mDownloadData["path"] = filePath;

	LL_INFOS("UpdateDownload") << "downloading " << filePath
		<< " from " << uri.asString() << LL_ENDL;
	LL_INFOS("UpdateDownload") << "hash of file is " << hash << LL_ENDL;
		
	llofstream dataStream(mDownloadRecordPath);
	LLSDSerialize::toPrettyXML(mDownloadData, dataStream);
	
	mDownloadStream.open(filePath, std::ios_base::out | std::ios_base::binary);
	initializeCurlGet(uri.asString(), true);
	start();
}
コード例 #5
0
ファイル: transfer.c プロジェクト: OpenMeridian105/Meridian59
/*
 * TransferStart: Retrieve files; the information needed to set up the
 * transfer is in info.
 *
 * This function is run in its own thread.
 */
void __cdecl TransferStart(void *download_info)
{
    Bool done;
    char filename[MAX_PATH + FILENAME_MAX];
    char local_filename[MAX_PATH + 1];  // Local filename of current downloaded file
    int i;
    int outfile;                   // Handle to output file
    DWORD size;                      // Size of block we're reading
    int bytes_read;                // Total # of bytes we've read

#if defined VANILLA_UPDATER
    const char *mime_types[2] = { "application/x-zip-compressed" };
#else
    const char *mime_types[4] = { "application/octet-stream", "text/plain", "application/x-msdownload", NULL };
    //const char *mime_types[2] = { "application/octet-stream", NULL };
#endif

    DWORD file_size;
    DWORD file_size_buf_len;
    DWORD index = 0;
    DownloadInfo *info = (DownloadInfo *)download_info;

    aborted = False;
    hConnection = NULL;
    hSession = NULL;
    hFile = NULL;

    hConnection = InternetOpen(szAppName, INTERNET_OPEN_TYPE_PRECONFIG,
                               NULL, NULL, INTERNET_FLAG_RELOAD);

    if (hConnection == NULL)
    {
        DownloadError(info->hPostWnd, GetString(hInst, IDS_CANTINIT));
        return;
    }

    if (aborted)
    {
        TransferCloseHandles();
        return;
    }

    hSession = InternetConnect(hConnection, info->machine, INTERNET_INVALID_PORT_NUMBER,
                               NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0);
    if (hSession == NULL)
    {
        DownloadError(info->hPostWnd, GetString(hInst, IDS_NOCONNECTION), info->machine);
        return;
    }

    for (i = info->current_file; i < info->num_files; i++)
    {
        if (aborted)
        {
            TransferCloseHandles();
            return;
        }

        // Skip non-guest files if we're a guest
        if (config.guest && !(info->files[i].flags & DF_GUEST))
        {
            PostMessage(info->hPostWnd, BK_FILEDONE, 0, i);
            continue;
        }

        // If not supposed to transfer file, inform main thread
        if (DownloadCommand(info->files[i].flags) != DF_RETRIEVE)
        {
            // Wait for main thread to finish processing previous file
            WaitForSingleObject(hSemaphore, INFINITE);

            if (aborted)
            {
                TransferCloseHandles();
                return;
            }

            PostMessage(info->hPostWnd, BK_FILEDONE, 0, i);
            continue;
        }
#if VANILLA_UPDATER
        sprintf(filename, "%s%s", info->path, info->files[i].filename);
#else
        sprintf(filename, "%s//%s", info->path, info->files[i].filename);
#endif
        hFile = HttpOpenRequest(hSession, NULL, filename, NULL, NULL,
                                mime_types, INTERNET_FLAG_NO_UI, 0);
        if (hFile == NULL)
        {
            debug(("HTTPOpenRequest failed, error = %d, %s\n",
                   GetLastError(), GetLastErrorStr()));
            DownloadError(info->hPostWnd, GetString(hInst, IDS_CANTFINDFILE),
                          filename, info->machine);
            return;
        }

        if (!HttpSendRequest(hFile, NULL, 0, NULL, 0)) {
            debug(("HTTPSendRequest failed, error = %d, %s\n",
                   GetLastError(), GetLastErrorStr()));
            DownloadError(info->hPostWnd, GetString(hInst, IDS_CANTSENDREQUEST),
                          filename, info->machine);
            return;
        }

        // Get file size
        file_size_buf_len = sizeof(file_size);
        index = 0;
        if (!HttpQueryInfo(hFile, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER,
                           &file_size, &file_size_buf_len, &index)) {
            debug(("HTTPQueryInfo failed, error = %d, %s\n",
                   GetLastError(), GetLastErrorStr()));
            DownloadError(info->hPostWnd, GetString(hInst, IDS_CANTGETFILESIZE),
                          filename, info->machine);
            return;
        }

        PostMessage(info->hPostWnd, BK_FILESIZE, i, file_size);
#if VANILLA_UPDATER
        sprintf(local_filename, "%s\\%s", download_dir, info->files[i].filename);
#else
        sprintf(local_filename, "%s\\%s", info->files[i].path, info->files[i].filename);
#endif
        outfile = open(local_filename, O_BINARY | O_RDWR | O_CREAT | O_TRUNC, S_IWRITE | S_IREAD);
        if (outfile <= 0)
        {
            debug(("Couldn't open local file %s for writing\n", local_filename));
            DownloadError(info->hPostWnd, GetString(hInst, IDS_CANTWRITELOCALFILE),
                          local_filename);
            return;
        }

        // Read first block
        done = False;
        bytes_read = 0;
        while (!done)
        {
            if (!InternetReadFile(hFile, buf, BUFSIZE, &size))
            {
                DownloadError(info->hPostWnd, GetString(hInst, IDS_CANTREADFTPFILE), filename);
            }

            if (size > 0)
            {
                if (write(outfile, buf, size) != size)
                {
                    DownloadError(info->hPostWnd, GetString(hInst, IDS_CANTWRITELOCALFILE),
                                  local_filename);
                    close(outfile);
                    return;
                }
            }

            // Update graph position
            bytes_read += size;
            PostMessage(info->hPostWnd, BK_PROGRESS, 0, bytes_read);

            // See if done with file
            if (size == 0)
            {
                close(outfile);
                InternetCloseHandle(hFile);
                done = True;

                // Wait for main thread to finish processing previous file
                WaitForSingleObject(hSemaphore, INFINITE);

                if (aborted)
                {
                    TransferCloseHandles();
                    return;
                }

                PostMessage(info->hPostWnd, BK_FILEDONE, 0, i);
            }
        }
    }

    InternetCloseHandle(hSession);
    InternetCloseHandle(hConnection);

    PostMessage(info->hPostWnd, BK_TRANSFERDONE, 0, 0);
}
コード例 #6
0
void game_compatibility::RequestCompatibility(bool online)
{
	// Creates new map from database
	auto ReadJSON = [=](const QJsonObject& json_data, bool after_download)
	{
		int return_code = json_data["return_code"].toInt();

		if (return_code < 0)
		{
			if (after_download)
			{
				std::string error_message;
				switch (return_code)
				{
				case -1:
					error_message = "Server Error - Internal Error";
					break;
				case -2:
					error_message = "Server Error - Maintenance Mode";
					break;
				default:
					error_message = "Server Error - Unknown Error";
					break;
				}
				LOG_ERROR(GENERAL, "Compatibility error: { %s: return code %d }", error_message, return_code);
				Q_EMIT DownloadError(qstr(error_message) + " " + QString::number(return_code));
			}
			else
			{
				LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Invalid: return code %d }", return_code);
			}
			return false;
		}

		if (!json_data["results"].isObject())
		{
			LOG_ERROR(GENERAL, "Compatibility error: { Database Error - No Results found }");
			return false;
		}

		m_compat_database.clear();

		QJsonObject json_results = json_data["results"].toObject();

		// Retrieve status data for every valid entry
		for (const auto& key : json_results.keys())
		{
			if (!json_results[key].isObject())
			{
				LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Unusable object %s }", sstr(key));
				continue;
			}

			QJsonObject json_result = json_results[key].toObject();

			// Retrieve compatibility information from json
			compat_status status = Status_Data.at(json_result.value("status").toString("NoResult"));

			// Add date if possible
			status.date = json_result.value("date").toString();

			// Add status to map
			m_compat_database.emplace(std::pair<std::string, compat_status>(sstr(key), status));
		}

		return true;
	};

	if (!online)
	{
		// Retrieve database from file
		QFile file(m_filepath);

		if (!file.exists())
		{
			LOG_NOTICE(GENERAL, "Compatibility notice: { Database file not found: %s }", sstr(m_filepath));
			return;
		}

		if (!file.open(QIODevice::ReadOnly))
		{
			LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Could not read database from file: %s }", sstr(m_filepath));
			return;
		}

		QByteArray data = file.readAll();
		file.close();

		LOG_NOTICE(GENERAL, "Compatibility notice: { Finished reading database from file: %s }", sstr(m_filepath));

		// Create new map from database
		ReadJSON(QJsonDocument::fromJson(data).object(), online);

		return;
	}

	if (QSslSocket::supportsSsl() == false)
	{
		LOG_ERROR(GENERAL, "Can not retrieve the online database! Please make sure your system supports SSL.");
		QMessageBox::warning(nullptr, tr("Warning!"), tr("Can not retrieve the online database! Please make sure your system supports SSL."));
		return;
	}

	LOG_NOTICE(GENERAL, "SSL supported! Beginning compatibility database download from: %s", sstr(m_url));

	// Send request and wait for response
	m_network_access_manager.reset(new QNetworkAccessManager());
	QNetworkReply* network_reply = m_network_access_manager->get(m_network_request);

	// Show Progress
	m_progress_dialog.reset(new QProgressDialog(tr(".Please wait."), tr("Abort"), 0, 100));
	m_progress_dialog->setWindowTitle(tr("Downloading Database"));
	m_progress_dialog->setFixedWidth(QLabel("This is the very length of the progressbar due to hidpi reasons.").sizeHint().width());
	m_progress_dialog->setValue(0);
	m_progress_dialog->show();

	// Animate progress dialog a bit more
	m_progress_timer.reset(new QTimer(this));
	connect(m_progress_timer.get(), &QTimer::timeout, [&]()
	{
		switch (++m_timer_count % 3)
		{
		case 0:
			m_timer_count = 0;
			m_progress_dialog->setLabelText(tr(".Please wait."));
			break;
		case 1:
			m_progress_dialog->setLabelText(tr("..Please wait.."));
			break;
		default:
			m_progress_dialog->setLabelText(tr("...Please wait..."));
			break;
		}
	});
	m_progress_timer->start(500);

	// Handle abort
	connect(m_progress_dialog.get(), &QProgressDialog::rejected, network_reply, &QNetworkReply::abort);

	// Handle progress
	connect(network_reply, &QNetworkReply::downloadProgress, [&](qint64 bytesReceived, qint64 bytesTotal)
	{
		m_progress_dialog->setMaximum(bytesTotal);
		m_progress_dialog->setValue(bytesReceived);
	});

	// Handle response according to its contents
	connect(network_reply, &QNetworkReply::finished, [=]()
	{
		// Clean up Progress Dialog
		if (m_progress_dialog)
		{
			m_progress_dialog->close();
		}
		if (m_progress_timer)
		{
			m_progress_timer->stop();
		}

		// Handle Errors
		if (network_reply->error() != QNetworkReply::NoError)
		{
			// We failed to retrieve a new database, therefore refresh gamelist to old state
			QString error = network_reply->errorString();
			Q_EMIT DownloadError(error);
			LOG_ERROR(GENERAL, "Compatibility error: { Network Error - %s }", sstr(error));
			return;
		}

		LOG_NOTICE(GENERAL, "Compatibility notice: { Database download finished }");

		// Read data from network reply
		QByteArray data = network_reply->readAll();
		network_reply->deleteLater();

		// Create new map from database and write database to file if database was valid
		if (ReadJSON(QJsonDocument::fromJson(data).object(), online))
		{
			// We have a new database in map, therefore refresh gamelist to new state
			Q_EMIT DownloadFinished();

			// Write database to file
			QFile file(m_filepath);

			if (file.exists())
			{
				LOG_NOTICE(GENERAL, "Compatibility notice: { Database file found: %s }", sstr(m_filepath));
			}

			if (!file.open(QIODevice::WriteOnly))
			{
				LOG_ERROR(GENERAL, "Compatibility error: { Database Error - Could not write database to file: %s }", sstr(m_filepath));
				return;
			}

			file.write(data);
			file.close();

			LOG_SUCCESS(GENERAL, "Compatibility success: { Write database to file: %s }", sstr(m_filepath));
		}
	});

	// We want to retrieve a new database, therefore refresh gamelist and indicate that
	Q_EMIT DownloadStarted();
}