diff options
| author | Loren Shih <seraph@lindenlab.com> | 2010-12-01 10:55:24 -0500 | 
|---|---|---|
| committer | Loren Shih <seraph@lindenlab.com> | 2010-12-01 10:55:24 -0500 | 
| commit | bfd8efb05753ac9ed858476797b5d407c912d926 (patch) | |
| tree | 52905cf59bcd5b67ddc186b26a23959201353a7a /indra/viewer_components/updater | |
| parent | ed64630a67c2c216c369099532323cab2e251cab (diff) | |
| parent | b778ba9b882ecb03e61b3b9bd833618ca096f5da (diff) | |
Automated merge up from viewer-development
Diffstat (limited to 'indra/viewer_components/updater')
13 files changed, 1845 insertions, 0 deletions
| diff --git a/indra/viewer_components/updater/CMakeLists.txt b/indra/viewer_components/updater/CMakeLists.txt new file mode 100644 index 0000000000..0e288bb496 --- /dev/null +++ b/indra/viewer_components/updater/CMakeLists.txt @@ -0,0 +1,82 @@ +# -*- cmake -*- + +project(updater_service) + +include(00-Common) +if(LL_TESTS) +  include(LLAddBuildTest) +endif(LL_TESTS) +include(CMakeCopyIfDifferent) +include(CURL) +include(LLCommon) +include(LLMessage) +include(LLPlugin) +include(LLVFS) + +include_directories( +    ${LLCOMMON_INCLUDE_DIRS} +    ${LLMESSAGE_INCLUDE_DIRS} +    ${LLPLUGIN_INCLUDE_DIRS} +	${LLVFS_INCLUDE_DIRS} +	${CURL_INCLUDE_DIRS} +    ) + +set(updater_service_SOURCE_FILES +    llupdaterservice.cpp +    llupdatechecker.cpp +    llupdatedownloader.cpp +    llupdateinstaller.cpp +    ) + +set(updater_service_HEADER_FILES +    llupdaterservice.h +    llupdatechecker.h +    llupdatedownloader.h +    llupdateinstaller.h +    ) + +set_source_files_properties(${updater_service_HEADER_FILES} +                            PROPERTIES HEADER_FILE_ONLY TRUE) + +list(APPEND  +    updater_service_SOURCE_FILES  +    ${updater_service_HEADER_FILES}  +    ) + +add_library(llupdaterservice +            ${updater_service_SOURCE_FILES} +            ) + +target_link_libraries(llupdaterservice +    ${LLCOMMON_LIBRARIES} +    ${LLMESSAGE_LIBRARIES} +    ${LLPLUGIN_LIBRARIES} +	${LLVFS_LIBRARIES} +    ) + +if(LL_TESTS) +  SET(llupdater_service_TEST_SOURCE_FILES +      llupdaterservice.cpp +      ) + +# *NOTE:Mani - I was trying to use the preprocessor seam to mock out +#              llifstream (and other) llcommon classes. I didn't work +#              because of the windows declspec(dllimport)attribute. +#set_source_files_properties( +#    llupdaterservice.cpp +#    PROPERTIES +#      LL_TEST_ADDITIONAL_CFLAGS "-Dllifstream=llus_mock_llifstream" +#    ) + +  LL_ADD_PROJECT_UNIT_TESTS(llupdaterservice "${llupdater_service_TEST_SOURCE_FILES}") +endif(LL_TESTS) + +set(UPDATER_INCLUDE_DIRS  +  ${LIBS_OPEN_DIR}/viewer_components/updater  +  CACHE INTERNAL "" +) + +set(UPDATER_LIBRARIES  +  llupdaterservice +  CACHE INTERNAL "" +) diff --git a/indra/viewer_components/updater/llupdatechecker.cpp b/indra/viewer_components/updater/llupdatechecker.cpp new file mode 100644 index 0000000000..c6aa9b0f11 --- /dev/null +++ b/indra/viewer_components/updater/llupdatechecker.cpp @@ -0,0 +1,194 @@ +/**  + * @file llupdaterservice.cpp + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include <stdexcept> +#include <boost/format.hpp> +#include "llhttpclient.h" +#include "llsd.h" +#include "llupdatechecker.h" +#include "lluri.h" + + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + + +class LLUpdateChecker::CheckError: +	public std::runtime_error +{ +public: +	CheckError(const char * message): +		std::runtime_error(message) +	{ +		; // No op. +	} +}; + + +class LLUpdateChecker::Implementation: +	public LLHTTPClient::Responder +{ +public: +	Implementation(Client & client); +	~Implementation(); +	void check(std::string const & protocolVersion, std::string const & hostUrl,  +			   std::string const & servicePath, std::string channel, std::string version); +	 +	// Responder: +	virtual void completed(U32 status, +						   const std::string & reason, +						   const LLSD& content); +	virtual void error(U32 status, const std::string & reason); +	 +private:	 +	static const char * sProtocolVersion; +	 +	Client & mClient; +	LLHTTPClient mHttpClient; +	bool mInProgress; +	std::string mVersion; +	 +	std::string buildUrl(std::string const & protocolVersion, std::string const & hostUrl,  +						 std::string const & servicePath, std::string channel, std::string version); + +	LOG_CLASS(LLUpdateChecker::Implementation); +}; + + + +// LLUpdateChecker +//----------------------------------------------------------------------------- + + +LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client): +	mImplementation(new LLUpdateChecker::Implementation(client)) +{ +	; // No op. +} + + +void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl,  +							std::string const & servicePath, std::string channel, std::string version) +{ +	mImplementation->check(protocolVersion, hostUrl, servicePath, channel, version); +} + + + +// LLUpdateChecker::Implementation +//----------------------------------------------------------------------------- + + +const char * LLUpdateChecker::Implementation::sProtocolVersion = "v1.0"; + + +LLUpdateChecker::Implementation::Implementation(LLUpdateChecker::Client & client): +	mClient(client), +	mInProgress(false) +{ +	; // No op. +} + + +LLUpdateChecker::Implementation::~Implementation() +{ +	; // No op. +} + + +void LLUpdateChecker::Implementation::check(std::string const & protocolVersion, std::string const & hostUrl,  +											std::string const & servicePath, std::string channel, std::string version) +{ +	llassert(!mInProgress); +	 +	if(protocolVersion != sProtocolVersion) throw CheckError("unsupported protocol"); +		 +	mInProgress = true; +	mVersion = version; +	std::string checkUrl = buildUrl(protocolVersion, hostUrl, servicePath, channel, version); +	LL_INFOS("UpdateCheck") << "checking for updates at " << checkUrl << llendl; +	 +	// The HTTP client will wrap a raw pointer in a boost::intrusive_ptr causing the +	// passed object to be silently and automatically deleted.  We pass a self- +	// referential intrusive pointer to which we add a reference to keep the +	// client from deleting the update checker implementation instance. +	LLHTTPClient::ResponderPtr temporaryPtr(this); +	boost::intrusive_ptr_add_ref(temporaryPtr.get()); +	mHttpClient.get(checkUrl, temporaryPtr); +} + +void LLUpdateChecker::Implementation::completed(U32 status, +							  const std::string & reason, +							  const LLSD & content) +{ +	mInProgress = false;	 +	 +	if(status != 200) { +		LL_WARNS("UpdateCheck") << "html error " << status << " (" << reason << ")" << llendl; +		mClient.error(reason); +	} else if(!content.asBoolean()) { +		LL_INFOS("UpdateCheck") << "up to date" << llendl; +		mClient.upToDate(); +	} else if(content["required"].asBoolean()) { +		LL_INFOS("UpdateCheck") << "version invalid" << llendl; +		LLURI uri(content["url"].asString()); +		mClient.requiredUpdate(content["version"].asString(), uri, content["hash"].asString()); +	} else { +		LL_INFOS("UpdateCheck") << "newer version " << content["version"].asString() << " available" << llendl; +		LLURI uri(content["url"].asString()); +		mClient.optionalUpdate(content["version"].asString(), uri, content["hash"].asString()); +	} +} + + +void LLUpdateChecker::Implementation::error(U32 status, const std::string & reason) +{ +	mInProgress = false; +	LL_WARNS("UpdateCheck") << "update check failed; " << reason << llendl; +	mClient.error(reason); +} + + +std::string LLUpdateChecker::Implementation::buildUrl(std::string const & protocolVersion, std::string const & hostUrl,  +													  std::string const & servicePath, std::string channel, std::string version) +{	 +#ifdef LL_WINDOWS +	static const char * platform = "win"; +#elif LL_DARWIN +	static const char * platform = "mac"; +#else +	static const char * platform = "lnx"; +#endif +	 +	LLSD path; +	path.append(servicePath); +	path.append(protocolVersion); +	path.append(channel); +	path.append(version); +	path.append(platform); +	return LLURI::buildHTTP(hostUrl, path).asString(); +} diff --git a/indra/viewer_components/updater/llupdatechecker.h b/indra/viewer_components/updater/llupdatechecker.h new file mode 100644 index 0000000000..cea1f13647 --- /dev/null +++ b/indra/viewer_components/updater/llupdatechecker.h @@ -0,0 +1,82 @@ +/**  + * @file llupdatechecker.h + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_UPDATERCHECKER_H +#define LL_UPDATERCHECKER_H + + +#include <boost/shared_ptr.hpp> + + +// +// Implements asynchronous checking for updates. +// +class LLUpdateChecker { +public: +	class Client; +	class Implementation; +	 +	// An exception that may be raised on check errors. +	class CheckError; +	 +	LLUpdateChecker(Client & client); +	 +	// Check status of current app on the given host for the channel and version provided. +	void check(std::string const & protocolVersion, std::string const & hostUrl,  +			   std::string const & servicePath, std::string channel, std::string version); +	 +private: +	boost::shared_ptr<Implementation> mImplementation; +}; + + +class LLURI; // From lluri.h + + +// +// The client interface implemented by a requestor checking for an update. +// +class LLUpdateChecker::Client +{ +public: +	// An error occurred while checking for an update. +	virtual void error(std::string const & message) = 0; +	 +	// A newer version is available, but the current version may still be used. +	virtual void optionalUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash) = 0; +	 +	// A newer version is available, and the current version is no longer valid.  +	virtual void requiredUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash) = 0; +	 +	// The checked version is up to date; no newer version exists. +	virtual void upToDate(void) = 0; +}; + + +#endif diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp new file mode 100644 index 0000000000..c17a50e242 --- /dev/null +++ b/indra/viewer_components/updater/llupdatedownloader.cpp @@ -0,0 +1,428 @@ +/**  + * @file llupdatedownloader.cpp + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include <stdexcept> +#include <boost/format.hpp> +#include <boost/lexical_cast.hpp> +#include <curl/curl.h> +#include "lldir.h" +#include "llfile.h" +#include "llmd5.h" +#include "llsd.h" +#include "llsdserialize.h" +#include "llthread.h" +#include "llupdatedownloader.h" +#include "llupdaterservice.h" + + +class LLUpdateDownloader::Implementation: +	public LLThread +{ +public: +	Implementation(LLUpdateDownloader::Client & client); +	~Implementation(); +	void cancel(void); +	void download(LLURI const & uri, std::string const & hash); +	bool isDownloading(void); +	size_t onHeader(void * header, size_t size); +	size_t onBody(void * header, size_t size); +	void resume(void); +	 +private: +	bool mCancelled; +	LLUpdateDownloader::Client & mClient; +	CURL * mCurl; +	LLSD mDownloadData; +	llofstream mDownloadStream; +	std::string mDownloadRecordPath; +	curl_slist * mHeaderList; +	 +	void initializeCurlGet(std::string const & url, bool processHeader); +	void resumeDownloading(size_t startByte); +	void run(void); +	void startDownloading(LLURI const & uri, std::string const & hash); +	void throwOnCurlError(CURLcode code); +	bool validateDownload(void); + +	LOG_CLASS(LLUpdateDownloader::Implementation); +}; + + +namespace { +	class DownloadError: +		public std::runtime_error +	{ +	public: +		DownloadError(const char * message): +			std::runtime_error(message) +		{ +			; // No op. +		} +	}; + +		 +	const char * gSecondLifeUpdateRecord = "SecondLifeUpdateDownload.xml"; +}; + + + +// LLUpdateDownloader +//----------------------------------------------------------------------------- + + + +std::string LLUpdateDownloader::downloadMarkerPath(void) +{ +	return gDirUtilp->getExpandedFilename(LL_PATH_LOGS, gSecondLifeUpdateRecord); +} + + +LLUpdateDownloader::LLUpdateDownloader(Client & client): +	mImplementation(new LLUpdateDownloader::Implementation(client)) +{ +	; // No op. +} + + +void LLUpdateDownloader::cancel(void) +{ +	mImplementation->cancel(); +} + + +void LLUpdateDownloader::download(LLURI const & uri, std::string const & hash) +{ +	mImplementation->download(uri, hash); +} + + +bool LLUpdateDownloader::isDownloading(void) +{ +	return mImplementation->isDownloading(); +} + + +void LLUpdateDownloader::resume(void) +{ +	mImplementation->resume(); +} + + + +// LLUpdateDownloader::Implementation +//----------------------------------------------------------------------------- + + +namespace { +	size_t write_function(void * data, size_t blockSize, size_t blocks, void * downloader) +	{ +		size_t bytes = blockSize * blocks; +		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onBody(data, bytes); +	} + + +	size_t header_function(void * data, size_t blockSize, size_t blocks, void * downloader) +	{ +		size_t bytes = blockSize * blocks; +		return reinterpret_cast<LLUpdateDownloader::Implementation *>(downloader)->onHeader(data, bytes); +	} +} + + +LLUpdateDownloader::Implementation::Implementation(LLUpdateDownloader::Client & client): +	LLThread("LLUpdateDownloader"), +	mCancelled(false), +	mClient(client), +	mCurl(0), +	mHeaderList(0) +{ +	CURLcode code = curl_global_init(CURL_GLOBAL_ALL); // Just in case. +	llverify(code == CURLE_OK); // TODO: real error handling here.  +} + + +LLUpdateDownloader::Implementation::~Implementation() +{ +	if(isDownloading()) { +		cancel(); +		shutdown(); +	} else { +		; // No op. +	} +	if(mCurl) curl_easy_cleanup(mCurl); +} + + +void LLUpdateDownloader::Implementation::cancel(void) +{ +	mCancelled = true; +} +	 + +void LLUpdateDownloader::Implementation::download(LLURI const & uri, std::string const & hash) +{ +	if(isDownloading()) mClient.downloadError("download in progress"); + +	mDownloadRecordPath = downloadMarkerPath(); +	mDownloadData = LLSD(); +	try { +		startDownloading(uri, hash); +	} catch(DownloadError const & e) { +		mClient.downloadError(e.what()); +	} +} + + +bool LLUpdateDownloader::Implementation::isDownloading(void) +{ +	return !isStopped(); +} + + +void LLUpdateDownloader::Implementation::resume(void) +{ +	mCancelled = false; + +	if(isDownloading()) { +		mClient.downloadError("download in progress"); +	} + +	mDownloadRecordPath = downloadMarkerPath(); +	llifstream dataStream(mDownloadRecordPath); +	if(!dataStream) { +		mClient.downloadError("no download marker"); +		return; +	} +	 +	LLSDSerialize::fromXMLDocument(mDownloadData, dataStream); +	 +	if(!mDownloadData.asBoolean()) { +		mClient.downloadError("no download information in marker"); +		return; +	} +	 +	std::string filePath = mDownloadData["path"].asString(); +	try { +		if(LLFile::isfile(filePath)) {		 +			llstat fileStatus; +			LLFile::stat(filePath, &fileStatus); +			if(fileStatus.st_size != mDownloadData["size"].asInteger()) { +				resumeDownloading(fileStatus.st_size); +			} else if(!validateDownload()) { +				LLFile::remove(filePath); +				download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString()); +			} else { +				mClient.downloadComplete(mDownloadData); +			} +		} else { +			download(LLURI(mDownloadData["url"].asString()), mDownloadData["hash"].asString()); +		} +	} catch(DownloadError & e) { +		mClient.downloadError(e.what()); +	} +} + + +size_t LLUpdateDownloader::Implementation::onHeader(void * buffer, size_t size) +{ +	char const * headerPtr = reinterpret_cast<const char *> (buffer); +	std::string header(headerPtr, headerPtr + size); +	size_t colonPosition = header.find(':'); +	if(colonPosition == std::string::npos) return size; // HTML response; ignore. +	 +	if(header.substr(0, colonPosition) == "Content-Length") { +		try { +			size_t firstDigitPos = header.find_first_of("0123456789", colonPosition); +			size_t lastDigitPos = header.find_last_of("0123456789"); +			std::string contentLength = header.substr(firstDigitPos, lastDigitPos - firstDigitPos + 1); +			size_t size = boost::lexical_cast<size_t>(contentLength); +			LL_INFOS("UpdateDownload") << "download size is " << size << LL_ENDL; +			 +			mDownloadData["size"] = LLSD(LLSD::Integer(size)); +			llofstream odataStream(mDownloadRecordPath); +			LLSDSerialize::toPrettyXML(mDownloadData, odataStream); +		} catch (std::exception const & e) { +			LL_WARNS("UpdateDownload") << "unable to read content length ("  +				<< e.what() << ")" << LL_ENDL; +		} +	} else { +		; // No op. +	} +	 +	return size; +} + + +size_t LLUpdateDownloader::Implementation::onBody(void * buffer, size_t size) +{ +	if(mCancelled) return 0; // Forces a write error which will halt curl thread. +	if((size == 0) || (buffer == 0)) return 0;  +	 +	mDownloadStream.write(reinterpret_cast<const char *>(buffer), size); +	if(mDownloadStream.bad()) { +		return 0; +	} else { +		return size; +	} +} + + +void LLUpdateDownloader::Implementation::run(void) +{ +	CURLcode code = curl_easy_perform(mCurl); +	mDownloadStream.close(); +	if(code == CURLE_OK) { +		LLFile::remove(mDownloadRecordPath); +		if(validateDownload()) { +			LL_INFOS("UpdateDownload") << "download successful" << LL_ENDL; +			mClient.downloadComplete(mDownloadData); +		} else { +			LL_INFOS("UpdateDownload") << "download failed hash check" << LL_ENDL; +			std::string filePath = mDownloadData["path"].asString(); +			if(filePath.size() != 0) LLFile::remove(filePath); +			mClient.downloadError("failed hash check"); +		} +	} else if(mCancelled && (code == CURLE_WRITE_ERROR)) { +		LL_INFOS("UpdateDownload") << "download canceled by user" << LL_ENDL; +		// Do not call back client. +	} else { +		LL_WARNS("UpdateDownload") << "download failed with error '" <<  +			curl_easy_strerror(code) << "'" << LL_ENDL; +		LLFile::remove(mDownloadRecordPath); +		if(mDownloadData.has("path")) LLFile::remove(mDownloadData["path"].asString()); +		mClient.downloadError("curl error"); +	} +	 +	if(mHeaderList) { +		curl_slist_free_all(mHeaderList); +		mHeaderList = 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())); +} + + +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(); +} + + +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(); +} + + +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. +	} +} + + +bool LLUpdateDownloader::Implementation::validateDownload(void) +{ +	std::string filePath = mDownloadData["path"].asString(); +	llifstream fileStream(filePath, std::ios_base::in | std::ios_base::binary); +	if(!fileStream) return false; + +	std::string hash = mDownloadData["hash"].asString(); +	if(hash.size() != 0) { +		LL_INFOS("UpdateDownload") << "checking hash..." << LL_ENDL; +		char digest[33]; +		LLMD5(fileStream).hex_digest(digest); +		if(hash != digest) { +			LL_WARNS("UpdateDownload") << "download hash mismatch; expeted " << hash << +				" but download is " << digest << LL_ENDL; +		} +		return hash == digest; +	} else { +		return true; // No hash check provided. +	} +} diff --git a/indra/viewer_components/updater/llupdatedownloader.h b/indra/viewer_components/updater/llupdatedownloader.h new file mode 100644 index 0000000000..1b3d7480fd --- /dev/null +++ b/indra/viewer_components/updater/llupdatedownloader.h @@ -0,0 +1,87 @@ +/**  + * @file llupdatedownloader.h + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_UPDATE_DOWNLOADER_H +#define LL_UPDATE_DOWNLOADER_H + + +#include <string> +#include <boost/shared_ptr.hpp> +#include "lluri.h" + + +// +// An asynchronous download service for fetching updates. +// +class LLUpdateDownloader +{ +public: +	class Client; +	class Implementation; +	 +	// Returns the path to the download marker file containing details of the +	// latest download. +	static std::string downloadMarkerPath(void); +	 +	LLUpdateDownloader(Client & client); +	 +	// Cancel any in progress download; a no op if none is in progress.  The +	// client will not receive a complete or error callback. +	void cancel(void); +	 +	// Start a new download. +	void download(LLURI const & uri, std::string const & hash); +	 +	// Returns true if a download is in progress. +	bool isDownloading(void); +	 +	// Resume a partial download. +	void resume(void); +	 +private: +	boost::shared_ptr<Implementation> mImplementation; +}; + + +// +// An interface to be implemented by clients initiating a update download. +// +class LLUpdateDownloader::Client { +public: +	 +	// The download has completed successfully. +	// data is a map containing the following items: +	// url - source (remote) location +	// hash - the md5 sum that should match the installer file. +	// path - destination (local) location +	// size - the size of the installer in bytes +	virtual void downloadComplete(LLSD const & data) = 0; +	 +	// The download failed. +	virtual void downloadError(std::string const & message) = 0; +}; + + +#endif diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp new file mode 100644 index 0000000000..650ef88af4 --- /dev/null +++ b/indra/viewer_components/updater/llupdateinstaller.cpp @@ -0,0 +1,90 @@ +/**  + * @file llupdateinstaller.cpp + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" +#include <apr_file_io.h> +#include "llapr.h" +#include "llprocesslauncher.h" +#include "llupdateinstaller.h" +#include "lldir.h"  + + +namespace { +	class RelocateError {}; +	 +	 +	std::string copy_to_temp(std::string const & path) +	{ +		std::string scriptFile = gDirUtilp->getBaseFileName(path); +		std::string newPath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, scriptFile); +		apr_status_t status = apr_file_copy(path.c_str(), newPath.c_str(), APR_FILE_SOURCE_PERMS, gAPRPoolp); +		if(status != APR_SUCCESS) throw RelocateError(); +		 +		return newPath; +	} +} + + +int ll_install_update(std::string const & script, std::string const & updatePath, LLInstallScriptMode mode) +{ +	std::string actualScriptPath; +	switch(mode) { +		case LL_COPY_INSTALL_SCRIPT_TO_TEMP: +			try { +				actualScriptPath = copy_to_temp(script); +			} +			catch (RelocateError &) { +				return -1; +			} +			break; +		case LL_RUN_INSTALL_SCRIPT_IN_PLACE: +			actualScriptPath = script; +			break; +		default: +			llassert(!"unpossible copy mode"); +	} +	 +	llinfos << "UpdateInstaller: installing " << updatePath << " using " << +		actualScriptPath << LL_ENDL; +	 +	LLProcessLauncher launcher; +	launcher.setExecutable(actualScriptPath); +	launcher.addArgument(updatePath); +	launcher.addArgument(ll_install_failed_marker_path().c_str()); +	int result = launcher.launch(); +	launcher.orphan(); +	 +	return result; +} + + +std::string const & ll_install_failed_marker_path(void) +{ +	static std::string path; +	if(path.empty()) { +		path = gDirUtilp->getExpandedFilename(LL_PATH_LOGS, "SecondLifeInstallFailed.marker"); +	} +	return path; +} diff --git a/indra/viewer_components/updater/llupdateinstaller.h b/indra/viewer_components/updater/llupdateinstaller.h new file mode 100644 index 0000000000..6ce08ce6fa --- /dev/null +++ b/indra/viewer_components/updater/llupdateinstaller.h @@ -0,0 +1,57 @@ +/**  + * @file llupdateinstaller.h + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_UPDATE_INSTALLER_H +#define LL_UPDATE_INSTALLER_H + + +#include <string> + + +enum LLInstallScriptMode { +	LL_RUN_INSTALL_SCRIPT_IN_PLACE, +	LL_COPY_INSTALL_SCRIPT_TO_TEMP +}; + +// +// Launch the installation script. +//  +// The updater will overwrite the current installation, so it is highly recommended +// that the current application terminate once this function is called. +// +int ll_install_update( +					   std::string const & script, // Script to execute. +					   std::string const & updatePath, // Path to update file. +					   LLInstallScriptMode mode=LL_COPY_INSTALL_SCRIPT_TO_TEMP); // Run in place or copy to temp? + + +// +// Returns the path which points to the failed install marker file, should it +// exist. +// +std::string const & ll_install_failed_marker_path(void); + + +#endif diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp new file mode 100644 index 0000000000..cc60eaead2 --- /dev/null +++ b/indra/viewer_components/updater/llupdaterservice.cpp @@ -0,0 +1,519 @@ +/**  + * @file llupdaterservice.cpp + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#include "linden_common.h" + +#include "llupdatedownloader.h" +#include "llevents.h" +#include "lltimer.h" +#include "llupdaterservice.h" +#include "llupdatechecker.h" +#include "llupdateinstaller.h" +#include "llversionviewer.h" + +#include <boost/scoped_ptr.hpp> +#include <boost/weak_ptr.hpp> +#include "lldir.h" +#include "llsdserialize.h" +#include "llfile.h" + +#if LL_WINDOWS +#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally +#endif + + +namespace  +{ +	boost::weak_ptr<LLUpdaterServiceImpl> gUpdater; + +	const std::string UPDATE_MARKER_FILENAME("SecondLifeUpdateReady.xml"); +	std::string update_marker_path() +	{ +		return gDirUtilp->getExpandedFilename(LL_PATH_LOGS,  +											  UPDATE_MARKER_FILENAME); +	} +	 +	std::string install_script_path(void) +	{ +#ifdef LL_WINDOWS +		std::string scriptFile = "update_install.bat"; +#else +		std::string scriptFile = "update_install"; +#endif +		return gDirUtilp->getExpandedFilename(LL_PATH_EXECUTABLE, scriptFile); +	} +	 +	LLInstallScriptMode install_script_mode(void)  +	{ +#ifdef LL_WINDOWS +		return LL_COPY_INSTALL_SCRIPT_TO_TEMP; +#else +		return LL_RUN_INSTALL_SCRIPT_IN_PLACE; +#endif +	}; +	 +} + +class LLUpdaterServiceImpl :  +	public LLUpdateChecker::Client, +	public LLUpdateDownloader::Client +{ +	static const std::string sListenerName; +	 +	std::string mProtocolVersion; +	std::string mUrl; +	std::string mPath; +	std::string mChannel; +	std::string mVersion; +	 +	unsigned int mCheckPeriod; +	bool mIsChecking; +	bool mIsDownloading; +	 +	LLUpdateChecker mUpdateChecker; +	LLUpdateDownloader mUpdateDownloader; +	LLTimer mTimer; + +	LLUpdaterService::app_exit_callback_t mAppExitCallback; +	 +	LOG_CLASS(LLUpdaterServiceImpl); +	 +public: +	LLUpdaterServiceImpl(); +	virtual ~LLUpdaterServiceImpl(); + +	void initialize(const std::string& protocol_version, +				   const std::string& url,  +				   const std::string& path, +				   const std::string& channel, +				   const std::string& version); +	 +	void setCheckPeriod(unsigned int seconds); + +	void startChecking(bool install_if_ready); +	void stopChecking(); +	bool isChecking(); +	 +	void setAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) { mAppExitCallback = aecb;} + +	bool checkForInstall(bool launchInstaller); // Test if a local install is ready. +	bool checkForResume(); // Test for resumeable d/l. + +	// LLUpdateChecker::Client: +	virtual void error(std::string const & message); +	virtual void optionalUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash); +	virtual void requiredUpdate(std::string const & newVersion, +								LLURI const & uri, +								std::string const & hash); +	virtual void upToDate(void); +	 +	// LLUpdateDownloader::Client +	void downloadComplete(LLSD const & data); +	void downloadError(std::string const & message); + +	bool onMainLoop(LLSD const & event); + +private: +	void restartTimer(unsigned int seconds); +	void stopTimer(); +}; + +const std::string LLUpdaterServiceImpl::sListenerName = "LLUpdaterServiceImpl"; + +LLUpdaterServiceImpl::LLUpdaterServiceImpl() : +	mIsChecking(false), +	mIsDownloading(false), +	mCheckPeriod(0), +	mUpdateChecker(*this), +	mUpdateDownloader(*this) +{ +} + +LLUpdaterServiceImpl::~LLUpdaterServiceImpl() +{ +	LL_INFOS("UpdaterService") << "shutting down updater service" << LL_ENDL; +	LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName); +} + +void LLUpdaterServiceImpl::initialize(const std::string& protocol_version, +									  const std::string& url,  +									  const std::string& path, +									  const std::string& channel, +									  const std::string& version) +{ +	if(mIsChecking || mIsDownloading) +	{ +		throw LLUpdaterService::UsageError("LLUpdaterService::initialize call " +										   "while updater is running."); +	} +		 +	mProtocolVersion = protocol_version; +	mUrl = url; +	mPath = path; +	mChannel = channel; +	mVersion = version; +} + +void LLUpdaterServiceImpl::setCheckPeriod(unsigned int seconds) +{ +	mCheckPeriod = seconds; +} + +void LLUpdaterServiceImpl::startChecking(bool install_if_ready) +{ +	if(mUrl.empty() || mChannel.empty() || mVersion.empty()) +	{ +		throw LLUpdaterService::UsageError("Set params before call to " +			"LLUpdaterService::startCheck()."); +	} + +	mIsChecking = true; + +    // Check to see if an install is ready. +	bool has_install = checkForInstall(install_if_ready); +	if(!has_install) +	{ +		checkForResume(); // will set mIsDownloading to true if resuming + +		if(!mIsDownloading) +		{ +			// Checking can only occur during the mainloop. +			// reset the timer to 0 so that the next mainloop event  +			// triggers a check; +			restartTimer(0);  +		} +	} +} + +void LLUpdaterServiceImpl::stopChecking() +{ +	if(mIsChecking) +	{ +		mIsChecking = false; +		stopTimer(); +	} + +    if(mIsDownloading) +    { +        mUpdateDownloader.cancel(); +		mIsDownloading = false; +    } +} + +bool LLUpdaterServiceImpl::isChecking() +{ +	return mIsChecking; +} + +bool LLUpdaterServiceImpl::checkForInstall(bool launchInstaller) +{ +	bool foundInstall = false; // return true if install is found. + +	llifstream update_marker(update_marker_path(),  +							 std::ios::in | std::ios::binary); + +	if(update_marker.is_open()) +	{ +		// Found an update info - now lets see if its valid. +		LLSD update_info; +		LLSDSerialize::fromXMLDocument(update_info, update_marker); +		update_marker.close(); + +		// Get the path to the installer file. +		LLSD path = update_info.get("path"); +		if(update_info["current_version"].asString() != ll_get_version()) +		{ +			// This viewer is not the same version as the one that downloaded +			// the update.  Do not install this update. +			if(!path.asString().empty()) +			{ +				llinfos << "ignoring update dowloaded by different client version" << llendl; +				LLFile::remove(path.asString()); +				LLFile::remove(update_marker_path()); +			} +			else +			{ +				; // Nothing to clean up. +			} +			 +			foundInstall = false; +		}  +		else if(path.isDefined() && !path.asString().empty()) +		{ +			if(launchInstaller) +			{ +				LLFile::remove(update_marker_path()); + +				int result = ll_install_update(install_script_path(), +											   update_info["path"].asString(), +											   install_script_mode());	 +				 +				if((result == 0) && mAppExitCallback) +				{ +					mAppExitCallback(); +				} else if(result != 0) { +					llwarns << "failed to run update install script" << LL_ENDL; +				} else { +					; // No op. +				} +			} +			 +			foundInstall = true; +		} +	} +	return foundInstall; +} + +bool LLUpdaterServiceImpl::checkForResume() +{ +	bool result = false; +	std::string download_marker_path = mUpdateDownloader.downloadMarkerPath(); +	if(LLFile::isfile(download_marker_path)) +	{ +		llifstream download_marker_stream(download_marker_path,  +								 std::ios::in | std::ios::binary); +		if(download_marker_stream.is_open()) +		{ +			LLSD download_info; +			LLSDSerialize::fromXMLDocument(download_info, download_marker_stream); +			download_marker_stream.close(); +			if(download_info["current_version"].asString() == ll_get_version()) +			{ +				mIsDownloading = true; +				mUpdateDownloader.resume(); +				result = true; +			} +			else  +			{ +				// The viewer that started this download is not the same as this viewer; ignore. +				llinfos << "ignoring partial download from different viewer version" << llendl; +				std::string path = download_info["path"].asString(); +				if(!path.empty()) LLFile::remove(path); +				LLFile::remove(download_marker_path); +			} +		}  +	} +	return result; +} + +void LLUpdaterServiceImpl::error(std::string const & message) +{ +	if(mIsChecking) +	{ +		restartTimer(mCheckPeriod); +	} +} + +void LLUpdaterServiceImpl::optionalUpdate(std::string const & newVersion, +										  LLURI const & uri, +										  std::string const & hash) +{ +	stopTimer(); +	mIsDownloading = true; +	mUpdateDownloader.download(uri, hash); +} + +void LLUpdaterServiceImpl::requiredUpdate(std::string const & newVersion, +										  LLURI const & uri, +										  std::string const & hash) +{ +	stopTimer(); +	mIsDownloading = true; +	mUpdateDownloader.download(uri, hash); +} + +void LLUpdaterServiceImpl::upToDate(void) +{ +	if(mIsChecking) +	{ +		restartTimer(mCheckPeriod); +	} +} + +void LLUpdaterServiceImpl::downloadComplete(LLSD const & data)  +{  +	mIsDownloading = false; + +	// Save out the download data to the SecondLifeUpdateReady +	// marker file.  +	llofstream update_marker(update_marker_path()); +	LLSDSerialize::toPrettyXML(data, update_marker); +	 +	LLSD event; +	event["pump"] = LLUpdaterService::pumpName(); +	LLSD payload; +	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_COMPLETE); +	event["payload"] = payload; +	LLEventPumps::instance().obtain("mainlooprepeater").post(event); +} + +void LLUpdaterServiceImpl::downloadError(std::string const & message)  +{  +	LL_INFOS("UpdaterService") << "Error downloading: " << message << LL_ENDL; + +	mIsDownloading = false; + +	// Restart the timer on error +	if(mIsChecking) +	{ +		restartTimer(mCheckPeriod);  +	} + +	LLSD event; +	event["pump"] = LLUpdaterService::pumpName(); +	LLSD payload; +	payload["type"] = LLSD(LLUpdaterService::DOWNLOAD_ERROR); +	payload["message"] = message; +	event["payload"] = payload; +	LLEventPumps::instance().obtain("mainlooprepeater").post(event); +} + +void LLUpdaterServiceImpl::restartTimer(unsigned int seconds) +{ +	LL_INFOS("UpdaterService") << "will check for update again in " <<  +	seconds << " seconds" << LL_ENDL;  +	mTimer.start(); +	mTimer.setTimerExpirySec(seconds); +	LLEventPumps::instance().obtain("mainloop").listen( +		sListenerName, boost::bind(&LLUpdaterServiceImpl::onMainLoop, this, _1)); +} + +void LLUpdaterServiceImpl::stopTimer() +{ +	mTimer.stop(); +	LLEventPumps::instance().obtain("mainloop").stopListening(sListenerName); +} + +bool LLUpdaterServiceImpl::onMainLoop(LLSD const & event) +{ +	if(mTimer.getStarted() && mTimer.hasExpired()) +	{ +		stopTimer(); + +		// Check for failed install. +		if(LLFile::isfile(ll_install_failed_marker_path())) +		{ +			// TODO: notify the user. +			llinfos << "found marker " << ll_install_failed_marker_path() << llendl; +			llinfos << "last install attempt failed" << llendl; +			LLFile::remove(ll_install_failed_marker_path()); +			 +			LLSD event; +			event["type"] = LLSD(LLUpdaterService::INSTALL_ERROR); +			LLEventPumps::instance().obtain(LLUpdaterService::pumpName()).post(event); +		} +		else +		{ +			mUpdateChecker.check(mProtocolVersion, mUrl, mPath, mChannel, mVersion); +		} +	}  +	else  +	{ +		// Keep on waiting... +	} +	 +	return false; +} + + +//----------------------------------------------------------------------- +// Facade interface + +std::string const & LLUpdaterService::pumpName(void) +{ +	static std::string name("updater_service"); +	return name; +} + +LLUpdaterService::LLUpdaterService() +{ +	if(gUpdater.expired()) +	{ +		mImpl =  +			boost::shared_ptr<LLUpdaterServiceImpl>(new LLUpdaterServiceImpl()); +		gUpdater = mImpl; +	} +	else +	{ +		mImpl = gUpdater.lock(); +	} +} + +LLUpdaterService::~LLUpdaterService() +{ +} + +void LLUpdaterService::initialize(const std::string& protocol_version, +								 const std::string& url,  +								 const std::string& path, +								 const std::string& channel, +								 const std::string& version) +{ +	mImpl->initialize(protocol_version, url, path, channel, version); +} + +void LLUpdaterService::setCheckPeriod(unsigned int seconds) +{ +	mImpl->setCheckPeriod(seconds); +} +	 +void LLUpdaterService::startChecking(bool install_if_ready) +{ +	mImpl->startChecking(install_if_ready); +} + +void LLUpdaterService::stopChecking() +{ +	mImpl->stopChecking(); +} + +bool LLUpdaterService::isChecking() +{ +	return mImpl->isChecking(); +} + +void LLUpdaterService::setImplAppExitCallback(LLUpdaterService::app_exit_callback_t aecb) +{ +	return mImpl->setAppExitCallback(aecb); +} + + +std::string const & ll_get_version(void) { +	static std::string version(""); +	 +	if (version.empty()) { +		std::ostringstream stream; +		stream << LL_VERSION_MAJOR << "." +		<< LL_VERSION_MINOR << "." +		<< LL_VERSION_PATCH << "." +		<< LL_VERSION_BUILD; +		version = stream.str(); +	} +	 +	return version; +} + diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h new file mode 100644 index 0000000000..752a6f834b --- /dev/null +++ b/indra/viewer_components/updater/llupdaterservice.h @@ -0,0 +1,85 @@ +/**  + * @file llupdaterservice.h + * + * $LicenseInfo:firstyear=2010&license=viewerlgpl$ + * Second Life Viewer Source Code + * Copyright (C) 2010, Linden Research, Inc. + *  + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; + * version 2.1 of the License only. + *  + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU + * Lesser General Public License for more details. + *  + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA + *  + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA + * $/LicenseInfo$ + */ + +#ifndef LL_UPDATERSERVICE_H +#define LL_UPDATERSERVICE_H + +#include <boost/shared_ptr.hpp> +#include <boost/function.hpp> + +class LLUpdaterServiceImpl; + +class LLUpdaterService +{ +public: +	class UsageError: public std::runtime_error +	{ +	public: +		UsageError(const std::string& msg) : std::runtime_error(msg) {} +	}; +	 +	// Name of the event pump through which update events will be delivered. +	static std::string const & pumpName(void); +	 +	// Type codes for events posted by this service.  Stored the event's 'type' element. +	enum eUpdateEvent { +		INVALID, +		DOWNLOAD_COMPLETE, +		DOWNLOAD_ERROR, +		INSTALL_ERROR +	}; + +	LLUpdaterService(); +	~LLUpdaterService(); + +	void initialize(const std::string& protocol_version, +				    const std::string& url,  +				    const std::string& path, +				    const std::string& channel, +				    const std::string& version); + +	void setCheckPeriod(unsigned int seconds); +	 +	void startChecking(bool install_if_ready = false); +	void stopChecking(); +	bool isChecking(); + +	typedef boost::function<void (void)> app_exit_callback_t; +	template <typename F> +	void setAppExitCallback(F const &callable)  +	{  +		app_exit_callback_t aecb = callable; +		setImplAppExitCallback(aecb); +	} + +private: +	boost::shared_ptr<LLUpdaterServiceImpl> mImpl; +	void setImplAppExitCallback(app_exit_callback_t aecb); +}; + +// Returns the full version as a string. +std::string const & ll_get_version(void); + +#endif // LL_UPDATERSERVICE_H diff --git a/indra/viewer_components/updater/scripts/darwin/update_install b/indra/viewer_components/updater/scripts/darwin/update_install new file mode 100644 index 0000000000..b174b3570a --- /dev/null +++ b/indra/viewer_components/updater/scripts/darwin/update_install @@ -0,0 +1,9 @@ +#! /bin/bash + +# +# The first argument contains the path to the installer app.  The second a path +# to a marker file which should be created if the installer fails.q +# + +open ../Resources/mac-updater.app --args -dmg "$1" -name "Second Life Viewer 2" -marker "$2" +exit 0 diff --git a/indra/viewer_components/updater/scripts/linux/update_install b/indra/viewer_components/updater/scripts/linux/update_install new file mode 100644 index 0000000000..fef5ef7d09 --- /dev/null +++ b/indra/viewer_components/updater/scripts/linux/update_install @@ -0,0 +1,10 @@ +#! /bin/bash +INSTALL_DIR=$(cd "$(dirname $0)/.." ; pwd) +export LD_LIBRARY_PATH=$INSTALL_DIR/lib +bin/linux-updater.bin --file "$1" --dest "$INSTALL_DIR" --name "Second Life Viewer 2" --stringsdir "$INSTALL_DIR/skins/default/xui/en" --stringsfile "strings.xml" + +if [ $? -ne 0 ] +   then touch $2 +fi + +rm -f $1 diff --git a/indra/viewer_components/updater/scripts/windows/update_install.bat b/indra/viewer_components/updater/scripts/windows/update_install.bat new file mode 100644 index 0000000000..42e148a707 --- /dev/null +++ b/indra/viewer_components/updater/scripts/windows/update_install.bat @@ -0,0 +1,3 @@ +start /WAIT %1 /SKIP_DIALOGS
 +IF ERRORLEVEL 1 ECHO %ERRORLEVEL% > %2
 +DEL %1
 diff --git a/indra/viewer_components/updater/tests/llupdaterservice_test.cpp b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp new file mode 100644 index 0000000000..04ed4e6364 --- /dev/null +++ b/indra/viewer_components/updater/tests/llupdaterservice_test.cpp @@ -0,0 +1,199 @@ +/**
 + * @file   llupdaterservice_test.cpp
 + * @brief  Tests of llupdaterservice.cpp.
 + * 
 + * $LicenseInfo:firstyear=2010&license=viewerlgpl$
 + * Second Life Viewer Source Code
 + * Copyright (C) 2010, Linden Research, Inc.
 + * 
 + * This library is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU Lesser General Public
 + * License as published by the Free Software Foundation;
 + * version 2.1 of the License only.
 + * 
 + * This library is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 + * Lesser General Public License for more details.
 + * 
 + * You should have received a copy of the GNU Lesser General Public
 + * License along with this library; if not, write to the Free Software
 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 + * 
 + * Linden Research, Inc., 945 Battery Street, San Francisco, CA  94111  USA
 + * $/LicenseInfo$
 + */
 +
 +// Precompiled header
 +#include "linden_common.h"
 +// associated header
 +#include "../llupdaterservice.h"
 +#include "../llupdatechecker.h"
 +#include "../llupdatedownloader.h"
 +#include "../llupdateinstaller.h"
 +
 +#include "../../../test/lltut.h"
 +//#define DEBUG_ON
 +#include "../../../test/debug.h"
 +
 +#include "llevents.h"
 +#include "lldir.h"
 +
 +/*****************************************************************************
 +*   MOCK'd
 +*****************************************************************************/
 +LLUpdateChecker::LLUpdateChecker(LLUpdateChecker::Client & client)
 +{}
 +void LLUpdateChecker::check(std::string const & protocolVersion, std::string const & hostUrl, 
 +								  std::string const & servicePath, std::string channel, std::string version)
 +{}
 +LLUpdateDownloader::LLUpdateDownloader(Client & ) {}
 +void LLUpdateDownloader::download(LLURI const & , std::string const &){}
 +
 +class LLDir_Mock : public LLDir
 +{
 +	void initAppDirs(const std::string &app_name, 
 +		   			 const std::string& app_read_only_data_dir = "") {}
 +	U32 countFilesInDir(const std::string &dirname, const std::string &mask) 
 +	{
 +		return 0;
 +	}
 +
 +	BOOL getNextFileInDir(const std::string &dirname, 
 +						  const std::string &mask, 
 +						  std::string &fname) 
 +	{
 +		return false;
 +	}
 +	void getRandomFileInDir(const std::string &dirname, 
 +							const std::string &mask, 
 +							std::string &fname) {}
 +	std::string getCurPath() { return ""; }
 +	BOOL fileExists(const std::string &filename) const { return false; }
 +	std::string getLLPluginLauncher() { return ""; }
 +	std::string getLLPluginFilename(std::string base_name) { return ""; }
 +
 +} gDirUtil;
 +LLDir* gDirUtilp = &gDirUtil;
 +LLDir::LLDir() {}
 +LLDir::~LLDir() {}
 +S32 LLDir::deleteFilesInDir(const std::string &dirname, 
 +							const std::string &mask)
 +{ return 0; }
 +
 +void LLDir::setChatLogsDir(const std::string &path){}		
 +void LLDir::setPerAccountChatLogsDir(const std::string &username){}
 +void LLDir::setLindenUserDir(const std::string &username){}		
 +void LLDir::setSkinFolder(const std::string &skin_folder){}
 +bool LLDir::setCacheDir(const std::string &path){ return true; }
 +void LLDir::dumpCurrentDirectories() {}
 +
 +std::string LLDir::getExpandedFilename(ELLPath location, 
 +									   const std::string &filename) const 
 +{
 +	return "";
 +}
 +
 +std::string LLUpdateDownloader::downloadMarkerPath(void)
 +{
 +	return "";
 +}
 +
 +void LLUpdateDownloader::resume(void) {}
 +void LLUpdateDownloader::cancel(void) {}
 +
 +int ll_install_update(std::string const &, std::string const &, LLInstallScriptMode)
 +{
 +	return 0;
 +}
 +
 +std::string const & ll_install_failed_marker_path()
 +{
 +	static std::string wubba;
 +	return wubba;
 +}
 +
 +/*
 +#pragma warning(disable: 4273)
 +llus_mock_llifstream::llus_mock_llifstream(const std::string& _Filename,
 +										   ios_base::openmode _Mode,
 +										   int _Prot) :
 +	std::basic_istream<char,std::char_traits< char > >(NULL,true)
 +{}
 +
 +llus_mock_llifstream::~llus_mock_llifstream() {}
 +bool llus_mock_llifstream::is_open() const {return true;}
 +void llus_mock_llifstream::close() {}
 +*/
 +
 +/*****************************************************************************
 +*   TUT
 +*****************************************************************************/
 +namespace tut
 +{
 +    struct llupdaterservice_data
 +    {
 +		llupdaterservice_data() :
 +            pumps(LLEventPumps::instance()),
 +			test_url("dummy_url"),
 +			test_channel("dummy_channel"),
 +			test_version("dummy_version")
 +		{}
 +		LLEventPumps& pumps;
 +		std::string test_url;
 +		std::string test_channel;
 +		std::string test_version;
 +	};
 +
 +    typedef test_group<llupdaterservice_data> llupdaterservice_group;
 +    typedef llupdaterservice_group::object llupdaterservice_object;
 +    llupdaterservice_group llupdaterservicegrp("LLUpdaterService");
 +
 +    template<> template<>
 +    void llupdaterservice_object::test<1>()
 +    {
 +        DEBUG;
 +		LLUpdaterService updater;
 +		bool got_usage_error = false;
 +		try
 +		{
 +			updater.startChecking();
 +		}
 +		catch(LLUpdaterService::UsageError)
 +		{
 +			got_usage_error = true;
 +		}
 +		ensure("Caught start before params", got_usage_error);
 +	}
 +
 +    template<> template<>
 +    void llupdaterservice_object::test<2>()
 +    {
 +        DEBUG;
 +		LLUpdaterService updater;
 +		bool got_usage_error = false;
 +		try
 +		{
 +			updater.initialize("1.0",test_url, "update" ,test_channel, test_version);
 +			updater.startChecking();
 +			updater.initialize("1.0", "other_url", "update", test_channel, test_version);
 +		}
 +		catch(LLUpdaterService::UsageError)
 +		{
 +			got_usage_error = true;
 +		}
 +		ensure("Caught params while running", got_usage_error);
 +	}
 +
 +    template<> template<>
 +    void llupdaterservice_object::test<3>()
 +    {
 +        DEBUG;
 +		LLUpdaterService updater;
 +		updater.initialize("1.0", test_url, "update", test_channel, test_version);
 +		updater.startChecking();
 +		ensure(updater.isChecking());
 +		updater.stopChecking();
 +		ensure(!updater.isChecking());
 +	}
 +}
 | 
