-
+// Copyright (C) 2013 James Turner - zakalawe@mac.com
+//
+// This library is free software; you can redistribute it and/or
+// modify it under the terms of the GNU Library General Public
+// License as published by the Free Software Foundation; either
+// version 2 of the License, or (at your option) any later version.
+//
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// Library General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
#include <simgear/package/Install.hxx>
#include <boost/foreach.hpp>
#include <fstream>
-// libarchive support
-#include <archive.h>
-#include <archive_entry.h>
-
+#include <simgear/package/unzip.h>
#include <simgear/package/md5.h>
#include <simgear/structure/exception.hxx>
#include <simgear/io/HTTPClient.hxx>
#include <simgear/misc/sg_dir.hxx>
+extern "C" {
+ void fill_memory_filefunc (zlib_filefunc_def*);
+}
+
namespace simgear {
namespace pkg {
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);
+ memset(&m_md5, 0, sizeof(SG_MD5_CTX));
+ SG_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;
+ SG_MD5Update(&m_md5, (unsigned char*) s, n);
+
+ m_owner->installProgress(m_buffer.size(), responseLength());
}
- virtual void responseComplete()
+ virtual void onDone()
{
if (responseCode() != 200) {
SG_LOG(SG_GENERAL, SG_ALERT, "download failure");
- doFailure();
+ doFailure(Delegate::FAIL_DOWNLOAD);
return;
}
- std::cout << "content lenth:" << responseLength() << std::endl;
- std::cout << m_buffer.size() << " total received" << std::endl;
- MD5Final(&m_md5);
+
+ SG_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;
<< "\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();
+ doFailure(Delegate::FAIL_CHECKSUM);
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);
+ if (!extractUnzip()) {
+ SG_LOG(SG_GENERAL, SG_WARN, "zip extraction failed");
+ doFailure(Delegate::FAIL_EXTRACT);
+ return;
}
-
- archive_read_free(a);
if (m_owner->path().exists()) {
- std::cout << "removing existing path" << std::endl;
+ //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());
+ bool ok = m_extractPath.rename(m_owner->path());
+ if (!ok) {
+ doFailure(Delegate::FAIL_FILESYSTEM);
+ return;
+ }
m_owner->m_revision = m_owner->package()->revision();
m_owner->writeRevisionFile();
+ m_owner->installResult(Delegate::FAIL_SUCCESS);
}
private:
- void doFailure()
+
+ void extractCurrentFile(unzFile zip, char* buffer, size_t bufferSize)
+ {
+ unz_file_info fileInfo;
+ unzGetCurrentFileInfo(zip, &fileInfo,
+ buffer, bufferSize,
+ NULL, 0, /* extra field */
+ NULL, 0 /* comment field */);
+
+ std::string name(buffer);
+ // no absolute paths, no 'up' traversals
+ // we could also look for suspicious file extensions here (forbid .dll, .exe, .so)
+ if ((name[0] == '/') || (name.find("../") != std::string::npos) || (name.find("..\\") != std::string::npos)) {
+ throw sg_format_exception("Bad zip path", name);
+ }
+
+ if (fileInfo.uncompressed_size == 0) {
+ // assume it's a directory for now
+ // since we create parent directories when extracting
+ // a path, we're done here
+ return;
+ }
+
+ int result = unzOpenCurrentFile(zip);
+ if (result != UNZ_OK) {
+ throw sg_io_exception("opening current zip file failed", sg_location(name));
+ }
+
+ std::ofstream outFile;
+ bool eof = false;
+ SGPath path(m_extractPath);
+ path.append(name);
+
+ // create enclosing directory heirarchy as required
+ Dir parentDir(path.dir());
+ if (!parentDir.exists()) {
+ bool ok = parentDir.create(0755);
+ if (!ok) {
+ throw sg_io_exception("failed to create directory heirarchy for extraction", path.c_str());
+ }
+ }
+
+ outFile.open(path.c_str(), std::ios::binary | std::ios::trunc | std::ios::out);
+ if (outFile.fail()) {
+ throw sg_io_exception("failed to open output file for writing", path.c_str());
+ }
+
+ while (!eof) {
+ int bytes = unzReadCurrentFile(zip, buffer, bufferSize);
+ if (bytes < 0) {
+ throw sg_io_exception("unzip failure reading curent archive", sg_location(name));
+ } else if (bytes == 0) {
+ eof = true;
+ } else {
+ outFile.write(buffer, bytes);
+ }
+ }
+
+ outFile.close();
+ unzCloseCurrentFile(zip);
+ }
+
+ bool extractUnzip()
+ {
+ bool result = true;
+ zlib_filefunc_def memoryAccessFuncs;
+ fill_memory_filefunc(&memoryAccessFuncs);
+
+ char bufferName[128];
+ snprintf(bufferName, 128, "%p+%lx", m_buffer.data(), m_buffer.size());
+ unzFile zip = unzOpen2(bufferName, &memoryAccessFuncs);
+
+ const size_t BUFFER_SIZE = 32 * 1024;
+ void* buf = malloc(BUFFER_SIZE);
+
+ try {
+ int result = unzGoToFirstFile(zip);
+ if (result != UNZ_OK) {
+ throw sg_exception("failed to go to first file in archive");
+ }
+
+ while (true) {
+ extractCurrentFile(zip, (char*) buf, BUFFER_SIZE);
+ result = unzGoToNextFile(zip);
+ if (result == UNZ_END_OF_LIST_OF_FILE) {
+ break;
+ } else if (result != UNZ_OK) {
+ throw sg_io_exception("failed to go to next file in the archive");
+ }
+ }
+ } catch (sg_exception& e) {
+ result = false;
+ }
+
+ free(buf);
+ unzClose(zip);
+ return result;
+ }
+
+ void doFailure(Delegate::FailureCode aReason)
{
Dir dir(m_extractPath);
dir.remove(true /* recursive */);
+
if (m_urls.size() == 1) {
-
+ std::cout << "failure:" << aReason << std::endl;
+ m_owner->installResult(aReason);
return;
}
+ std::cout << "retrying download" << std::endl;
m_urls.erase(m_urls.begin()); // pop first URL
}
Install* m_owner;
string_list m_urls;
- MD5_CTX m_md5;
+ SG_MD5_CTX m_md5;
std::string m_buffer;
SGPath m_extractPath;
};
}
m_download = new PackageArchiveDownloader(this);
- m_package->catalog()->root()->getHTTPClient()->makeRequest(m_download);
+ m_package->catalog()->root()->makeHTTPRequest(m_download);
+ m_package->catalog()->root()->startInstall(this);
}
void Install::uninstall()
delete this;
}
+void Install::installResult(Delegate::FailureCode aReason)
+{
+ if (aReason == Delegate::FAIL_SUCCESS) {
+ m_package->catalog()->root()->finishInstall(this);
+ } else {
+ m_package->catalog()->root()->failedInstall(this, aReason);
+ }
+}
+
+void Install::installProgress(unsigned int aBytes, unsigned int aTotal)
+{
+ m_package->catalog()->root()->installProgress(this, aBytes, aTotal);
+}
+
+
} // of namespace pkg
} // of namespace simgear