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.
This commit is contained in:
parent
3c8cfe9b76
commit
a530712491
@ -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)
|
||||
|
||||
|
77
CMakeModules/FindLibArchive.cmake
Normal file
77
CMakeModules/FindLibArchive.cmake
Normal file
@ -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()
|
@ -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})
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
41
simgear/package/CMakeLists.txt
Normal file
41
simgear/package/CMakeLists.txt
Normal file
@ -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)
|
267
simgear/package/Catalog.cxx
Normal file
267
simgear/package/Catalog.cxx
Normal file
@ -0,0 +1,267 @@
|
||||
|
||||
|
||||
#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
|
95
simgear/package/Catalog.hxx
Normal file
95
simgear/package/Catalog.hxx
Normal file
@ -0,0 +1,95 @@
|
||||
#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
|
41
simgear/package/Delegate.hxx
Normal file
41
simgear/package/Delegate.hxx
Normal file
@ -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
|
218
simgear/package/Install.cxx
Normal file
218
simgear/package/Install.cxx
Normal file
@ -0,0 +1,218 @@
|
||||
|
||||
|
||||
#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
|
72
simgear/package/Install.hxx
Normal file
72
simgear/package/Install.hxx
Normal file
@ -0,0 +1,72 @@
|
||||
#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
|
126
simgear/package/Package.cxx
Normal file
126
simgear/package/Package.cxx
Normal file
@ -0,0 +1,126 @@
|
||||
|
||||
|
||||
#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
|
68
simgear/package/Package.hxx
Normal file
68
simgear/package/Package.hxx
Normal file
@ -0,0 +1,68 @@
|
||||
#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
|
||||
|
236
simgear/package/Root.cxx
Normal file
236
simgear/package/Root.cxx
Normal file
@ -0,0 +1,236 @@
|
||||
|
||||
|
||||
#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
|
110
simgear/package/Root.hxx
Normal file
110
simgear/package/Root.hxx
Normal file
@ -0,0 +1,110 @@
|
||||
|
||||
|
||||
#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
|
266
simgear/package/md5.c
Normal file
266
simgear/package/md5.c
Normal file
@ -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;
|
||||
}
|
||||
|
68
simgear/package/md5.h
Normal file
68
simgear/package/md5.h
Normal file
@ -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
|
||||
|
||||
|
86
simgear/package/pkgutil.cxx
Normal file
86
simgear/package/pkgutil.cxx
Normal file
@ -0,0 +1,86 @@
|
||||
#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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user