]> git.mxchange.org Git - simgear.git/blobdiff - simgear/package/Install.cxx
Tweak HTTP code to always sleep.
[simgear.git] / simgear / package / Install.cxx
index 7d5b66da16608423274d415cfe4509ca7b75ab13..d513786c27e605ebe5dc34e18d2bbebe1cf9d1e3 100644 (file)
@@ -1,14 +1,26 @@
-
+// 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 {
@@ -50,32 +66,30 @@ protected:
     
     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;
@@ -89,63 +103,152 @@ protected:
                 << "\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;
 };
@@ -203,7 +306,8 @@ void Install::startUpdate()
     }
     
     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()
@@ -213,6 +317,21 @@ 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