summaryrefslogtreecommitdiff
path: root/rbutil/rbutilqt/zip
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/rbutilqt/zip')
-rw-r--r--rbutil/rbutilqt/zip/LICENSE.GPL341
-rw-r--r--rbutil/rbutilqt/zip/README68
-rw-r--r--rbutil/rbutilqt/zip/README.ROCKBOX7
-rw-r--r--rbutil/rbutilqt/zip/unzip.cpp1360
-rw-r--r--rbutil/rbutilqt/zip/unzip.h144
-rw-r--r--rbutil/rbutilqt/zip/unzip_p.h112
-rw-r--r--rbutil/rbutilqt/zip/zip.cpp1219
-rw-r--r--rbutil/rbutilqt/zip/zip.h113
-rw-r--r--rbutil/rbutilqt/zip/zip_p.h93
-rw-r--r--rbutil/rbutilqt/zip/zipentry_p.h78
10 files changed, 3535 insertions, 0 deletions
diff --git a/rbutil/rbutilqt/zip/LICENSE.GPL b/rbutil/rbutilqt/zip/LICENSE.GPL
new file mode 100644
index 0000000000..40c8c38e9c
--- /dev/null
+++ b/rbutil/rbutilqt/zip/LICENSE.GPL
@@ -0,0 +1,341 @@
1 GNU GENERAL PUBLIC LICENSE
2 Version 2, June 1991
3
4 Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
6 Everyone is permitted to copy and distribute verbatim copies
7 of this license document, but changing it is not allowed.
8
9 Preamble
10
11 The licenses for most software are designed to take away your
12freedom to share and change it. By contrast, the GNU General Public
13License is intended to guarantee your freedom to share and change free
14software--to make sure the software is free for all its users. This
15General Public License applies to most of the Free Software
16Foundation's software and to any other program whose authors commit to
17using it. (Some other Free Software Foundation software is covered by
18the GNU Library General Public License instead.) You can apply it to
19your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22price. Our General Public Licenses are designed to make sure that you
23have the freedom to distribute copies of free software (and charge for
24this service if you wish), that you receive source code or can get it
25if you want it, that you can change the software or use pieces of it
26in new free programs; and that you know you can do these things.
27
28 To protect your rights, we need to make restrictions that forbid
29anyone to deny you these rights or to ask you to surrender the rights.
30These restrictions translate to certain responsibilities for you if you
31distribute copies of the software, or if you modify it.
32
33 For example, if you distribute copies of such a program, whether
34gratis or for a fee, you must give the recipients all the rights that
35you have. You must make sure that they, too, receive or can get the
36source code. And you must show them these terms so they know their
37rights.
38
39 We protect your rights with two steps: (1) copyright the software, and
40(2) offer you this license which gives you legal permission to copy,
41distribute and/or modify the software.
42
43 Also, for each author's protection and ours, we want to make certain
44that everyone understands that there is no warranty for this free
45software. If the software is modified by someone else and passed on, we
46want its recipients to know that what they have is not the original, so
47that any problems introduced by others will not reflect on the original
48authors' reputations.
49
50 Finally, any free program is threatened constantly by software
51patents. We wish to avoid the danger that redistributors of a free
52program will individually obtain patent licenses, in effect making the
53program proprietary. To prevent this, we have made it clear that any
54patent must be licensed for everyone's free use or not licensed at all.
55
56 The precise terms and conditions for copying, distribution and
57modification follow.
58
59 GNU GENERAL PUBLIC LICENSE
60 TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
62 0. This License applies to any program or other work which contains
63a notice placed by the copyright holder saying it may be distributed
64under the terms of this General Public License. The "Program", below,
65refers to any such program or work, and a "work based on the Program"
66means either the Program or any derivative work under copyright law:
67that is to say, a work containing the Program or a portion of it,
68either verbatim or with modifications and/or translated into another
69language. (Hereinafter, translation is included without limitation in
70the term "modification".) Each licensee is addressed as "you".
71
72Activities other than copying, distribution and modification are not
73covered by this License; they are outside its scope. The act of
74running the Program is not restricted, and the output from the Program
75is covered only if its contents constitute a work based on the
76Program (independent of having been made by running the Program).
77Whether that is true depends on what the Program does.
78
79 1. You may copy and distribute verbatim copies of the Program's
80source code as you receive it, in any medium, provided that you
81conspicuously and appropriately publish on each copy an appropriate
82copyright notice and disclaimer of warranty; keep intact all the
83notices that refer to this License and to the absence of any warranty;
84and give any other recipients of the Program a copy of this License
85along with the Program.
86
87You may charge a fee for the physical act of transferring a copy, and
88you may at your option offer warranty protection in exchange for a fee.
89
90 2. You may modify your copy or copies of the Program or any portion
91of it, thus forming a work based on the Program, and copy and
92distribute such modifications or work under the terms of Section 1
93above, provided that you also meet all of these conditions:
94
95 a) You must cause the modified files to carry prominent notices
96 stating that you changed the files and the date of any change.
97
98 b) You must cause any work that you distribute or publish, that in
99 whole or in part contains or is derived from the Program or any
100 part thereof, to be licensed as a whole at no charge to all third
101 parties under the terms of this License.
102
103 c) If the modified program normally reads commands interactively
104 when run, you must cause it, when started running for such
105 interactive use in the most ordinary way, to print or display an
106 announcement including an appropriate copyright notice and a
107 notice that there is no warranty (or else, saying that you provide
108 a warranty) and that users may redistribute the program under
109 these conditions, and telling the user how to view a copy of this
110 License. (Exception: if the Program itself is interactive but
111 does not normally print such an announcement, your work based on
112 the Program is not required to print an announcement.)
113
114These requirements apply to the modified work as a whole. If
115identifiable sections of that work are not derived from the Program,
116and can be reasonably considered independent and separate works in
117themselves, then this License, and its terms, do not apply to those
118sections when you distribute them as separate works. But when you
119distribute the same sections as part of a whole which is a work based
120on the Program, the distribution of the whole must be on the terms of
121this License, whose permissions for other licensees extend to the
122entire whole, and thus to each and every part regardless of who wrote it.
123
124Thus, it is not the intent of this section to claim rights or contest
125your rights to work written entirely by you; rather, the intent is to
126exercise the right to control the distribution of derivative or
127collective works based on the Program.
128
129In addition, mere aggregation of another work not based on the Program
130with the Program (or with a work based on the Program) on a volume of
131a storage or distribution medium does not bring the other work under
132the scope of this License.
133
134 3. You may copy and distribute the Program (or a work based on it,
135under Section 2) in object code or executable form under the terms of
136Sections 1 and 2 above provided that you also do one of the following:
137
138 a) Accompany it with the complete corresponding machine-readable
139 source code, which must be distributed under the terms of Sections
140 1 and 2 above on a medium customarily used for software interchange; or,
141
142 b) Accompany it with a written offer, valid for at least three
143 years, to give any third party, for a charge no more than your
144 cost of physically performing source distribution, a complete
145 machine-readable copy of the corresponding source code, to be
146 distributed under the terms of Sections 1 and 2 above on a medium
147 customarily used for software interchange; or,
148
149 c) Accompany it with the information you received as to the offer
150 to distribute corresponding source code. (This alternative is
151 allowed only for noncommercial distribution and only if you
152 received the program in object code or executable form with such
153 an offer, in accord with Subsection b above.)
154
155The source code for a work means the preferred form of the work for
156making modifications to it. For an executable work, complete source
157code means all the source code for all modules it contains, plus any
158associated interface definition files, plus the scripts used to
159control compilation and installation of the executable. However, as a
160special exception, the source code distributed need not include
161anything that is normally distributed (in either source or binary
162form) with the major components (compiler, kernel, and so on) of the
163operating system on which the executable runs, unless that component
164itself accompanies the executable.
165
166If distribution of executable or object code is made by offering
167access to copy from a designated place, then offering equivalent
168access to copy the source code from the same place counts as
169distribution of the source code, even though third parties are not
170compelled to copy the source along with the object code.
171
172 4. You may not copy, modify, sublicense, or distribute the Program
173except as expressly provided under this License. Any attempt
174otherwise to copy, modify, sublicense or distribute the Program is
175void, and will automatically terminate your rights under this License.
176However, parties who have received copies, or rights, from you under
177this License will not have their licenses terminated so long as such
178parties remain in full compliance.
179
180 5. You are not required to accept this License, since you have not
181signed it. However, nothing else grants you permission to modify or
182distribute the Program or its derivative works. These actions are
183prohibited by law if you do not accept this License. Therefore, by
184modifying or distributing the Program (or any work based on the
185Program), you indicate your acceptance of this License to do so, and
186all its terms and conditions for copying, distributing or modifying
187the Program or works based on it.
188
189 6. Each time you redistribute the Program (or any work based on the
190Program), the recipient automatically receives a license from the
191original licensor to copy, distribute or modify the Program subject to
192these terms and conditions. You may not impose any further
193restrictions on the recipients' exercise of the rights granted herein.
194You are not responsible for enforcing compliance by third parties to
195this License.
196
197 7. If, as a consequence of a court judgment or allegation of patent
198infringement or for any other reason (not limited to patent issues),
199conditions are imposed on you (whether by court order, agreement or
200otherwise) that contradict the conditions of this License, they do not
201excuse you from the conditions of this License. If you cannot
202distribute so as to satisfy simultaneously your obligations under this
203License and any other pertinent obligations, then as a consequence you
204may not distribute the Program at all. For example, if a patent
205license would not permit royalty-free redistribution of the Program by
206all those who receive copies directly or indirectly through you, then
207the only way you could satisfy both it and this License would be to
208refrain entirely from distribution of the Program.
209
210If any portion of this section is held invalid or unenforceable under
211any particular circumstance, the balance of the section is intended to
212apply and the section as a whole is intended to apply in other
213circumstances.
214
215It is not the purpose of this section to induce you to infringe any
216patents or other property right claims or to contest validity of any
217such claims; this section has the sole purpose of protecting the
218integrity of the free software distribution system, which is
219implemented by public license practices. Many people have made
220generous contributions to the wide range of software distributed
221through that system in reliance on consistent application of that
222system; it is up to the author/donor to decide if he or she is willing
223to distribute software through any other system and a licensee cannot
224impose that choice.
225
226This section is intended to make thoroughly clear what is believed to
227be a consequence of the rest of this License.
228
229 8. If the distribution and/or use of the Program is restricted in
230certain countries either by patents or by copyrighted interfaces, the
231original copyright holder who places the Program under this License
232may add an explicit geographical distribution limitation excluding
233those countries, so that distribution is permitted only in or among
234countries not thus excluded. In such case, this License incorporates
235the limitation as if written in the body of this License.
236
237 9. The Free Software Foundation may publish revised and/or new versions
238of the General Public License from time to time. Such new versions will
239be similar in spirit to the present version, but may differ in detail to
240address new problems or concerns.
241
242Each version is given a distinguishing version number. If the Program
243specifies a version number of this License which applies to it and "any
244later version", you have the option of following the terms and conditions
245either of that version or of any later version published by the Free
246Software Foundation. If the Program does not specify a version number of
247this License, you may choose any version ever published by the Free Software
248Foundation.
249
250 10. If you wish to incorporate parts of the Program into other free
251programs whose distribution conditions are different, write to the author
252to ask for permission. For software which is copyrighted by the Free
253Software Foundation, write to the Free Software Foundation; we sometimes
254make exceptions for this. Our decision will be guided by the two goals
255of preserving the free status of all derivatives of our free software and
256of promoting the sharing and reuse of software generally.
257
258 NO WARRANTY
259
260 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268REPAIR OR CORRECTION.
269
270 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278POSSIBILITY OF SUCH DAMAGES.
279
280 END OF TERMS AND CONDITIONS
281
282 How to Apply These Terms to Your New Programs
283
284 If you develop a new program, and you want it to be of the greatest
285possible use to the public, the best way to achieve this is to make it
286free software which everyone can redistribute and change under these terms.
287
288 To do so, attach the following notices to the program. It is safest
289to attach them to the start of each source file to most effectively
290convey the exclusion of warranty; and each file should have at least
291the "copyright" line and a pointer to where the full notice is found.
292
293 <one line to give the program's name and a brief idea of what it does.>
294 Copyright (C) <year> <name of author>
295
296 This program is free software; you can redistribute it and/or modify
297 it under the terms of the GNU General Public License as published by
298 the Free Software Foundation; either version 2 of the License, or
299 (at your option) any later version.
300
301 This program is distributed in the hope that it will be useful,
302 but WITHOUT ANY WARRANTY; without even the implied warranty of
303 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 GNU General Public License for more details.
305
306 You should have received a copy of the GNU General Public License
307 along with this program; if not, write to the Free Software
308 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
309
310Also add information on how to contact you by electronic and paper mail.
311
312If the program is interactive, make it output a short notice like this
313when it starts in an interactive mode:
314
315 Gnomovision version 69, Copyright (C) year name of author
316 Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 This is free software, and you are welcome to redistribute it
318 under certain conditions; type `show c' for details.
319
320The hypothetical commands `show w' and `show c' should show the appropriate
321parts of the General Public License. Of course, the commands you use may
322be called something other than `show w' and `show c'; they could even be
323mouse-clicks or menu items--whatever suits your program.
324
325You should also get your employer (if you work as a programmer) or your
326school, if any, to sign a "copyright disclaimer" for the program, if
327necessary. Here is a sample; alter the names:
328
329 Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
332 <signature of Ty Coon>, 1 April 1989
333 Ty Coon, President of Vice
334
335This General Public License does not permit incorporating your program into
336proprietary programs. If your program is a subroutine library, you may
337consider it more useful to permit linking proprietary applications with the
338library. If this is what you want to do, use the GNU Library General
339Public License instead of this License.
340
341-------------------------------------------------------------------------
diff --git a/rbutil/rbutilqt/zip/README b/rbutil/rbutilqt/zip/README
new file mode 100644
index 0000000000..81891e8516
--- /dev/null
+++ b/rbutil/rbutilqt/zip/README
@@ -0,0 +1,68 @@
1The *.cpp and *.h files in this archive are
2Copyright (C) 2007 Angius Fabrizio (blue_death@users.sourceforge.net)
3
4You may use, distribute and copy the files under the terms of
5GNU General Public License version 2 (see the file LICENSE.GPL for details)
6
7-------------------------------------------------------------------------
8
9Website: http://osdab.sourceforge.net/
10SourceForge.net project page: https://sourceforge.net/projects/osdab/
11
12**************** ******* ******************************************************
13
14detailed description
15--------------------
16Meet a couple of pure-Qt/C++ classes capable of handling PKZIP 2.0 compatible zip archives.
17
18This is not a "port" of some other existing implementation, everything has been written from scratch (althought some code was actually inspired by existing public domain projects) and it's all pure C++/Qt. Please note that this is not a complete stand-alone library, it's just a bunch of classes. You will have to add them to your project and modify them to best fit your needs.
19
20It supports basic features like file extraction and compression (with optional password encryption) and archive comments. There are methods to extract single files or the whole archive and methods to compress the contents of a whole directory. Nevertheless, it should be quite trivial to add other features. The public API only has a few methods because this is what I was looking for. This does not mean you can't write a few more lines of code (it really shouldn't take more than a few lines!) to add more features.
21
22The classes are great if you only need to use the PKZIP format for loading/saving your application's data. Just remember that you will need to customize a few parts of the code, i.e. to add your own password retrieval method.
23
24zlib is used for actual compression and decompression.
25
26Please refer to the example application's main.cpp file or to the class comments in the source files for details and more usage examples.
27
28history
29-------
302007-02-01 - New IgnorePaths compression option and two more "addDirectoryContents()" convenience methods to use this option.
312007-01-28 - Major changes:
32 - Bug fix: there was a big problem with directory names.
33 - API changes: the Zip::addDirectory() method is now easier to use.
34 The password can now be set using a setPassword() method and a new flag allows to preserve absolute paths.
35 - Added an "encrypted" flag to the Unzip::ZipEntry struct.
36 - Removed QObject inheritance. Internationalization is now achieved through QCoreApplication::translate().
372006-11-30 - A good part of the UnZip class has been rewritten. Zip entries with a Data Descriptor (like OpenOffice files) are now correctly parsed; some minor API changes and bug fixes; new features: comments, zip entry list, skip paths unzip option
382006-11-24 - Bug fix. An additional directory was being created for some files in the root directory
392006-10-23 - Minor API changes; QIODevice support added; better binary compatibility; "long long" issue with older compilers solved
402006-06-09 - Minor API changes
412005-10-03 - First public release
42
43features
44--------
45Pure C++/Qt based, clean & oo implementation.
46Retrieve archive contents information before extracting any file.
47Fast (but less robust with corrupt archives) parsing of the ZIP file format.
48Traditional PKWARE password encryption (strong encryption as introduced by PKZip versions 5.0 and later is NOT available).
49Support for archive comments.
50
51missing features and restrictions
52---------------------------------
53Needs to be modified to fit into an existing project (i.e.: you might need to add your own password handling routine).
54Weak support of corrupted archives (althought some files could be extracted even if the archive is corrupted).
55No support for filesystem specific features like unix symbolic links.
56No support for spanned archives.
57No support for strong encryption or features introduced after PKZIP version 2.0 (see the PKWARE specs for details).
58
59requirements
60------------
61Qt version 4.0.x or later
62zlib library
63
64
65**************** ******* ******************************************************
66BEFORE SENDING ANY EMAIL, CHECK OUT THE OSDAB WEBSITE HOSTED AT SOURCEFORGE.NET
67**************** ******* ******************************************************
68
diff --git a/rbutil/rbutilqt/zip/README.ROCKBOX b/rbutil/rbutilqt/zip/README.ROCKBOX
new file mode 100644
index 0000000000..36bc9f59fd
--- /dev/null
+++ b/rbutil/rbutilqt/zip/README.ROCKBOX
@@ -0,0 +1,7 @@
1This folder contains the zip extraction classes of the OSDaB project.
2These classes are distributed under the GPL license (see the file
3LICENSE.GPL in this folder). The source files are included unmodified as
4of 2007-07-24 with the examples folder left out.
5
6Details at http://osdab.sourceforge.net/snippets/zip.php
7
diff --git a/rbutil/rbutilqt/zip/unzip.cpp b/rbutil/rbutilqt/zip/unzip.cpp
new file mode 100644
index 0000000000..3cc385ab36
--- /dev/null
+++ b/rbutil/rbutilqt/zip/unzip.cpp
@@ -0,0 +1,1360 @@
1/****************************************************************************
2** Filename: unzip.cpp
3** Last updated [dd/mm/yyyy]: 28/01/2007
4**
5** pkzip 2.0 decompression.
6**
7** Some of the code has been inspired by other open source projects,
8** (mainly Info-Zip and Gilles Vollant's minizip).
9** Compression and decompression actually uses the zlib library.
10**
11** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12**
13** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22**
23** See the file LICENSE.GPL that came with this software distribution or
24** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25**
26**********************************************************************/
27
28#include "unzip.h"
29#include "unzip_p.h"
30#include "zipentry_p.h"
31
32#include <QString>
33#include <QStringList>
34#include <QDir>
35#include <QFile>
36#include <QCoreApplication>
37
38// You can remove this #include if you replace the qDebug() statements.
39#include <QtDebug>
40
41/*!
42 \class UnZip unzip.h
43
44 \brief PKZip 2.0 file decompression.
45 Compatibility with later versions is not ensured as they may use
46 unsupported compression algorithms.
47 Versions after 2.7 may have an incompatible header format and thus be
48 completely incompatible.
49*/
50
51/*! \enum UnZip::ErrorCode The result of a decompression operation.
52 \value UnZip::Ok No error occurred.
53 \value UnZip::ZlibInit Failed to init or load the zlib library.
54 \value UnZip::ZlibError The zlib library returned some error.
55 \value UnZip::OpenFailed Unable to create or open a device.
56 \value UnZip::PartiallyCorrupted Corrupted zip archive - some files could be extracted.
57 \value UnZip::Corrupted Corrupted or invalid zip archive.
58 \value UnZip::WrongPassword Unable to decrypt a password protected file.
59 \value UnZip::NoOpenArchive No archive has been opened yet.
60 \value UnZip::FileNotFound Unable to find the requested file in the archive.
61 \value UnZip::ReadFailed Reading of a file failed.
62 \value UnZip::WriteFailed Writing of a file failed.
63 \value UnZip::SeekFailed Seek failed.
64 \value UnZip::CreateDirFailed Could not create a directory.
65 \value UnZip::InvalidDevice A null device has been passed as parameter.
66 \value UnZip::InvalidArchive This is not a valid (or supported) ZIP archive.
67 \value UnZip::HeaderConsistencyError Local header record info does not match with the central directory record info. The archive may be corrupted.
68
69 \value UnZip::Skip Internal use only.
70 \value UnZip::SkipAll Internal use only.
71*/
72
73/*! \enum UnZip::ExtractionOptions Some options for the file extraction methods.
74 \value UnZip::ExtractPaths Default. Does not ignore the path of the zipped files.
75 \value UnZip::SkipPaths Default. Ignores the path of the zipped files and extracts them all to the same root directory.
76*/
77
78//! Local header size (excluding signature, excluding variable length fields)
79#define UNZIP_LOCAL_HEADER_SIZE 26
80//! Central Directory file entry size (excluding signature, excluding variable length fields)
81#define UNZIP_CD_ENTRY_SIZE_NS 42
82//! Data descriptor size (excluding signature)
83#define UNZIP_DD_SIZE 12
84//! End Of Central Directory size (including signature, excluding variable length fields)
85#define UNZIP_EOCD_SIZE 22
86//! Local header entry encryption header size
87#define UNZIP_LOCAL_ENC_HEADER_SIZE 12
88
89// Some offsets inside a CD record (excluding signature)
90#define UNZIP_CD_OFF_VERSION 0
91#define UNZIP_CD_OFF_GPFLAG 4
92#define UNZIP_CD_OFF_CMETHOD 6
93#define UNZIP_CD_OFF_MODT 8
94#define UNZIP_CD_OFF_MODD 10
95#define UNZIP_CD_OFF_CRC32 12
96#define UNZIP_CD_OFF_CSIZE 16
97#define UNZIP_CD_OFF_USIZE 20
98#define UNZIP_CD_OFF_NAMELEN 24
99#define UNZIP_CD_OFF_XLEN 26
100#define UNZIP_CD_OFF_COMMLEN 28
101#define UNZIP_CD_OFF_LHOFFSET 38
102
103// Some offsets inside a local header record (excluding signature)
104#define UNZIP_LH_OFF_VERSION 0
105#define UNZIP_LH_OFF_GPFLAG 2
106#define UNZIP_LH_OFF_CMETHOD 4
107#define UNZIP_LH_OFF_MODT 6
108#define UNZIP_LH_OFF_MODD 8
109#define UNZIP_LH_OFF_CRC32 10
110#define UNZIP_LH_OFF_CSIZE 14
111#define UNZIP_LH_OFF_USIZE 18
112#define UNZIP_LH_OFF_NAMELEN 22
113#define UNZIP_LH_OFF_XLEN 24
114
115// Some offsets inside a data descriptor record (excluding signature)
116#define UNZIP_DD_OFF_CRC32 0
117#define UNZIP_DD_OFF_CSIZE 4
118#define UNZIP_DD_OFF_USIZE 8
119
120// Some offsets inside a EOCD record
121#define UNZIP_EOCD_OFF_ENTRIES 6
122#define UNZIP_EOCD_OFF_CDOFF 12
123#define UNZIP_EOCD_OFF_COMMLEN 16
124
125/*!
126 Max version handled by this API.
127 0x1B = 2.7 --> full compatibility only up to version 2.0 (0x14)
128 versions from 2.1 to 2.7 may use unsupported compression methods
129 versions after 2.7 may have an incompatible header format
130*/
131#define UNZIP_VERSION 0x1B
132//! Full compatibility granted until this version
133#define UNZIP_VERSION_STRICT 0x14
134
135//! CRC32 routine
136#define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8)
137
138//! Checks if some file has been already extracted.
139#define UNZIP_CHECK_FOR_VALID_DATA \
140 {\
141 if (headers != 0)\
142 {\
143 qDebug() << "Corrupted zip archive. Some files might be extracted.";\
144 ec = headers->size() != 0 ? UnZip::PartiallyCorrupted : UnZip::Corrupted;\
145 break;\
146 }\
147 else\
148 {\
149 delete device;\
150 device = 0;\
151 qDebug() << "Corrupted or invalid zip archive";\
152 ec = UnZip::Corrupted;\
153 break;\
154 }\
155 }
156
157
158/************************************************************************
159 Public interface
160*************************************************************************/
161
162/*!
163 Creates a new Zip file decompressor.
164*/
165UnZip::UnZip()
166{
167 d = new UnzipPrivate;
168}
169
170/*!
171 Closes any open archive and releases used resources.
172*/
173UnZip::~UnZip()
174{
175 closeArchive();
176 delete d;
177}
178
179/*!
180 Returns true if there is an open archive.
181*/
182bool UnZip::isOpen() const
183{
184 return d->device != 0;
185}
186
187/*!
188 Opens a zip archive and reads the files list. Closes any previously opened archive.
189*/
190UnZip::ErrorCode UnZip::openArchive(const QString& filename)
191{
192 QFile* file = new QFile(filename);
193
194 if (!file->exists()) {
195 delete file;
196 return UnZip::FileNotFound;
197 }
198
199 if (!file->open(QIODevice::ReadOnly)) {
200 delete file;
201 return UnZip::OpenFailed;
202 }
203
204 return openArchive(file);
205}
206
207/*!
208 Opens a zip archive and reads the entries list.
209 Closes any previously opened archive.
210 \warning The class takes ownership of the device so don't delete it!
211*/
212UnZip::ErrorCode UnZip::openArchive(QIODevice* device)
213{
214 if (device == 0)
215 {
216 qDebug() << "Invalid device.";
217 return UnZip::InvalidDevice;
218 }
219
220 return d->openArchive(device);
221}
222
223/*!
224 Closes the archive and releases all the used resources (like cached passwords).
225*/
226void UnZip::closeArchive()
227{
228 d->closeArchive();
229}
230
231QString UnZip::archiveComment() const
232{
233 if (d->device == 0)
234 return QString();
235 return d->comment;
236}
237
238/*!
239 Returns a locale translated error string for a given error code.
240*/
241QString UnZip::formatError(UnZip::ErrorCode c) const
242{
243 switch (c)
244 {
245 case Ok: return QCoreApplication::translate("UnZip", "ZIP operation completed successfully."); break;
246 case ZlibInit: return QCoreApplication::translate("UnZip", "Failed to initialize or load zlib library."); break;
247 case ZlibError: return QCoreApplication::translate("UnZip", "zlib library error."); break;
248 case OpenFailed: return QCoreApplication::translate("UnZip", "Unable to create or open file."); break;
249 case PartiallyCorrupted: return QCoreApplication::translate("UnZip", "Partially corrupted archive. Some files might be extracted."); break;
250 case Corrupted: return QCoreApplication::translate("UnZip", "Corrupted archive."); break;
251 case WrongPassword: return QCoreApplication::translate("UnZip", "Wrong password."); break;
252 case NoOpenArchive: return QCoreApplication::translate("UnZip", "No archive has been created yet."); break;
253 case FileNotFound: return QCoreApplication::translate("UnZip", "File or directory does not exist."); break;
254 case ReadFailed: return QCoreApplication::translate("UnZip", "File read error."); break;
255 case WriteFailed: return QCoreApplication::translate("UnZip", "File write error."); break;
256 case SeekFailed: return QCoreApplication::translate("UnZip", "File seek error."); break;
257 case CreateDirFailed: return QCoreApplication::translate("UnZip", "Unable to create a directory."); break;
258 case InvalidDevice: return QCoreApplication::translate("UnZip", "Invalid device."); break;
259 case InvalidArchive: return QCoreApplication::translate("UnZip", "Invalid or incompatible zip archive."); break;
260 case HeaderConsistencyError: return QCoreApplication::translate("UnZip", "Inconsistent headers. Archive might be corrupted."); break;
261 default: ;
262 }
263
264 return QCoreApplication::translate("UnZip", "Unknown error.");
265}
266
267/*!
268 Returns true if the archive contains a file with the given path and name.
269*/
270bool UnZip::contains(const QString& file) const
271{
272 if (d->headers == 0)
273 return false;
274
275 return d->headers->contains(file);
276}
277
278/*!
279 Returns complete paths of files and directories in this archive.
280*/
281QStringList UnZip::fileList() const
282{
283 return d->headers == 0 ? QStringList() : d->headers->keys();
284}
285
286/*!
287 Returns information for each (correctly parsed) entry of this archive.
288*/
289QList<UnZip::ZipEntry> UnZip::entryList() const
290{
291 QList<UnZip::ZipEntry> list;
292
293 if (d->headers != 0)
294 {
295 for (QMap<QString,ZipEntryP*>::ConstIterator it = d->headers->constBegin(); it != d->headers->constEnd(); ++it)
296 {
297 const ZipEntryP* entry = it.value();
298 Q_ASSERT(entry != 0);
299
300 ZipEntry z;
301
302 z.filename = it.key();
303 if (!entry->comment.isEmpty())
304 z.comment = entry->comment;
305 z.compressedSize = entry->szComp;
306 z.uncompressedSize = entry->szUncomp;
307 z.crc32 = entry->crc;
308 z.lastModified = d->convertDateTime(entry->modDate, entry->modTime);
309
310 z.compression = entry->compMethod == 0 ? NoCompression : entry->compMethod == 8 ? Deflated : UnknownCompression;
311 z.type = z.filename.endsWith("/") ? Directory : File;
312
313 z.encrypted = entry->isEncrypted();
314
315 list.append(z);
316 }
317 }
318
319 return list;
320}
321
322/*!
323 Extracts the whole archive to a directory.
324*/
325UnZip::ErrorCode UnZip::extractAll(const QString& dirname, ExtractionOptions options)
326{
327 return extractAll(QDir(dirname), options);
328}
329
330/*!
331 Extracts the whole archive to a directory.
332*/
333UnZip::ErrorCode UnZip::extractAll(const QDir& dir, ExtractionOptions options)
334{
335 // this should only happen if we didn't call openArchive() yet
336 if (d->device == 0)
337 return NoOpenArchive;
338
339 if (d->headers == 0)
340 return Ok;
341
342 bool end = false;
343 for (QMap<QString,ZipEntryP*>::Iterator itr = d->headers->begin(); itr != d->headers->end(); ++itr)
344 {
345 ZipEntryP* entry = itr.value();
346 Q_ASSERT(entry != 0);
347
348 if ((entry->isEncrypted()) && d->skipAllEncrypted)
349 continue;
350
351 switch (d->extractFile(itr.key(), *entry, dir, options))
352 {
353 case Corrupted:
354 qDebug() << "Removing corrupted entry" << itr.key();
355 d->headers->erase(itr++);
356 if (itr == d->headers->end())
357 end = true;
358 break;
359 case CreateDirFailed:
360 break;
361 case Skip:
362 break;
363 case SkipAll:
364 d->skipAllEncrypted = true;
365 break;
366 default:
367 ;
368 }
369
370 if (end)
371 break;
372 }
373
374 return Ok;
375}
376
377/*!
378 Extracts a single file to a directory.
379*/
380UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QString& dirname, ExtractionOptions options)
381{
382 return extractFile(filename, QDir(dirname), options);
383}
384
385/*!
386 Extracts a single file to a directory.
387*/
388UnZip::ErrorCode UnZip::extractFile(const QString& filename, const QDir& dir, ExtractionOptions options)
389{
390 QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
391 if (itr != d->headers->end())
392 {
393 ZipEntryP* entry = itr.value();
394 Q_ASSERT(entry != 0);
395 return d->extractFile(itr.key(), *entry, dir, options);
396 }
397
398 return FileNotFound;
399}
400
401/*!
402 Extracts a single file to a directory.
403*/
404UnZip::ErrorCode UnZip::extractFile(const QString& filename, QIODevice* dev, ExtractionOptions options)
405{
406 if (dev == 0)
407 return InvalidDevice;
408
409 QMap<QString,ZipEntryP*>::Iterator itr = d->headers->find(filename);
410 if (itr != d->headers->end()) {
411 ZipEntryP* entry = itr.value();
412 Q_ASSERT(entry != 0);
413 return d->extractFile(itr.key(), *entry, dev, options);
414 }
415
416 return FileNotFound;
417}
418
419/*!
420 Extracts a list of files.
421 Stops extraction at the first error (but continues if a file does not exist in the archive).
422 */
423UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options)
424{
425 QDir dir(dirname);
426 ErrorCode ec;
427
428 for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
429 {
430 ec = extractFile(*itr, dir, options);
431 if (ec == FileNotFound)
432 continue;
433 if (ec != Ok)
434 return ec;
435 }
436
437 return Ok;
438}
439
440/*!
441 Extracts a list of files.
442 Stops extraction at the first error (but continues if a file does not exist in the archive).
443 */
444UnZip::ErrorCode UnZip::extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options)
445{
446 ErrorCode ec;
447
448 for (QStringList::ConstIterator itr = filenames.constBegin(); itr != filenames.constEnd(); ++itr)
449 {
450 ec = extractFile(*itr, dir, options);
451 if (ec == FileNotFound)
452 continue;
453 if (ec != Ok)
454 return ec;
455 }
456
457 return Ok;
458}
459
460/*!
461 Remove/replace this method to add your own password retrieval routine.
462*/
463void UnZip::setPassword(const QString& pwd)
464{
465 d->password = pwd;
466}
467
468/*!
469 ZipEntry constructor - initialize data. Type is set to File.
470*/
471UnZip::ZipEntry::ZipEntry()
472{
473 compressedSize = uncompressedSize = crc32 = 0;
474 compression = NoCompression;
475 type = File;
476 encrypted = false;
477}
478
479
480/************************************************************************
481 Private interface
482*************************************************************************/
483
484//! \internal
485UnzipPrivate::UnzipPrivate()
486{
487 skipAllEncrypted = false;
488 headers = 0;
489 device = 0;
490
491 uBuffer = (unsigned char*) buffer1;
492 crcTable = (quint32*) get_crc_table();
493
494 cdOffset = eocdOffset = 0;
495 cdEntryCount = 0;
496 unsupportedEntryCount = 0;
497}
498
499//! \internal Parses a Zip archive.
500UnZip::ErrorCode UnzipPrivate::openArchive(QIODevice* dev)
501{
502 Q_ASSERT(dev != 0);
503
504 if (device != 0)
505 closeArchive();
506
507 device = dev;
508
509 if (!(device->isOpen() || device->open(QIODevice::ReadOnly)))
510 {
511 delete device;
512 device = 0;
513
514 qDebug() << "Unable to open device for reading";
515 return UnZip::OpenFailed;
516 }
517
518 UnZip::ErrorCode ec;
519
520 ec = seekToCentralDirectory();
521 if (ec != UnZip::Ok)
522 {
523 closeArchive();
524 return ec;
525 }
526
527 //! \todo Ignore CD entry count? CD may be corrupted.
528 if (cdEntryCount == 0)
529 {
530 return UnZip::Ok;
531 }
532
533 bool continueParsing = true;
534
535 while (continueParsing)
536 {
537 if (device->read(buffer1, 4) != 4)
538 UNZIP_CHECK_FOR_VALID_DATA
539
540 if (! (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x01 && buffer1[3] == 0x02) )
541 break;
542
543 if ( (ec = parseCentralDirectoryRecord()) != UnZip::Ok )
544 break;
545 }
546
547 if (ec != UnZip::Ok)
548 closeArchive();
549
550 return ec;
551}
552
553/*
554 \internal Parses a local header record and makes some consistency check
555 with the information stored in the Central Directory record for this entry
556 that has been previously parsed.
557 \todo Optional consistency check (as a ExtractionOptions flag)
558
559 local file header signature 4 bytes (0x04034b50)
560 version needed to extract 2 bytes
561 general purpose bit flag 2 bytes
562 compression method 2 bytes
563 last mod file time 2 bytes
564 last mod file date 2 bytes
565 crc-32 4 bytes
566 compressed size 4 bytes
567 uncompressed size 4 bytes
568 file name length 2 bytes
569 extra field length 2 bytes
570
571 file name (variable size)
572 extra field (variable size)
573*/
574UnZip::ErrorCode UnzipPrivate::parseLocalHeaderRecord(const QString& path, ZipEntryP& entry)
575{
576 if (!device->seek(entry.lhOffset))
577 return UnZip::SeekFailed;
578
579 // Test signature
580 if (device->read(buffer1, 4) != 4)
581 return UnZip::ReadFailed;
582
583 if ((buffer1[0] != 'P') || (buffer1[1] != 'K') || (buffer1[2] != 0x03) || (buffer1[3] != 0x04))
584 return UnZip::InvalidArchive;
585
586 if (device->read(buffer1, UNZIP_LOCAL_HEADER_SIZE) != UNZIP_LOCAL_HEADER_SIZE)
587 return UnZip::ReadFailed;
588
589 /*
590 Check 3rd general purpose bit flag.
591
592 "bit 3: If this bit is set, the fields crc-32, compressed size
593 and uncompressed size are set to zero in the local
594 header. The correct values are put in the data descriptor
595 immediately following the compressed data."
596 */
597 bool hasDataDescriptor = entry.hasDataDescriptor();
598
599 bool checkFailed = false;
600
601 if (!checkFailed)
602 checkFailed = entry.compMethod != getUShort(uBuffer, UNZIP_LH_OFF_CMETHOD);
603 if (!checkFailed)
604 checkFailed = entry.gpFlag[0] != uBuffer[UNZIP_LH_OFF_GPFLAG];
605 if (!checkFailed)
606 checkFailed = entry.gpFlag[1] != uBuffer[UNZIP_LH_OFF_GPFLAG + 1];
607 if (!checkFailed)
608 checkFailed = entry.modTime[0] != uBuffer[UNZIP_LH_OFF_MODT];
609 if (!checkFailed)
610 checkFailed = entry.modTime[1] != uBuffer[UNZIP_LH_OFF_MODT + 1];
611 if (!checkFailed)
612 checkFailed = entry.modDate[0] != uBuffer[UNZIP_LH_OFF_MODD];
613 if (!checkFailed)
614 checkFailed = entry.modDate[1] != uBuffer[UNZIP_LH_OFF_MODD + 1];
615 if (!hasDataDescriptor)
616 {
617 if (!checkFailed)
618 checkFailed = entry.crc != getULong(uBuffer, UNZIP_LH_OFF_CRC32);
619 if (!checkFailed)
620 checkFailed = entry.szComp != getULong(uBuffer, UNZIP_LH_OFF_CSIZE);
621 if (!checkFailed)
622 checkFailed = entry.szUncomp != getULong(uBuffer, UNZIP_LH_OFF_USIZE);
623 }
624
625 if (checkFailed)
626 return UnZip::HeaderConsistencyError;
627
628 // Check filename
629 quint16 szName = getUShort(uBuffer, UNZIP_LH_OFF_NAMELEN);
630 if (szName == 0)
631 return UnZip::HeaderConsistencyError;
632
633 if (device->read(buffer2, szName) != szName)
634 return UnZip::ReadFailed;
635
636 QString filename = QString::fromAscii(buffer2, szName);
637 if (filename != path)
638 {
639 qDebug() << "Filename in local header mismatches.";
640 return UnZip::HeaderConsistencyError;
641 }
642
643 // Skip extra field
644 quint16 szExtra = getUShort(uBuffer, UNZIP_LH_OFF_XLEN);
645 if (szExtra != 0)
646 {
647 if (!device->seek(device->pos() + szExtra))
648 return UnZip::SeekFailed;
649 }
650
651 entry.dataOffset = device->pos();
652
653 if (hasDataDescriptor)
654 {
655 /*
656 The data descriptor has this OPTIONAL signature: PK\7\8
657 We try to skip the compressed data relying on the size set in the
658 Central Directory record.
659 */
660 if (!device->seek(device->pos() + entry.szComp))
661 return UnZip::SeekFailed;
662
663 // Read 4 bytes and check if there is a data descriptor signature
664 if (device->read(buffer2, 4) != 4)
665 return UnZip::ReadFailed;
666
667 bool hasSignature = buffer2[0] == 'P' && buffer2[1] == 'K' && buffer2[2] == 0x07 && buffer2[3] == 0x08;
668 if (hasSignature)
669 {
670 if (device->read(buffer2, UNZIP_DD_SIZE) != UNZIP_DD_SIZE)
671 return UnZip::ReadFailed;
672 }
673 else
674 {
675 if (device->read(buffer2 + 4, UNZIP_DD_SIZE - 4) != UNZIP_DD_SIZE - 4)
676 return UnZip::ReadFailed;
677 }
678
679 // DD: crc, compressed size, uncompressed size
680 if (
681 entry.crc != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CRC32) ||
682 entry.szComp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_CSIZE) ||
683 entry.szUncomp != getULong((unsigned char*)buffer2, UNZIP_DD_OFF_USIZE)
684 )
685 return UnZip::HeaderConsistencyError;
686 }
687
688 return UnZip::Ok;
689}
690
691/*! \internal Attempts to find the start of the central directory record.
692
693 We seek the file back until we reach the "End Of Central Directory"
694 signature PK\5\6.
695
696 end of central dir signature 4 bytes (0x06054b50)
697 number of this disk 2 bytes
698 number of the disk with the
699 start of the central directory 2 bytes
700 total number of entries in the
701 central directory on this disk 2 bytes
702 total number of entries in
703 the central directory 2 bytes
704 size of the central directory 4 bytes
705 offset of start of central
706 directory with respect to
707 the starting disk number 4 bytes
708 .ZIP file comment length 2 bytes
709 --- SIZE UNTIL HERE: UNZIP_EOCD_SIZE ---
710 .ZIP file comment (variable size)
711*/
712UnZip::ErrorCode UnzipPrivate::seekToCentralDirectory()
713{
714 qint64 length = device->size();
715 qint64 offset = length - UNZIP_EOCD_SIZE;
716
717 if (length < UNZIP_EOCD_SIZE)
718 return UnZip::InvalidArchive;
719
720 if (!device->seek( offset ))
721 return UnZip::SeekFailed;
722
723 if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
724 return UnZip::ReadFailed;
725
726 bool eocdFound = (buffer1[0] == 'P' && buffer1[1] == 'K' && buffer1[2] == 0x05 && buffer1[3] == 0x06);
727
728 if (eocdFound)
729 {
730 // Zip file has no comment (the only variable length field in the EOCD record)
731 eocdOffset = offset;
732 }
733 else
734 {
735 qint64 read;
736 char* p = 0;
737
738 offset -= UNZIP_EOCD_SIZE;
739
740 if (offset <= 0)
741 return UnZip::InvalidArchive;
742
743 if (!device->seek( offset ))
744 return UnZip::SeekFailed;
745
746 while ((read = device->read(buffer1, UNZIP_EOCD_SIZE)) >= 0)
747 {
748 if ( (p = strstr(buffer1, "PK\5\6")) != 0)
749 {
750 // Seek to the start of the EOCD record so we can read it fully
751 // Yes... we could simply read the missing bytes and append them to the buffer
752 // but this is far easier so heck it!
753 device->seek( offset + (p - buffer1) );
754 eocdFound = true;
755 eocdOffset = offset + (p - buffer1);
756
757 // Read EOCD record
758 if (device->read(buffer1, UNZIP_EOCD_SIZE) != UNZIP_EOCD_SIZE)
759 return UnZip::ReadFailed;
760
761 break;
762 }
763
764 offset -= UNZIP_EOCD_SIZE;
765 if (offset <= 0)
766 return UnZip::InvalidArchive;
767
768 if (!device->seek( offset ))
769 return UnZip::SeekFailed;
770 }
771 }
772
773 if (!eocdFound)
774 return UnZip::InvalidArchive;
775
776 // Parse EOCD to locate CD offset
777 offset = getULong((const unsigned char*)buffer1, UNZIP_EOCD_OFF_CDOFF + 4);
778
779 cdOffset = offset;
780
781 cdEntryCount = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_ENTRIES + 4);
782
783 quint16 commentLength = getUShort((const unsigned char*)buffer1, UNZIP_EOCD_OFF_COMMLEN + 4);
784 if (commentLength != 0)
785 {
786 QByteArray c = device->read(commentLength);
787 if (c.count() != commentLength)
788 return UnZip::ReadFailed;
789
790 comment = c;
791 }
792
793 // Seek to the start of the CD record
794 if (!device->seek( cdOffset ))
795 return UnZip::SeekFailed;
796
797 return UnZip::Ok;
798}
799
800/*!
801 \internal Parses a central directory record.
802
803 Central Directory record structure:
804
805 [file header 1]
806 .
807 .
808 .
809 [file header n]
810 [digital signature] // PKZip 6.2 or later only
811
812 File header:
813
814 central file header signature 4 bytes (0x02014b50)
815 version made by 2 bytes
816 version needed to extract 2 bytes
817 general purpose bit flag 2 bytes
818 compression method 2 bytes
819 last mod file time 2 bytes
820 last mod file date 2 bytes
821 crc-32 4 bytes
822 compressed size 4 bytes
823 uncompressed size 4 bytes
824 file name length 2 bytes
825 extra field length 2 bytes
826 file comment length 2 bytes
827 disk number start 2 bytes
828 internal file attributes 2 bytes
829 external file attributes 4 bytes
830 relative offset of local header 4 bytes
831
832 file name (variable size)
833 extra field (variable size)
834 file comment (variable size)
835*/
836UnZip::ErrorCode UnzipPrivate::parseCentralDirectoryRecord()
837{
838 // Read CD record
839 if (device->read(buffer1, UNZIP_CD_ENTRY_SIZE_NS) != UNZIP_CD_ENTRY_SIZE_NS)
840 return UnZip::ReadFailed;
841
842 bool skipEntry = false;
843
844 // Get compression type so we can skip non compatible algorithms
845 quint16 compMethod = getUShort(uBuffer, UNZIP_CD_OFF_CMETHOD);
846
847 // Get variable size fields length so we can skip the whole record
848 // if necessary
849 quint16 szName = getUShort(uBuffer, UNZIP_CD_OFF_NAMELEN);
850 quint16 szExtra = getUShort(uBuffer, UNZIP_CD_OFF_XLEN);
851 quint16 szComment = getUShort(uBuffer, UNZIP_CD_OFF_COMMLEN);
852
853 quint32 skipLength = szName + szExtra + szComment;
854
855 UnZip::ErrorCode ec = UnZip::Ok;
856
857 if ((compMethod != 0) && (compMethod != 8))
858 {
859 qDebug() << "Unsupported compression method. Skipping file.";
860 skipEntry = true;
861 }
862
863 // Header parsing may be a problem if version is bigger than UNZIP_VERSION
864 if (!skipEntry && buffer1[UNZIP_CD_OFF_VERSION] > UNZIP_VERSION)
865 {
866 qDebug() << "Unsupported PKZip version. Skipping file.";
867 skipEntry = true;
868 }
869
870 if (!skipEntry && szName == 0)
871 {
872 qDebug() << "Skipping file with no name.";
873 skipEntry = true;
874 }
875
876 if (!skipEntry && device->read(buffer2, szName) != szName)
877 {
878 ec = UnZip::ReadFailed;
879 skipEntry = true;
880 }
881
882 if (skipEntry)
883 {
884 if (ec == UnZip::Ok)
885 {
886 if (!device->seek( device->pos() + skipLength ))
887 ec = UnZip::SeekFailed;
888
889 unsupportedEntryCount++;
890 }
891
892 return ec;
893 }
894
895 QString filename = QString::fromAscii(buffer2, szName);
896
897 ZipEntryP* h = new ZipEntryP;
898 h->compMethod = compMethod;
899
900 h->gpFlag[0] = buffer1[UNZIP_CD_OFF_GPFLAG];
901 h->gpFlag[1] = buffer1[UNZIP_CD_OFF_GPFLAG + 1];
902
903 h->modTime[0] = buffer1[UNZIP_CD_OFF_MODT];
904 h->modTime[1] = buffer1[UNZIP_CD_OFF_MODT + 1];
905
906 h->modDate[0] = buffer1[UNZIP_CD_OFF_MODD];
907 h->modDate[1] = buffer1[UNZIP_CD_OFF_MODD + 1];
908
909 h->crc = getULong(uBuffer, UNZIP_CD_OFF_CRC32);
910 h->szComp = getULong(uBuffer, UNZIP_CD_OFF_CSIZE);
911 h->szUncomp = getULong(uBuffer, UNZIP_CD_OFF_USIZE);
912
913 // Skip extra field (if any)
914 if (szExtra != 0)
915 {
916 if (!device->seek( device->pos() + szExtra ))
917 {
918 delete h;
919 return UnZip::SeekFailed;
920 }
921 }
922
923 // Read comment field (if any)
924 if (szComment != 0)
925 {
926 if (device->read(buffer2, szComment) != szComment)
927 {
928 delete h;
929 return UnZip::ReadFailed;
930 }
931
932 h->comment = QString::fromAscii(buffer2, szComment);
933 }
934
935 h->lhOffset = getULong(uBuffer, UNZIP_CD_OFF_LHOFFSET);
936
937 if (headers == 0)
938 headers = new QMap<QString, ZipEntryP*>();
939 headers->insert(filename, h);
940
941 return UnZip::Ok;
942}
943
944//! \internal Closes the archive and resets the internal status.
945void UnzipPrivate::closeArchive()
946{
947 if (device == 0)
948 return;
949
950 skipAllEncrypted = false;
951
952 if (headers != 0)
953 {
954 qDeleteAll(*headers);
955 delete headers;
956 headers = 0;
957 }
958
959 delete device; device = 0;
960
961 cdOffset = eocdOffset = 0;
962 cdEntryCount = 0;
963 unsupportedEntryCount = 0;
964
965 comment.clear();
966}
967
968//! \internal
969UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options)
970{
971 QString name(path);
972 QString dirname;
973 QString directory;
974
975 int pos = name.lastIndexOf('/');
976
977 // This entry is for a directory
978 if (pos == name.length() - 1)
979 {
980 if (options.testFlag(UnZip::SkipPaths))
981 return UnZip::Ok;
982
983 directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(name));
984 if (!createDirectory(directory))
985 {
986 qDebug() << QString("Unable to create directory: %1").arg(directory);
987 return UnZip::CreateDirFailed;
988 }
989
990 return UnZip::Ok;
991 }
992
993 // Extract path from entry
994 if (pos > 0)
995 {
996 // get directory part
997 dirname = name.left(pos);
998 if (options.testFlag(UnZip::SkipPaths))
999 {
1000 directory = dir.absolutePath();
1001 }
1002 else
1003 {
1004 directory = QString("%1/%2").arg(dir.absolutePath()).arg(QDir::cleanPath(dirname));
1005 if (!createDirectory(directory))
1006 {
1007 qDebug() << QString("Unable to create directory: %1").arg(directory);
1008 return UnZip::CreateDirFailed;
1009 }
1010 }
1011 name = name.right(name.length() - pos - 1);
1012 } else directory = dir.absolutePath();
1013
1014 name = QString("%1/%2").arg(directory).arg(name);
1015
1016 QFile outFile(name);
1017
1018 if (!outFile.open(QIODevice::WriteOnly))
1019 {
1020 qDebug() << QString("Unable to open %1 for writing").arg(name);
1021 return UnZip::OpenFailed;
1022 }
1023
1024 //! \todo Set creation/last_modified date/time
1025
1026 UnZip::ErrorCode ec = extractFile(path, entry, &outFile, options);
1027
1028 outFile.close();
1029
1030 if (ec != UnZip::Ok)
1031 {
1032 if (!outFile.remove())
1033 qDebug() << QString("Unable to remove corrupted file: %1").arg(name);
1034 }
1035
1036 return ec;
1037}
1038
1039//! \internal
1040UnZip::ErrorCode UnzipPrivate::extractFile(const QString& path, ZipEntryP& entry, QIODevice* dev, UnZip::ExtractionOptions options)
1041{
1042 Q_UNUSED(options);
1043 Q_ASSERT(dev != 0);
1044
1045 if (!entry.lhEntryChecked)
1046 {
1047 UnZip::ErrorCode ec = parseLocalHeaderRecord(path, entry);
1048 entry.lhEntryChecked = true;
1049
1050 if (ec != UnZip::Ok)
1051 return ec;
1052 }
1053
1054 if (!device->seek(entry.dataOffset))
1055 return UnZip::SeekFailed;
1056
1057 // Encryption keys
1058 quint32 keys[3];
1059
1060 if (entry.isEncrypted())
1061 {
1062 UnZip::ErrorCode e = testPassword(keys, path, entry);
1063 if (e != UnZip::Ok)
1064 {
1065 qDebug() << QString("Unable to decrypt %1").arg(path);
1066 return e;
1067 }//! Encryption header size
1068 entry.szComp -= UNZIP_LOCAL_ENC_HEADER_SIZE; // remove encryption header size
1069 }
1070
1071 if (entry.szComp == 0)
1072 {
1073 if (entry.crc != 0)
1074 return UnZip::Corrupted;
1075
1076 return UnZip::Ok;
1077 }
1078
1079 uInt rep = entry.szComp / UNZIP_READ_BUFFER;
1080 uInt rem = entry.szComp % UNZIP_READ_BUFFER;
1081 uInt cur = 0;
1082
1083 // extract data
1084 qint64 read;
1085 quint64 tot = 0;
1086
1087 quint32 myCRC = crc32(0L, Z_NULL, 0);
1088
1089 if (entry.compMethod == 0)
1090 {
1091 while ( (read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem)) > 0 )
1092 {
1093 if (entry.isEncrypted())
1094 decryptBytes(keys, buffer1, read);
1095
1096 myCRC = crc32(myCRC, uBuffer, read);
1097
1098 if (dev->write(buffer1, read) != read)
1099 return UnZip::WriteFailed;
1100
1101 cur++;
1102 tot += read;
1103
1104 if (tot == entry.szComp)
1105 break;
1106 }
1107
1108 if (read < 0)
1109 return UnZip::ReadFailed;
1110 }
1111 else if (entry.compMethod == 8)
1112 {
1113 /* Allocate inflate state */
1114 z_stream zstr;
1115 zstr.zalloc = Z_NULL;
1116 zstr.zfree = Z_NULL;
1117 zstr.opaque = Z_NULL;
1118 zstr.next_in = Z_NULL;
1119 zstr.avail_in = 0;
1120
1121 int zret;
1122
1123 // Use inflateInit2 with negative windowBits to get raw decompression
1124 if ( (zret = inflateInit2_(&zstr, -MAX_WBITS, ZLIB_VERSION, sizeof(z_stream))) != Z_OK )
1125 return UnZip::ZlibError;
1126
1127 int szDecomp;
1128
1129 // Decompress until deflate stream ends or end of file
1130 do
1131 {
1132 read = device->read(buffer1, cur < rep ? UNZIP_READ_BUFFER : rem);
1133 if (read == 0)
1134 break;
1135 if (read < 0)
1136 {
1137 (void)inflateEnd(&zstr);
1138 return UnZip::ReadFailed;
1139 }
1140
1141 if (entry.isEncrypted())
1142 decryptBytes(keys, buffer1, read);
1143
1144 cur++;
1145 tot += read;
1146
1147 zstr.avail_in = (uInt) read;
1148 zstr.next_in = (Bytef*) buffer1;
1149
1150
1151 // Run inflate() on input until output buffer not full
1152 do {
1153 zstr.avail_out = UNZIP_READ_BUFFER;
1154 zstr.next_out = (Bytef*) buffer2;;
1155
1156 zret = inflate(&zstr, Z_NO_FLUSH);
1157
1158 switch (zret) {
1159 case Z_NEED_DICT:
1160 case Z_DATA_ERROR:
1161 case Z_MEM_ERROR:
1162 inflateEnd(&zstr);
1163 return UnZip::WriteFailed;
1164 default:
1165 ;
1166 }
1167
1168 szDecomp = UNZIP_READ_BUFFER - zstr.avail_out;
1169 if (dev->write(buffer2, szDecomp) != szDecomp)
1170 {
1171 inflateEnd(&zstr);
1172 return UnZip::ZlibError;
1173 }
1174
1175 myCRC = crc32(myCRC, (const Bytef*) buffer2, szDecomp);
1176
1177 } while (zstr.avail_out == 0);
1178
1179 }
1180 while (zret != Z_STREAM_END);
1181
1182 inflateEnd(&zstr);
1183 }
1184
1185 if (myCRC != entry.crc)
1186 return UnZip::Corrupted;
1187
1188 return UnZip::Ok;
1189}
1190
1191//! \internal Creates a new directory and all the needed parent directories.
1192bool UnzipPrivate::createDirectory(const QString& path)
1193{
1194 QDir d(path);
1195 if (!d.exists())
1196 {
1197 int sep = path.lastIndexOf("/");
1198 if (sep <= 0) return true;
1199
1200 if (!createDirectory(path.left(sep)))
1201 return false;
1202
1203 if (!d.mkdir(path))
1204 {
1205 qDebug() << QString("Unable to create directory: %1").arg(path);
1206 return false;
1207 }
1208 }
1209
1210 return true;
1211}
1212
1213/*!
1214 \internal Reads an quint32 (4 bytes) from a byte array starting at given offset.
1215*/
1216quint32 UnzipPrivate::getULong(const unsigned char* data, quint32 offset) const
1217{
1218 quint32 res = (quint32) data[offset];
1219 res |= (((quint32)data[offset+1]) << 8);
1220 res |= (((quint32)data[offset+2]) << 16);
1221 res |= (((quint32)data[offset+3]) << 24);
1222
1223 return res;
1224}
1225
1226/*!
1227 \internal Reads an quint64 (8 bytes) from a byte array starting at given offset.
1228*/
1229quint64 UnzipPrivate::getULLong(const unsigned char* data, quint32 offset) const
1230{
1231 quint64 res = (quint64) data[offset];
1232 res |= (((quint64)data[offset+1]) << 8);
1233 res |= (((quint64)data[offset+2]) << 16);
1234 res |= (((quint64)data[offset+3]) << 24);
1235 res |= (((quint64)data[offset+1]) << 32);
1236 res |= (((quint64)data[offset+2]) << 40);
1237 res |= (((quint64)data[offset+3]) << 48);
1238 res |= (((quint64)data[offset+3]) << 56);
1239
1240 return res;
1241}
1242
1243/*!
1244 \internal Reads an quint16 (2 bytes) from a byte array starting at given offset.
1245*/
1246quint16 UnzipPrivate::getUShort(const unsigned char* data, quint32 offset) const
1247{
1248 return (quint16) data[offset] | (((quint16)data[offset+1]) << 8);
1249}
1250
1251/*!
1252 \internal Return the next byte in the pseudo-random sequence
1253 */
1254int UnzipPrivate::decryptByte(quint32 key2) const
1255{
1256 quint16 temp = ((quint16)(key2) & 0xffff) | 2;
1257 return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
1258}
1259
1260/*!
1261 \internal Update the encryption keys with the next byte of plain text
1262 */
1263void UnzipPrivate::updateKeys(quint32* keys, int c) const
1264{
1265 keys[0] = CRC32(keys[0], c);
1266 keys[1] += keys[0] & 0xff;
1267 keys[1] = keys[1] * 134775813L + 1;
1268 keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);
1269}
1270
1271/*!
1272 \internal Initialize the encryption keys and the random header according to
1273 the given password.
1274 */
1275void UnzipPrivate::initKeys(const QString& pwd, quint32* keys) const
1276{
1277 keys[0] = 305419896L;
1278 keys[1] = 591751049L;
1279 keys[2] = 878082192L;
1280
1281 QByteArray pwdBytes = pwd.toAscii();
1282 int sz = pwdBytes.size();
1283 const char* ascii = pwdBytes.data();
1284
1285 for (int i=0; i<sz; ++i)
1286 updateKeys(keys, (int)ascii[i]);
1287}
1288
1289/*!
1290 \internal Attempts to test a password without actually extracting a file.
1291 The \p file parameter can be used in the user interface or for debugging purposes
1292 as it is the name of the encrypted file for wich the password is being tested.
1293*/
1294UnZip::ErrorCode UnzipPrivate::testPassword(quint32* keys, const QString& file, const ZipEntryP& header)
1295{
1296 Q_UNUSED(file);
1297
1298 // read encryption keys
1299 if (device->read(buffer1, 12) != 12)
1300 return UnZip::Corrupted;
1301
1302 // Replace this code if you want to i.e. call some dialog and ask the user for a password
1303 initKeys(password, keys);
1304 if (testKeys(header, keys))
1305 return UnZip::Ok;
1306
1307 return UnZip::Skip;
1308}
1309
1310/*!
1311 \internal Tests a set of keys on the encryption header.
1312*/
1313bool UnzipPrivate::testKeys(const ZipEntryP& header, quint32* keys)
1314{
1315 char lastByte;
1316
1317 // decrypt encryption header
1318 for (int i=0; i<11; ++i)
1319 updateKeys(keys, lastByte = buffer1[i] ^ decryptByte(keys[2]));
1320 updateKeys(keys, lastByte = buffer1[11] ^ decryptByte(keys[2]));
1321
1322 // if there is an extended header (bit in the gp flag) buffer[11] is a byte from the file time
1323 // with no extended header we have to check the crc high-order byte
1324 char c = ((header.gpFlag[0] & 0x08) == 8) ? header.modTime[1] : header.crc >> 24;
1325
1326 return (lastByte == c);
1327}
1328
1329/*!
1330 \internal Decrypts an array of bytes long \p read.
1331*/
1332void UnzipPrivate::decryptBytes(quint32* keys, char* buffer, qint64 read)
1333{
1334 for (int i=0; i<(int)read; ++i)
1335 updateKeys(keys, buffer[i] ^= decryptByte(keys[2]));
1336}
1337
1338/*!
1339 \internal Converts date and time values from ZIP format to a QDateTime object.
1340*/
1341QDateTime UnzipPrivate::convertDateTime(const unsigned char date[2], const unsigned char time[2]) const
1342{
1343 QDateTime dt;
1344
1345 // Usual PKZip low-byte to high-byte order
1346
1347 // Date: 7 bits = years from 1980, 4 bits = month, 5 bits = day
1348 quint16 year = (date[1] >> 1) & 127;
1349 quint16 month = ((date[1] << 3) & 14) | ((date[0] >> 5) & 7);
1350 quint16 day = date[0] & 31;
1351
1352 // Time: 5 bits hour, 6 bits minutes, 5 bits seconds with a 2sec precision
1353 quint16 hour = (time[1] >> 3) & 31;
1354 quint16 minutes = ((time[1] << 3) & 56) | ((time[0] >> 5) & 7);
1355 quint16 seconds = (time[0] & 31) * 2;
1356
1357 dt.setDate(QDate(1980 + year, month, day));
1358 dt.setTime(QTime(hour, minutes, seconds));
1359 return dt;
1360}
diff --git a/rbutil/rbutilqt/zip/unzip.h b/rbutil/rbutilqt/zip/unzip.h
new file mode 100644
index 0000000000..a48fbe8724
--- /dev/null
+++ b/rbutil/rbutilqt/zip/unzip.h
@@ -0,0 +1,144 @@
1/****************************************************************************
2** Filename: unzip.h
3** Last updated [dd/mm/yyyy]: 28/01/2007
4**
5** pkzip 2.0 decompression.
6**
7** Some of the code has been inspired by other open source projects,
8** (mainly Info-Zip and Gilles Vollant's minizip).
9** Compression and decompression actually uses the zlib library.
10**
11** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12**
13** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22**
23** See the file LICENSE.GPL that came with this software distribution or
24** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25**
26**********************************************************************/
27
28#ifndef OSDAB_UNZIP__H
29#define OSDAB_UNZIP__H
30
31#include <QtGlobal>
32#include <QMap>
33#include <QDateTime>
34
35#include <zlib/zlib.h>
36
37class UnzipPrivate;
38class QIODevice;
39class QFile;
40class QDir;
41class QStringList;
42class QString;
43
44
45class UnZip
46{
47public:
48 enum ErrorCode
49 {
50 Ok,
51 ZlibInit,
52 ZlibError,
53 OpenFailed,
54 PartiallyCorrupted,
55 Corrupted,
56 WrongPassword,
57 NoOpenArchive,
58 FileNotFound,
59 ReadFailed,
60 WriteFailed,
61 SeekFailed,
62 CreateDirFailed,
63 InvalidDevice,
64 InvalidArchive,
65 HeaderConsistencyError,
66
67 Skip, SkipAll // internal use only
68 };
69
70 enum ExtractionOption
71 {
72 //! Extracts paths (default)
73 ExtractPaths = 0x0001,
74 //! Ignores paths and extracts all the files to the same directory
75 SkipPaths = 0x0002
76 };
77 Q_DECLARE_FLAGS(ExtractionOptions, ExtractionOption)
78
79 enum CompressionMethod
80 {
81 NoCompression, Deflated, UnknownCompression
82 };
83
84 enum FileType
85 {
86 File, Directory
87 };
88
89 typedef struct ZipEntry
90 {
91 ZipEntry();
92
93 QString filename;
94 QString comment;
95
96 quint32 compressedSize;
97 quint32 uncompressedSize;
98 quint32 crc32;
99
100 QDateTime lastModified;
101
102 CompressionMethod compression;
103 FileType type;
104
105 bool encrypted;
106 };
107
108 UnZip();
109 virtual ~UnZip();
110
111 bool isOpen() const;
112
113 ErrorCode openArchive(const QString& filename);
114 ErrorCode openArchive(QIODevice* device);
115 void closeArchive();
116
117 QString archiveComment() const;
118
119 QString formatError(UnZip::ErrorCode c) const;
120
121 bool contains(const QString& file) const;
122
123 QStringList fileList() const;
124 QList<ZipEntry> entryList() const;
125
126 ErrorCode extractAll(const QString& dirname, ExtractionOptions options = ExtractPaths);
127 ErrorCode extractAll(const QDir& dir, ExtractionOptions options = ExtractPaths);
128
129 ErrorCode extractFile(const QString& filename, const QString& dirname, ExtractionOptions options = ExtractPaths);
130 ErrorCode extractFile(const QString& filename, const QDir& dir, ExtractionOptions options = ExtractPaths);
131 ErrorCode extractFile(const QString& filename, QIODevice* device, ExtractionOptions options = ExtractPaths);
132
133 ErrorCode extractFiles(const QStringList& filenames, const QString& dirname, ExtractionOptions options = ExtractPaths);
134 ErrorCode extractFiles(const QStringList& filenames, const QDir& dir, ExtractionOptions options = ExtractPaths);
135
136 void setPassword(const QString& pwd);
137
138private:
139 UnzipPrivate* d;
140};
141
142Q_DECLARE_OPERATORS_FOR_FLAGS(UnZip::ExtractionOptions)
143
144#endif // OSDAB_UNZIP__H
diff --git a/rbutil/rbutilqt/zip/unzip_p.h b/rbutil/rbutilqt/zip/unzip_p.h
new file mode 100644
index 0000000000..c05ac67b47
--- /dev/null
+++ b/rbutil/rbutilqt/zip/unzip_p.h
@@ -0,0 +1,112 @@
1/****************************************************************************
2** Filename: unzip_p.h
3** Last updated [dd/mm/yyyy]: 28/01/2007
4**
5** pkzip 2.0 decompression.
6**
7** Some of the code has been inspired by other open source projects,
8** (mainly Info-Zip and Gilles Vollant's minizip).
9** Compression and decompression actually uses the zlib library.
10**
11** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12**
13** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22**
23** See the file LICENSE.GPL that came with this software distribution or
24** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25**
26**********************************************************************/
27
28//
29// W A R N I N G
30// -------------
31//
32// This file is not part of the Zip/UnZip API. It exists purely as an
33// implementation detail. This header file may change from version to
34// version without notice, or even be removed.
35//
36// We mean it.
37//
38
39#ifndef OSDAB_UNZIP_P__H
40#define OSDAB_UNZIP_P__H
41
42#include "unzip.h"
43#include "zipentry_p.h"
44
45#include <QtGlobal>
46
47// zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate())
48// we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;)
49#define UNZIP_READ_BUFFER (256*1024)
50
51class UnzipPrivate
52{
53public:
54 UnzipPrivate();
55
56 // Replace this with whatever else you use to store/retrieve the password.
57 QString password;
58
59 bool skipAllEncrypted;
60
61 QMap<QString,ZipEntryP*>* headers;
62
63 QIODevice* device;
64
65 char buffer1[UNZIP_READ_BUFFER];
66 char buffer2[UNZIP_READ_BUFFER];
67
68 unsigned char* uBuffer;
69 const quint32* crcTable;
70
71 // Central Directory (CD) offset
72 quint32 cdOffset;
73 // End of Central Directory (EOCD) offset
74 quint32 eocdOffset;
75
76 // Number of entries in the Central Directory (as to the EOCD record)
77 quint16 cdEntryCount;
78
79 // The number of detected entries that have been skipped because of a non compatible format
80 quint16 unsupportedEntryCount;
81
82 QString comment;
83
84 UnZip::ErrorCode openArchive(QIODevice* device);
85
86 UnZip::ErrorCode seekToCentralDirectory();
87 UnZip::ErrorCode parseCentralDirectoryRecord();
88 UnZip::ErrorCode parseLocalHeaderRecord(const QString& path, ZipEntryP& entry);
89
90 void closeArchive();
91
92 UnZip::ErrorCode extractFile(const QString& path, ZipEntryP& entry, const QDir& dir, UnZip::ExtractionOptions options);
93 UnZip::ErrorCode extractFile(const QString& path, ZipEntryP& entry, QIODevice* device, UnZip::ExtractionOptions options);
94
95 UnZip::ErrorCode testPassword(quint32* keys, const QString& file, const ZipEntryP& header);
96 bool testKeys(const ZipEntryP& header, quint32* keys);
97
98 bool createDirectory(const QString& path);
99
100 inline void decryptBytes(quint32* keys, char* buffer, qint64 read);
101
102 inline quint32 getULong(const unsigned char* data, quint32 offset) const;
103 inline quint64 getULLong(const unsigned char* data, quint32 offset) const;
104 inline quint16 getUShort(const unsigned char* data, quint32 offset) const;
105 inline int decryptByte(quint32 key2) const;
106 inline void updateKeys(quint32* keys, int c) const;
107 inline void initKeys(const QString& pwd, quint32* keys) const;
108
109 inline QDateTime convertDateTime(const unsigned char date[2], const unsigned char time[2]) const;
110};
111
112#endif // OSDAB_UNZIP_P__H
diff --git a/rbutil/rbutilqt/zip/zip.cpp b/rbutil/rbutilqt/zip/zip.cpp
new file mode 100644
index 0000000000..bb02147da3
--- /dev/null
+++ b/rbutil/rbutilqt/zip/zip.cpp
@@ -0,0 +1,1219 @@
1/****************************************************************************
2** Filename: zip.cpp
3** Last updated [dd/mm/yyyy]: 01/02/2007
4**
5** pkzip 2.0 file compression.
6**
7** Some of the code has been inspired by other open source projects,
8** (mainly Info-Zip and Gilles Vollant's minizip).
9** Compression and decompression actually uses the zlib library.
10**
11** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12**
13** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22**
23** See the file LICENSE.GPL that came with this software distribution or
24** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25**
26**********************************************************************/
27
28#include "zip.h"
29#include "zip_p.h"
30#include "zipentry_p.h"
31
32// we only use this to seed the random number generator
33#include <time.h>
34
35#include <QMap>
36#include <QString>
37#include <QStringList>
38#include <QDir>
39#include <QFile>
40#include <QDateTime>
41#include <QCoreApplication>
42
43// You can remove this #include if you replace the qDebug() statements.
44#include <QtDebug>
45
46//! Local header size (including signature, excluding variable length fields)
47#define ZIP_LOCAL_HEADER_SIZE 30
48//! Encryption header size
49#define ZIP_LOCAL_ENC_HEADER_SIZE 12
50//! Data descriptor size (signature included)
51#define ZIP_DD_SIZE_WS 16
52//! Central Directory record size (signature included)
53#define ZIP_CD_SIZE 46
54//! End of Central Directory record size (signature included)
55#define ZIP_EOCD_SIZE 22
56
57// Some offsets inside a local header record (signature included)
58#define ZIP_LH_OFF_VERS 4
59#define ZIP_LH_OFF_GPFLAG 6
60#define ZIP_LH_OFF_CMET 8
61#define ZIP_LH_OFF_MODT 10
62#define ZIP_LH_OFF_MODD 12
63#define ZIP_LH_OFF_CRC 14
64#define ZIP_LH_OFF_CSIZE 18
65#define ZIP_LH_OFF_USIZE 22
66#define ZIP_LH_OFF_NAMELEN 26
67#define ZIP_LH_OFF_XLEN 28
68
69// Some offsets inside a data descriptor record (including signature)
70#define ZIP_DD_OFF_CRC32 4
71#define ZIP_DD_OFF_CSIZE 8
72#define ZIP_DD_OFF_USIZE 12
73
74// Some offsets inside a Central Directory record (including signature)
75#define ZIP_CD_OFF_MADEBY 4
76#define ZIP_CD_OFF_VERSION 6
77#define ZIP_CD_OFF_GPFLAG 8
78#define ZIP_CD_OFF_CMET 10
79#define ZIP_CD_OFF_MODT 12
80#define ZIP_CD_OFF_MODD 14
81#define ZIP_CD_OFF_CRC 16
82#define ZIP_CD_OFF_CSIZE 20
83#define ZIP_CD_OFF_USIZE 24
84#define ZIP_CD_OFF_NAMELEN 28
85#define ZIP_CD_OFF_XLEN 30
86#define ZIP_CD_OFF_COMMLEN 32
87#define ZIP_CD_OFF_DISKSTART 34
88#define ZIP_CD_OFF_IATTR 36
89#define ZIP_CD_OFF_EATTR 38
90#define ZIP_CD_OFF_LHOFF 42
91
92// Some offsets inside a EOCD record (including signature)
93#define ZIP_EOCD_OFF_DISKNUM 4
94#define ZIP_EOCD_OFF_CDDISKNUM 6
95#define ZIP_EOCD_OFF_ENTRIES 8
96#define ZIP_EOCD_OFF_CDENTRIES 10
97#define ZIP_EOCD_OFF_CDSIZE 12
98#define ZIP_EOCD_OFF_CDOFF 16
99#define ZIP_EOCD_OFF_COMMLEN 20
100
101//! PKZip version for archives created by this API
102#define ZIP_VERSION 0x14
103
104//! Do not store very small files as the compression headers overhead would be to big
105#define ZIP_COMPRESSION_THRESHOLD 60
106
107//! This macro updates a one-char-only CRC; it's the Info-Zip macro re-adapted
108#define CRC32(c, b) crcTable[((int)c^b) & 0xff] ^ (c >> 8)
109
110/*!
111 \class Zip zip.h
112
113 \brief Zip file compression.
114
115 Some quick usage examples.
116
117 \verbatim
118 Suppose you have this directory structure:
119
120 /root/dir1/
121 /root/dir1/file1.1
122 /root/dir1/file1.2
123 /root/dir1/dir1.1/
124 /root/dir1/dir1.2/file1.2.1
125
126 EXAMPLE 1:
127 myZipInstance.addDirectory("/root/dir1");
128
129 RESULT:
130 Beheaves like any common zip software and creates a zip file with this structure:
131
132 dir1/
133 dir1/file1.1
134 dir1/file1.2
135 dir1/dir1.1/
136 dir1/dir1.2/file1.2.1
137
138 EXAMPLE 2:
139 myZipInstance.addDirectory("/root/dir1", "myRoot/myFolder");
140
141 RESULT:
142 Adds a custom root to the paths and creates a zip file with this structure:
143
144 myRoot/myFolder/dir1/
145 myRoot/myFolder/dir1/file1.1
146 myRoot/myFolder/dir1/file1.2
147 myRoot/myFolder/dir1/dir1.1/
148 myRoot/myFolder/dir1/dir1.2/file1.2.1
149
150 EXAMPLE 3:
151 myZipInstance.addDirectory("/root/dir1", Zip::AbsolutePaths);
152
153 NOTE:
154 Same as calling addDirectory(SOME_PATH, PARENT_PATH_of_SOME_PATH).
155
156 RESULT:
157 Preserves absolute paths and creates a zip file with this structure:
158
159 /root/dir1/
160 /root/dir1/file1.1
161 /root/dir1/file1.2
162 /root/dir1/dir1.1/
163 /root/dir1/dir1.2/file1.2.1
164
165 EXAMPLE 4:
166 myZipInstance.setPassword("hellopass");
167 myZipInstance.addDirectory("/root/dir1", "/");
168
169 RESULT:
170 Adds and encrypts the files in /root/dir1, creating the following zip structure:
171
172 /dir1/
173 /dir1/file1.1
174 /dir1/file1.2
175 /dir1/dir1.1/
176 /dir1/dir1.2/file1.2.1
177
178 \endverbatim
179*/
180
181/*! \enum Zip::ErrorCode The result of a compression operation.
182 \value Zip::Ok No error occurred.
183 \value Zip::ZlibInit Failed to init or load the zlib library.
184 \value Zip::ZlibError The zlib library returned some error.
185 \value Zip::FileExists The file already exists and will not be overwritten.
186 \value Zip::OpenFailed Unable to create or open a device.
187 \value Zip::NoOpenArchive CreateArchive() has not been called yet.
188 \value Zip::FileNotFound File or directory does not exist.
189 \value Zip::ReadFailed Reading of a file failed.
190 \value Zip::WriteFailed Writing of a file failed.
191 \value Zip::SeekFailed Seek failed.
192*/
193
194/*! \enum Zip::CompressionLevel Returns the result of a decompression operation.
195 \value Zip::Store No compression.
196 \value Zip::Deflate1 Deflate compression level 1(lowest compression).
197 \value Zip::Deflate1 Deflate compression level 2.
198 \value Zip::Deflate1 Deflate compression level 3.
199 \value Zip::Deflate1 Deflate compression level 4.
200 \value Zip::Deflate1 Deflate compression level 5.
201 \value Zip::Deflate1 Deflate compression level 6.
202 \value Zip::Deflate1 Deflate compression level 7.
203 \value Zip::Deflate1 Deflate compression level 8.
204 \value Zip::Deflate1 Deflate compression level 9 (maximum compression).
205 \value Zip::AutoCPU Adapt compression level to CPU speed (faster CPU => better compression).
206 \value Zip::AutoMIME Adapt compression level to MIME type of the file being compressed.
207 \value Zip::AutoFull Use both CPU and MIME type detection.
208*/
209
210
211/************************************************************************
212 Public interface
213*************************************************************************/
214
215/*!
216 Creates a new Zip file compressor.
217*/
218Zip::Zip()
219{
220 d = new ZipPrivate;
221}
222
223/*!
224 Closes any open archive and releases used resources.
225*/
226Zip::~Zip()
227{
228 closeArchive();
229 delete d;
230}
231
232/*!
233 Returns true if there is an open archive.
234*/
235bool Zip::isOpen() const
236{
237 return d->device != 0;
238}
239
240/*!
241 Sets the password to be used for the next files being added!
242 Files added before calling this method will use the previously
243 set password (if any).
244 Closing the archive won't clear the password!
245*/
246void Zip::setPassword(const QString& pwd)
247{
248 d->password = pwd;
249}
250
251//! Convenience method, clears the current password.
252void Zip::clearPassword()
253{
254 d->password.clear();
255}
256
257//! Returns the currently used password.
258QString Zip::password() const
259{
260 return d->password;
261}
262
263/*!
264 Attempts to create a new Zip archive. If \p overwrite is true and the file
265 already exist it will be overwritten.
266 Any open archive will be closed.
267 */
268Zip::ErrorCode Zip::createArchive(const QString& filename, bool overwrite)
269{
270 QFile* file = new QFile(filename);
271
272 if (file->exists() && !overwrite) {
273 delete file;
274 return Zip::FileExists;
275 }
276
277 if (!file->open(QIODevice::WriteOnly)) {
278 delete file;
279 return Zip::OpenFailed;
280 }
281
282 Zip::ErrorCode ec = createArchive(file);
283 if (ec != Zip::Ok) {
284 file->remove();
285 }
286
287 return ec;
288}
289
290/*!
291 Attempts to create a new Zip archive. If there is another open archive this will be closed.
292 \warning The class takes ownership of the device!
293 */
294Zip::ErrorCode Zip::createArchive(QIODevice* device)
295{
296 if (device == 0)
297 {
298 qDebug() << "Invalid device.";
299 return Zip::OpenFailed;
300 }
301
302 return d->createArchive(device);
303}
304
305/*!
306 Returns the current archive comment.
307*/
308QString Zip::archiveComment() const
309{
310 return d->comment;
311}
312
313/*!
314 Sets the comment for this archive. Note: createArchive() should have been
315 called before.
316*/
317void Zip::setArchiveComment(const QString& comment)
318{
319 if (d->device != 0)
320 d->comment = comment;
321}
322
323/*!
324 Convenience method, same as calling
325 Zip::addDirectory(const QString&,const QString&,CompressionLevel)
326 with an empty \p root parameter (or with the parent directory of \p path if the
327 AbsolutePaths options is set).
328
329 The ExtractionOptions are checked in the order they are defined in the zip.h heaser file.
330 This means that the last one overwrites the previous one (if some conflict occurs), i.e.
331 Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths.
332 */
333Zip::ErrorCode Zip::addDirectory(const QString& path, CompressionOptions options, CompressionLevel level)
334{
335 return addDirectory(path, QString(), options, level);
336}
337
338/*!
339 Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel)
340 with the Zip::RelativePaths flag as compression option.
341 */
342Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionLevel level)
343{
344 return addDirectory(path, root, Zip::RelativePaths, level);
345}
346
347/*!
348 Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel)
349 with the Zip::IgnorePaths flag as compression option and an empty \p root parameter.
350*/
351Zip::ErrorCode Zip::addDirectoryContents(const QString& path, CompressionLevel level)
352{
353 return addDirectory(path, QString(), IgnorePaths, level);
354}
355
356/*!
357 Convenience method, same as calling Zip::addDirectory(const QString&,const QString&,CompressionOptions,CompressionLevel)
358 with the Zip::IgnorePaths flag as compression option.
359*/
360Zip::ErrorCode Zip::addDirectoryContents(const QString& path, const QString& root, CompressionLevel level)
361{
362 return addDirectory(path, root, IgnorePaths, level);
363}
364
365/*!
366 Recursively adds files contained in \p dir to the archive, using \p root as name for the root folder.
367 Stops adding files if some error occurs.
368
369 The ExtractionOptions are checked in the order they are defined in the zip.h heaser file.
370 This means that the last one overwrites the previous one (if some conflict occurs), i.e.
371 Zip::IgnorePaths | Zip::AbsolutePaths would be interpreted as Zip::IgnorePaths.
372
373 The \p root parameter is ignored with the Zip::IgnorePaths parameter and used as path prefix (a trailing /
374 is always added as directory separator!) otherwise (even with Zip::AbsolutePaths set!).
375*/
376Zip::ErrorCode Zip::addDirectory(const QString& path, const QString& root, CompressionOptions options, CompressionLevel level)
377{
378 // qDebug() << QString("addDir(path=%1, root=%2)").arg(path, root);
379
380 // Bad boy didn't call createArchive() yet :)
381 if (d->device == 0)
382 return Zip::NoOpenArchive;
383
384 QDir dir(path);
385 if (!dir.exists())
386 return Zip::FileNotFound;
387
388 // Remove any trailing separator
389 QString actualRoot = root.trimmed();
390
391 // Preserve Unix root
392 if (actualRoot != "/")
393 {
394 while (actualRoot.endsWith("/") || actualRoot.endsWith("\\"))
395 actualRoot.truncate(actualRoot.length() - 1);
396 }
397
398 // QDir::cleanPath() fixes some issues with QDir::dirName()
399 QFileInfo current(QDir::cleanPath(path));
400
401 if (!actualRoot.isEmpty() && actualRoot != "/")
402 actualRoot.append("/");
403
404 /* This part is quite confusing and needs some test or check */
405 /* An attempt to compress the / root directory evtl. using a root prefix should be a good test */
406 if (options.testFlag(AbsolutePaths) && !options.testFlag(IgnorePaths))
407 {
408 QString absolutePath = d->extractRoot(path);
409 if (!absolutePath.isEmpty() && absolutePath != "/")
410 absolutePath.append("/");
411 actualRoot.append(absolutePath);
412 }
413
414 if (!options.testFlag(IgnorePaths))
415 {
416 actualRoot = actualRoot.append(QDir(current.absoluteFilePath()).dirName());
417 actualRoot.append("/");
418 }
419
420 // actualRoot now contains the path of the file relative to the zip archive
421 // with a trailing /
422
423 QFileInfoList list = dir.entryInfoList(
424 QDir::Files |
425 QDir::Dirs |
426 QDir::NoDotAndDotDot |
427 QDir::NoSymLinks);
428
429 ErrorCode ec = Zip::Ok;
430 bool filesAdded = false;
431
432 CompressionOptions recursionOptions;
433 if (options.testFlag(IgnorePaths))
434 recursionOptions |= IgnorePaths;
435 else recursionOptions |= RelativePaths;
436
437 for (int i = 0; i < list.size() && ec == Zip::Ok; ++i)
438 {
439 QFileInfo info = list.at(i);
440
441 if (info.isDir())
442 {
443 // Recursion :)
444 ec = addDirectory(info.absoluteFilePath(), actualRoot, recursionOptions, level);
445 }
446 else
447 {
448 ec = d->createEntry(info, actualRoot, level);
449 filesAdded = true;
450 }
451 }
452
453
454 // We need an explicit record for this dir
455 // Non-empty directories don't need it because they have a path component in the filename
456 if (!filesAdded && !options.testFlag(IgnorePaths))
457 ec = d->createEntry(current, actualRoot, level);
458
459 return ec;
460}
461
462/*!
463 Closes the archive and writes any pending data.
464*/
465Zip::ErrorCode Zip::closeArchive()
466{
467 Zip::ErrorCode ec = d->closeArchive();
468 d->reset();
469 return ec;
470}
471
472/*!
473 Returns a locale translated error string for a given error code.
474*/
475QString Zip::formatError(Zip::ErrorCode c) const
476{
477 switch (c)
478 {
479 case Ok: return QCoreApplication::translate("Zip", "ZIP operation completed successfully."); break;
480 case ZlibInit: return QCoreApplication::translate("Zip", "Failed to initialize or load zlib library."); break;
481 case ZlibError: return QCoreApplication::translate("Zip", "zlib library error."); break;
482 case OpenFailed: return QCoreApplication::translate("Zip", "Unable to create or open file."); break;
483 case NoOpenArchive: return QCoreApplication::translate("Zip", "No archive has been created yet."); break;
484 case FileNotFound: return QCoreApplication::translate("Zip", "File or directory does not exist."); break;
485 case ReadFailed: return QCoreApplication::translate("Zip", "File read error."); break;
486 case WriteFailed: return QCoreApplication::translate("Zip", "File write error."); break;
487 case SeekFailed: return QCoreApplication::translate("Zip", "File seek error."); break;
488 default: ;
489 }
490
491 return QCoreApplication::translate("Zip", "Unknown error.");
492}
493
494
495/************************************************************************
496 Private interface
497*************************************************************************/
498
499//! \internal
500ZipPrivate::ZipPrivate()
501{
502 headers = 0;
503 device = 0;
504
505 // keep an unsigned pointer so we avoid to over bloat the code with casts
506 uBuffer = (unsigned char*) buffer1;
507 crcTable = (quint32*) get_crc_table();
508}
509
510//! \internal
511ZipPrivate::~ZipPrivate()
512{
513 closeArchive();
514}
515
516//! \internal
517Zip::ErrorCode ZipPrivate::createArchive(QIODevice* dev)
518{
519 Q_ASSERT(dev != 0);
520
521 if (device != 0)
522 closeArchive();
523
524 device = dev;
525
526 if (!device->isOpen())
527 {
528 if (!device->open(QIODevice::ReadOnly)) {
529 delete device;
530 device = 0;
531 qDebug() << "Unable to open device for writing.";
532 return Zip::OpenFailed;
533 }
534 }
535
536 headers = new QMap<QString,ZipEntryP*>;
537 return Zip::Ok;
538}
539
540//! \internal Writes a new entry in the zip file.
541Zip::ErrorCode ZipPrivate::createEntry(const QFileInfo& file, const QString& root, Zip::CompressionLevel level)
542{
543 //! \todo Automatic level detection (cpu, extension & file size)
544
545 // Directories and very small files are always stored
546 // (small files would get bigger due to the compression headers overhead)
547
548 // Need this for zlib
549 bool isPNGFile = false;
550 bool dirOnly = file.isDir();
551
552 QString entryName = root;
553
554 // Directory entry
555 if (dirOnly)
556 level = Zip::Store;
557 else
558 {
559 entryName.append(file.fileName());
560
561 QString ext = file.completeSuffix().toLower();
562 isPNGFile = ext == "png";
563
564 if (file.size() < ZIP_COMPRESSION_THRESHOLD)
565 level = Zip::Store;
566 else
567 switch (level)
568 {
569 case Zip::AutoCPU:
570 level = Zip::Deflate5;
571 break;
572 case Zip::AutoMIME:
573 level = detectCompressionByMime(ext);
574 break;
575 case Zip::AutoFull:
576 level = detectCompressionByMime(ext);
577 break;
578 default:
579 ;
580 }
581 }
582
583 // entryName contains the path as it should be written
584 // in the zip file records
585 // qDebug() << QString("addDir(file=%1, root=%2, entry=%3)").arg(file.absoluteFilePath(), root, entryName);
586
587 // create header and store it to write a central directory later
588 ZipEntryP* h = new ZipEntryP;
589
590 h->compMethod = (level == Zip::Store) ? 0 : 0x0008;
591
592 // Set encryption bit and set the data descriptor bit
593 // so we can use mod time instead of crc for password check
594 bool encrypt = !dirOnly && !password.isEmpty();
595 if (encrypt)
596 h->gpFlag[0] |= 9;
597
598 QDateTime dt = file.lastModified();
599 QDate d = dt.date();
600 h->modDate[1] = ((d.year() - 1980) << 1) & 254;
601 h->modDate[1] |= ((d.month() >> 3) & 1);
602 h->modDate[0] = ((d.month() & 7) << 5) & 224;
603 h->modDate[0] |= d.day();
604
605 QTime t = dt.time();
606 h->modTime[1] = (t.hour() << 3) & 248;
607 h->modTime[1] |= ((t.minute() >> 3) & 7);
608 h->modTime[0] = ((t.minute() & 7) << 5) & 224;
609 h->modTime[0] |= t.second() / 2;
610
611 h->szUncomp = dirOnly ? 0 : file.size();
612
613 // **** Write local file header ****
614
615 // signature
616 buffer1[0] = 'P'; buffer1[1] = 'K';
617 buffer1[2] = 0x3; buffer1[3] = 0x4;
618
619 // version needed to extract
620 buffer1[ZIP_LH_OFF_VERS] = ZIP_VERSION;
621 buffer1[ZIP_LH_OFF_VERS + 1] = 0;
622
623 // general purpose flag
624 buffer1[ZIP_LH_OFF_GPFLAG] = h->gpFlag[0];
625 buffer1[ZIP_LH_OFF_GPFLAG + 1] = h->gpFlag[1];
626
627 // compression method
628 buffer1[ZIP_LH_OFF_CMET] = h->compMethod & 0xFF;
629 buffer1[ZIP_LH_OFF_CMET + 1] = (h->compMethod>>8) & 0xFF;
630
631 // last mod file time
632 buffer1[ZIP_LH_OFF_MODT] = h->modTime[0];
633 buffer1[ZIP_LH_OFF_MODT + 1] = h->modTime[1];
634
635 // last mod file date
636 buffer1[ZIP_LH_OFF_MODD] = h->modDate[0];
637 buffer1[ZIP_LH_OFF_MODD + 1] = h->modDate[1];
638
639 // skip crc (4bytes) [14,15,16,17]
640
641 // skip compressed size but include evtl. encryption header (4bytes: [18,19,20,21])
642 buffer1[ZIP_LH_OFF_CSIZE] =
643 buffer1[ZIP_LH_OFF_CSIZE + 1] =
644 buffer1[ZIP_LH_OFF_CSIZE + 2] =
645 buffer1[ZIP_LH_OFF_CSIZE + 3] = 0;
646
647 h->szComp = encrypt ? ZIP_LOCAL_ENC_HEADER_SIZE : 0;
648
649 // uncompressed size [22,23,24,25]
650 setULong(h->szUncomp, buffer1, ZIP_LH_OFF_USIZE);
651
652 // filename length
653 QByteArray entryNameBytes = entryName.toAscii();
654 int sz = entryNameBytes.size();
655
656 buffer1[ZIP_LH_OFF_NAMELEN] = sz & 0xFF;
657 buffer1[ZIP_LH_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF;
658
659 // extra field length
660 buffer1[ZIP_LH_OFF_XLEN] = buffer1[ZIP_LH_OFF_XLEN + 1] = 0;
661
662 // Store offset to write crc and compressed size
663 h->lhOffset = device->pos();
664 quint32 crcOffset = h->lhOffset + ZIP_LH_OFF_CRC;
665
666 if (device->write(buffer1, ZIP_LOCAL_HEADER_SIZE) != ZIP_LOCAL_HEADER_SIZE)
667 {
668 delete h;
669 return Zip::WriteFailed;
670 }
671
672 // Write out filename
673 if (device->write(entryNameBytes) != sz)
674 {
675 delete h;
676 return Zip::WriteFailed;
677 }
678
679 // Encryption keys
680 quint32 keys[3] = { 0, 0, 0 };
681
682 if (encrypt)
683 {
684 // **** encryption header ****
685
686 // XOR with PI to ensure better random numbers
687 // with poorly implemented rand() as suggested by Info-Zip
688 srand(time(NULL) ^ 3141592654UL);
689 int randByte;
690
691 initKeys(keys);
692 for (int i=0; i<10; ++i)
693 {
694 randByte = (rand() >> 7) & 0xff;
695 buffer1[i] = decryptByte(keys[2]) ^ randByte;
696 updateKeys(keys, randByte);
697 }
698
699 // Encrypt encryption header
700 initKeys(keys);
701 for (int i=0; i<10; ++i)
702 {
703 randByte = decryptByte(keys[2]);
704 updateKeys(keys, buffer1[i]);
705 buffer1[i] ^= randByte;
706 }
707
708 // We don't know the CRC at this time, so we use the modification time
709 // as the last two bytes
710 randByte = decryptByte(keys[2]);
711 updateKeys(keys, h->modTime[0]);
712 buffer1[10] ^= randByte;
713
714 randByte = decryptByte(keys[2]);
715 updateKeys(keys, h->modTime[1]);
716 buffer1[11] ^= randByte;
717
718 // Write out encryption header
719 if (device->write(buffer1, ZIP_LOCAL_ENC_HEADER_SIZE) != ZIP_LOCAL_ENC_HEADER_SIZE)
720 {
721 delete h;
722 return Zip::WriteFailed;
723 }
724 }
725
726 qint64 written = 0;
727 quint32 crc = crc32(0L, Z_NULL, 0);
728
729 if (!dirOnly)
730 {
731 QFile actualFile(file.absoluteFilePath());
732 if (!actualFile.open(QIODevice::ReadOnly))
733 {
734 qDebug() << QString("An error occurred while opening %1").arg(file.absoluteFilePath());
735 return Zip::OpenFailed;
736 }
737
738 // Write file data
739 qint64 read = 0;
740 qint64 totRead = 0;
741 qint64 toRead = actualFile.size();
742
743 if (level == Zip::Store)
744 {
745 while ( (read = actualFile.read(buffer1, ZIP_READ_BUFFER)) > 0 )
746 {
747 crc = crc32(crc, uBuffer, read);
748
749 if (password != 0)
750 encryptBytes(keys, buffer1, read);
751
752 if ( (written = device->write(buffer1, read)) != read )
753 {
754 actualFile.close();
755 delete h;
756 return Zip::WriteFailed;
757 }
758 }
759 }
760 else
761 {
762 z_stream zstr;
763
764 // Initialize zalloc, zfree and opaque before calling the init function
765 zstr.zalloc = Z_NULL;
766 zstr.zfree = Z_NULL;
767 zstr.opaque = Z_NULL;
768
769 int zret;
770
771 // Use deflateInit2 with negative windowBits to get raw compression
772 if ((zret = deflateInit2_(
773 &zstr,
774 (int)level,
775 Z_DEFLATED,
776 -MAX_WBITS,
777 8,
778 isPNGFile ? Z_RLE : Z_DEFAULT_STRATEGY,
779 ZLIB_VERSION,
780 sizeof(z_stream)
781 )) != Z_OK )
782 {
783 actualFile.close();
784 qDebug() << "Could not initialize zlib for compression";
785 delete h;
786 return Zip::ZlibError;
787 }
788
789 qint64 compressed;
790
791 int flush = Z_NO_FLUSH;
792
793 do
794 {
795 read = actualFile.read(buffer1, ZIP_READ_BUFFER);
796 totRead += read;
797
798 if (read == 0)
799 break;
800 if (read < 0)
801 {
802 actualFile.close();
803 deflateEnd(&zstr);
804 qDebug() << QString("Error while reading %1").arg(file.absoluteFilePath());
805 delete h;
806 return Zip::ReadFailed;
807 }
808
809 crc = crc32(crc, uBuffer, read);
810
811 zstr.next_in = (Bytef*) buffer1;
812 zstr.avail_in = (uInt)read;
813
814 // Tell zlib if this is the last chunk we want to encode
815 // by setting the flush parameter to Z_FINISH
816 flush = (totRead == toRead) ? Z_FINISH : Z_NO_FLUSH;
817
818 // Run deflate() on input until output buffer not full
819 // finish compression if all of source has been read in
820 do
821 {
822 zstr.next_out = (Bytef*) buffer2;
823 zstr.avail_out = ZIP_READ_BUFFER;
824
825 zret = deflate(&zstr, flush);
826 // State not clobbered
827 Q_ASSERT(zret != Z_STREAM_ERROR);
828
829 // Write compressed data to file and empty buffer
830 compressed = ZIP_READ_BUFFER - zstr.avail_out;
831
832 if (password != 0)
833 encryptBytes(keys, buffer2, compressed);
834
835 if (device->write(buffer2, compressed) != compressed)
836 {
837 deflateEnd(&zstr);
838 actualFile.close();
839 qDebug() << QString("Error while writing %1").arg(file.absoluteFilePath());
840 delete h;
841 return Zip::WriteFailed;
842 }
843
844 written += compressed;
845
846 } while (zstr.avail_out == 0);
847
848 // All input will be used
849 Q_ASSERT(zstr.avail_in == 0);
850
851 } while (flush != Z_FINISH);
852
853 // Stream will be complete
854 Q_ASSERT(zret == Z_STREAM_END);
855
856 deflateEnd(&zstr);
857
858 } // if (level != STORE)
859
860 actualFile.close();
861 }
862
863 // Store end of entry offset
864 quint32 current = device->pos();
865
866 // Update crc and compressed size in local header
867 if (!device->seek(crcOffset))
868 {
869 delete h;
870 return Zip::SeekFailed;
871 }
872
873 h->crc = dirOnly ? 0 : crc;
874 h->szComp += written;
875
876 setULong(h->crc, buffer1, 0);
877 setULong(h->szComp, buffer1, 4);
878 if ( device->write(buffer1, 8) != 8)
879 {
880 delete h;
881 return Zip::WriteFailed;
882 }
883
884 // Seek to end of entry
885 if (!device->seek(current))
886 {
887 delete h;
888 return Zip::SeekFailed;
889 }
890
891 if ((h->gpFlag[0] & 8) == 8)
892 {
893 // Write data descriptor
894
895 // Signature: PK\7\8
896 buffer1[0] = 'P';
897 buffer1[1] = 'K';
898 buffer1[2] = 0x07;
899 buffer1[3] = 0x08;
900
901 // CRC
902 setULong(h->crc, buffer1, ZIP_DD_OFF_CRC32);
903
904 // Compressed size
905 setULong(h->szComp, buffer1, ZIP_DD_OFF_CSIZE);
906
907 // Uncompressed size
908 setULong(h->szUncomp, buffer1, ZIP_DD_OFF_USIZE);
909
910 if (device->write(buffer1, ZIP_DD_SIZE_WS) != ZIP_DD_SIZE_WS)
911 {
912 delete h;
913 return Zip::WriteFailed;
914 }
915 }
916
917 headers->insert(entryName, h);
918 return Zip::Ok;
919}
920
921//! \internal
922int ZipPrivate::decryptByte(quint32 key2) const
923{
924 quint16 temp = ((quint16)(key2) & 0xffff) | 2;
925 return (int)(((temp * (temp ^ 1)) >> 8) & 0xff);
926}
927
928//! \internal Writes an quint32 (4 bytes) to a byte array at given offset.
929void ZipPrivate::setULong(quint32 v, char* buffer, unsigned int offset)
930{
931 buffer[offset+3] = ((v >> 24) & 0xFF);
932 buffer[offset+2] = ((v >> 16) & 0xFF);
933 buffer[offset+1] = ((v >> 8) & 0xFF);
934 buffer[offset] = (v & 0xFF);
935}
936
937//! \internal Initializes decryption keys using a password.
938void ZipPrivate::initKeys(quint32* keys) const
939{
940 // Encryption keys initialization constants are taken from the
941 // PKZip file format specification docs
942 keys[0] = 305419896L;
943 keys[1] = 591751049L;
944 keys[2] = 878082192L;
945
946 QByteArray pwdBytes = password.toAscii();
947 int sz = pwdBytes.size();
948 const char* ascii = pwdBytes.data();
949
950 for (int i=0; i<sz; ++i)
951 updateKeys(keys, (int)ascii[i]);
952}
953
954//! \internal Updates encryption keys.
955void ZipPrivate::updateKeys(quint32* keys, int c) const
956{
957 keys[0] = CRC32(keys[0], c);
958 keys[1] += keys[0] & 0xff;
959 keys[1] = keys[1] * 134775813L + 1;
960 keys[2] = CRC32(keys[2], ((int)keys[1]) >> 24);
961}
962
963//! \internal Encrypts a byte array.
964void ZipPrivate::encryptBytes(quint32* keys, char* buffer, qint64 read)
965{
966 char t;
967
968 for (int i=0; i<(int)read; ++i)
969 {
970 t = buffer[i];
971 buffer[i] ^= decryptByte(keys[2]);
972 updateKeys(keys, t);
973 }
974}
975
976//! \internal Detects the best compression level for a given file extension.
977Zip::CompressionLevel ZipPrivate::detectCompressionByMime(const QString& ext)
978{
979 // files really hard to compress
980 if ((ext == "png") ||
981 (ext == "jpg") ||
982 (ext == "jpeg") ||
983 (ext == "mp3") ||
984 (ext == "ogg") ||
985 (ext == "ogm") ||
986 (ext == "avi") ||
987 (ext == "mov") ||
988 (ext == "rm") ||
989 (ext == "ra") ||
990 (ext == "zip") ||
991 (ext == "rar") ||
992 (ext == "bz2") ||
993 (ext == "gz") ||
994 (ext == "7z") ||
995 (ext == "z") ||
996 (ext == "jar")
997 ) return Zip::Store;
998
999 // files slow and hard to compress
1000 if ((ext == "exe") ||
1001 (ext == "bin") ||
1002 (ext == "rpm") ||
1003 (ext == "deb")
1004 ) return Zip::Deflate2;
1005
1006 return Zip::Deflate9;
1007}
1008
1009/*!
1010 Closes the current archive and writes out pending data.
1011*/
1012Zip::ErrorCode ZipPrivate::closeArchive()
1013{
1014 // Close current archive by writing out central directory
1015 // and free up resources
1016
1017 if (device == 0)
1018 return Zip::Ok;
1019
1020 if (headers == 0)
1021 return Zip::Ok;
1022
1023 const ZipEntryP* h;
1024
1025 unsigned int sz;
1026 quint32 szCentralDir = 0;
1027 quint32 offCentralDir = device->pos();
1028
1029 for (QMap<QString,ZipEntryP*>::ConstIterator itr = headers->constBegin(); itr != headers->constEnd(); ++itr)
1030 {
1031 h = itr.value();
1032
1033 // signature
1034 buffer1[0] = 'P';
1035 buffer1[1] = 'K';
1036 buffer1[2] = 0x01;
1037 buffer1[3] = 0x02;
1038
1039 // version made by (currently only MS-DOS/FAT - no symlinks or other stuff supported)
1040 buffer1[ZIP_CD_OFF_MADEBY] = buffer1[ZIP_CD_OFF_MADEBY + 1] = 0;
1041
1042 // version needed to extract
1043 buffer1[ZIP_CD_OFF_VERSION] = ZIP_VERSION;
1044 buffer1[ZIP_CD_OFF_VERSION + 1] = 0;
1045
1046 // general purpose flag
1047 buffer1[ZIP_CD_OFF_GPFLAG] = h->gpFlag[0];
1048 buffer1[ZIP_CD_OFF_GPFLAG + 1] = h->gpFlag[1];
1049
1050 // compression method
1051 buffer1[ZIP_CD_OFF_CMET] = h->compMethod & 0xFF;
1052 buffer1[ZIP_CD_OFF_CMET + 1] = (h->compMethod >> 8) & 0xFF;
1053
1054 // last mod file time
1055 buffer1[ZIP_CD_OFF_MODT] = h->modTime[0];
1056 buffer1[ZIP_CD_OFF_MODT + 1] = h->modTime[1];
1057
1058 // last mod file date
1059 buffer1[ZIP_CD_OFF_MODD] = h->modDate[0];
1060 buffer1[ZIP_CD_OFF_MODD + 1] = h->modDate[1];
1061
1062 // crc (4bytes) [16,17,18,19]
1063 setULong(h->crc, buffer1, ZIP_CD_OFF_CRC);
1064
1065 // compressed size (4bytes: [20,21,22,23])
1066 setULong(h->szComp, buffer1, ZIP_CD_OFF_CSIZE);
1067
1068 // uncompressed size [24,25,26,27]
1069 setULong(h->szUncomp, buffer1, ZIP_CD_OFF_USIZE);
1070
1071 // filename
1072 QByteArray fileNameBytes = itr.key().toAscii();
1073 sz = fileNameBytes.size();
1074 buffer1[ZIP_CD_OFF_NAMELEN] = sz & 0xFF;
1075 buffer1[ZIP_CD_OFF_NAMELEN + 1] = (sz >> 8) & 0xFF;
1076
1077 // extra field length
1078 buffer1[ZIP_CD_OFF_XLEN] = buffer1[ZIP_CD_OFF_XLEN + 1] = 0;
1079
1080 // file comment length
1081 buffer1[ZIP_CD_OFF_COMMLEN] = buffer1[ZIP_CD_OFF_COMMLEN + 1] = 0;
1082
1083 // disk number start
1084 buffer1[ZIP_CD_OFF_DISKSTART] = buffer1[ZIP_CD_OFF_DISKSTART + 1] = 0;
1085
1086 // internal file attributes
1087 buffer1[ZIP_CD_OFF_IATTR] = buffer1[ZIP_CD_OFF_IATTR + 1] = 0;
1088
1089 // external file attributes
1090 buffer1[ZIP_CD_OFF_EATTR] =
1091 buffer1[ZIP_CD_OFF_EATTR + 1] =
1092 buffer1[ZIP_CD_OFF_EATTR + 2] =
1093 buffer1[ZIP_CD_OFF_EATTR + 3] = 0;
1094
1095 // relative offset of local header [42->45]
1096 setULong(h->lhOffset, buffer1, ZIP_CD_OFF_LHOFF);
1097
1098 if (device->write(buffer1, ZIP_CD_SIZE) != ZIP_CD_SIZE)
1099 {
1100 //! \todo See if we can detect QFile objects using the Qt Meta Object System
1101 /*
1102 if (!device->remove())
1103 qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1104 */
1105 return Zip::WriteFailed;
1106 }
1107
1108 // Write out filename
1109 if ((unsigned int)device->write(fileNameBytes) != sz)
1110 {
1111 //! \todo SAME AS ABOVE: See if we can detect QFile objects using the Qt Meta Object System
1112 /*
1113 if (!device->remove())
1114 qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1115 */
1116 return Zip::WriteFailed;
1117 }
1118
1119 szCentralDir += (ZIP_CD_SIZE + sz);
1120
1121 } // central dir headers loop
1122
1123
1124 // Write end of central directory
1125
1126 // signature
1127 buffer1[0] = 'P';
1128 buffer1[1] = 'K';
1129 buffer1[2] = 0x05;
1130 buffer1[3] = 0x06;
1131
1132 // number of this disk
1133 buffer1[ZIP_EOCD_OFF_DISKNUM] = buffer1[ZIP_EOCD_OFF_DISKNUM + 1] = 0;
1134
1135 // number of disk with central directory
1136 buffer1[ZIP_EOCD_OFF_CDDISKNUM] = buffer1[ZIP_EOCD_OFF_CDDISKNUM + 1] = 0;
1137
1138 // number of entries in this disk
1139 sz = headers->count();
1140 buffer1[ZIP_EOCD_OFF_ENTRIES] = sz & 0xFF;
1141 buffer1[ZIP_EOCD_OFF_ENTRIES + 1] = (sz >> 8) & 0xFF;
1142
1143 // total number of entries
1144 buffer1[ZIP_EOCD_OFF_CDENTRIES] = buffer1[ZIP_EOCD_OFF_ENTRIES];
1145 buffer1[ZIP_EOCD_OFF_CDENTRIES + 1] = buffer1[ZIP_EOCD_OFF_ENTRIES + 1];
1146
1147 // size of central directory [12->15]
1148 setULong(szCentralDir, buffer1, ZIP_EOCD_OFF_CDSIZE);
1149
1150 // central dir offset [16->19]
1151 setULong(offCentralDir, buffer1, ZIP_EOCD_OFF_CDOFF);
1152
1153 // ZIP file comment length
1154 QByteArray commentBytes = comment.toAscii();
1155 quint16 commentLength = commentBytes.size();
1156
1157 if (commentLength == 0)
1158 {
1159 buffer1[ZIP_EOCD_OFF_COMMLEN] = buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = 0;
1160 }
1161 else
1162 {
1163 buffer1[ZIP_EOCD_OFF_COMMLEN] = commentLength & 0xFF;
1164 buffer1[ZIP_EOCD_OFF_COMMLEN + 1] = (commentLength >> 8) & 0xFF;
1165 }
1166
1167 if (device->write(buffer1, ZIP_EOCD_SIZE) != ZIP_EOCD_SIZE)
1168 {
1169 //! \todo SAME AS ABOVE: See if we can detect QFile objects using the Qt Meta Object System
1170 /*
1171 if (!device->remove())
1172 qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1173 */
1174 return Zip::WriteFailed;
1175 }
1176
1177 if (commentLength != 0)
1178 {
1179 if ((unsigned int)device->write(commentBytes) != commentLength)
1180 {
1181 //! \todo SAME AS ABOVE: See if we can detect QFile objects using the Qt Meta Object System
1182 /*
1183 if (!device->remove())
1184 qDebug() << tr("Unable to delete corrupted archive: %1").arg(device->fileName());
1185 */
1186 return Zip::WriteFailed;
1187 }
1188 }
1189
1190 return Zip::Ok;
1191}
1192
1193//! \internal
1194void ZipPrivate::reset()
1195{
1196 comment.clear();
1197
1198 if (headers != 0)
1199 {
1200 qDeleteAll(*headers);
1201 delete headers;
1202 headers = 0;
1203 }
1204
1205 delete device; device = 0;
1206}
1207
1208//! \internal Returns the path of the parent directory
1209QString ZipPrivate::extractRoot(const QString& p)
1210{
1211 QDir d(QDir::cleanPath(p));
1212 if (!d.exists())
1213 return QString();
1214
1215 if (!d.cdUp())
1216 return QString();
1217
1218 return d.absolutePath();
1219}
diff --git a/rbutil/rbutilqt/zip/zip.h b/rbutil/rbutilqt/zip/zip.h
new file mode 100644
index 0000000000..03c4d8af0b
--- /dev/null
+++ b/rbutil/rbutilqt/zip/zip.h
@@ -0,0 +1,113 @@
1/****************************************************************************
2** Filename: zip.h
3** Last updated [dd/mm/yyyy]: 01/02/2007
4**
5** pkzip 2.0 file compression.
6**
7** Some of the code has been inspired by other open source projects,
8** (mainly Info-Zip and Gilles Vollant's minizip).
9** Compression and decompression actually uses the zlib library.
10**
11** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12**
13** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22**
23** See the file LICENSE.GPL that came with this software distribution or
24** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25**
26**********************************************************************/
27
28#ifndef OSDAB_ZIP__H
29#define OSDAB_ZIP__H
30
31#include <QtGlobal>
32#include <QMap>
33
34#include <zlib/zlib.h>
35
36class ZipPrivate;
37
38class QIODevice;
39class QFile;
40class QDir;
41class QStringList;
42class QString;
43
44
45class Zip
46{
47public:
48 enum ErrorCode
49 {
50 Ok,
51 ZlibInit,
52 ZlibError,
53 FileExists,
54 OpenFailed,
55 NoOpenArchive,
56 FileNotFound,
57 ReadFailed,
58 WriteFailed,
59 SeekFailed
60 };
61
62 enum CompressionLevel
63 {
64 Store,
65 Deflate1 = 1, Deflate2, Deflate3, Deflate4,
66 Deflate5, Deflate6, Deflate7, Deflate8, Deflate9,
67 AutoCPU, AutoMIME, AutoFull
68 };
69
70 enum CompressionOption
71 {
72 //! Does not preserve absolute paths in the zip file when adding a file/directory (default)
73 RelativePaths = 0x0001,
74 //! Preserve absolute paths
75 AbsolutePaths = 0x0002,
76 //! Do not store paths. All the files are put in the (evtl. user defined) root of the zip file
77 IgnorePaths = 0x0004
78 };
79 Q_DECLARE_FLAGS(CompressionOptions, CompressionOption)
80
81 Zip();
82 virtual ~Zip();
83
84 bool isOpen() const;
85
86 void setPassword(const QString& pwd);
87 void clearPassword();
88 QString password() const;
89
90 ErrorCode createArchive(const QString& file, bool overwrite = true);
91 ErrorCode createArchive(QIODevice* device);
92
93 QString archiveComment() const;
94 void setArchiveComment(const QString& comment);
95
96 ErrorCode addDirectoryContents(const QString& path, CompressionLevel level = AutoFull);
97 ErrorCode addDirectoryContents(const QString& path, const QString& root, CompressionLevel level = AutoFull);
98
99 ErrorCode addDirectory(const QString& path, CompressionOptions options = RelativePaths, CompressionLevel level = AutoFull);
100 ErrorCode addDirectory(const QString& path, const QString& root, CompressionLevel level = AutoFull);
101 ErrorCode addDirectory(const QString& path, const QString& root, CompressionOptions options = RelativePaths, CompressionLevel level = AutoFull);
102
103 ErrorCode closeArchive();
104
105 QString formatError(ErrorCode c) const;
106
107private:
108 ZipPrivate* d;
109};
110
111Q_DECLARE_OPERATORS_FOR_FLAGS(Zip::CompressionOptions)
112
113#endif // OSDAB_ZIP__H
diff --git a/rbutil/rbutilqt/zip/zip_p.h b/rbutil/rbutilqt/zip/zip_p.h
new file mode 100644
index 0000000000..b3c45ac7b8
--- /dev/null
+++ b/rbutil/rbutilqt/zip/zip_p.h
@@ -0,0 +1,93 @@
1/****************************************************************************
2** Filename: zip_p.h
3** Last updated [dd/mm/yyyy]: 28/01/2007
4**
5** pkzip 2.0 file compression.
6**
7** Some of the code has been inspired by other open source projects,
8** (mainly Info-Zip and Gilles Vollant's minizip).
9** Compression and decompression actually uses the zlib library.
10**
11** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12**
13** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22**
23** See the file LICENSE.GPL that came with this software distribution or
24** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25**
26**********************************************************************/
27
28//
29// W A R N I N G
30// -------------
31//
32// This file is not part of the Zip/UnZip API. It exists purely as an
33// implementation detail. This header file may change from version to
34// version without notice, or even be removed.
35//
36// We mean it.
37//
38
39#ifndef OSDAB_ZIP_P__H
40#define OSDAB_ZIP_P__H
41
42#include "zip.h"
43#include "zipentry_p.h"
44
45#include <QtGlobal>
46#include <QFileInfo>
47
48/*!
49 zLib authors suggest using larger buffers (128K or 256K) for (de)compression (especially for inflate())
50 we use a 256K buffer here - if you want to use this code on a pre-iceage mainframe please change it ;)
51*/
52#define ZIP_READ_BUFFER (256*1024)
53
54class ZipPrivate
55{
56public:
57 ZipPrivate();
58 virtual ~ZipPrivate();
59
60 QMap<QString,ZipEntryP*>* headers;
61
62 QIODevice* device;
63
64 char buffer1[ZIP_READ_BUFFER];
65 char buffer2[ZIP_READ_BUFFER];
66
67 unsigned char* uBuffer;
68
69 const quint32* crcTable;
70
71 QString comment;
72 QString password;
73
74 Zip::ErrorCode createArchive(QIODevice* device);
75 Zip::ErrorCode closeArchive();
76 void reset();
77
78 bool zLibInit();
79
80 Zip::ErrorCode createEntry(const QFileInfo& file, const QString& root, Zip::CompressionLevel level);
81 Zip::CompressionLevel detectCompressionByMime(const QString& ext);
82
83 inline void encryptBytes(quint32* keys, char* buffer, qint64 read);
84
85 inline void setULong(quint32 v, char* buffer, unsigned int offset);
86 inline void updateKeys(quint32* keys, int c) const;
87 inline void initKeys(quint32* keys) const;
88 inline int decryptByte(quint32 key2) const;
89
90 inline QString extractRoot(const QString& p);
91};
92
93#endif // OSDAB_ZIP_P__H
diff --git a/rbutil/rbutilqt/zip/zipentry_p.h b/rbutil/rbutilqt/zip/zipentry_p.h
new file mode 100644
index 0000000000..a201d0ac6e
--- /dev/null
+++ b/rbutil/rbutilqt/zip/zipentry_p.h
@@ -0,0 +1,78 @@
1/****************************************************************************
2** Filename: ZipEntryP.h
3** Last updated [dd/mm/yyyy]: 28/01/2007
4**
5** Wrapper for a ZIP local header.
6**
7** Some of the code has been inspired by other open source projects,
8** (mainly Info-Zip and Gilles Vollant's minizip).
9** Compression and decompression actually uses the zlib library.
10**
11** Copyright (C) 2007 Angius Fabrizio. All rights reserved.
12**
13** This file is part of the OSDaB project (http://osdab.sourceforge.net/).
14**
15** This file may be distributed and/or modified under the terms of the
16** GNU General Public License version 2 as published by the Free Software
17** Foundation and appearing in the file LICENSE.GPL included in the
18** packaging of this file.
19**
20** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
21** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
22**
23** See the file LICENSE.GPL that came with this software distribution or
24** visit http://www.gnu.org/copyleft/gpl.html for GPL licensing information.
25**
26**********************************************************************/
27
28//
29// W A R N I N G
30// -------------
31//
32// This file is not part of the Zip/UnZip API. It exists purely as an
33// implementation detail. This header file may change from version to
34// version without notice, or even be removed.
35//
36// We mean it.
37//
38
39#ifndef OSDAB_ZIPENTRY_P__H
40#define OSDAB_ZIPENTRY_P__H
41
42#include <QtGlobal>
43#include <QString>
44
45class ZipEntryP
46{
47public:
48 ZipEntryP()
49 {
50 lhOffset = 0;
51 dataOffset = 0;
52 gpFlag[0] = gpFlag[1] = 0;
53 compMethod = 0;
54 modTime[0] = modTime[1] = 0;
55 modDate[0] = modDate[1] = 0;
56 crc = 0;
57 szComp = szUncomp = 0;
58 lhEntryChecked = false;
59 }
60
61 quint32 lhOffset; // Offset of the local header record for this entry
62 quint32 dataOffset; // Offset of the file data for this entry
63 unsigned char gpFlag[2]; // General purpose flag
64 quint16 compMethod; // Compression method
65 unsigned char modTime[2]; // Last modified time
66 unsigned char modDate[2]; // Last modified date
67 quint32 crc; // CRC32
68 quint32 szComp; // Compressed file size
69 quint32 szUncomp; // Uncompressed file size
70 QString comment; // File comment
71
72 bool lhEntryChecked; // Is true if the local header record for this entry has been parsed
73
74 inline bool isEncrypted() const { return gpFlag[0] & 0x01; }
75 inline bool hasDataDescriptor() const { return gpFlag[0] & 0x08; }
76};
77
78#endif // OSDAB_ZIPENTRY_P__H