]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/zipstrm.cpp
Better place for coordinate mirroring and removal
[wxWidgets.git] / src / common / zipstrm.cpp
index b39c2856320a107cc7f50b6259ad356d07ea6188..6116b8e0c54fe2993986badb58f452813870c8cb 100644 (file)
@@ -1,5 +1,5 @@
 /////////////////////////////////////////////////////////////////////////////
-// Name:        zipstrm.cpp
+// Name:        src/common/zipstrm.cpp
 // Purpose:     Streams for Zip files
 // Author:      Mike Wetherell
 // RCS-ID:      $Id$
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-  #pragma hdrstop
+    #pragma hdrstop
 #endif
 
+#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
+
 #ifndef WX_PRECOMP
-  #include "wx/defs.h"
+    #include "wx/intl.h"
+    #include "wx/log.h"
+    #include "wx/utils.h"
 #endif
 
-#if wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
-
 #include "wx/zipstrm.h"
-#include "wx/log.h"
-#include "wx/intl.h"
 #include "wx/datstrm.h"
 #include "wx/zstream.h"
 #include "wx/mstream.h"
-#include "wx/utils.h"
 #include "wx/buffer.h"
 #include "wx/ptr_scpd.h"
 #include "wx/wfstream.h"
@@ -88,6 +87,9 @@ wxFORCE_LINK_THIS_MODULE(zipstrm)
 //
 static wxString ReadString(wxInputStream& stream, wxUint16 len, wxMBConv& conv)
 {
+    if (len == 0)
+        return wxEmptyString;
+
 #if wxUSE_UNICODE
     wxCharBuffer buf(len);
     stream.Read(buf.data(), len);
@@ -112,12 +114,20 @@ static inline wxUint32 CrackUint32(const char *m)
     return (n[3] << 24) | (n[2] << 16) | (n[1] << 8) | n[0];
 }
 
+// Decode a little endian wxUint16 number from a character array
+//
+static inline wxUint16 CrackUint16(const char *m)
+{
+    const unsigned char *n = (const unsigned char*)m;
+    return (n[1] << 8) | n[0];
+}
+
 // Temporarily lower the logging level in debug mode to avoid a warning
 // from SeekI about seeking on a stream with data written back to it.
 //
 static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos)
 {
-#ifdef __WXDEBUG__
+#if defined(__WXDEBUG__) && wxUSE_LOG
     wxLogLevel level = wxLog::GetLogLevel();
     wxLog::SetLogLevel(wxLOG_Debug - 1);
     wxFileOffset result = stream.SeekI(pos);
@@ -129,6 +139,69 @@ static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos)
 }
 
 
+/////////////////////////////////////////////////////////////////////////////
+// Read a zip header
+
+class wxZipHeader
+{
+public:
+    wxZipHeader(wxInputStream& stream, size_t size);
+
+    inline wxUint8 Read8();
+    inline wxUint16 Read16();
+    inline wxUint32 Read32();
+
+    const char *GetData() const             { return m_data; }
+    size_t GetSize() const                  { return m_size; }
+    operator bool() const                   { return m_ok; }
+
+    size_t Seek(size_t pos)                 { m_pos = pos; return m_pos; }
+    size_t Skip(size_t size)                { m_pos += size; return m_pos; }
+
+    wxZipHeader& operator>>(wxUint8& n)     { n = Read8();  return *this; }
+    wxZipHeader& operator>>(wxUint16& n)    { n = Read16(); return *this; }
+    wxZipHeader& operator>>(wxUint32& n)    { n = Read32(); return *this; }
+
+private:
+    char m_data[64];
+    size_t m_size;
+    size_t m_pos;
+    bool m_ok;
+};
+
+wxZipHeader::wxZipHeader(wxInputStream& stream, size_t size)
+  : m_size(0),
+    m_pos(0),
+    m_ok(false)
+{
+    wxCHECK_RET(size <= sizeof(m_data), _T("buffer too small"));
+    m_size = stream.Read(m_data, size).LastRead();
+    m_ok = m_size == size;
+}
+
+wxUint8 wxZipHeader::Read8()
+{
+    wxASSERT(m_pos < m_size);
+    return m_data[m_pos++];
+}
+
+wxUint16 wxZipHeader::Read16()
+{
+    wxASSERT(m_pos + 2 <= m_size);
+    wxUint16 n = CrackUint16(m_data + m_pos);
+    m_pos += 2;
+    return n;
+}
+
+wxUint32 wxZipHeader::Read32()
+{
+    wxASSERT(m_pos + 4 <= m_size);
+    wxUint32 n = CrackUint32(m_data + m_pos);
+    m_pos += 4;
+    return n;
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 // Stored input stream
 // Trival decompressor for files which are 'stored' in the zip file.
@@ -169,10 +242,8 @@ size_t wxStoredInputStream::OnSysRead(void *buffer, size_t size)
     count = m_parent_i_stream->Read(buffer, count).LastRead();
     m_pos += count;
 
-    if (m_pos == m_len && count < size)
-        m_lasterror = wxSTREAM_EOF;
-    else if (!*m_parent_i_stream)
-        m_lasterror = wxSTREAM_READ_ERROR;
+    if (count < size)
+        m_lasterror = m_pos == m_len ? wxSTREAM_EOF : wxSTREAM_READ_ERROR;
 
     return count;
 }
@@ -295,7 +366,8 @@ wxInputStream& wxTeeInputStream::Read(void *buffer, size_t size)
 size_t wxTeeInputStream::OnSysRead(void *buffer, size_t size)
 {
     size_t count = m_parent_i_stream->Read(buffer, size).LastRead();
-    m_lasterror = m_parent_i_stream->GetLastError();
+    if (count < size)
+        m_lasterror = m_parent_i_stream->GetLastError();
     return count;
 }
 
@@ -309,6 +381,7 @@ size_t wxTeeInputStream::GetData(char *buffer, size_t size)
             wxFAIL; // we've already returned data that's now being ungot
             m_end = len;
         }
+        m_parent_i_stream->Reset();
         m_parent_i_stream->Ungetch(m_wback, m_wbacksize);
         free(m_wback);
         m_wback = NULL;
@@ -484,6 +557,8 @@ private:
     size_t m_size;
     size_t m_capacity;
     int m_ref;
+
+    wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipMemory)
 };
 
 wxZipMemory *wxZipMemory::Unique(size_t size)
@@ -558,12 +633,14 @@ public:
     bool IsEmpty() const { return m_entries.empty(); }
 
 private:
-    typedef wx__OffsetZipEntryMap::key_type key_type;
-
     ~wxZipWeakLinks() { wxASSERT(IsEmpty()); }
 
+    typedef wx__OffsetZipEntryMap::key_type key_type;
+
     int m_ref;
     wx__OffsetZipEntryMap m_entries;
+
+    wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipWeakLinks)
 };
 
 wxZipWeakLinks *wxZipWeakLinks::AddEntry(wxZipEntry *entry, wxFileOffset key)
@@ -684,7 +761,7 @@ wxString wxZipEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
         case wxPATH_DOS:
         {
             wxString name(isDir ? m_Name + _T("\\") : m_Name);
-            for (size_t i = name.length() - 1; i > 0; --i)
+            for (size_t i = 0; i < name.length(); i++)
                 if (name[i] == _T('/'))
                     name[i] = _T('\\');
             return name;
@@ -865,7 +942,9 @@ size_t wxZipEntry::ReadLocal(wxInputStream& stream, wxMBConv& conv)
     wxUint16 nameLen, extraLen;
     wxUint32 compressedSize, size, crc;
 
-    wxDataInputStream ds(stream);
+    wxZipHeader ds(stream, LOCAL_SIZE - 4);
+    if (!ds)
+        return 0;
 
     ds >> m_VersionNeeded >> m_Flags >> m_Method;
     SetDateTime(wxDateTime().SetFromDOS(ds.Read32()));
@@ -881,11 +960,16 @@ size_t wxZipEntry::ReadLocal(wxInputStream& stream, wxMBConv& conv)
         m_Size = size;
 
     SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
+    if (stream.LastRead() != nameLen + 0u)
+        return 0;
 
     if (extraLen || GetLocalExtraLen()) {
         Unique(m_LocalExtra, extraLen);
-        if (extraLen)
+        if (extraLen) {
             stream.Read(m_LocalExtra->GetData(), extraLen);
+            if (stream.LastRead() != extraLen + 0u)
+                return 0;
+        }
     }
 
     return LOCAL_SIZE + nameLen + extraLen;
@@ -925,7 +1009,9 @@ size_t wxZipEntry::ReadCentral(wxInputStream& stream, wxMBConv& conv)
 {
     wxUint16 nameLen, extraLen, commentLen;
 
-    wxDataInputStream ds(stream);
+    wxZipHeader ds(stream, CENTRAL_SIZE - 4);
+    if (!ds)
+        return 0;
 
     ds >> m_VersionMadeBy >> m_SystemMadeBy;
 
@@ -942,17 +1028,25 @@ size_t wxZipEntry::ReadCentral(wxInputStream& stream, wxMBConv& conv)
     SetOffset(ds.Read32());
 
     SetName(ReadString(stream, nameLen, conv), wxPATH_UNIX);
+    if (stream.LastRead() != nameLen + 0u)
+        return 0;
 
     if (extraLen || GetExtraLen()) {
         Unique(m_Extra, extraLen);
-        if (extraLen)
+        if (extraLen) {
             stream.Read(m_Extra->GetData(), extraLen);
+            if (stream.LastRead() != extraLen + 0u)
+                return 0;
+        }
     }
 
-    if (commentLen)
+    if (commentLen) {
         m_Comment = ReadString(stream, commentLen, conv);
-    else
+        if (stream.LastRead() != commentLen + 0u)
+            return 0;
+    } else {
         m_Comment.clear();
+    }
 
     return CENTRAL_SIZE + nameLen + extraLen + commentLen;
 }
@@ -1004,7 +1098,9 @@ size_t wxZipEntry::WriteCentral(wxOutputStream& stream, wxMBConv& conv) const
 //
 size_t wxZipEntry::ReadDescriptor(wxInputStream& stream)
 {
-    wxDataInputStream ds(stream);
+    wxZipHeader ds(stream, SUMS_SIZE);
+    if (!ds)
+        return 0;
 
     m_Crc = ds.Read32();
     m_CompressedSize = ds.Read32();
@@ -1013,22 +1109,23 @@ size_t wxZipEntry::ReadDescriptor(wxInputStream& stream)
     // if 1st value is the signature then this is probably an info-zip record
     if (m_Crc == SUMS_MAGIC)
     {
-        char buf[8];
-        stream.Read(buf, sizeof(buf));
-        wxUint32 u1 = CrackUint32(buf);
-        wxUint32 u2 = CrackUint32(buf + 4);
+        wxZipHeader buf(stream, 8);
+        wxUint32 u1 = buf.GetSize() >= 4 ? buf.Read32() : (wxUint32)LOCAL_MAGIC;
+        wxUint32 u2 = buf.GetSize() == 8 ? buf.Read32() : 0;
 
         // look for the signature of the following record to decide which
         if ((u1 == LOCAL_MAGIC || u1 == CENTRAL_MAGIC) &&
             (u2 != LOCAL_MAGIC && u2 != CENTRAL_MAGIC))
         {
             // it's a pkzip style record after all!
-            stream.Ungetch(buf, sizeof(buf));
+            if (buf.GetSize() > 0)
+                stream.Ungetch(buf.GetData(), buf.GetSize());
         }
         else
         {
             // it's an info-zip record as expected
-            stream.Ungetch(buf + 4, sizeof(buf) - 4);
+            if (buf.GetSize() > 4)
+                stream.Ungetch(buf.GetData() + 4, buf.GetSize() - 4);
             m_Crc = wx_truncate_cast(wxUint32, m_CompressedSize);
             m_CompressedSize = m_Size;
             m_Size = u1;
@@ -1129,23 +1226,26 @@ bool wxZipEndRec::Write(wxOutputStream& stream, wxMBConv& conv) const
 
 bool wxZipEndRec::Read(wxInputStream& stream, wxMBConv& conv)
 {
-    wxDataInputStream ds(stream);
+    wxZipHeader ds(stream, END_SIZE - 4);
+    if (!ds)
+        return false;
+
     wxUint16 commentLen;
 
     ds >> m_DiskNumber >> m_StartDisk >> m_EntriesHere
        >> m_TotalEntries >> m_Size >> m_Offset >> commentLen;
 
-    if (commentLen)
+    if (commentLen) {
         m_Comment = ReadString(stream, commentLen, conv);
+        if (stream.LastRead() != commentLen + 0u)
+            return false;
+    }
 
-    if (stream.IsOk())
-        if (m_DiskNumber == 0 && m_StartDisk == 0 &&
-                m_EntriesHere == m_TotalEntries)
-            return true;
-        else
-            wxLogError(_("unsupported zip archive"));
+    if (m_DiskNumber != 0 || m_StartDisk != 0 ||
+            m_EntriesHere != m_TotalEntries)
+        wxLogWarning(_("assuming this is a multi-part zip concatenated"));
 
-    return false;
+    return true;
 }
 
 
@@ -1170,6 +1270,8 @@ private:
 
     int m_ref;
     wxZipOutputStream *m_stream;
+
+    wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipStreamLink)
 };
 
 
@@ -1313,9 +1415,7 @@ bool wxZipInputStream::LoadEndRecord()
         else {
             wxLogNull nolog;
             wxFileOffset pos = m_parent_i_stream->TellI();
-            // FIXME
-            //if (pos != wxInvalidOffset)
-            if (pos >= 0 && pos <= LONG_MAX)
+            if (pos != wxInvalidOffset)
                 m_offsetAdjustment = m_position = pos;
             return true;
         }
@@ -1325,14 +1425,8 @@ bool wxZipInputStream::LoadEndRecord()
 
     // Read in the end record
     wxFileOffset endPos = m_parent_i_stream->TellI() - 4;
-    if (!endrec.Read(*m_parent_i_stream, GetConv())) {
-        if (!*m_parent_i_stream) {
-            m_lasterror = wxSTREAM_READ_ERROR;
-            return false;
-        }
-        // TODO: try this out
-        wxLogWarning(_("assuming this is a multi-part zip concatenated"));
-    }
+    if (!endrec.Read(*m_parent_i_stream, GetConv()))
+        return false;
 
     m_TotalEntries = endrec.GetTotalEntries();
     m_Comment = endrec.GetComment();
@@ -1451,12 +1545,13 @@ wxStreamError wxZipInputStream::ReadCentral()
     if (QuietSeek(*m_parent_i_stream, m_position + 4) == wxInvalidOffset)
         return wxSTREAM_READ_ERROR;
 
-    m_position += m_entry.ReadCentral(*m_parent_i_stream, GetConv());
-    if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR) {
+    size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+    if (!size) {
         m_signature = 0;
         return wxSTREAM_READ_ERROR;
     }
 
+    m_position += size;
     m_signature = ReadSignature();
 
     if (m_offsetAdjustment)
@@ -1485,9 +1580,10 @@ wxStreamError wxZipInputStream::ReadLocal(bool readEndRec /*=false*/)
         if (m_weaklinks->IsEmpty() && m_streamlink == NULL)
             return wxSTREAM_EOF;
 
-        m_position += m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+        size_t size = m_entry.ReadCentral(*m_parent_i_stream, GetConv());
+        m_position += size;
         m_signature = 0;
-        if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR)
+        if (!size)
             return wxSTREAM_READ_ERROR;
 
         wxZipEntry *entry = m_weaklinks->GetEntry(m_entry.GetOffset());
@@ -1521,22 +1617,20 @@ wxStreamError wxZipInputStream::ReadLocal(bool readEndRec /*=false*/)
         return wxSTREAM_EOF;
     }
 
-    if (m_signature != LOCAL_MAGIC) {
-        wxLogError(_("error reading zip local header"));
-        return wxSTREAM_READ_ERROR;
-    }
-
-    m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
-    m_signature = 0;
-    m_entry.SetOffset(m_position);
-    m_entry.SetKey(m_position);
+    if (m_signature == LOCAL_MAGIC) {
+        m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
+        m_signature = 0;
+        m_entry.SetOffset(m_position);
+        m_entry.SetKey(m_position);
 
-    if (m_parent_i_stream->GetLastError() == wxSTREAM_READ_ERROR) {
-        return wxSTREAM_READ_ERROR;
-    } else {
-        m_TotalEntries++;
-        return wxSTREAM_NO_ERROR;
+        if (m_headerSize) {
+            m_TotalEntries++;
+            return wxSTREAM_NO_ERROR;
+        }
     }
+
+    wxLogError(_("error reading zip local header"));
+    return wxSTREAM_READ_ERROR;
 }
 
 wxUint32 wxZipInputStream::ReadSignature()
@@ -1590,7 +1684,7 @@ bool wxZipInputStream::DoOpen(wxZipEntry *entry, bool raw)
 
     if (m_parentSeekable || AtHeader()) {
         m_headerSize = m_entry.ReadLocal(*m_parent_i_stream, GetConv());
-        if (m_parentSeekable) {
+        if (m_headerSize && m_parentSeekable) {
             wxZipEntry *ref = m_weaklinks->GetEntry(m_entry.GetKey());
             if (ref) {
                 Copy(ref->m_LocalExtra, m_entry.m_LocalExtra);
@@ -1604,7 +1698,8 @@ bool wxZipInputStream::DoOpen(wxZipEntry *entry, bool raw)
         }
     }
 
-    m_lasterror = m_parent_i_stream->GetLastError();
+    if (m_headerSize)
+        m_lasterror = wxSTREAM_NO_ERROR;
     return IsOk();
 }
 
@@ -1723,7 +1818,8 @@ size_t wxZipInputStream::OnSysRead(void *buffer, size_t size)
     size_t count = m_decomp->Read(buffer, size).LastRead();
     if (!m_raw)
         m_crcAccumulator = crc32(m_crcAccumulator, (Byte*)buffer, count);
-    m_lasterror = m_decomp->GetLastError();
+    if (count < size)
+        m_lasterror = m_decomp->GetLastError();
 
     if (Eof()) {
         if ((m_entry.GetFlags() & wxZIP_SUMS_FOLLOW) != 0) {
@@ -1741,16 +1837,14 @@ size_t wxZipInputStream::OnSysRead(void *buffer, size_t size)
         if (!m_raw) {
             m_lasterror = wxSTREAM_READ_ERROR;
 
-            if (m_parent_i_stream->IsOk()) {
-                if (m_entry.GetSize() != TellI())
-                    wxLogError(_("reading zip stream (entry %s): bad length"),
-                               m_entry.GetName().c_str());
-                else if (m_crcAccumulator != m_entry.GetCrc())
-                    wxLogError(_("reading zip stream (entry %s): bad crc"),
-                               m_entry.GetName().c_str());
-                else
-                    m_lasterror = wxSTREAM_EOF;
-            }
+            if (m_entry.GetSize() != TellI())
+                wxLogError(_("reading zip stream (entry %s): bad length"),
+                           m_entry.GetName().c_str());
+            else if (m_crcAccumulator != m_entry.GetCrc())
+                wxLogError(_("reading zip stream (entry %s): bad crc"),
+                           m_entry.GetName().c_str());
+            else
+                m_lasterror = wxSTREAM_EOF;
         }
     }