From a53071249171facbbfd0a1840155248f2175dc3c Mon Sep 17 00:00:00 2001 From: James Turner Date: Wed, 13 Feb 2013 13:32:19 +0000 Subject: [PATCH] Initial work on package management. Basic library infrastructure, catalog download/refresh, and package install, uninstall and update. Disabled at cmake time by default, and not yet hooked into FlightGear. --- CMakeLists.txt | 15 +- CMakeModules/FindLibArchive.cmake | 77 +++++++++ simgear/CMakeLists.txt | 5 + simgear/io/HTTPClient.cxx | 15 ++ simgear/io/HTTPClient.hxx | 6 + simgear/package/CMakeLists.txt | 41 +++++ simgear/package/Catalog.cxx | 267 ++++++++++++++++++++++++++++++ simgear/package/Catalog.hxx | 95 +++++++++++ simgear/package/Delegate.hxx | 41 +++++ simgear/package/Install.cxx | 218 ++++++++++++++++++++++++ simgear/package/Install.hxx | 72 ++++++++ simgear/package/Package.cxx | 126 ++++++++++++++ simgear/package/Package.hxx | 68 ++++++++ simgear/package/Root.cxx | 236 ++++++++++++++++++++++++++ simgear/package/Root.hxx | 110 ++++++++++++ simgear/package/md5.c | 266 +++++++++++++++++++++++++++++ simgear/package/md5.h | 68 ++++++++ simgear/package/pkgutil.cxx | 86 ++++++++++ 18 files changed, 1810 insertions(+), 2 deletions(-) create mode 100644 CMakeModules/FindLibArchive.cmake create mode 100644 simgear/package/CMakeLists.txt create mode 100644 simgear/package/Catalog.cxx create mode 100644 simgear/package/Catalog.hxx create mode 100644 simgear/package/Delegate.hxx create mode 100644 simgear/package/Install.cxx create mode 100644 simgear/package/Install.hxx create mode 100644 simgear/package/Package.cxx create mode 100644 simgear/package/Package.hxx create mode 100644 simgear/package/Root.cxx create mode 100644 simgear/package/Root.hxx create mode 100644 simgear/package/md5.c create mode 100644 simgear/package/md5.h create mode 100644 simgear/package/pkgutil.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index f6dd5fed..1a077f15 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,7 @@ option(ENABLE_LIBSVN "Set to ON to build SimGear with libsvnclient support" O option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF) option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON) option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON) +option(ENABLE_PACKAGE "Set to ON to build package-management support" OFF) if (MSVC) GET_FILENAME_COMPONENT(PARENT_DIR ${PROJECT_SOURCE_DIR} PATH) @@ -223,6 +224,13 @@ else() add_definitions(-DHAVE_EXPAT_CONFIG_H) endif(SYSTEM_EXPAT) +if (ENABLE_PACKAGE) + message(STATUS "package management: ENABLED: libArchive is needed") + find_package(LibArchive REQUIRED) +else() + message(STATUS "package management: DISABLED") +endif(ENABLE_PACKAGE) + check_include_file(inttypes.h HAVE_INTTYPES_H) check_include_file(sys/time.h HAVE_SYS_TIME_H) check_include_file(sys/timeb.h HAVE_SYS_TIMEB_H) @@ -335,8 +343,11 @@ include_directories(${PROJECT_BINARY_DIR}/simgear) include_directories(${PROJECT_BINARY_DIR}/simgear/xml) include_directories(${OPENSCENEGRAPH_INCLUDE_DIRS} - ${Boost_INCLUDE_DIRS} ${ZLIB_INCLUDE_DIR} - ${OPENAL_INCLUDE_DIR} ) + ${Boost_INCLUDE_DIRS} + ${ZLIB_INCLUDE_DIR} + ${OPENAL_INCLUDE_DIR} + ${LibArchive_INCLUDE_DIRS} +) add_definitions(-DHAVE_CONFIG_H) diff --git a/CMakeModules/FindLibArchive.cmake b/CMakeModules/FindLibArchive.cmake new file mode 100644 index 00000000..5929b24e --- /dev/null +++ b/CMakeModules/FindLibArchive.cmake @@ -0,0 +1,77 @@ +# - Find libarchive library and headers +# The module defines the following variables: +# +# LibArchive_FOUND - true if libarchive was found +# LibArchive_INCLUDE_DIRS - include search path +# LibArchive_LIBRARIES - libraries to link +# LibArchive_VERSION - libarchive 3-component version number + +#============================================================================= +# Copyright 2010 Kitware, Inc. +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distribute this file outside of CMake, substitute the full +# License text for the above reference.) + +find_path(LibArchive_INCLUDE_DIR + NAMES archive.h + PATHS + ${CMAKE_INSTALL_PREFIX} + ${ADDITIONAL_LIBRARY_PATHS} + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\LibArchive;InstallPath]/include" + ) + +# NO_DEFAULT_PATH is important on Mac - the libarchive in /usr/lib +# is too old, and there's no matching headers :( +find_library(LibArchive_LIBRARY +if(APPLE) + NO_DEFAULT_PATH +endif(APPLE) + NAMES archive libarchive + PATH_SUFFIXES lib64 lib libs64 libs libs/Win32 libs/Win64 + PATHS + ${CMAKE_INSTALL_PREFIX} + ${ADDITIONAL_LIBRARY_PATHS} + "[HKEY_LOCAL_MACHINE\\SOFTWARE\\GnuWin32\\LibArchive;InstallPath]/lib" + ) + +mark_as_advanced(LibArchive_INCLUDE_DIR LibArchive_LIBRARY) + +# Extract the version number from the header. +if(LibArchive_INCLUDE_DIR AND EXISTS "${LibArchive_INCLUDE_DIR}/archive.h") + # The version string appears in one of two known formats in the header: + # #define ARCHIVE_LIBRARY_VERSION "libarchive 2.4.12" + # #define ARCHIVE_VERSION_STRING "libarchive 2.8.4" + # Match either format. + set(_LibArchive_VERSION_REGEX "^#define[ \t]+ARCHIVE[_A-Z]+VERSION[_A-Z]*[ \t]+\"libarchive +([0-9]+)\\.([0-9]+)\\.([0-9]+)[^\"]*\".*$") + file(STRINGS "${LibArchive_INCLUDE_DIR}/archive.h" _LibArchive_VERSION_STRING LIMIT_COUNT 1 REGEX "${_LibArchive_VERSION_REGEX}") + if(_LibArchive_VERSION_STRING) + string(REGEX REPLACE "${_LibArchive_VERSION_REGEX}" "\\1.\\2.\\3" LibArchive_VERSION "${_LibArchive_VERSION_STRING}") + endif() + unset(_LibArchive_VERSION_REGEX) + unset(_LibArchive_VERSION_STRING) +endif() + +# Handle the QUIETLY and REQUIRED arguments and set LIBARCHIVE_FOUND +# to TRUE if all listed variables are TRUE. +# (Use ${CMAKE_ROOT}/Modules instead of ${CMAKE_CURRENT_LIST_DIR} because CMake +# itself includes this FindLibArchive when built with an older CMake that does +# not provide it. The older CMake also does not have CMAKE_CURRENT_LIST_DIR.) +include(${CMAKE_ROOT}/Modules/FindPackageHandleStandardArgs.cmake) +find_package_handle_standard_args(LibArchive + REQUIRED_VARS LibArchive_LIBRARY LibArchive_INCLUDE_DIR + VERSION_VAR LibArchive_VERSION + ) +set(LibArchive_FOUND ${LIBARCHIVE_FOUND}) +unset(LIBARCHIVE_FOUND) + +if(LibArchive_FOUND) + set(LibArchive_INCLUDE_DIRS ${LibArchive_INCLUDE_DIR}) + set(LibArchive_LIBRARIES ${LibArchive_LIBRARY}) +endif() diff --git a/simgear/CMakeLists.txt b/simgear/CMakeLists.txt index b4e299ae..78dad8c7 100644 --- a/simgear/CMakeLists.txt +++ b/simgear/CMakeLists.txt @@ -24,6 +24,10 @@ foreach( mylibfolder endforeach( mylibfolder ) +if (ENABLE_PACKAGE) + add_subdirectory(package) +endif(ENABLE_PACKAGE) + if(NOT SIMGEAR_HEADLESS) add_subdirectory(canvas) add_subdirectory(environment) @@ -63,6 +67,7 @@ if(SIMGEAR_SHARED) set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION}) target_link_libraries(SimGearCore ${ZLIB_LIBRARY} ${RT_LIBRARY} + ${LibArchive_LIBRARIES} ${EXPAT_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT}) diff --git a/simgear/io/HTTPClient.cxx b/simgear/io/HTTPClient.cxx index 8a48bff6..7a32ab49 100644 --- a/simgear/io/HTTPClient.cxx +++ b/simgear/io/HTTPClient.cxx @@ -455,6 +455,11 @@ public: { return !queuedRequests.empty() && (sentRequests.size() < MAX_INFLIGHT_REQUESTS); } + + bool isActive() const + { + return !queuedRequests.empty() || !sentRequests.empty(); + } private: bool connectToHost() { @@ -733,6 +738,16 @@ void Client::setProxy(const string& proxy, int port, const string& auth) _proxyAuth = auth; } +bool Client::hasActiveRequests() const +{ + ConnectionDict::const_iterator it = _connections.begin(); + for (; it != _connections.end(); ++it) { + if (it->second->isActive()) return true; + } + + return false; +} + } // of namespace HTTP } // of namespace simgear diff --git a/simgear/io/HTTPClient.hxx b/simgear/io/HTTPClient.hxx index f949c7be..f200b9f4 100644 --- a/simgear/io/HTTPClient.hxx +++ b/simgear/io/HTTPClient.hxx @@ -33,6 +33,12 @@ public: const std::string& proxyAuth() const { return _proxyAuth; } + + /** + * predicate, check if at least one connection is active, with at + * least one request active or queued. + */ + bool hasActiveRequests() const; private: void requestFinished(Connection* con); diff --git a/simgear/package/CMakeLists.txt b/simgear/package/CMakeLists.txt new file mode 100644 index 00000000..4c41020f --- /dev/null +++ b/simgear/package/CMakeLists.txt @@ -0,0 +1,41 @@ + +include (SimGearComponent) + +set(HEADERS + Catalog.hxx + Package.hxx + Install.hxx + Root.hxx + Delegate.hxx + ) + +set(SOURCES + Catalog.cxx + Package.cxx + Install.cxx + Root.cxx + md5.c + ) + +simgear_component(package package "${SOURCES}" "${HEADERS}") + +if (SIMGEAR_SHARED) + set(APP_LIBS SimGearCore) +else() + set(APP_LIBS + ${LibArchive_LIBRARIES} + SimGearCore + ${CMAKE_THREAD_LIBS_INIT} + ${WINSOCK_LIBRARY} + ${ZLIB_LIBRARY} + ${RT_LIBRARY} + ) +endif() + +add_executable(sg_pkgutil pkgutil.cxx) +target_link_libraries(sg_pkgutil ${APP_LIBS}) + +if(ENABLE_TESTS) + + +endif(ENABLE_TESTS) diff --git a/simgear/package/Catalog.cxx b/simgear/package/Catalog.cxx new file mode 100644 index 00000000..e8a17573 --- /dev/null +++ b/simgear/package/Catalog.cxx @@ -0,0 +1,267 @@ + + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace simgear { + +namespace pkg { + +CatalogList static_catalogs; + +////////////////////////////////////////////////////////////////////////////// + +class Catalog::Downloader : public HTTP::Request +{ +public: + Downloader(Catalog* aOwner, const std::string& aUrl) : + HTTP::Request(aUrl), + m_owner(aOwner) + { + } + +protected: + virtual void responseHeadersComplete() + { + + } + + virtual void gotBodyData(const char* s, int n) + { + m_buffer += std::string(s, n); + } + + virtual void responseComplete() + { + if (responseCode() != 200) { + SG_LOG(SG_GENERAL, SG_ALERT, "catalog download failure:" << m_owner->url()); + m_owner->refreshComplete(false); + return; + } + + SGPropertyNode* props = new SGPropertyNode; + + try { + readProperties(m_buffer.data(), m_buffer.size(), props); + m_owner->parseProps(props); + } catch (sg_exception& e) { + SG_LOG(SG_GENERAL, SG_ALERT, "catalog parse failure:" << m_owner->url()); + m_owner->refreshComplete(false); + return; + } + + // cache the catalog data, now we have a valid install root + Dir d(m_owner->installRoot()); + SGPath p = d.file("catalog.xml"); + + std::ofstream f(p.c_str(), std::ios::out | std::ios::trunc); + f.write(m_buffer.data(), m_buffer.size()); + f.close(); + + time(&m_owner->m_retrievedTime); + m_owner->writeTimestamp(); + m_owner->refreshComplete(true); + } + +private: + Catalog* m_owner; + std::string m_buffer; +}; + +////////////////////////////////////////////////////////////////////////////// + +CatalogList Catalog::allCatalogs() +{ + return static_catalogs; +} + +Catalog::Catalog(Root *aRoot) : + m_root(aRoot), + m_retrievedTime(0) +{ + static_catalogs.push_back(this); +} + +Catalog::~Catalog() +{ + CatalogList::iterator it = std::find(static_catalogs.begin(), static_catalogs.end(), this); + static_catalogs.erase(it); +} + +Catalog* Catalog::createFromUrl(Root* aRoot, const std::string& aUrl) +{ + Catalog* c = new Catalog(aRoot); + Downloader* dl = new Downloader(c, aUrl); + aRoot->getHTTPClient()->makeRequest(dl); + + return c; +} + +Catalog* Catalog::createFromPath(Root* aRoot, const SGPath& aPath) +{ + SGPath xml = aPath; + xml.append("catalog.xml"); + if (!xml.exists()) { + return NULL; + } + + SGPropertyNode_ptr props; + try { + props = new SGPropertyNode; + readProperties(xml.str(), props); + } catch (sg_exception& e) { + return NULL; + } + + Catalog* c = new Catalog(aRoot); + c->m_installRoot = aPath; + c->parseProps(props); + c->parseTimestamp(); + + return c; +} + +PackageList +Catalog::packagesMatching(const SGPropertyNode* aFilter) const +{ + PackageList r; + BOOST_FOREACH(Package* p, m_packages) { + if (p->matches(aFilter)) { + r.push_back(p); + } + } + return r; +} + +PackageList +Catalog::packagesNeedingUpdate() const +{ + PackageList r; + BOOST_FOREACH(Package* p, m_packages) { + if (!p->isInstalled()) { + continue; + } + + if (p->install()->hasUpdate()) { + r.push_back(p); + } + } + return r; +} + +void Catalog::refresh() +{ + Downloader* dl = new Downloader(this, url()); + m_root->getHTTPClient()->makeRequest(dl); + m_root->catalogRefreshBegin(this); +} + +void Catalog::parseProps(const SGPropertyNode* aProps) +{ + // copy everything except package children? + m_props = new SGPropertyNode; + + int nChildren = aProps->nChildren(); + for (int i = 0; i < nChildren; i++) { + const SGPropertyNode* pkgProps = aProps->getChild(i); + if (strcmp(pkgProps->getName(), "package") == 0) { + Package* p = new Package(pkgProps, this); + m_packages.push_back(p); + } else { + SGPropertyNode* c = m_props->getChild(pkgProps->getName(), pkgProps->getIndex(), true); + copyProperties(pkgProps, c); + } + } // of children iteration + + if (m_installRoot.isNull()) { + m_installRoot = m_root->path(); + m_installRoot.append(id()); + + Dir d(m_installRoot); + d.create(0755); + } +} + +Package* Catalog::getPackageById(const std::string& aId) const +{ + BOOST_FOREACH(Package* p, m_packages) { + if (p->id() == aId) { + return p; + } + } + + return NULL; // not found +} + +std::string Catalog::id() const +{ + return m_props->getStringValue("id"); +} + +std::string Catalog::url() const +{ + return m_props->getStringValue("url"); +} + +std::string Catalog::description() const +{ + return getLocalisedString(m_props, "description"); +} + +void Catalog::parseTimestamp() +{ + SGPath timestampFile = m_installRoot; + timestampFile.append(".timestamp"); + std::ifstream f(timestampFile.c_str(), std::ios::in); + f >> m_retrievedTime; +} + +void Catalog::writeTimestamp() +{ + SGPath timestampFile = m_installRoot; + timestampFile.append(".timestamp"); + std::ofstream f(timestampFile.c_str(), std::ios::out | std::ios::trunc); + f << m_retrievedTime << std::endl; +} + +int Catalog::ageInSeconds() const +{ + time_t now; + time(&now); + return ::difftime(now, m_retrievedTime); +} + +std::string Catalog::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const +{ + if (aRoot->hasChild(m_root->getLocale())) { + const SGPropertyNode* localeRoot = aRoot->getChild(m_root->getLocale().c_str()); + if (localeRoot->hasChild(aName)) { + return localeRoot->getStringValue(aName); + } + } + + return aRoot->getStringValue(aName); +} + +void Catalog::refreshComplete(bool aSuccess) +{ + m_root->catalogRefreshComplete(this, aSuccess); +} + + +} // of namespace pkg + +} // of namespace simgear diff --git a/simgear/package/Catalog.hxx b/simgear/package/Catalog.hxx new file mode 100644 index 00000000..49fd2af6 --- /dev/null +++ b/simgear/package/Catalog.hxx @@ -0,0 +1,95 @@ +#ifndef SG_PACKAGE_CATALOG_HXX +#define SG_PACKAGE_CATALOG_HXX + +#include +#include + +#include +#include + +namespace simgear +{ + +namespace HTTP { class Client; } + +namespace pkg +{ + +// forward decls +class Package; +class Catalog; +class Root; + +typedef std::vector PackageList; +typedef std::vector CatalogList; + +class Catalog +{ +public: + virtual ~Catalog(); + + static Catalog* createFromUrl(Root* aRoot, const std::string& aUrl); + + static Catalog* createFromPath(Root* aRoot, const SGPath& aPath); + + static CatalogList allCatalogs(); + + Root* root() const + { return m_root;}; + + /** + * perform a refresh of the catalog contents + */ + void refresh(); + /** + * retrieve packages in this catalog matching a filter. + * filter consists of required / minimum values, AND-ed together. + */ + PackageList packagesMatching(const SGPropertyNode* aFilter) const; + + /** + * retrieve all the packages in the catalog which are installed + * and have a pendig update + */ + PackageList packagesNeedingUpdate() const; + + SGPath installRoot() const + { return m_installRoot; } + + std::string id() const; + + std::string url() const; + + std::string description() const; + + Package* getPackageById(const std::string& aId) const; + + int ageInSeconds() const; +private: + Catalog(Root* aRoot); + + class Downloader; + friend class Downloader; + + void parseProps(const SGPropertyNode* aProps); + + void refreshComplete(bool aSuccess); + + void parseTimestamp(); + void writeTimestamp(); + + std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const; + + Root* m_root; + SGPropertyNode_ptr m_props; + SGPath m_installRoot; + + PackageList m_packages; + time_t m_retrievedTime; +}; + +} // of namespace pkg + +} // of namespace simgear + +#endif // of SG_PACKAGE_CATALOG_HXX diff --git a/simgear/package/Delegate.hxx b/simgear/package/Delegate.hxx new file mode 100644 index 00000000..2925d248 --- /dev/null +++ b/simgear/package/Delegate.hxx @@ -0,0 +1,41 @@ + + +#ifndef SG_PACKAGE_DELEGATE_HXX +#define SG_PACKAGE_DELEGATE_HXX + +namespace simgear +{ + +namespace pkg +{ + +class Install; + +class Delegate +{ +public: + virtual ~Delegate() { } + + virtual void refreshComplete() = 0; + + virtual void startInstall(Install* aInstall) = 0; + virtual void installProgress(Install* aInstall, unsigned int aBytes, unsigned int aTotal) = 0; + virtual void finishInstall(Install* aInstall) = 0; + + typedef enum { + FAIL_UNKNOWN = 0, + FAIL_CHECKSUM, + FAIL_DOWNLOAD, + FAIL_EXTRACT, + FAIL_FILESYSTEM + } FailureCode; + + virtual void failedInstall(Install* aInstall, FailureCode aReason) = 0; + +}; + +} // of namespace pkg + +} // of namespace simgear + +#endif // of SG_PACKAGE_DELEGATE_HXX diff --git a/simgear/package/Install.cxx b/simgear/package/Install.cxx new file mode 100644 index 00000000..7d5b66da --- /dev/null +++ b/simgear/package/Install.cxx @@ -0,0 +1,218 @@ + + +#include + +#include +#include + +// libarchive support +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace simgear { + +namespace pkg { + +class Install::PackageArchiveDownloader : public HTTP::Request +{ +public: + PackageArchiveDownloader(Install* aOwner) : + HTTP::Request("" /* dummy URL */), + m_owner(aOwner) + { + m_urls = m_owner->package()->downloadUrls(); + if (m_urls.empty()) { + throw sg_exception("no package download URLs"); + } + + // TODO randomise order of m_urls + + m_extractPath = aOwner->path().dir(); + m_extractPath.append("_DOWNLOAD"); // add some temporary value + + } + +protected: + virtual std::string url() const + { + return m_urls.front(); + } + + virtual void responseHeadersComplete() + { + std::cout << "starting download of " << m_owner->package()->id() << " from " + << url() << std::endl; + Dir d(m_extractPath); + d.create(0755); + + memset(&m_md5, 0, sizeof(MD5_CTX)); + MD5Init(&m_md5); + } + + virtual void gotBodyData(const char* s, int n) + { + m_buffer += std::string(s, n); + MD5Update(&m_md5, (unsigned char*) s, n); + std::cout << "got " << m_buffer.size() << " bytes" << std::endl; + } + + virtual void responseComplete() + { + if (responseCode() != 200) { + SG_LOG(SG_GENERAL, SG_ALERT, "download failure"); + doFailure(); + return; + } + std::cout << "content lenth:" << responseLength() << std::endl; + std::cout << m_buffer.size() << " total received" << std::endl; + MD5Final(&m_md5); + // convert final sum to hex + const char hexChar[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + std::stringstream hexMd5; + for (int i=0; i<16;++i) { + hexMd5 << hexChar[m_md5.digest[i] >> 4]; + hexMd5 << hexChar[m_md5.digest[i] & 0x0f]; + } + + if (hexMd5.str() != m_owner->package()->md5()) { + SG_LOG(SG_GENERAL, SG_ALERT, "md5 verification failed:\n" + << "\t" << hexMd5.str() << "\n\t" + << m_owner->package()->md5() << "\n\t" + << "downloading from:" << url()); + doFailure(); + return; + } else { + std::cout << "MD5 checksum is ok" << std::endl; + } + + struct archive* a = archive_read_new(); + archive_read_support_filter_all(a); + archive_read_support_format_all(a); + int result = archive_read_open_memory(a, (void*) m_buffer.data(), m_buffer.size()); + + if (result != ARCHIVE_OK) { + doFailure(); + return; + } + + struct archive_entry* entry; + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { + SGPath finalPath(m_extractPath); + finalPath.append(archive_entry_pathname(entry)); + // std::cout << "writing:" << finalPath << std::endl; + archive_entry_set_pathname(entry, finalPath.c_str()); + archive_read_extract(a, entry, 0); + } + + archive_read_free(a); + + if (m_owner->path().exists()) { + std::cout << "removing existing path" << std::endl; + Dir destDir(m_owner->path()); + destDir.remove(true /* recursive */); + } + + std::cout << "renaming to " << m_owner->path() << std::endl; + m_extractPath.append(m_owner->package()->id()); + m_extractPath.rename(m_owner->path()); + + m_owner->m_revision = m_owner->package()->revision(); + m_owner->writeRevisionFile(); + } + +private: + void doFailure() + { + Dir dir(m_extractPath); + dir.remove(true /* recursive */); + if (m_urls.size() == 1) { + + return; + } + + m_urls.erase(m_urls.begin()); // pop first URL + } + + Install* m_owner; + string_list m_urls; + MD5_CTX m_md5; + std::string m_buffer; + SGPath m_extractPath; +}; + +//////////////////////////////////////////////////////////////////// + +Install::Install(Package* aPkg, const SGPath& aPath) : + m_package(aPkg), + m_path(aPath), + m_download(NULL) +{ + parseRevision(); +} + +Install* Install::createFromPath(const SGPath& aPath, Catalog* aCat) +{ + std::string id = aPath.file(); + Package* pkg = aCat->getPackageById(id); + if (!pkg) + throw sg_exception("no package with id:" + id); + + return new Install(pkg, aPath); +} + +void Install::parseRevision() +{ + SGPath revisionFile = m_path; + revisionFile.append(".revision"); + if (!revisionFile.exists()) { + m_revision = 0; + return; + } + + std::ifstream f(revisionFile.c_str(), std::ios::in); + f >> m_revision; +} + +void Install::writeRevisionFile() +{ + SGPath revisionFile = m_path; + revisionFile.append(".revision"); + std::ofstream f(revisionFile.c_str(), std::ios::out | std::ios::trunc); + f << m_revision << std::endl; +} + +bool Install::hasUpdate() const +{ + return m_package->revision() > m_revision; +} + +void Install::startUpdate() +{ + if (m_download) { + return; // already active + } + + m_download = new PackageArchiveDownloader(this); + m_package->catalog()->root()->getHTTPClient()->makeRequest(m_download); +} + +void Install::uninstall() +{ + Dir d(m_path); + d.remove(true); + delete this; +} + +} // of namespace pkg + +} // of namespace simgear diff --git a/simgear/package/Install.hxx b/simgear/package/Install.hxx new file mode 100644 index 00000000..92e2c4fc --- /dev/null +++ b/simgear/package/Install.hxx @@ -0,0 +1,72 @@ +#ifndef SG_PACKAGE_INSTALL_HXX +#define SG_PACKAGE_INSTALL_HXX + +#include + +#include + +namespace simgear +{ + +namespace pkg +{ + +// forward decls +class Package; +class Catalog; + +/** + * + */ +class Install +{ +public: + /** + * create from a directory on disk, or fail. + */ + static Install* createFromPath(const SGPath& aPath, Catalog* aCat); + + unsigned int revsion() const + { return m_revision; } + + Package* package() const + { return m_package; } + + SGPath path() const + { return m_path; } + + bool hasUpdate() const; + + void startUpdate(); + + void uninstall(); + +// boost signals time? + // failure + // progress + // completed + +private: + friend class Package; + + class PackageArchiveDownloader; + friend class PackageArchiveDownloader; + + Install(Package* aPkg, const SGPath& aPath); + + void parseRevision(); + void writeRevisionFile(); + + Package* m_package; + unsigned int m_revision; ///< revision on disk + SGPath m_path; ///< installation point on disk + + PackageArchiveDownloader* m_download; +}; + + +} // of namespace pkg + +} // of namespace simgear + +#endif // of SG_PACKAGE_CATALOG_HXX diff --git a/simgear/package/Package.cxx b/simgear/package/Package.cxx new file mode 100644 index 00000000..1f43d3ae --- /dev/null +++ b/simgear/package/Package.cxx @@ -0,0 +1,126 @@ + + +#include + +#include + +#include +#include +#include +#include + +namespace simgear { + +namespace pkg { + +Package::Package(const SGPropertyNode* aProps, Catalog* aCatalog) : + m_catalog(aCatalog) +{ + initWithProps(aProps); +} + +void Package::initWithProps(const SGPropertyNode* aProps) +{ + m_props = const_cast(aProps); +// cache tag values + BOOST_FOREACH(const SGPropertyNode* c, aProps->getChildren("tag")) { + m_tags.insert(c->getStringValue()); + } +} + +bool Package::matches(const SGPropertyNode* aFilter) const +{ + int nChildren = aFilter->nChildren(); + for (int i = 0; i < nChildren; i++) { + const SGPropertyNode* c = aFilter->getChild(i); + if (strutils::starts_with(c->getName(), "rating-")) { + int minRating = c->getIntValue(); + std::string rname = c->getName() + 7; + int ourRating = m_props->getChild("rating")->getIntValue(rname, 0); + if (ourRating < minRating) { + return false; + } + } + + if (strcmp(c->getName(), "tag") == 0) { + std::string tag(c->getStringValue()); + if (m_tags.find(tag) == m_tags.end()) { + return false; + } + } + + SG_LOG(SG_GENERAL, SG_WARN, "unknown filter term:" << c->getName()); + } // of filter props iteration + + return true; +} + +bool Package::isInstalled() const +{ + SGPath p(m_catalog->installRoot()); + p.append("Aircraft"); + p.append(id()); + + // anything to check for? look for a valid revision file? + return p.exists(); +} + +Install* Package::install() +{ + SGPath p(m_catalog->installRoot()); + p.append("Aircraft"); + p.append(id()); + if (p.exists()) { + return Install::createFromPath(p, m_catalog); + } + + Install* ins = new Install(this, p); + m_catalog->root()->scheduleToUpdate(ins); + return ins; +} + +std::string Package::id() const +{ + return m_props->getStringValue("id"); +} + +std::string Package::md5() const +{ + return m_props->getStringValue("md5"); +} + +unsigned int Package::revision() const +{ + return m_props->getIntValue("revision"); +} + +string_list Package::downloadUrls() const +{ + string_list r; + BOOST_FOREACH(SGPropertyNode* dl, m_props->getChildren("download")) { + r.push_back(dl->getStringValue()); + } + return r; +} + +std::string Package::getLocalisedProp(const std::string& aName) const +{ + return getLocalisedString(m_props, aName.c_str()); +} + +std::string Package::getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const +{ + std::string locale = m_catalog->root()->getLocale(); + if (aRoot->hasChild(locale)) { + const SGPropertyNode* localeRoot = aRoot->getChild(locale.c_str()); + if (localeRoot->hasChild(aName)) { + return localeRoot->getStringValue(aName); + } + } + + return aRoot->getStringValue(aName); +} + +} // of namespace pkg + +} // of namespace simgear diff --git a/simgear/package/Package.hxx b/simgear/package/Package.hxx new file mode 100644 index 00000000..416384d8 --- /dev/null +++ b/simgear/package/Package.hxx @@ -0,0 +1,68 @@ +#ifndef SG_PACKAGE_PACKAGE_HXX +#define SG_PACKAGE_PACKAGE_HXX + +#include +#include + +#include +#include + +typedef std::set string_set; + +namespace simgear +{ + +namespace pkg +{ + +// forward decls +class Install; +class Catalog; + +class Package +{ +public: + /** + * get or create an install for the package + */ + Install* install(); + + bool isInstalled() const; + + std::string id() const; + + std::string md5() const; + + std::string getLocalisedProp(const std::string& aName) const; + + unsigned int revision() const; + + Catalog* catalog() const + { return m_catalog; } + + bool matches(const SGPropertyNode* aFilter) const; + + string_list downloadUrls() const; +private: + friend class Catalog; + + Package(const SGPropertyNode* aProps, Catalog* aCatalog); + + void initWithProps(const SGPropertyNode* aProps); + + std::string getLocalisedString(const SGPropertyNode* aRoot, const char* aName) const; + + SGPropertyNode_ptr m_props; + string_set m_tags; + Catalog* m_catalog; +}; + +typedef std::vector PackageList; + + +} // of namespace pkg + +} // of namespace simgear + +#endif // of SG_PACKAGE_PACKAGE_HXX + diff --git a/simgear/package/Root.cxx b/simgear/package/Root.cxx new file mode 100644 index 00000000..780617f7 --- /dev/null +++ b/simgear/package/Root.cxx @@ -0,0 +1,236 @@ + + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace simgear { + +namespace pkg { + +void Root::setMaxAgeSeconds(int seconds) +{ + m_maxAgeSeconds = seconds; +} + +void Root::setHTTPClient(HTTP::Client* aHTTP) +{ + m_http = aHTTP; +} + +HTTP::Client* Root::getHTTPClient() const +{ + return m_http; +} + +Root::Root(const SGPath& aPath) : + m_path(aPath), + m_http(NULL), + m_maxAgeSeconds(60 * 60 * 24), + m_delegate(NULL) +{ + if (getenv("LOCALE")) { + m_locale = getenv("LOCALE"); + } + + Dir d(aPath); + if (!d.exists()) { + d.create(0755); + return; + } + + BOOST_FOREACH(SGPath c, d.children(Dir::TYPE_DIR)) { + Catalog* cat = Catalog::createFromPath(this, c); + if (cat) { + m_catalogs[cat->id()] = cat; + } + } // of child directories iteration +} + +Root::~Root() +{ + +} + +Catalog* Root::getCatalogById(const std::string& aId) const +{ + CatalogDict::const_iterator it = m_catalogs.find(aId); + if (it == m_catalogs.end()) { + return NULL; + } + + return it->second; +} + +Package* Root::getPackageById(const std::string& aName) const +{ + size_t lastDot = aName.rfind('.'); + + Package* pkg = NULL; + if (lastDot == -1) { + // naked package ID + CatalogDict::const_iterator it = m_catalogs.begin(); + for (; it != m_catalogs.end(); ++it) { + pkg = it->second->getPackageById(aName); + if (pkg) { + return pkg; + } + } + + return NULL; + } + + std::string catalogId = aName.substr(0, lastDot); + std::string id = aName.substr(lastDot + 1); + Catalog* catalog = getCatalogById(catalogId); + if (!catalog) { + return NULL; + } + + return catalog->getPackageById(id); +} + +CatalogList Root::catalogs() const +{ + CatalogList r; + CatalogDict::const_iterator it = m_catalogs.begin(); + for (; it != m_catalogs.end(); ++it) { + r.push_back(it->second); + } + + return r; +} + +PackageList +Root::packagesMatching(const SGPropertyNode* aFilter) const +{ + PackageList r; + + CatalogDict::const_iterator it = m_catalogs.begin(); + for (; it != m_catalogs.end(); ++it) { + PackageList r2(it->second->packagesMatching(aFilter)); + r.insert(r.end(), r2.begin(), r2.end()); + } + + return r; +} + +PackageList +Root::packagesNeedingUpdate() const +{ + PackageList r; + + CatalogDict::const_iterator it = m_catalogs.begin(); + for (; it != m_catalogs.end(); ++it) { + PackageList r2(it->second->packagesNeedingUpdate()); + r.insert(r.end(), r2.begin(), r2.end()); + } + + return r; +} + +void Root::refresh(bool aForce) +{ + CatalogDict::iterator it = m_catalogs.begin(); + for (; it != m_catalogs.end(); ++it) { + if (aForce || (it->second->ageInSeconds() > m_maxAgeSeconds)) { + it->second->refresh(); + } + } +} + +void Root::setLocale(const std::string& aLocale) +{ + m_locale = aLocale; +} + +std::string Root::getLocale() const +{ + return m_locale; +} + +void Root::scheduleToUpdate(Install* aInstall) +{ + bool wasEmpty = m_updateDeque.empty(); + m_updateDeque.push_back(aInstall); + if (wasEmpty) { + aInstall->startUpdate(); + } +} + +void Root::startInstall(Install* aInstall) +{ + if (m_delegate) { + m_delegate->startInstall(aInstall); + } +} + +void Root::installProgress(Install* aInstall, unsigned int aBytes, unsigned int aTotal) +{ + if (m_delegate) { + m_delegate->installProgress(aInstall, aBytes, aTotal); + } +} + +void Root::startNext(Install* aCurrent) +{ + if (m_updateDeque.front() != aCurrent) { + SG_LOG(SG_GENERAL, SG_ALERT, "current install of package not head of the deque"); + } else { + m_updateDeque.pop_front(); + } + + if (!m_updateDeque.empty()) { + m_updateDeque.front()->startUpdate(); + } +} + +void Root::finishInstall(Install* aInstall) +{ + if (m_delegate) { + m_delegate->finishInstall(aInstall); + } + + startNext(aInstall); +} + +void Root::failedInstall(Install* aInstall, Delegate::FailureCode aReason) +{ + SG_LOG(SG_GENERAL, SG_ALERT, "failed to install package:" + << aInstall->package()->id() << ":" << aReason); + if (m_delegate) { + m_delegate->failedInstall(aInstall, aReason); + } + + startNext(aInstall); +} + +void Root::catalogRefreshBegin(Catalog* aCat) +{ + m_refreshing.insert(aCat); +} + +void Root::catalogRefreshComplete(Catalog* aCat, bool aSuccess) +{ + m_refreshing.erase(aCat); + if (m_refreshing.empty()) { + if (m_delegate) { + m_delegate->refreshComplete(); + } + } +} + +} // of namespace pkg + +} // of namespace simgear diff --git a/simgear/package/Root.hxx b/simgear/package/Root.hxx new file mode 100644 index 00000000..576b23c7 --- /dev/null +++ b/simgear/package/Root.hxx @@ -0,0 +1,110 @@ + + +#ifndef SG_PACKAGE_ROOT_HXX +#define SG_PACKAGE_ROOT_HXX + +#include +#include +#include +#include + +#include +#include + +class SGPropertyNode; + +namespace simgear +{ + +namespace HTTP { class Client; } + +namespace pkg +{ + +// forward decls +class Package; +class Catalog; +class Install; + +typedef std::vector PackageList; +typedef std::vector CatalogList; + +typedef std::map CatalogDict; + +class Root +{ +public: + Root(const SGPath& aPath); + virtual ~Root(); + + SGPath path() const + { return m_path; } + + void setLocale(const std::string& aLocale); + + void setDelegate(Delegate* aDelegate); + + std::string getLocale() const; + + CatalogList catalogs() const; + + void setMaxAgeSeconds(int seconds); + + void setHTTPClient(HTTP::Client* aHTTP); + + HTTP::Client* getHTTPClient() const; + + /** + * refresh catalogs which are more than the maximum age (24 hours by default) + * set force to true, to download all catalogs regardless of age. + */ + void refresh(bool aForce = false); + + /** + * retrieve packages matching a filter. + * filter consists of required / minimum values, AND-ed together. + */ + PackageList packagesMatching(const SGPropertyNode* aFilter) const; + + /** + * retrieve all the packages which are installed + * and have a pending update + */ + PackageList packagesNeedingUpdate() const; + + Package* getPackageById(const std::string& aId) const; + + Catalog* getCatalogById(const std::string& aId) const; + + void scheduleToUpdate(Install* aInstall); +private: + friend class Install; + friend class Catalog; + + + void catalogRefreshBegin(Catalog* aCat); + void catalogRefreshComplete(Catalog* aCat, bool aSuccess); + + void startNext(Install* aCurrent); + + void startInstall(Install* aInstall); + void installProgress(Install* aInstall, unsigned int aBytes, unsigned int aTotal); + void finishInstall(Install* aInstall); + void failedInstall(Install* aInstall, Delegate::FailureCode aReason); + + SGPath m_path; + std::string m_locale; + HTTP::Client* m_http; + CatalogDict m_catalogs; + unsigned int m_maxAgeSeconds; + Delegate* m_delegate; + + std::set m_refreshing; + std::deque m_updateDeque; +}; + +} // of namespace pkg + +} // of namespace simgear + +#endif // of SG_PACKAGE_ROOT_HXX diff --git a/simgear/package/md5.c b/simgear/package/md5.c new file mode 100644 index 00000000..7a67e62f --- /dev/null +++ b/simgear/package/md5.c @@ -0,0 +1,266 @@ +/* + ********************************************************************** + ** md5.c ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +/* -- include the following line if the md5.h header file is separate -- */ +#include "md5.h" + +/* forward declaration */ +static void Transform (); + +static unsigned char PADDING[64] = { + 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* F, G and H are basic MD5 functions: selection, majority, parity */ +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +/* ROTATE_LEFT rotates x left n bits */ +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */ +/* Rotation is separate from addition to prevent recomputation */ +#define FF(a, b, c, d, x, s, ac) \ + {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) \ + {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) \ + {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) \ + {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +void MD5Init (MD5_CTX *mdContext) +{ + mdContext->i[0] = mdContext->i[1] = (UINT4)0; + + /* Load magic initialization constants. + */ + mdContext->buf[0] = (UINT4)0x67452301; + mdContext->buf[1] = (UINT4)0xefcdab89; + mdContext->buf[2] = (UINT4)0x98badcfe; + mdContext->buf[3] = (UINT4)0x10325476; +} + +void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen) +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* update number of bits */ + if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0]) + mdContext->i[1]++; + mdContext->i[0] += ((UINT4)inLen << 3); + mdContext->i[1] += ((UINT4)inLen >> 29); + + while (inLen--) { + /* add new character to buffer, increment mdi */ + mdContext->in[mdi++] = *inBuf++; + + /* transform if necessary */ + if (mdi == 0x40) { + for (i = 0, ii = 0; i < 16; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + mdi = 0; + } + } +} + +void MD5Final (MD5_CTX *mdContext) +{ + UINT4 in[16]; + int mdi; + unsigned int i, ii; + unsigned int padLen; + + /* save number of bits */ + in[14] = mdContext->i[0]; + in[15] = mdContext->i[1]; + + /* compute number of bytes mod 64 */ + mdi = (int)((mdContext->i[0] >> 3) & 0x3F); + + /* pad out to 56 mod 64 */ + padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi); + MD5Update (mdContext, PADDING, padLen); + + /* append length in bits and transform */ + for (i = 0, ii = 0; i < 14; i++, ii += 4) + in[i] = (((UINT4)mdContext->in[ii+3]) << 24) | + (((UINT4)mdContext->in[ii+2]) << 16) | + (((UINT4)mdContext->in[ii+1]) << 8) | + ((UINT4)mdContext->in[ii]); + Transform (mdContext->buf, in); + + /* store buffer in digest */ + for (i = 0, ii = 0; i < 4; i++, ii += 4) { + mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF); + mdContext->digest[ii+1] = + (unsigned char)((mdContext->buf[i] >> 8) & 0xFF); + mdContext->digest[ii+2] = + (unsigned char)((mdContext->buf[i] >> 16) & 0xFF); + mdContext->digest[ii+3] = + (unsigned char)((mdContext->buf[i] >> 24) & 0xFF); + } +} + +/* Basic MD5 step. Transform buf based on in. + */ +static void Transform (buf, in) +UINT4 *buf; +UINT4 *in; +{ + UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3]; + + /* Round 1 */ +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 + FF ( a, b, c, d, in[ 0], S11, 3614090360); /* 1 */ + FF ( d, a, b, c, in[ 1], S12, 3905402710); /* 2 */ + FF ( c, d, a, b, in[ 2], S13, 606105819); /* 3 */ + FF ( b, c, d, a, in[ 3], S14, 3250441966); /* 4 */ + FF ( a, b, c, d, in[ 4], S11, 4118548399); /* 5 */ + FF ( d, a, b, c, in[ 5], S12, 1200080426); /* 6 */ + FF ( c, d, a, b, in[ 6], S13, 2821735955); /* 7 */ + FF ( b, c, d, a, in[ 7], S14, 4249261313); /* 8 */ + FF ( a, b, c, d, in[ 8], S11, 1770035416); /* 9 */ + FF ( d, a, b, c, in[ 9], S12, 2336552879); /* 10 */ + FF ( c, d, a, b, in[10], S13, 4294925233); /* 11 */ + FF ( b, c, d, a, in[11], S14, 2304563134); /* 12 */ + FF ( a, b, c, d, in[12], S11, 1804603682); /* 13 */ + FF ( d, a, b, c, in[13], S12, 4254626195); /* 14 */ + FF ( c, d, a, b, in[14], S13, 2792965006); /* 15 */ + FF ( b, c, d, a, in[15], S14, 1236535329); /* 16 */ + + /* Round 2 */ +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 + GG ( a, b, c, d, in[ 1], S21, 4129170786); /* 17 */ + GG ( d, a, b, c, in[ 6], S22, 3225465664); /* 18 */ + GG ( c, d, a, b, in[11], S23, 643717713); /* 19 */ + GG ( b, c, d, a, in[ 0], S24, 3921069994); /* 20 */ + GG ( a, b, c, d, in[ 5], S21, 3593408605); /* 21 */ + GG ( d, a, b, c, in[10], S22, 38016083); /* 22 */ + GG ( c, d, a, b, in[15], S23, 3634488961); /* 23 */ + GG ( b, c, d, a, in[ 4], S24, 3889429448); /* 24 */ + GG ( a, b, c, d, in[ 9], S21, 568446438); /* 25 */ + GG ( d, a, b, c, in[14], S22, 3275163606); /* 26 */ + GG ( c, d, a, b, in[ 3], S23, 4107603335); /* 27 */ + GG ( b, c, d, a, in[ 8], S24, 1163531501); /* 28 */ + GG ( a, b, c, d, in[13], S21, 2850285829); /* 29 */ + GG ( d, a, b, c, in[ 2], S22, 4243563512); /* 30 */ + GG ( c, d, a, b, in[ 7], S23, 1735328473); /* 31 */ + GG ( b, c, d, a, in[12], S24, 2368359562); /* 32 */ + + /* Round 3 */ +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 + HH ( a, b, c, d, in[ 5], S31, 4294588738); /* 33 */ + HH ( d, a, b, c, in[ 8], S32, 2272392833); /* 34 */ + HH ( c, d, a, b, in[11], S33, 1839030562); /* 35 */ + HH ( b, c, d, a, in[14], S34, 4259657740); /* 36 */ + HH ( a, b, c, d, in[ 1], S31, 2763975236); /* 37 */ + HH ( d, a, b, c, in[ 4], S32, 1272893353); /* 38 */ + HH ( c, d, a, b, in[ 7], S33, 4139469664); /* 39 */ + HH ( b, c, d, a, in[10], S34, 3200236656); /* 40 */ + HH ( a, b, c, d, in[13], S31, 681279174); /* 41 */ + HH ( d, a, b, c, in[ 0], S32, 3936430074); /* 42 */ + HH ( c, d, a, b, in[ 3], S33, 3572445317); /* 43 */ + HH ( b, c, d, a, in[ 6], S34, 76029189); /* 44 */ + HH ( a, b, c, d, in[ 9], S31, 3654602809); /* 45 */ + HH ( d, a, b, c, in[12], S32, 3873151461); /* 46 */ + HH ( c, d, a, b, in[15], S33, 530742520); /* 47 */ + HH ( b, c, d, a, in[ 2], S34, 3299628645); /* 48 */ + + /* Round 4 */ +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + II ( a, b, c, d, in[ 0], S41, 4096336452); /* 49 */ + II ( d, a, b, c, in[ 7], S42, 1126891415); /* 50 */ + II ( c, d, a, b, in[14], S43, 2878612391); /* 51 */ + II ( b, c, d, a, in[ 5], S44, 4237533241); /* 52 */ + II ( a, b, c, d, in[12], S41, 1700485571); /* 53 */ + II ( d, a, b, c, in[ 3], S42, 2399980690); /* 54 */ + II ( c, d, a, b, in[10], S43, 4293915773); /* 55 */ + II ( b, c, d, a, in[ 1], S44, 2240044497); /* 56 */ + II ( a, b, c, d, in[ 8], S41, 1873313359); /* 57 */ + II ( d, a, b, c, in[15], S42, 4264355552); /* 58 */ + II ( c, d, a, b, in[ 6], S43, 2734768916); /* 59 */ + II ( b, c, d, a, in[13], S44, 1309151649); /* 60 */ + II ( a, b, c, d, in[ 4], S41, 4149444226); /* 61 */ + II ( d, a, b, c, in[11], S42, 3174756917); /* 62 */ + II ( c, d, a, b, in[ 2], S43, 718787259); /* 63 */ + II ( b, c, d, a, in[ 9], S44, 3951481745); /* 64 */ + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} + diff --git a/simgear/package/md5.h b/simgear/package/md5.h new file mode 100644 index 00000000..d0560aac --- /dev/null +++ b/simgear/package/md5.h @@ -0,0 +1,68 @@ +#ifndef SG_PACKAGE_MD5_H +#define SG_PACKAGE_MD5_H + +/* + ********************************************************************** + ** md5.h -- Header file for implementation of MD5 ** + ** RSA Data Security, Inc. MD5 Message Digest Algorithm ** + ** Created: 2/17/90 RLR ** + ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version ** + ** Revised (for MD5): RLR 4/27/91 ** + ** -- G modified to have y&~z instead of y&z ** + ** -- FF, GG, HH modified to add in last register done ** + ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 ** + ** -- distinct additive constant for each step ** + ** -- round 4 added, working mod 7 ** + ********************************************************************** + */ + +/* + ********************************************************************** + ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. ** + ** ** + ** License to copy and use this software is granted provided that ** + ** it is identified as the "RSA Data Security, Inc. MD5 Message ** + ** Digest Algorithm" in all material mentioning or referencing this ** + ** software or this function. ** + ** ** + ** License is also granted to make and use derivative works ** + ** provided that such works are identified as "derived from the RSA ** + ** Data Security, Inc. MD5 Message Digest Algorithm" in all ** + ** material mentioning or referencing the derived work. ** + ** ** + ** RSA Data Security, Inc. makes no representations concerning ** + ** either the merchantability of this software or the suitability ** + ** of this software for any particular purpose. It is provided "as ** + ** is" without express or implied warranty of any kind. ** + ** ** + ** These notices must be retained in any copies of any part of this ** + ** documentation and/or software. ** + ********************************************************************** + */ + +#ifdef __cplusplus + extern "C" { +#endif + +/* typedef a 32 bit type */ +typedef unsigned int UINT4; + +/* Data structure for MD5 (Message Digest) computation */ +typedef struct { + UINT4 i[2]; /* number of _bits_ handled mod 2^64 */ + UINT4 buf[4]; /* scratch buffer */ + unsigned char in[64]; /* input buffer */ + unsigned char digest[16]; /* actual digest after MD5Final call */ +} MD5_CTX; + +void MD5Init (MD5_CTX *mdContext); +void MD5Update (MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen); +void MD5Final (MD5_CTX *mdContext); + +#ifdef __cplusplus +} // of extern C +#endif + +#endif // of SG_PACKAGE_MD5_H + + \ No newline at end of file diff --git a/simgear/package/pkgutil.cxx b/simgear/package/pkgutil.cxx new file mode 100644 index 00000000..c72c1f17 --- /dev/null +++ b/simgear/package/pkgutil.cxx @@ -0,0 +1,86 @@ +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace simgear; +using namespace std; + +bool keepRunning = true; + +int main(int argc, char** argv) +{ + + HTTP::Client* http = new HTTP::Client(); + pkg::Root* root = new pkg::Root(Dir::current().path()); + + cout << "Package root is:" << Dir::current().path() << endl; + cout << "have " << pkg::Catalog::allCatalogs().size() << " catalog(s)" << endl; + + root->setHTTPClient(http); + + if (!strcmp(argv[1], "add")) { + std::string url(argv[2]); + pkg::Catalog::createFromUrl(root, url); + } else if (!strcmp(argv[1], "refresh")) { + root->refresh(); + } else if (!strcmp(argv[1], "install")) { + pkg::Package* pkg = root->getPackageById(argv[2]); + if (!pkg) { + cerr << "unknown package:" << argv[2] << endl; + return EXIT_FAILURE; + } + + if (pkg->isInstalled()) { + cout << "package " << pkg->id() << " is already installed at " << pkg->install()->path() << endl; + return EXIT_SUCCESS; + } + + pkg::Catalog* catalog = pkg->catalog(); + cout << "Will install:" << pkg->id() << " from " << catalog->id() << + "(" << catalog->description() << ")" << endl; + pkg->install(); + } else if (!strcmp(argv[1], "uninstall")) { + pkg::Package* pkg = root->getPackageById(argv[2]); + if (!pkg) { + cerr << "unknown package:" << argv[2] << endl; + return EXIT_FAILURE; + } + + if (!pkg->isInstalled()) { + cerr << "package " << argv[2] << " not installed" << endl; + return EXIT_FAILURE; + } + + cout << "Will uninstall:" << pkg->id() << endl; + pkg->install()->uninstall(); + } else if (!strcmp(argv[1], "update-all")) { + pkg::PackageList updates = root->packagesNeedingUpdate(); + BOOST_FOREACH(pkg::Package* p, updates) { + root->scheduleToUpdate(p->install()); + } + } else if (!strcmp(argv[1], "list-updated")) { + pkg::PackageList updates = root->packagesNeedingUpdate(); + if (updates.empty()) { + cout << "no packages with updates" << endl; + return EXIT_SUCCESS; + } + + cout << updates.size() << " packages have updates" << endl; + BOOST_FOREACH(pkg::Package* p, updates) { + cout << "\t" << p->id() << " " << p->getLocalisedProp("name") << endl; + } + } + + while (http->hasActiveRequests()) { + http->update(); + } + + return EXIT_SUCCESS; +} -- 2.39.5