~ubuntu-branches/ubuntu/oneiric/strigi/oneiric

« back to all changes in this revision

Viewing changes to src/streams/zipinputstream.cpp

  • Committer: Package Import Robot
  • Author(s): Fathi Boudra
  • Date: 2011-09-20 08:50:25 UTC
  • mto: (1.1.20 upstream) (5.1.6 sid)
  • mto: This revision was merged to the branch mainline in revision 44.
  • Revision ID: package-import@ubuntu.com-20110920085025-wszfu6x8rshrjq0e
ImportĀ upstreamĀ versionĀ 0.7.6

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* This file is part of Strigi Desktop Search
2
 
 *
3
 
 * Copyright (C) 2006 Jos van den Oever <jos@vandenoever.info>
4
 
 *
5
 
 * This library is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU Library General Public
7
 
 * License as published by the Free Software Foundation; either
8
 
 * version 2 of the License, or (at your option) any later version.
9
 
 *
10
 
 * This library is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 
 * Library General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU Library General Public License
16
 
 * along with this library; see the file COPYING.LIB.  If not, write to
17
 
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18
 
 * Boston, MA 02110-1301, USA.
19
 
 */
20
 
 
21
 
#include "zipinputstream.h"
22
 
#include <strigi/strigiconfig.h>
23
 
#include "gzipinputstream.h"
24
 
#include "subinputstream.h"
25
 
 
26
 
#include "dostime.h"
27
 
#include "textutils.h"
28
 
#include <cstring>
29
 
 
30
 
 
31
 
using namespace Strigi;
32
 
 
33
 
bool
34
 
ZipInputStream::checkHeader(const char* data, int32_t datasize) {
35
 
    static const char magic[] = {0x50, 0x4b, 0x03, 0x04};
36
 
    if (datasize < 4) return false;
37
 
    bool ok = std::memcmp(data, magic, 4) == 0 && datasize > 8;
38
 
    return ok;
39
 
}
40
 
ZipInputStream::ZipInputStream(InputStream* input)
41
 
        : SubStreamProvider(input) {
42
 
    compressedEntryStream = 0;
43
 
    uncompressionStream = 0;
44
 
}
45
 
ZipInputStream::~ZipInputStream() {
46
 
    if (compressedEntryStream) {
47
 
        delete compressedEntryStream;
48
 
    }
49
 
    if (uncompressionStream) {
50
 
        delete uncompressionStream;
51
 
    }
52
 
}
53
 
InputStream*
54
 
ZipInputStream::nextEntry() {
55
 
    if (m_status) return 0;
56
 
    // clean up the last stream(s)
57
 
    if (m_entrystream) {
58
 
        // if this entry is a compressed entry of know size, we can skip to
59
 
        // the end by skipping in the compressed stream, without decompressing
60
 
        if (compressedEntryStream) {
61
 
            compressedEntryStream->skip(compressedEntryStream->size());
62
 
            delete compressedEntryStream;
63
 
            compressedEntryStream = 0;
64
 
            delete uncompressionStream;
65
 
            uncompressionStream = 0;
66
 
 
67
 
            // check for a potential signature and skip it if it is there
68
 
            const char* c;
69
 
            int64_t p = m_input->position();
70
 
            int32_t n = m_input->read(c, 16, 16);
71
 
            if (n == 16) {
72
 
                n = readLittleEndianUInt32((const unsigned char*)c);
73
 
                if (n != 0x08074b50) {
74
 
                    m_input->reset(p);
75
 
                }
76
 
            }
77
 
        } else {
78
 
            int64_t size = m_entrystream->size();
79
 
            if (size < 1) {
80
 
                size = 1024;
81
 
            }
82
 
            while (m_entrystream->status() == Ok) {
83
 
                m_entrystream->skip(size);
84
 
            }
85
 
            if (m_entryinfo.size < 0) {
86
 
                // skip the data descriptor that occurs after the data
87
 
                const char* c;
88
 
                int32_t n = m_input->read(c, 4, 4);
89
 
                if (n == 4) {
90
 
                    n = readLittleEndianUInt32((const unsigned char*)c);
91
 
                    if (n == 0x08074b50) { // sometimes this signature appears
92
 
                        n = m_input->read(c, 12, 12);
93
 
                        n -= 8;
94
 
                    } else {
95
 
                        n = m_input->read(c, 8, 8);
96
 
                        n -= 4;
97
 
                    }
98
 
                }
99
 
                if (n != 4) {
100
 
                    m_status = Error;
101
 
                    m_error = "No valid data descriptor after entry data.";
102
 
                    return 0;
103
 
                }
104
 
            }
105
 
        }
106
 
        delete m_entrystream;
107
 
        m_entrystream = 0;
108
 
    }
109
 
    // are we at the end of the zip file?
110
 
    if (m_input->status() == Eof) {
111
 
        m_status = Eof;
112
 
        return 0;
113
 
    }
114
 
    readHeader();
115
 
    if (m_status) return 0;
116
 
    if (compressionMethod == 8) {
117
 
        if (m_entryinfo.size >= 0) {
118
 
            compressedEntryStream
119
 
                = new SubInputStream(m_input, entryCompressedSize);
120
 
            if (uncompressionStream) {
121
 
                delete uncompressionStream;
122
 
            }
123
 
            uncompressionStream = new GZipInputStream(compressedEntryStream,
124
 
                GZipInputStream::ZIPFORMAT);
125
 
            m_entrystream
126
 
                = new SubInputStream(uncompressionStream, m_entryinfo.size);
127
 
        } else {
128
 
            m_entrystream = new GZipInputStream(m_input,
129
 
                GZipInputStream::ZIPFORMAT);
130
 
        }
131
 
    } else {
132
 
        m_entrystream = new SubInputStream(m_input, m_entryinfo.size);
133
 
    }
134
 
    return m_entrystream;
135
 
}
136
 
void
137
 
ZipInputStream::readHeader() {
138
 
    const unsigned char *hb;
139
 
    const char *b;
140
 
    int32_t toread;
141
 
    int32_t nread;
142
 
 
143
 
    // read the first 30 characters
144
 
    toread = 30;
145
 
    nread = m_input->read(b, toread, toread);
146
 
    if (nread != toread) {
147
 
        m_error = "Error reading zip header: ";
148
 
        if (nread == -1) {
149
 
            m_error += m_input->error();
150
 
        } else {
151
 
            m_error += " premature end of file.";
152
 
        }
153
 
        m_status = Error;
154
 
        fprintf(stderr, "%s\n", m_error.c_str());
155
 
        return;
156
 
    }
157
 
    hb = (const unsigned char*)b;
158
 
    // check the signature
159
 
    // check the first half of the signature
160
 
    if (hb[0] != 0x50 || hb[1] != 0x4b) {
161
 
        // signature is invalid
162
 
        m_status = Error;
163
 
        m_error = "Error: wrong zip signature.";
164
 
        return;
165
 
    }
166
 
    // check the second half of the signature
167
 
    if (hb[2] != 0x03 || hb[3] != 0x04) {
168
 
        // this may be the start of the central file header
169
 
        if (hb[2] != 0x01 || hb[3] != 0x02) {
170
 
            fprintf(stderr, "This code in a zip file is strange: %x %x %x %x\n",
171
 
                hb[0], hb[1], hb[2], hb[3]);
172
 
        }
173
 
        m_status = Eof;
174
 
        return;
175
 
    }
176
 
    // read 2 bytes into the filename size
177
 
    int32_t filenamelen = readLittleEndianUInt16(hb + 26);
178
 
    int64_t extralen = readLittleEndianUInt16(hb + 28);
179
 
    // read 4 bytes into the length of the uncompressed size
180
 
    m_entryinfo.size = readLittleEndianUInt32(hb + 22);
181
 
    // read 4 bytes into the length of the compressed size
182
 
    entryCompressedSize = readLittleEndianUInt32(hb + 18);
183
 
    if (entryCompressedSize < 0) {
184
 
        m_status = Error;
185
 
        m_error = "Corrupt zip file with negative compressed size.";
186
 
        return;
187
 
    }
188
 
    compressionMethod = readLittleEndianUInt16(hb + 8);
189
 
    int32_t generalBitFlags = readLittleEndianUInt16(hb+6);
190
 
    if (generalBitFlags & 8) { // is bit 3 set?
191
 
        // ohoh, the file size and compressed file size are unknown at this
192
 
        // point
193
 
        // if the file is compressed with method 8 we rely on the decompression
194
 
        // stream to signal the end of the stream properly
195
 
        if (compressionMethod != 8) {
196
 
            m_status = Error;
197
 
            m_error = "This particular zip file format is not supported for "
198
 
                "reading as a stream.";
199
 
            return;
200
 
        }
201
 
        m_entryinfo.size = -1;
202
 
        entryCompressedSize = -1;
203
 
    }
204
 
    unsigned long dost = readLittleEndianUInt32(hb+10);
205
 
    m_entryinfo.mtime = dos2unixtime(dost);
206
 
 
207
 
    readFileName(filenamelen);
208
 
    if (m_status) {
209
 
        m_status = Error;
210
 
        m_error = "Error reading file name: ";
211
 
        m_error += m_input->error();
212
 
        return;
213
 
    }
214
 
    // read 2 bytes into the length of the extra field
215
 
    int64_t skipped = m_input->skip(extralen);
216
 
    if (skipped != extralen) {
217
 
        m_status = Error;
218
 
//      printf("skipped %li extralen %li position: %li size: %li\n", skipped, extralen, m_input->position(), m_input->size());
219
 
        m_error = "Error skipping extra field: ";
220
 
        m_error += m_input->error();
221
 
        return;
222
 
    }
223
 
}
224
 
void
225
 
ZipInputStream::readFileName(int32_t len) {
226
 
    m_entryinfo.filename.resize(0);
227
 
    const char *begin;
228
 
    int32_t nread = m_input->read(begin, len, len);
229
 
    if (nread != len) {
230
 
        m_error = "Error reading filename: ";
231
 
        if (nread == -1) {
232
 
            m_error += m_input->error();
233
 
        } else {
234
 
            m_error += " premature end of file.";
235
 
        }
236
 
        return;
237
 
    }
238
 
    m_entryinfo.filename.assign(begin, nread);
239
 
 
240
 
    // temporary hack for determining if this is a directory:
241
 
    // does the filename end in '/'?
242
 
    len = (int32_t)m_entryinfo.filename.length();
243
 
    if (m_entryinfo.filename[len-1] == '/') {
244
 
        m_entryinfo.filename.resize(len-1);
245
 
        m_entryinfo.type = EntryInfo::Dir;
246
 
    } else {
247
 
        m_entryinfo.type = EntryInfo::File;
248
 
    }
249
 
}