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)
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)
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)
--- /dev/null
+# - 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()
endforeach( mylibfolder )
+if (ENABLE_PACKAGE)
+ add_subdirectory(package)
+endif(ENABLE_PACKAGE)
+
if(NOT SIMGEAR_HEADLESS)
add_subdirectory(canvas)
add_subdirectory(environment)
set_property(TARGET SimGearCore PROPERTY SOVERSION ${SIMGEAR_SOVERSION})
target_link_libraries(SimGearCore ${ZLIB_LIBRARY} ${RT_LIBRARY}
+ ${LibArchive_LIBRARIES}
${EXPAT_LIBRARIES}
${CMAKE_THREAD_LIBS_INIT})
{
return !queuedRequests.empty() && (sentRequests.size() < MAX_INFLIGHT_REQUESTS);
}
+
+ bool isActive() const
+ {
+ return !queuedRequests.empty() || !sentRequests.empty();
+ }
private:
bool connectToHost()
{
_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
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);
--- /dev/null
+
+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)
--- /dev/null
+
+
+#include <simgear/package/Catalog.hxx>
+
+#include <boost/foreach.hpp>
+#include <fstream>
+#include <cstring>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/props/props_io.hxx>
+#include <simgear/io/HTTPRequest.hxx>
+#include <simgear/io/HTTPClient.hxx>
+#include <simgear/misc/sg_dir.hxx>
+#include <simgear/structure/exception.hxx>
+#include <simgear/package/Package.hxx>
+#include <simgear/package/Root.hxx>
+#include <simgear/package/Install.hxx>
+
+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
--- /dev/null
+#ifndef SG_PACKAGE_CATALOG_HXX
+#define SG_PACKAGE_CATALOG_HXX
+
+#include <vector>
+#include <ctime>
+
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/props/props.hxx>
+
+namespace simgear
+{
+
+namespace HTTP { class Client; }
+
+namespace pkg
+{
+
+// forward decls
+class Package;
+class Catalog;
+class Root;
+
+typedef std::vector<Package*> PackageList;
+typedef std::vector<Catalog*> 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
--- /dev/null
+
+
+#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
--- /dev/null
+
+
+#include <simgear/package/Install.hxx>
+
+#include <boost/foreach.hpp>
+#include <fstream>
+
+// libarchive support
+#include <archive.h>
+#include <archive_entry.h>
+
+#include <simgear/package/md5.h>
+
+#include <simgear/structure/exception.hxx>
+#include <simgear/package/Catalog.hxx>
+#include <simgear/package/Package.hxx>
+#include <simgear/package/Root.hxx>
+#include <simgear/io/HTTPRequest.hxx>
+#include <simgear/io/HTTPClient.hxx>
+#include <simgear/misc/sg_dir.hxx>
+
+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
--- /dev/null
+#ifndef SG_PACKAGE_INSTALL_HXX
+#define SG_PACKAGE_INSTALL_HXX
+
+#include <vector>
+
+#include <simgear/misc/sg_path.hxx>
+
+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
--- /dev/null
+
+
+#include <simgear/package/Package.hxx>
+
+#include <boost/foreach.hpp>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/package/Catalog.hxx>
+#include <simgear/package/Install.hxx>
+#include <simgear/package/Root.hxx>
+
+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<SGPropertyNode*>(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
--- /dev/null
+#ifndef SG_PACKAGE_PACKAGE_HXX
+#define SG_PACKAGE_PACKAGE_HXX
+
+#include <set>
+#include <vector>
+
+#include <simgear/props/props.hxx>
+#include <simgear/misc/strutils.hxx>
+
+typedef std::set<std::string> 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<Package*> PackageList;
+
+
+} // of namespace pkg
+
+} // of namespace simgear
+
+#endif // of SG_PACKAGE_PACKAGE_HXX
+
--- /dev/null
+
+
+#include <simgear/package/Root.hxx>
+
+#include <boost/foreach.hpp>
+#include <cstring>
+
+#include <simgear/debug/logstream.hxx>
+#include <simgear/props/props_io.hxx>
+#include <simgear/io/HTTPRequest.hxx>
+#include <simgear/io/HTTPClient.hxx>
+#include <simgear/misc/sg_dir.hxx>
+#include <simgear/structure/exception.hxx>
+#include <simgear/package/Package.hxx>
+#include <simgear/package/Install.hxx>
+#include <simgear/package/Catalog.hxx>
+
+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
--- /dev/null
+
+
+#ifndef SG_PACKAGE_ROOT_HXX
+#define SG_PACKAGE_ROOT_HXX
+
+#include <vector>
+#include <map>
+#include <deque>
+#include <set>
+
+#include <simgear/misc/sg_path.hxx>
+#include <simgear/package/Delegate.hxx>
+
+class SGPropertyNode;
+
+namespace simgear
+{
+
+namespace HTTP { class Client; }
+
+namespace pkg
+{
+
+// forward decls
+class Package;
+class Catalog;
+class Install;
+
+typedef std::vector<Package*> PackageList;
+typedef std::vector<Catalog*> CatalogList;
+
+typedef std::map<std::string, Catalog*> 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<Catalog*> m_refreshing;
+ std::deque<Install*> m_updateDeque;
+};
+
+} // of namespace pkg
+
+} // of namespace simgear
+
+#endif // of SG_PACKAGE_ROOT_HXX
--- /dev/null
+/*
+ **********************************************************************
+ ** 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;
+}
+
--- /dev/null
+#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
--- /dev/null
+#include <simgear/io/HTTPClient.hxx>
+#include <simgear/package/Catalog.hxx>
+#include <simgear/package/Package.hxx>
+#include <simgear/package/Install.hxx>
+#include <simgear/package/Root.hxx>
+#include <simgear/misc/sg_dir.hxx>
+
+#include <boost/foreach.hpp>
+#include <iostream>
+#include <cstring>
+
+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;
+}