~ubuntu-branches/ubuntu/saucy/libwpd/saucy-proposed

« back to all changes in this revision

Viewing changes to src/lib/WPXZipStream.cpp

  • Committer: Package Import Robot
  • Author(s): Rene Engelhard
  • Date: 2013-04-19 00:47:04 UTC
  • mfrom: (11.1.7 experimental)
  • Revision ID: package-import@ubuntu.com-20130419004704-j04jp8dan8plg630
Tags: 0.9.7-2
upload to unstable 

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
 
2
/* libwpd
 
3
 * Version: MPL 2.0 / LGPLv2.1+
 
4
 *
 
5
 * This Source Code Form is subject to the terms of the Mozilla Public
 
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
8
 *
 
9
 * Major Contributor(s):
 
10
 * Copyright (C) 2012 Fridrich Strba (fridrich.strba@bluewin.ch)
 
11
 *
 
12
 * For minor contributions see the git repository.
 
13
 *
 
14
 * Alternatively, the contents of this file may be used under the terms
 
15
 * of the GNU Lesser General Public License Version 2.1 or later
 
16
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
 
17
 * applicable instead of those above.
 
18
 *
 
19
 * For further information visit http://libwpd.sourceforge.net
 
20
 */
 
21
 
 
22
#ifdef HAVE_CONFIG_H
 
23
#include "config.h"
 
24
#endif
 
25
 
 
26
#ifdef BUILD_ZIP_STREAM
 
27
 
 
28
#include <string>
 
29
#include <string.h>
 
30
#include <stdio.h>
 
31
#include <zlib.h>
 
32
#include "WPXZipStream.h"
 
33
#include "WPXStreamImplementation.h"
 
34
 
 
35
namespace
 
36
{
 
37
class StreamException
 
38
{
 
39
};
 
40
 
 
41
struct LocalFileHeader
 
42
{
 
43
        unsigned short min_version;
 
44
        unsigned short general_flag;
 
45
        unsigned short compression;
 
46
        unsigned short lastmod_time;
 
47
        unsigned short lastmod_date;
 
48
        unsigned crc32;
 
49
        unsigned compressed_size;
 
50
        unsigned uncompressed_size;
 
51
        unsigned short filename_size;
 
52
        unsigned short extra_field_size;
 
53
        std::string filename;
 
54
        std::string extra_field;
 
55
        LocalFileHeader()
 
56
                : min_version(0), general_flag(0), compression(0), lastmod_time(0), lastmod_date(0),
 
57
                  crc32(0), compressed_size(0), uncompressed_size(0), filename_size(0), extra_field_size(0),
 
58
                  filename(), extra_field() {}
 
59
        ~LocalFileHeader() {}
 
60
};
 
61
 
 
62
struct CentralDirectoryEntry
 
63
{
 
64
        unsigned short creator_version;
 
65
        unsigned short min_version;
 
66
        unsigned short general_flag;
 
67
        unsigned short compression;
 
68
        unsigned short lastmod_time;
 
69
        unsigned short lastmod_date;
 
70
        unsigned crc32;
 
71
        unsigned compressed_size;
 
72
        unsigned uncompressed_size;
 
73
        unsigned short filename_size;
 
74
        unsigned short extra_field_size;
 
75
        unsigned short file_comment_size;
 
76
        unsigned short disk_num;
 
77
        unsigned short internal_attr;
 
78
        unsigned external_attr;
 
79
        unsigned offset;
 
80
        std::string filename;
 
81
        std::string extra_field;
 
82
        std::string file_comment;
 
83
        CentralDirectoryEntry()
 
84
                : creator_version(0), min_version(0), general_flag(0), compression(0), lastmod_time(0),
 
85
                  lastmod_date(0), crc32(0), compressed_size(0), uncompressed_size(0), filename_size(0),
 
86
                  extra_field_size(0), file_comment_size(0), disk_num(0), internal_attr(0),
 
87
                  external_attr(0), offset(0), filename(), extra_field(), file_comment() {}
 
88
        ~CentralDirectoryEntry() {}
 
89
};
 
90
 
 
91
struct CentralDirectoryEnd
 
92
{
 
93
        unsigned short disk_num;
 
94
        unsigned short cdir_disk;
 
95
        unsigned short disk_entries;
 
96
        unsigned short cdir_entries;
 
97
        unsigned cdir_size;
 
98
        unsigned cdir_offset;
 
99
        unsigned short comment_size;
 
100
        std::string comment;
 
101
        CentralDirectoryEnd()
 
102
                : disk_num(0), cdir_disk(0), disk_entries(0), cdir_entries(0),
 
103
                  cdir_size(0), cdir_offset(0), comment_size(0), comment() {}
 
104
        ~CentralDirectoryEnd() {}
 
105
};
 
106
 
 
107
#define CDIR_ENTRY_SIG 0x02014b50
 
108
#define LOC_FILE_HEADER_SIG 0x04034b50
 
109
#define CDIR_END_SIG 0x06054b50
 
110
 
 
111
static unsigned char getByte(WPXInputStream *input)
 
112
{
 
113
        unsigned long numBytesRead = 0;
 
114
        const unsigned char *ret = input->read(1, numBytesRead);
 
115
        if (numBytesRead != 1)
 
116
                throw StreamException();
 
117
        return ret[0];
 
118
}
 
119
 
 
120
static unsigned short getShort(WPXInputStream *input)
 
121
{
 
122
        unsigned long numBytesRead = 0;
 
123
        const unsigned char *ret = input->read(2, numBytesRead);
 
124
        if (numBytesRead != 2)
 
125
                throw StreamException();
 
126
        return (unsigned short)(ret[0]|((unsigned short)ret[1]<<8));
 
127
}
 
128
 
 
129
static unsigned getInt(WPXInputStream *input)
 
130
{
 
131
        unsigned long numBytesRead = 0;
 
132
        const unsigned char *ret = input->read(4, numBytesRead);
 
133
        if (numBytesRead != 4)
 
134
                throw StreamException();
 
135
        return (unsigned)(ret[0]|((unsigned)ret[1]<<8)|((unsigned)ret[2]<<16)|((unsigned)ret[3]<<24));
 
136
}
 
137
 
 
138
static bool readCentralDirectoryEnd(WPXInputStream *input, CentralDirectoryEnd &end)
 
139
{
 
140
        try
 
141
        {
 
142
                unsigned signature = getInt(input);
 
143
                if (signature != CDIR_END_SIG)
 
144
                        return false;
 
145
 
 
146
                end.disk_num = getShort(input);
 
147
                end.cdir_disk = getShort(input);
 
148
                end.disk_entries = getShort(input);
 
149
                end.cdir_entries = getShort(input);
 
150
                end.cdir_size = getInt(input);
 
151
                end.cdir_offset = getInt(input);
 
152
                end.comment_size = getShort(input);
 
153
                end.comment.clear();
 
154
                for (unsigned short i = 0; i < end.comment_size; i++)
 
155
                        end.comment.append(1, (char)getByte(input));
 
156
        }
 
157
        catch (...)
 
158
        {
 
159
                return false;
 
160
        }
 
161
        return true;
 
162
}
 
163
 
 
164
static bool readCentralDirectoryEntry(WPXInputStream *input, CentralDirectoryEntry &entry)
 
165
{
 
166
        try
 
167
        {
 
168
                unsigned signature = getInt(input);
 
169
                if (signature != CDIR_ENTRY_SIG)
 
170
                        return false;
 
171
 
 
172
                entry.creator_version = getShort(input);
 
173
                entry.min_version = getShort(input);
 
174
                entry.general_flag = getShort(input);
 
175
                entry.compression = getShort(input);
 
176
                entry.lastmod_time = getShort(input);
 
177
                entry.lastmod_date = getShort(input);
 
178
                entry.crc32 = getInt(input);
 
179
                entry.compressed_size = getInt(input);
 
180
                entry.uncompressed_size = getInt(input);
 
181
                entry.filename_size = getShort(input);
 
182
                entry.extra_field_size = getShort(input);
 
183
                entry.file_comment_size = getShort(input);
 
184
                entry.disk_num = getShort(input);
 
185
                entry.internal_attr = getShort(input);
 
186
                entry.external_attr = getInt(input);
 
187
                entry.offset = getInt(input);
 
188
                unsigned short i = 0;
 
189
                entry.filename.clear();
 
190
                for (i=0; i < entry.filename_size; i++)
 
191
                        entry.filename.append(1, (char)getByte(input));
 
192
                entry.extra_field.clear();
 
193
                for (i=0; i < entry.extra_field_size; i++)
 
194
                        entry.extra_field.append(1, (char)getByte(input));
 
195
                entry.file_comment.clear();
 
196
                for (i=0; i < entry.file_comment_size; i++)
 
197
                        entry.file_comment.append(1, (char)getByte(input));
 
198
        }
 
199
        catch (...)
 
200
        {
 
201
                return false;
 
202
        }
 
203
        return true;
 
204
}
 
205
 
 
206
static bool readLocalFileHeader(WPXInputStream *input, LocalFileHeader &header)
 
207
{
 
208
        try
 
209
        {
 
210
                unsigned signature = getInt(input);
 
211
                if (signature != LOC_FILE_HEADER_SIG)
 
212
                        return false;
 
213
 
 
214
                header.min_version = getShort(input);
 
215
                header.general_flag = getShort(input);
 
216
                header.compression = getShort(input);
 
217
                header.lastmod_time = getShort(input);
 
218
                header.lastmod_date = getShort(input);
 
219
                header.crc32 = getInt(input);
 
220
                header.compressed_size = getInt(input);
 
221
                header.uncompressed_size = getInt(input);
 
222
                header.filename_size = getShort(input);
 
223
                header.extra_field_size = getShort(input);
 
224
                unsigned short i = 0;
 
225
                header.filename.clear();
 
226
                for (i=0; i < header.filename_size; i++)
 
227
                        header.filename.append(1, (char)getByte(input));
 
228
                header.extra_field.clear();
 
229
                for (i=0; i < header.extra_field_size; i++)
 
230
                        header.extra_field.append(1, (char)getByte(input));
 
231
        }
 
232
        catch (...)
 
233
        {
 
234
                return false;
 
235
        }
 
236
        return true;
 
237
}
 
238
 
 
239
static bool areHeadersConsistent(const LocalFileHeader &header, const CentralDirectoryEntry &entry)
 
240
{
 
241
        if (header.min_version != entry.min_version)
 
242
                return false;
 
243
        if (header.general_flag != entry.general_flag)
 
244
                return false;
 
245
        if (header.compression != entry.compression)
 
246
                return false;
 
247
        if (!(header.general_flag & 0x08))
 
248
        {
 
249
                if (header.crc32 != entry.crc32)
 
250
                        return false;
 
251
                if (header.compressed_size != entry.compressed_size)
 
252
                        return false;
 
253
                if (header.uncompressed_size != entry.uncompressed_size)
 
254
                        return false;
 
255
        }
 
256
        return true;
 
257
}
 
258
 
 
259
static bool findCentralDirectoryEnd(WPXInputStream *input)
 
260
{
 
261
        if (input->seek(-1024, WPX_SEEK_END))
 
262
                input->seek(0, WPX_SEEK_SET);
 
263
 
 
264
        try
 
265
        {
 
266
                while (!input->atEOS())
 
267
                {
 
268
                        unsigned signature = getInt(input);
 
269
                        if (signature == CDIR_END_SIG)
 
270
                        {
 
271
                                input->seek(-4, WPX_SEEK_CUR);
 
272
                                return true;
 
273
                        }
 
274
                        else
 
275
                                input->seek(-3, WPX_SEEK_CUR);
 
276
                }
 
277
        }
 
278
        catch (...)
 
279
        {
 
280
                return false;
 
281
        }
 
282
        return false;
 
283
}
 
284
 
 
285
static bool findDataStream(WPXInputStream *input, CentralDirectoryEntry &entry, const char *name)
 
286
{
 
287
        size_t name_size = strlen(name);
 
288
        if (!findCentralDirectoryEnd(input))
 
289
                return false;
 
290
        CentralDirectoryEnd end;
 
291
        if (!readCentralDirectoryEnd(input, end))
 
292
                return false;
 
293
        input->seek(end.cdir_offset, WPX_SEEK_SET);
 
294
        while (!input->atEOS() && (unsigned)input->tell() < end.cdir_offset + end.cdir_size)
 
295
        {
 
296
                if (!readCentralDirectoryEntry(input, entry))
 
297
                        return false;
 
298
                if (name_size == entry.filename_size && entry.filename == name)
 
299
                        break;
 
300
        }
 
301
        if (name_size != entry.filename_size)
 
302
                return false;
 
303
        if (entry.filename != name)
 
304
                return false;
 
305
        input->seek(entry.offset, WPX_SEEK_SET);
 
306
        LocalFileHeader header;
 
307
        if (!readLocalFileHeader(input, header))
 
308
                return false;
 
309
        if (!areHeadersConsistent(header, entry))
 
310
                return false;
 
311
        return true;
 
312
}
 
313
 
 
314
} // anonymous namespace
 
315
 
 
316
bool WPXZipStream::isZipFile(WPXInputStream *input)
 
317
{
 
318
        // look for central directory end
 
319
        if (!findCentralDirectoryEnd(input))
 
320
                return false;
 
321
        CentralDirectoryEnd end;
 
322
        if (!readCentralDirectoryEnd(input, end))
 
323
                return false;
 
324
        input->seek(end.cdir_offset, WPX_SEEK_SET);
 
325
        // read first entry in the central directory
 
326
        CentralDirectoryEntry entry;
 
327
        if (!readCentralDirectoryEntry(input, entry))
 
328
                return false;
 
329
        input->seek(entry.offset, WPX_SEEK_SET);
 
330
        // read the local file header and compare with the central directory information
 
331
        LocalFileHeader header;
 
332
        if (!readLocalFileHeader(input, header))
 
333
                return false;
 
334
        if (!areHeadersConsistent(header, entry))
 
335
                return false;
 
336
        return true;
 
337
}
 
338
 
 
339
WPXInputStream *WPXZipStream::getSubstream(WPXInputStream *input, const char *name)
 
340
{
 
341
        CentralDirectoryEntry entry;
 
342
        if (!findDataStream(input, entry, name))
 
343
                return 0;
 
344
        if (!entry.compressed_size)
 
345
                return 0;
 
346
        unsigned long numBytesRead = 0;
 
347
        unsigned char *compressedData = const_cast<unsigned char *>(input->read(entry.compressed_size, numBytesRead));
 
348
        if (numBytesRead != entry.compressed_size)
 
349
                return 0;
 
350
        if (!entry.compression)
 
351
                return new WPXStringStream(compressedData, (unsigned)numBytesRead);
 
352
        else
 
353
        {
 
354
                int ret;
 
355
                z_stream strm;
 
356
 
 
357
                /* allocate inflate state */
 
358
                strm.zalloc = Z_NULL;
 
359
                strm.zfree = Z_NULL;
 
360
                strm.opaque = Z_NULL;
 
361
                strm.avail_in = 0;
 
362
                strm.next_in = Z_NULL;
 
363
                ret = inflateInit2(&strm,-MAX_WBITS);
 
364
                if (ret != Z_OK)
 
365
                        return 0;
 
366
 
 
367
                strm.avail_in = (unsigned)numBytesRead;
 
368
                strm.next_in = (Bytef *)compressedData;
 
369
 
 
370
                std::vector<unsigned char>data(entry.uncompressed_size);
 
371
 
 
372
                strm.avail_out = entry.uncompressed_size;
 
373
                strm.next_out = reinterpret_cast<Bytef *>(&data[0]);
 
374
                ret = inflate(&strm, Z_FINISH);
 
375
                switch (ret)
 
376
                {
 
377
                case Z_NEED_DICT:
 
378
                case Z_DATA_ERROR:
 
379
                case Z_MEM_ERROR:
 
380
                        (void)inflateEnd(&strm);
 
381
                        data.clear();
 
382
                        return 0;
 
383
                default:
 
384
                        break;
 
385
                }
 
386
                (void)inflateEnd(&strm);
 
387
                return new WPXStringStream(&data[0], (unsigned)data.size());
 
388
        }
 
389
}
 
390
 
 
391
#endif /* BUILD_ZIP_STREAM */
 
392
 
 
393
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */