]> git.saurik.com Git - wxWidgets.git/blobdiff - src/common/zipstrm.cpp
fixing overrelease and out-of-bounds write, fixes #13725
[wxWidgets.git] / src / common / zipstrm.cpp
index fc5670266d56e49b97f1b1abb3d3dacc50218ca9..4902738a07aaf7bed96e3e1c1a17a27bc3b8c998 100644 (file)
 #include "wx/wxprec.h"
 
 #ifdef __BORLANDC__
-  #pragma hdrstop
+    #pragma hdrstop
 #endif
 
+#if wxUSE_ZIPSTREAM
+
+#include "wx/zipstrm.h"
+
 #ifndef WX_PRECOMP
-  #include "wx/defs.h"
+    #include "wx/hashmap.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/scopedptr.h"
 #include "wx/wfstream.h"
-#include "wx/link.h"
 #include "zlib.h"
 
 // value for the 'version needed to extract' field (20 means 2.0)
@@ -78,8 +77,6 @@ enum {
 IMPLEMENT_DYNAMIC_CLASS(wxZipEntry, wxArchiveEntry)
 IMPLEMENT_DYNAMIC_CLASS(wxZipClassFactory, wxArchiveClassFactory)
 
-wxFORCE_LINK_THIS_MODULE(zipstrm)
-
 
 /////////////////////////////////////////////////////////////////////////////
 // Helpers
@@ -88,6 +85,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 +112,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 wxUSE_LOG
     wxLogLevel level = wxLog::GetLogLevel();
     wxLog::SetLogLevel(wxLOG_Debug - 1);
     wxFileOffset result = stream.SeekI(pos);
@@ -129,6 +137,34 @@ static wxFileOffset QuietSeek(wxInputStream& stream, wxFileOffset pos)
 }
 
 
+/////////////////////////////////////////////////////////////////////////////
+// Class factory
+
+static wxZipClassFactory g_wxZipClassFactory;
+
+wxZipClassFactory::wxZipClassFactory()
+{
+    if (this == &g_wxZipClassFactory)
+        PushFront();
+}
+
+const wxChar * const *
+wxZipClassFactory::GetProtocols(wxStreamProtocolType type) const
+{
+    static const wxChar *protocols[] = { wxT("zip"), NULL };
+    static const wxChar *mimetypes[] = { wxT("application/zip"), NULL };
+    static const wxChar *fileexts[]  = { wxT(".zip"), wxT(".htb"), NULL };
+    static const wxChar *empty[]     = { NULL };
+
+    switch (type) {
+        case wxSTREAM_PROTOCOL: return protocols;
+        case wxSTREAM_MIMETYPE: return mimetypes;
+        case wxSTREAM_FILEEXT:  return fileexts;
+        default:                return empty;
+    }
+}
+
+
 /////////////////////////////////////////////////////////////////////////////
 // Read a zip header
 
@@ -164,31 +200,31 @@ wxZipHeader::wxZipHeader(wxInputStream& stream, size_t size)
     m_pos(0),
     m_ok(false)
 {
-    wxCHECK_RET(size <= sizeof(m_data), _T("buffer too small"));
+    wxCHECK_RET(size <= sizeof(m_data), wxT("buffer too small"));
     m_size = stream.Read(m_data, size).LastRead();
     m_ok = m_size == size;
 }
 
-wxUint8 wxZipHeader::Read8()
+inline wxUint8 wxZipHeader::Read8()
 {
     wxASSERT(m_pos < m_size);
-    return *wx_reinterpret_cast(wxUint8*, m_data + m_pos++);
+    return m_data[m_pos++];
 }
 
-wxUint16 wxZipHeader::Read16()
+inline wxUint16 wxZipHeader::Read16()
 {
     wxASSERT(m_pos + 2 <= m_size);
-    wxUint16 n = *wx_reinterpret_cast(wxUint16*, m_data + m_pos);
+    wxUint16 n = CrackUint16(m_data + m_pos);
     m_pos += 2;
-    return wxUINT16_SWAP_ON_BE(n);
+    return n;
 }
 
-wxUint32 wxZipHeader::Read32()
+inline wxUint32 wxZipHeader::Read32()
 {
     wxASSERT(m_pos + 4 <= m_size);
-    wxUint32 n = *wx_reinterpret_cast(wxUint32*, m_data + m_pos);
+    wxUint32 n = CrackUint32(m_data + m_pos);
     m_pos += 4;
-    return wxUINT32_SWAP_ON_BE(n);
+    return n;
 }
 
 
@@ -215,7 +251,7 @@ private:
     wxFileOffset m_pos;
     wxFileOffset m_len;
 
-    DECLARE_NO_COPY_CLASS(wxStoredInputStream)
+    wxDECLARE_NO_COPY_CLASS(wxStoredInputStream);
 };
 
 wxStoredInputStream::wxStoredInputStream(wxInputStream& stream)
@@ -261,7 +297,7 @@ protected:
 
 private:
     wxFileOffset m_pos;
-    DECLARE_NO_COPY_CLASS(wxStoredOutputStream)
+    wxDECLARE_NO_COPY_CLASS(wxStoredOutputStream);
 };
 
 size_t wxStoredOutputStream::OnSysWrite(const void *buffer, size_t size)
@@ -323,7 +359,7 @@ private:
     size_t m_start;
     size_t m_end;
 
-    DECLARE_NO_COPY_CLASS(wxTeeInputStream)
+    wxDECLARE_NO_COPY_CLASS(wxTeeInputStream);
 };
 
 wxTeeInputStream::wxTeeInputStream(wxInputStream& stream)
@@ -419,7 +455,7 @@ private:
     enum { BUFSIZE = 8192 };
     wxCharBuffer m_dummy;
 
-    DECLARE_NO_COPY_CLASS(wxRawInputStream)
+    wxDECLARE_NO_COPY_CLASS(wxRawInputStream);
 };
 
 wxRawInputStream::wxRawInputStream(wxInputStream& stream)
@@ -604,7 +640,7 @@ static void Unique(wxZipMemory*& zm, size_t size)
 // Collection of weak references to entries
 
 WX_DECLARE_HASH_MAP(long, wxZipEntry*, wxIntegerHash,
-                    wxIntegerEqual, wx__OffsetZipEntryMap);
+                    wxIntegerEqual, wxOffsetZipEntryMap_);
 
 class wxZipWeakLinks
 {
@@ -625,10 +661,10 @@ public:
 private:
     ~wxZipWeakLinks() { wxASSERT(IsEmpty()); }
 
-    typedef wx__OffsetZipEntryMap::key_type key_type;
+    typedef wxOffsetZipEntryMap_::key_type key_type;
 
     int m_ref;
-    wx__OffsetZipEntryMap m_entries;
+    wxOffsetZipEntryMap_ m_entries;
 
     wxSUPPRESS_GCC_PRIVATE_DTOR_WARNING(wxZipWeakLinks)
 };
@@ -642,7 +678,7 @@ wxZipWeakLinks *wxZipWeakLinks::AddEntry(wxZipEntry *entry, wxFileOffset key)
 
 wxZipEntry *wxZipWeakLinks::GetEntry(wxFileOffset key) const
 {
-    wx__OffsetZipEntryMap::const_iterator it =
+    wxOffsetZipEntryMap_::const_iterator it =
         m_entries.find(wx_truncate_cast(key_type, key));
     return it != m_entries.end() ?  it->second : NULL;
 }
@@ -750,15 +786,15 @@ wxString wxZipEntry::GetName(wxPathFormat format /*=wxPATH_NATIVE*/) const
     switch (wxFileName::GetFormat(format)) {
         case wxPATH_DOS:
         {
-            wxString name(isDir ? m_Name + _T("\\") : m_Name);
-            for (size_t i = name.length() - 1; i > 0; --i)
-                if (name[i] == _T('/'))
-                    name[i] = _T('\\');
+            wxString name(isDir ? m_Name + wxT("\\") : m_Name);
+            for (size_t i = 0; i < name.length(); i++)
+                if (name[i] == wxT('/'))
+                    name[i] = wxT('\\');
             return name;
         }
 
         case wxPATH_UNIX:
-            return isDir ? m_Name + _T("/") : m_Name;
+            return isDir ? m_Name + wxT("/") : m_Name;
 
         default:
             ;
@@ -798,9 +834,9 @@ wxString wxZipEntry::GetInternalName(const wxString& name,
 
     while (!internal.empty() && *internal.begin() == '/')
         internal.erase(0, 1);
-    while (!internal.empty() && internal.compare(0, 2, _T("./")) == 0)
+    while (!internal.empty() && internal.compare(0, 2, wxT("./")) == 0)
         internal.erase(0, 2);
-    if (internal == _T(".") || internal == _T(".."))
+    if (internal == wxT(".") || internal == wxT(".."))
         internal = wxEmptyString;
 
     return internal;
@@ -968,7 +1004,7 @@ size_t wxZipEntry::ReadLocal(wxInputStream& stream, wxMBConv& conv)
 size_t wxZipEntry::WriteLocal(wxOutputStream& stream, wxMBConv& conv) const
 {
     wxString unixName = GetName(wxPATH_UNIX);
-    const wxWX2MBbuf name_buf = conv.cWX2MB(unixName);
+    const wxWX2MBbuf name_buf = unixName.mb_str(conv);
     const char *name = name_buf;
     if (!name) name = "";
     wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name));
@@ -1044,12 +1080,12 @@ size_t wxZipEntry::ReadCentral(wxInputStream& stream, wxMBConv& conv)
 size_t wxZipEntry::WriteCentral(wxOutputStream& stream, wxMBConv& conv) const
 {
     wxString unixName = GetName(wxPATH_UNIX);
-    const wxWX2MBbuf name_buf = conv.cWX2MB(unixName);
+    const wxWX2MBbuf name_buf = unixName.mb_str(conv);
     const char *name = name_buf;
     if (!name) name = "";
     wxUint16 nameLen = wx_truncate_cast(wxUint16, strlen(name));
 
-    const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment);
+    const wxWX2MBbuf comment_buf = m_Comment.mb_str(conv);
     const char *comment = comment_buf;
     if (!comment) comment = "";
     wxUint16 commentLen = wx_truncate_cast(wxUint16, strlen(comment));
@@ -1199,7 +1235,7 @@ wxZipEndRec::wxZipEndRec()
 
 bool wxZipEndRec::Write(wxOutputStream& stream, wxMBConv& conv) const
 {
-    const wxWX2MBbuf comment_buf = conv.cWX2MB(m_Comment);
+    const wxWX2MBbuf comment_buf = m_Comment.mb_str(conv);
     const char *comment = comment_buf;
     if (!comment) comment = "";
     wxUint16 commentLen = (wxUint16)strlen(comment);
@@ -1233,7 +1269,9 @@ bool wxZipEndRec::Read(wxInputStream& stream, wxMBConv& conv)
 
     if (m_DiskNumber != 0 || m_StartDisk != 0 ||
             m_EntriesHere != m_TotalEntries)
+    {
         wxLogWarning(_("assuming this is a multi-part zip concatenated"));
+    }
 
     return true;
 }
@@ -1269,8 +1307,8 @@ private:
 // Input stream
 
 // leave the default wxZipEntryPtr free for users
-wxDECLARE_SCOPED_PTR(wxZipEntry, wx__ZipEntryPtr)
-wxDEFINE_SCOPED_PTR (wxZipEntry, wx__ZipEntryPtr)
+wxDECLARE_SCOPED_PTR(wxZipEntry, wxZipEntryPtr_)
+wxDEFINE_SCOPED_PTR (wxZipEntry, wxZipEntryPtr_)
 
 // constructor
 //
@@ -1281,7 +1319,14 @@ wxZipInputStream::wxZipInputStream(wxInputStream& stream,
     Init();
 }
 
-#if 1 //WXWIN_COMPATIBILITY_2_6
+wxZipInputStream::wxZipInputStream(wxInputStream *stream,
+                                   wxMBConv& conv /*=wxConvLocal*/)
+  : wxArchiveInputStream(stream, conv)
+{
+    Init();
+}
+
+#if WXWIN_COMPATIBILITY_2_6 && wxUSE_FFILE
 
 // Part of the compatibility constructor, which has been made inline to
 // avoid a problem with it not being exported by mingw 3.2.3
@@ -1292,10 +1337,11 @@ void wxZipInputStream::Init(const wxString& file)
     wxLogNull nolog;
     Init();
     m_allowSeeking = true;
-    m_ffile = wx_static_cast(wxFFileInputStream*, m_parent_i_stream);
-    wx__ZipEntryPtr entry;
+    wxFFileInputStream *ffile;
+    ffile = static_cast<wxFFileInputStream*>(m_parent_i_stream);
+    wxZipEntryPtr_ entry;
 
-    if (m_ffile->Ok()) {
+    if (ffile->IsOk()) {
         do {
             entry.reset(GetNextEntry());
         }
@@ -1306,13 +1352,13 @@ void wxZipInputStream::Init(const wxString& file)
         m_lasterror = wxSTREAM_READ_ERROR;
 }
 
-wxInputStream& wxZipInputStream::OpenFile(const wxString& archive)
+wxInputStream* wxZipInputStream::OpenFile(const wxString& archive)
 {
     wxLogNull nolog;
-    return *new wxFFileInputStream(archive);
+    return new wxFFileInputStream(archive);
 }
 
-#endif // WXWIN_COMPATIBILITY_2_6
+#endif // WXWIN_COMPATIBILITY_2_6 && wxUSE_FFILE
 
 void wxZipInputStream::Init()
 {
@@ -1330,8 +1376,7 @@ void wxZipInputStream::Init()
     m_signature = 0;
     m_TotalEntries = 0;
     m_lasterror = m_parent_i_stream->GetLastError();
-    m_ffile = NULL;
-#if 1 //WXWIN_COMPATIBILITY_2_6
+#if WXWIN_COMPATIBILITY_2_6
     m_allowSeeking = false;
 #endif
 }
@@ -1343,7 +1388,6 @@ wxZipInputStream::~wxZipInputStream()
     delete m_store;
     delete m_inflate;
     delete m_rawin;
-    delete m_ffile;
 
     m_weaklinks->Release(this);
 
@@ -1421,11 +1465,13 @@ bool wxZipInputStream::LoadEndRecord()
     m_TotalEntries = endrec.GetTotalEntries();
     m_Comment = endrec.GetComment();
 
+    wxUint32 magic = m_TotalEntries ? CENTRAL_MAGIC : END_MAGIC;
+
     // Now find the central-directory. we have the file offset of
     // the CD, so look there first.
     if (m_parent_i_stream->SeekI(endrec.GetOffset()) != wxInvalidOffset &&
-            ReadSignature() == CENTRAL_MAGIC) {
-        m_signature = CENTRAL_MAGIC;
+            ReadSignature() == magic) {
+        m_signature = magic;
         m_position = endrec.GetOffset();
         m_offsetAdjustment = 0;
         return true;
@@ -1435,8 +1481,8 @@ bool wxZipInputStream::LoadEndRecord()
     // to a self extractor, so take the CD size (also in endrec), subtract
     // it from the file offset of the end-central-directory and look there.
     if (m_parent_i_stream->SeekI(endPos - endrec.GetSize())
-            != wxInvalidOffset && ReadSignature() == CENTRAL_MAGIC) {
-        m_signature = CENTRAL_MAGIC;
+            != wxInvalidOffset && ReadSignature() == magic) {
+        m_signature = magic;
         m_position = endPos - endrec.GetSize();
         m_offsetAdjustment = m_position - endrec.GetOffset();
         return true;
@@ -1514,7 +1560,7 @@ wxZipEntry *wxZipInputStream::GetNextEntry()
     if (!IsOk())
         return NULL;
 
-    wx__ZipEntryPtr entry(new wxZipEntry(m_entry));
+    wxZipEntryPtr_ entry(new wxZipEntry(m_entry));
     entry->m_backlink = m_weaklinks->AddEntry(entry.get(), entry->GetKey());
     return entry.release();
 }
@@ -1607,22 +1653,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_headerSize) {
-        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()
@@ -1729,7 +1773,7 @@ bool wxZipInputStream::OpenDecompressor(bool raw /*=false*/)
     return IsOk();
 }
 
-// Can be overriden to add support for additional decompression methods
+// Can be overridden to add support for additional decompression methods
 //
 wxInputStream *wxZipInputStream::OpenDecompressor(wxInputStream& stream)
 {
@@ -1830,20 +1874,26 @@ size_t wxZipInputStream::OnSysRead(void *buffer, size_t size)
             m_lasterror = wxSTREAM_READ_ERROR;
 
             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;
+            }
         }
     }
 
     return count;
 }
 
-#if 1 //WXWIN_COMPATIBILITY_2_6
+#if WXWIN_COMPATIBILITY_2_6
 
 // Borrowed from VS's zip stream (c) 1999 Vaclav Slavik
 //
@@ -1915,32 +1965,46 @@ wxFileOffset wxZipInputStream::OnSysSeek(wxFileOffset seek, wxSeekMode mode)
 // Output stream
 
 #include "wx/listimpl.cpp"
-WX_DEFINE_LIST(wx__ZipEntryList)
+WX_DEFINE_LIST(wxZipEntryList_)
 
 wxZipOutputStream::wxZipOutputStream(wxOutputStream& stream,
                                      int level      /*=-1*/,
                                      wxMBConv& conv /*=wxConvLocal*/)
-  : wxArchiveOutputStream(stream, conv),
-    m_store(new wxStoredOutputStream(stream)),
-    m_deflate(NULL),
-    m_backlink(NULL),
-    m_initialData(new char[OUTPUT_LATENCY]),
-    m_initialSize(0),
-    m_pending(NULL),
-    m_raw(false),
-    m_headerOffset(0),
-    m_headerSize(0),
-    m_entrySize(0),
-    m_comp(NULL),
-    m_level(level),
-    m_offsetAdjustment(wxInvalidOffset)
+  : wxArchiveOutputStream(stream, conv)
 {
+    Init(level);
+}
+
+wxZipOutputStream::wxZipOutputStream(wxOutputStream *stream,
+                                     int level      /*=-1*/,
+                                     wxMBConv& conv /*=wxConvLocal*/)
+  : wxArchiveOutputStream(stream, conv)
+{
+    Init(level);
+}
+
+void wxZipOutputStream::Init(int level)
+{
+    m_store = new wxStoredOutputStream(*m_parent_o_stream);
+    m_deflate = NULL;
+    m_backlink = NULL;
+    m_initialData = new char[OUTPUT_LATENCY];
+    m_initialSize = 0;
+    m_pending = NULL;
+    m_raw = false;
+    m_headerOffset = 0;
+    m_headerSize = 0;
+    m_entrySize = 0;
+    m_comp = NULL;
+    m_level = level;
+    m_offsetAdjustment = wxInvalidOffset;
+    m_endrecWritten = false;
 }
 
 wxZipOutputStream::~wxZipOutputStream()
 {
     Close();
-    WX_CLEAR_LIST(wx__ZipEntryList, m_entries);
+    WX_CLEAR_LIST(wxZipEntryList_, m_entries);
     delete m_store;
     delete m_deflate;
     delete m_pending;
@@ -1969,7 +2033,7 @@ bool wxZipOutputStream::PutNextDirEntry(
 bool wxZipOutputStream::CopyEntry(wxZipEntry *entry,
                                   wxZipInputStream& inputStream)
 {
-    wx__ZipEntryPtr e(entry);
+    wxZipEntryPtr_ e(entry);
 
     return
         inputStream.DoOpen(e.get(), true) &&
@@ -1995,7 +2059,7 @@ bool wxZipOutputStream::CopyEntry(wxArchiveEntry *entry,
         return false;
     }
 
-    return CopyEntry(zipEntry, wx_static_cast(wxZipInputStream&, stream));
+    return CopyEntry(zipEntry, static_cast<wxZipInputStream&>(stream));
 }
 
 bool wxZipOutputStream::CopyArchiveMetaData(wxZipInputStream& inputStream)
@@ -2009,7 +2073,7 @@ bool wxZipOutputStream::CopyArchiveMetaData(wxZipInputStream& inputStream)
 
 bool wxZipOutputStream::CopyArchiveMetaData(wxArchiveInputStream& stream)
 {
-    return CopyArchiveMetaData(wx_static_cast(wxZipInputStream&, stream));
+    return CopyArchiveMetaData(static_cast<wxZipInputStream&>(stream));
 }
 
 void wxZipOutputStream::SetLevel(int level)
@@ -2064,7 +2128,7 @@ bool wxZipOutputStream::DoCreate(wxZipEntry *entry, bool raw /*=false*/)
     return true;
 }
 
-// Can be overriden to add support for additional compression methods
+// Can be overridden to add support for additional compression methods
 //
 wxOutputStream *wxZipOutputStream::OpenCompressor(
     wxOutputStream& stream,
@@ -2139,7 +2203,7 @@ bool wxZipOutputStream::CloseCompressor(wxOutputStream *comp)
 void wxZipOutputStream::CreatePendingEntry(const void *buffer, size_t size)
 {
     wxASSERT(IsOk() && m_pending && !m_comp);
-    wx__ZipEntryPtr spPending(m_pending);
+    wxZipEntryPtr_ spPending(m_pending);
     m_pending = NULL;
 
     Buffer bufs[] = {
@@ -2180,7 +2244,7 @@ void wxZipOutputStream::CreatePendingEntry(const void *buffer, size_t size)
 void wxZipOutputStream::CreatePendingEntry()
 {
     wxASSERT(IsOk() && m_pending && !m_comp);
-    wx__ZipEntryPtr spPending(m_pending);
+    wxZipEntryPtr_ spPending(m_pending);
     m_pending = NULL;
     m_lasterror = wxSTREAM_WRITE_ERROR;
 
@@ -2234,8 +2298,12 @@ bool wxZipOutputStream::Close()
 {
     CloseEntry();
 
-    if (m_lasterror == wxSTREAM_WRITE_ERROR || m_entries.size() == 0)
+    if (m_lasterror == wxSTREAM_WRITE_ERROR
+        || (m_entries.size() == 0 && m_endrecWritten))
+    {
+        wxFilterOutputStream::Close();
         return false;
+    }
 
     wxZipEndRec endrec;
 
@@ -2244,7 +2312,7 @@ bool wxZipOutputStream::Close()
     endrec.SetOffset(m_headerOffset);
     endrec.SetComment(m_Comment);
 
-    wx__ZipEntryList::iterator it;
+    wxZipEntryList_::iterator it;
     wxFileOffset size = 0;
 
     for (it = m_entries.begin(); it != m_entries.end(); ++it) {
@@ -2257,7 +2325,9 @@ bool wxZipOutputStream::Close()
     endrec.Write(*m_parent_o_stream, GetConv());
 
     m_lasterror = m_parent_o_stream->GetLastError();
-    if (!IsOk())
+    m_endrecWritten = true;
+
+    if (!wxFilterOutputStream::Close() || !IsOk())
         return false;
     m_lasterror = wxSTREAM_EOF;
     return true;
@@ -2366,4 +2436,4 @@ size_t wxZipOutputStream::OnSysWrite(const void *buffer, size_t size)
     return m_comp->LastWrite();
 }
 
-#endif // wxUSE_ZLIB && wxUSE_STREAMS && wxUSE_ZIPSTREAM
+#endif // wxUSE_ZIPSTREAM