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

« back to all changes in this revision

Viewing changes to libstreams/lib/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 <strigi/zipinputstream.h>
 
22
#include <strigi/strigiconfig.h>
 
23
#include <strigi/gzipinputstream.h>
 
24
#include <strigi/subinputstream.h>
 
25
 
 
26
#include "dostime.h"
 
27
#include <strigi/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 NULL;
 
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 NULL;
 
113
    }
 
114
    readHeader();
 
115
    if (m_status != Ok) return NULL;
 
116
    if (m_entryinfo.filename.length()<=0) {
 
117
        m_status = Error;
 
118
        m_error = "Archived file name is empty";
 
119
        return NULL;
 
120
    }    
 
121
    if (compressionMethod == 8) {
 
122
        if (m_entryinfo.size >= 0) {
 
123
            compressedEntryStream
 
124
                = new SubInputStream(m_input, entryCompressedSize);
 
125
            if (uncompressionStream) {
 
126
                delete uncompressionStream;
 
127
            }
 
128
            uncompressionStream = new GZipInputStream(compressedEntryStream,
 
129
                GZipInputStream::ZIPFORMAT);
 
130
            m_entrystream
 
131
                = new SubInputStream(uncompressionStream, m_entryinfo.size);
 
132
        } else {
 
133
            m_entrystream = new GZipInputStream(m_input,
 
134
                GZipInputStream::ZIPFORMAT);
 
135
        }
 
136
    } else {
 
137
        m_entrystream = new SubInputStream(m_input, m_entryinfo.size);
 
138
    }
 
139
    return m_entrystream;
 
140
}
 
141
void
 
142
ZipInputStream::readHeader() {
 
143
    const unsigned char *hb;
 
144
    const char *b;
 
145
    int32_t toread;
 
146
    int32_t nread;
 
147
 
 
148
    // read the first 30 characters
 
149
    toread = 30;
 
150
    nread = m_input->read(b, toread, toread);
 
151
    if (nread != toread) {
 
152
        m_error = "Error reading zip header: ";
 
153
        if (nread == -1) {
 
154
            m_error += m_input->error();
 
155
        } else {
 
156
            m_error += " premature end of file.";
 
157
        }
 
158
        m_status = Error;
 
159
        fprintf(stderr, "%s\n", m_error.c_str());
 
160
        return;
 
161
    }
 
162
    hb = (const unsigned char*)b;
 
163
    // check the signature
 
164
    // check the first half of the signature
 
165
    if (hb[0] != 0x50 || hb[1] != 0x4b) {
 
166
        // signature is invalid
 
167
        m_status = Error;
 
168
        m_error = "Error: wrong zip signature.";
 
169
        return;
 
170
    }
 
171
    // check the second half of the signature
 
172
    if (hb[2] != 0x03 || hb[3] != 0x04) {
 
173
        // this may be the start of the central file header
 
174
        if (hb[2] != 0x01 || hb[3] != 0x02) {
 
175
            fprintf(stderr, "This code in a zip file is strange: %x %x %x %x\n",
 
176
                hb[0], hb[1], hb[2], hb[3]);
 
177
        }
 
178
        m_status = Eof;
 
179
        return;
 
180
    }
 
181
    // read 2 bytes into the filename size
 
182
    int32_t filenamelen = readLittleEndianUInt16(hb + 26);
 
183
    int64_t extralen = readLittleEndianUInt16(hb + 28);
 
184
    // read 4 bytes into the length of the uncompressed size
 
185
    m_entryinfo.size = readLittleEndianUInt32(hb + 22);
 
186
    // read 4 bytes into the length of the compressed size
 
187
    entryCompressedSize = readLittleEndianUInt32(hb + 18);
 
188
    if (entryCompressedSize < 0) {
 
189
        m_status = Error;
 
190
        m_error = "Corrupt zip file with negative compressed size.";
 
191
        return;
 
192
    }
 
193
    compressionMethod = readLittleEndianUInt16(hb + 8);
 
194
    int32_t generalBitFlags = readLittleEndianUInt16(hb+6);
 
195
    if (generalBitFlags & 8) { // is bit 3 set?
 
196
        // ohoh, the file size and compressed file size are unknown at this
 
197
        // point
 
198
        // if the file is compressed with method 8 we rely on the decompression
 
199
        // stream to signal the end of the stream properly
 
200
        if (compressionMethod != 8) {
 
201
            m_status = Error;
 
202
            m_error = "This particular zip file format is not supported for "
 
203
                "reading as a stream.";
 
204
            return;
 
205
        }
 
206
        m_entryinfo.size = -1;
 
207
        entryCompressedSize = -1;
 
208
    }
 
209
    unsigned long dost = readLittleEndianUInt32(hb+10);
 
210
    m_entryinfo.mtime = dos2unixtime(dost);
 
211
 
 
212
    readFileName(filenamelen);
 
213
    if (m_status) {
 
214
        m_status = Error;
 
215
        m_error = "Error reading file name: ";
 
216
        m_error += m_input->error();
 
217
        return;
 
218
    }
 
219
    // read 2 bytes into the length of the extra field
 
220
    int64_t skipped = m_input->skip(extralen);
 
221
    if (skipped != extralen) {
 
222
        m_status = Error;
 
223
//      printf("skipped %li extralen %li position: %li size: %li\n", skipped, extralen, m_input->position(), m_input->size());
 
224
        m_error = "Error skipping extra field: ";
 
225
        m_error += m_input->error();
 
226
        return;
 
227
    }
 
228
}
 
229
void
 
230
ZipInputStream::readFileName(int32_t len) {
 
231
    m_entryinfo.filename.resize(0);
 
232
    const char *begin;
 
233
    int32_t nread = m_input->read(begin, len, len);
 
234
    if (nread != len) {
 
235
        m_error = "Error reading filename: ";
 
236
        if (nread == -1) {
 
237
            m_error += m_input->error();
 
238
        } else {
 
239
            m_error += " premature end of file.";
 
240
        }
 
241
        return;
 
242
    }
 
243
    m_entryinfo.filename.assign(begin, nread);
 
244
 
 
245
    // temporary hack for determining if this is a directory:
 
246
    // does the filename end in '/'?
 
247
    len = (int32_t)m_entryinfo.filename.length();
 
248
    if (m_entryinfo.filename[len-1] == '/') {
 
249
        m_entryinfo.filename.resize(len-1);
 
250
        m_entryinfo.type = EntryInfo::Dir;
 
251
    } else {
 
252
        m_entryinfo.type = EntryInfo::File;
 
253
    }
 
254
}