|
1 /* |
|
2 Copyright (C) 2005-2011 Sergey A. Tachenov |
|
3 |
|
4 This program is free software; you can redistribute it and/or modify it |
|
5 under the terms of the GNU Lesser General Public License as published by |
|
6 the Free Software Foundation; either version 2 of the License, or (at |
|
7 your option) any later version. |
|
8 |
|
9 This program is distributed in the hope that it will be useful, but |
|
10 WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser |
|
12 General Public License for more details. |
|
13 |
|
14 You should have received a copy of the GNU Lesser General Public License |
|
15 along with this program; if not, write to the Free Software Foundation, |
|
16 Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|
17 |
|
18 See COPYING file for the full LGPL text. |
|
19 |
|
20 Original ZIP package is copyrighted by Gilles Vollant, see |
|
21 quazip/(un)zip.h files for details, basically it's zlib license. |
|
22 **/ |
|
23 |
|
24 #include "quazipfile.h" |
|
25 |
|
26 using namespace std; |
|
27 |
|
28 class QuaZipFilePrivate { |
|
29 friend class QuaZipFile; |
|
30 private: |
|
31 QuaZipFile *q; |
|
32 QuaZip *zip; |
|
33 QString fileName; |
|
34 QuaZip::CaseSensitivity caseSensitivity; |
|
35 bool raw; |
|
36 qint64 writePos; |
|
37 // these two are for writing raw files |
|
38 ulong uncompressedSize; |
|
39 quint32 crc; |
|
40 bool internal; |
|
41 int zipError; |
|
42 inline void resetZipError() const {setZipError(UNZ_OK);} |
|
43 // const, but sets zipError! |
|
44 void setZipError(int zipError) const; |
|
45 inline QuaZipFilePrivate(QuaZipFile *q): |
|
46 q(q), zip(NULL), internal(true), zipError(UNZ_OK) {} |
|
47 inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName): |
|
48 q(q), internal(true), zipError(UNZ_OK) |
|
49 { |
|
50 zip=new QuaZip(zipName); |
|
51 } |
|
52 inline QuaZipFilePrivate(QuaZipFile *q, const QString &zipName, const QString &fileName, |
|
53 QuaZip::CaseSensitivity cs): |
|
54 q(q), internal(true), zipError(UNZ_OK) |
|
55 { |
|
56 zip=new QuaZip(zipName); |
|
57 this->fileName=fileName; |
|
58 this->caseSensitivity=cs; |
|
59 } |
|
60 inline QuaZipFilePrivate(QuaZipFile *q, QuaZip *zip): |
|
61 q(q), zip(zip), internal(false), zipError(UNZ_OK) {} |
|
62 inline ~QuaZipFilePrivate() |
|
63 { |
|
64 if (internal) |
|
65 delete zip; |
|
66 } |
|
67 }; |
|
68 |
|
69 QuaZipFile::QuaZipFile(): |
|
70 p(new QuaZipFilePrivate(this)) |
|
71 { |
|
72 } |
|
73 |
|
74 QuaZipFile::QuaZipFile(QObject *parent): |
|
75 QIODevice(parent), |
|
76 p(new QuaZipFilePrivate(this)) |
|
77 { |
|
78 } |
|
79 |
|
80 QuaZipFile::QuaZipFile(const QString& zipName, QObject *parent): |
|
81 QIODevice(parent), |
|
82 p(new QuaZipFilePrivate(this, zipName)) |
|
83 { |
|
84 } |
|
85 |
|
86 QuaZipFile::QuaZipFile(const QString& zipName, const QString& fileName, |
|
87 QuaZip::CaseSensitivity cs, QObject *parent): |
|
88 QIODevice(parent), |
|
89 p(new QuaZipFilePrivate(this, zipName, fileName, cs)) |
|
90 { |
|
91 } |
|
92 |
|
93 QuaZipFile::QuaZipFile(QuaZip *zip, QObject *parent): |
|
94 QIODevice(parent), |
|
95 p(new QuaZipFilePrivate(this, zip)) |
|
96 { |
|
97 } |
|
98 |
|
99 QuaZipFile::~QuaZipFile() |
|
100 { |
|
101 if (isOpen()) |
|
102 close(); |
|
103 delete p; |
|
104 } |
|
105 |
|
106 QString QuaZipFile::getZipName() const |
|
107 { |
|
108 return p->zip==NULL ? QString() : p->zip->getZipName(); |
|
109 } |
|
110 |
|
111 QString QuaZipFile::getActualFileName()const |
|
112 { |
|
113 p->setZipError(UNZ_OK); |
|
114 if (p->zip == NULL || (openMode() & WriteOnly)) |
|
115 return QString(); |
|
116 QString name=p->zip->getCurrentFileName(); |
|
117 if(name.isNull()) |
|
118 p->setZipError(p->zip->getZipError()); |
|
119 return name; |
|
120 } |
|
121 |
|
122 void QuaZipFile::setZipName(const QString& zipName) |
|
123 { |
|
124 if(isOpen()) { |
|
125 qWarning("QuaZipFile::setZipName(): file is already open - can not set ZIP name"); |
|
126 return; |
|
127 } |
|
128 if(p->zip!=NULL && p->internal) |
|
129 delete p->zip; |
|
130 p->zip=new QuaZip(zipName); |
|
131 p->internal=true; |
|
132 } |
|
133 |
|
134 void QuaZipFile::setZip(QuaZip *zip) |
|
135 { |
|
136 if(isOpen()) { |
|
137 qWarning("QuaZipFile::setZip(): file is already open - can not set ZIP"); |
|
138 return; |
|
139 } |
|
140 if(p->zip!=NULL && p->internal) |
|
141 delete p->zip; |
|
142 p->zip=zip; |
|
143 p->fileName=QString(); |
|
144 p->internal=false; |
|
145 } |
|
146 |
|
147 void QuaZipFile::setFileName(const QString& fileName, QuaZip::CaseSensitivity cs) |
|
148 { |
|
149 if(p->zip==NULL) { |
|
150 qWarning("QuaZipFile::setFileName(): call setZipName() first"); |
|
151 return; |
|
152 } |
|
153 if(!p->internal) { |
|
154 qWarning("QuaZipFile::setFileName(): should not be used when not using internal QuaZip"); |
|
155 return; |
|
156 } |
|
157 if(isOpen()) { |
|
158 qWarning("QuaZipFile::setFileName(): can not set file name for already opened file"); |
|
159 return; |
|
160 } |
|
161 p->fileName=fileName; |
|
162 p->caseSensitivity=cs; |
|
163 } |
|
164 |
|
165 void QuaZipFilePrivate::setZipError(int zipError) const |
|
166 { |
|
167 QuaZipFilePrivate *fakeThis = const_cast<QuaZipFilePrivate*>(this); // non-const |
|
168 fakeThis->zipError=zipError; |
|
169 if(zipError==UNZ_OK) |
|
170 q->setErrorString(QString()); |
|
171 else |
|
172 q->setErrorString(q->tr("ZIP/UNZIP API error %1").arg(zipError)); |
|
173 } |
|
174 |
|
175 bool QuaZipFile::open(OpenMode mode) |
|
176 { |
|
177 return open(mode, NULL); |
|
178 } |
|
179 |
|
180 bool QuaZipFile::open(OpenMode mode, int *method, int *level, bool raw, const char *password) |
|
181 { |
|
182 p->resetZipError(); |
|
183 if(isOpen()) { |
|
184 qWarning("QuaZipFile::open(): already opened"); |
|
185 return false; |
|
186 } |
|
187 if(mode&Unbuffered) { |
|
188 qWarning("QuaZipFile::open(): Unbuffered mode is not supported"); |
|
189 return false; |
|
190 } |
|
191 if((mode&ReadOnly)&&!(mode&WriteOnly)) { |
|
192 if(p->internal) { |
|
193 if(!p->zip->open(QuaZip::mdUnzip)) { |
|
194 p->setZipError(p->zip->getZipError()); |
|
195 return false; |
|
196 } |
|
197 if(!p->zip->setCurrentFile(p->fileName, p->caseSensitivity)) { |
|
198 p->setZipError(p->zip->getZipError()); |
|
199 p->zip->close(); |
|
200 return false; |
|
201 } |
|
202 } else { |
|
203 if(p->zip==NULL) { |
|
204 qWarning("QuaZipFile::open(): zip is NULL"); |
|
205 return false; |
|
206 } |
|
207 if(p->zip->getMode()!=QuaZip::mdUnzip) { |
|
208 qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", |
|
209 (int)mode, (int)p->zip->getMode()); |
|
210 return false; |
|
211 } |
|
212 if(!p->zip->hasCurrentFile()) { |
|
213 qWarning("QuaZipFile::open(): zip does not have current file"); |
|
214 return false; |
|
215 } |
|
216 } |
|
217 p->setZipError(unzOpenCurrentFile3(p->zip->getUnzFile(), method, level, (int)raw, password)); |
|
218 if(p->zipError==UNZ_OK) { |
|
219 setOpenMode(mode); |
|
220 p->raw=raw; |
|
221 return true; |
|
222 } else |
|
223 return false; |
|
224 } |
|
225 qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); |
|
226 return false; |
|
227 } |
|
228 |
|
229 bool QuaZipFile::open(OpenMode mode, const QuaZipNewInfo& info, |
|
230 const char *password, quint32 crc, |
|
231 int method, int level, bool raw, |
|
232 int windowBits, int memLevel, int strategy) |
|
233 { |
|
234 zip_fileinfo info_z; |
|
235 p->resetZipError(); |
|
236 if(isOpen()) { |
|
237 qWarning("QuaZipFile::open(): already opened"); |
|
238 return false; |
|
239 } |
|
240 if((mode&WriteOnly)&&!(mode&ReadOnly)) { |
|
241 if(p->internal) { |
|
242 qWarning("QuaZipFile::open(): write mode is incompatible with internal QuaZip approach"); |
|
243 return false; |
|
244 } |
|
245 if(p->zip==NULL) { |
|
246 qWarning("QuaZipFile::open(): zip is NULL"); |
|
247 return false; |
|
248 } |
|
249 if(p->zip->getMode()!=QuaZip::mdCreate&&p->zip->getMode()!=QuaZip::mdAppend&&p->zip->getMode()!=QuaZip::mdAdd) { |
|
250 qWarning("QuaZipFile::open(): file open mode %d incompatible with ZIP open mode %d", |
|
251 (int)mode, (int)p->zip->getMode()); |
|
252 return false; |
|
253 } |
|
254 info_z.tmz_date.tm_year=info.dateTime.date().year(); |
|
255 info_z.tmz_date.tm_mon=info.dateTime.date().month() - 1; |
|
256 info_z.tmz_date.tm_mday=info.dateTime.date().day(); |
|
257 info_z.tmz_date.tm_hour=info.dateTime.time().hour(); |
|
258 info_z.tmz_date.tm_min=info.dateTime.time().minute(); |
|
259 info_z.tmz_date.tm_sec=info.dateTime.time().second(); |
|
260 info_z.dosDate = 0; |
|
261 info_z.internal_fa=(uLong)info.internalAttr; |
|
262 info_z.external_fa=(uLong)info.externalAttr; |
|
263 p->setZipError(zipOpenNewFileInZip3(p->zip->getZipFile(), |
|
264 p->zip->getFileNameCodec()->fromUnicode(info.name).constData(), &info_z, |
|
265 info.extraLocal.constData(), info.extraLocal.length(), |
|
266 info.extraGlobal.constData(), info.extraGlobal.length(), |
|
267 p->zip->getCommentCodec()->fromUnicode(info.comment).constData(), |
|
268 method, level, (int)raw, |
|
269 windowBits, memLevel, strategy, |
|
270 password, (uLong)crc)); |
|
271 if(p->zipError==UNZ_OK) { |
|
272 p->writePos=0; |
|
273 setOpenMode(mode); |
|
274 p->raw=raw; |
|
275 if(raw) { |
|
276 p->crc=crc; |
|
277 p->uncompressedSize=info.uncompressedSize; |
|
278 } |
|
279 return true; |
|
280 } else |
|
281 return false; |
|
282 } |
|
283 qWarning("QuaZipFile::open(): open mode %d not supported by this function", (int)mode); |
|
284 return false; |
|
285 } |
|
286 |
|
287 bool QuaZipFile::isSequential()const |
|
288 { |
|
289 return true; |
|
290 } |
|
291 |
|
292 qint64 QuaZipFile::pos()const |
|
293 { |
|
294 if(p->zip==NULL) { |
|
295 qWarning("QuaZipFile::pos(): call setZipName() or setZip() first"); |
|
296 return -1; |
|
297 } |
|
298 if(!isOpen()) { |
|
299 qWarning("QuaZipFile::pos(): file is not open"); |
|
300 return -1; |
|
301 } |
|
302 if(openMode()&ReadOnly) |
|
303 return unztell(p->zip->getUnzFile()); |
|
304 else |
|
305 return p->writePos; |
|
306 } |
|
307 |
|
308 bool QuaZipFile::atEnd()const |
|
309 { |
|
310 if(p->zip==NULL) { |
|
311 qWarning("QuaZipFile::atEnd(): call setZipName() or setZip() first"); |
|
312 return false; |
|
313 } |
|
314 if(!isOpen()) { |
|
315 qWarning("QuaZipFile::atEnd(): file is not open"); |
|
316 return false; |
|
317 } |
|
318 if(openMode()&ReadOnly) |
|
319 return unzeof(p->zip->getUnzFile())==1; |
|
320 else |
|
321 return true; |
|
322 } |
|
323 |
|
324 qint64 QuaZipFile::size()const |
|
325 { |
|
326 if(!isOpen()) { |
|
327 qWarning("QuaZipFile::atEnd(): file is not open"); |
|
328 return -1; |
|
329 } |
|
330 if(openMode()&ReadOnly) |
|
331 return p->raw?csize():usize(); |
|
332 else |
|
333 return p->writePos; |
|
334 } |
|
335 |
|
336 qint64 QuaZipFile::csize()const |
|
337 { |
|
338 unz_file_info info_z; |
|
339 p->setZipError(UNZ_OK); |
|
340 if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; |
|
341 p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); |
|
342 if(p->zipError!=UNZ_OK) |
|
343 return -1; |
|
344 return info_z.compressed_size; |
|
345 } |
|
346 |
|
347 qint64 QuaZipFile::usize()const |
|
348 { |
|
349 unz_file_info info_z; |
|
350 p->setZipError(UNZ_OK); |
|
351 if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return -1; |
|
352 p->setZipError(unzGetCurrentFileInfo(p->zip->getUnzFile(), &info_z, NULL, 0, NULL, 0, NULL, 0)); |
|
353 if(p->zipError!=UNZ_OK) |
|
354 return -1; |
|
355 return info_z.uncompressed_size; |
|
356 } |
|
357 |
|
358 bool QuaZipFile::getFileInfo(QuaZipFileInfo *info) |
|
359 { |
|
360 if(p->zip==NULL||p->zip->getMode()!=QuaZip::mdUnzip) return false; |
|
361 p->zip->getCurrentFileInfo(info); |
|
362 p->setZipError(p->zip->getZipError()); |
|
363 return p->zipError==UNZ_OK; |
|
364 } |
|
365 |
|
366 void QuaZipFile::close() |
|
367 { |
|
368 p->resetZipError(); |
|
369 if(p->zip==NULL||!p->zip->isOpen()) return; |
|
370 if(!isOpen()) { |
|
371 qWarning("QuaZipFile::close(): file isn't open"); |
|
372 return; |
|
373 } |
|
374 if(openMode()&ReadOnly) |
|
375 p->setZipError(unzCloseCurrentFile(p->zip->getUnzFile())); |
|
376 else if(openMode()&WriteOnly) |
|
377 if(isRaw()) p->setZipError(zipCloseFileInZipRaw(p->zip->getZipFile(), p->uncompressedSize, p->crc)); |
|
378 else p->setZipError(zipCloseFileInZip(p->zip->getZipFile())); |
|
379 else { |
|
380 qWarning("Wrong open mode: %d", (int)openMode()); |
|
381 return; |
|
382 } |
|
383 if(p->zipError==UNZ_OK) setOpenMode(QIODevice::NotOpen); |
|
384 else return; |
|
385 if(p->internal) { |
|
386 p->zip->close(); |
|
387 p->setZipError(p->zip->getZipError()); |
|
388 } |
|
389 } |
|
390 |
|
391 qint64 QuaZipFile::readData(char *data, qint64 maxSize) |
|
392 { |
|
393 p->setZipError(UNZ_OK); |
|
394 qint64 bytesRead=unzReadCurrentFile(p->zip->getUnzFile(), data, (unsigned)maxSize); |
|
395 if(bytesRead<0) p->setZipError((int)bytesRead); |
|
396 return bytesRead; |
|
397 } |
|
398 |
|
399 qint64 QuaZipFile::writeData(const char* data, qint64 maxSize) |
|
400 { |
|
401 p->setZipError(ZIP_OK); |
|
402 p->setZipError(zipWriteInFileInZip(p->zip->getZipFile(), data, (uint)maxSize)); |
|
403 if(p->zipError!=ZIP_OK) return -1; |
|
404 else { |
|
405 p->writePos+=maxSize; |
|
406 return maxSize; |
|
407 } |
|
408 } |
|
409 |
|
410 QString QuaZipFile::getFileName() const |
|
411 { |
|
412 return p->fileName; |
|
413 } |
|
414 |
|
415 QuaZip::CaseSensitivity QuaZipFile::getCaseSensitivity() const |
|
416 { |
|
417 return p->caseSensitivity; |
|
418 } |
|
419 |
|
420 bool QuaZipFile::isRaw() const |
|
421 { |
|
422 return p->raw; |
|
423 } |
|
424 |
|
425 int QuaZipFile::getZipError() const |
|
426 { |
|
427 return p->zipError; |
|
428 } |