| 1 | /* | 
 
 
 
 
 | 2 | Copyright (C) 2005-2014 Sergey A. Tachenov | 
 
 
 
 
 | 3 |  | 
 
 
 
 
 | 4 | This file is part of QuaZIP. | 
 
 
 
 
 | 5 |  | 
 
 
 
 
 | 6 | QuaZIP is free software: you can redistribute it and/or modify | 
 
 
 
 
 | 7 | it under the terms of the GNU Lesser General Public License as published by | 
 
 
 
 
 | 8 | the Free Software Foundation, either version 2.1 of the License, or | 
 
 
 
 
 | 9 | (at your option) any later version. | 
 
 
 
 
 | 10 |  | 
 
 
 
 
 | 11 | QuaZIP is distributed in the hope that it will be useful, | 
 
 
 
 
 | 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
 
 
 
 
 | 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
 
 
 
 
 | 14 | GNU Lesser General Public License for more details. | 
 
 
 
 
 | 15 |  | 
 
 
 
 
 | 16 | You should have received a copy of the GNU Lesser General Public License | 
 
 
 
 
 | 17 | along with QuaZIP.  If not, see <http://www.gnu.org/licenses/>. | 
 
 
 
 
 | 18 |  | 
 
 
 
 
 | 19 | See COPYING file for the full LGPL text. | 
 
 
 
 
 | 20 |  | 
 
 
 
 
 | 21 | Original ZIP package is copyrighted by Gilles Vollant and contributors, | 
 
 
 
 
 | 22 | see quazip/(un)zip.h files for details. Basically it's the zlib license. | 
 
 
 
 
 | 23 | */ | 
 
 
 
 
 | 24 |  | 
 
 
 
 
 | 25 | #include "quaziodevice.h" | 
 
 
 
 
 | 26 |  | 
 
 
 
 
 | 27 | #define QUAZIO_INBUFSIZE 4096 | 
 
 
 
 
 | 28 | #define QUAZIO_OUTBUFSIZE 4096 | 
 
 
 
 
 | 29 |  | 
 
 
 
 
 | 30 | /// \cond internal | 
 
 
 
 
 | 31 | class QuaZIODevicePrivate { | 
 
 
 
 
 | 32 | friend class QuaZIODevice; | 
 
 
 
 
 | 33 | QuaZIODevicePrivate(QIODevice *io); | 
 
 
 
 
 | 34 | ~QuaZIODevicePrivate(); | 
 
 
 
 
 | 35 | QIODevice *io; | 
 
 
 
 
 | 36 | z_stream zins; | 
 
 
 
 
 | 37 | z_stream zouts; | 
 
 
 
 
 | 38 | char *inBuf; | 
 
 
 
 
 | 39 | int inBufPos; | 
 
 
 
 
 | 40 | int inBufSize; | 
 
 
 
 
 | 41 | char *outBuf; | 
 
 
 
 
 | 42 | int outBufPos; | 
 
 
 
 
 | 43 | int outBufSize; | 
 
 
 
 
 | 44 | bool zBufError; | 
 
 
 
 
 | 45 | bool atEnd; | 
 
 
 
 
 | 46 | int doFlush(QString &error); | 
 
 
 
 
 | 47 | }; | 
 
 
 
 
 | 48 |  | 
 
 
 
 
 | 49 | QuaZIODevicePrivate::QuaZIODevicePrivate(QIODevice *io): | 
 
 
 
 
 | 50 | io(io), | 
 
 
 
 
 | 51 | inBuf(NULL), | 
 
 
 
 
 | 52 | inBufPos(0), | 
 
 
 
 
 | 53 | inBufSize(0), | 
 
 
 
 
 | 54 | outBuf(NULL), | 
 
 
 
 
 | 55 | outBufPos(0), | 
 
 
 
 
 | 56 | outBufSize(0), | 
 
 
 
 
 | 57 | zBufError(false), | 
 
 
 
 
 | 58 | atEnd(false) | 
 
 
 
 
 | 59 | { | 
 
 
 
 
 | 60 | zins.zalloc = (alloc_func) NULL; | 
 
 
 
 
 | 61 | zins.zfree = (free_func) NULL; | 
 
 
 
 
 | 62 | zins.opaque = NULL; | 
 
 
 
 
 | 63 | zouts.zalloc = (alloc_func) NULL; | 
 
 
 
 
 | 64 | zouts.zfree = (free_func) NULL; | 
 
 
 
 
 | 65 | zouts.opaque = NULL; | 
 
 
 
 
 | 66 | inBuf = new char[QUAZIO_INBUFSIZE]; | 
 
 
 
 
 | 67 | outBuf = new char[QUAZIO_OUTBUFSIZE]; | 
 
 
 
 
 | 68 | #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT | 
 
 
 
 
 | 69 | debug.setFileName("debug.out"); | 
 
 
 
 
 | 70 | debug.open(QIODevice::WriteOnly); | 
 
 
 
 
 | 71 | #endif | 
 
 
 
 
 | 72 | #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT | 
 
 
 
 
 | 73 | indebug.setFileName("debug.in"); | 
 
 
 
 
 | 74 | indebug.open(QIODevice::WriteOnly); | 
 
 
 
 
 | 75 | #endif | 
 
 
 
 
 | 76 | } | 
 
 
 
 
 | 77 |  | 
 
 
 
 
 | 78 | QuaZIODevicePrivate::~QuaZIODevicePrivate() | 
 
 
 
 
 | 79 | { | 
 
 
 
 
 | 80 | #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT | 
 
 
 
 
 | 81 | debug.close(); | 
 
 
 
 
 | 82 | #endif | 
 
 
 
 
 | 83 | #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT | 
 
 
 
 
 | 84 | indebug.close(); | 
 
 
 
 
 | 85 | #endif | 
 
 
 
 
 | 86 | if (inBuf != NULL) | 
 
 
 
 
 | 87 | delete[] inBuf; | 
 
 
 
 
 | 88 | if (outBuf != NULL) | 
 
 
 
 
 | 89 | delete[] outBuf; | 
 
 
 
 
 | 90 | } | 
 
 
 
 
 | 91 |  | 
 
 
 
 
 | 92 | int QuaZIODevicePrivate::doFlush(QString &error) | 
 
 
 
 
 | 93 | { | 
 
 
 
 
 | 94 | int flushed = 0; | 
 
 
 
 
 | 95 | while (outBufPos < outBufSize) { | 
 
 
 
 
 | 96 | int more = io->write(outBuf + outBufPos, outBufSize - outBufPos); | 
 
 
 
 
 | 97 | if (more == -1) { | 
 
 
 
 
 | 98 | error = io->errorString(); | 
 
 
 
 
 | 99 | return -1; | 
 
 
 
 
 | 100 | } | 
 
 
 
 
 | 101 | if (more == 0) | 
 
 
 
 
 | 102 | break; | 
 
 
 
 
 | 103 | outBufPos += more; | 
 
 
 
 
 | 104 | flushed += more; | 
 
 
 
 
 | 105 | } | 
 
 
 
 
 | 106 | if (outBufPos == outBufSize) { | 
 
 
 
 
 | 107 | outBufPos = outBufSize = 0; | 
 
 
 
 
 | 108 | } | 
 
 
 
 
 | 109 | return flushed; | 
 
 
 
 
 | 110 | } | 
 
 
 
 
 | 111 |  | 
 
 
 
 
 | 112 | /// \endcond | 
 
 
 
 
 | 113 |  | 
 
 
 
 
 | 114 | // #define QUAZIP_ZIODEVICE_DEBUG_OUTPUT | 
 
 
 
 
 | 115 | // #define QUAZIP_ZIODEVICE_DEBUG_INPUT | 
 
 
 
 
 | 116 | #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT | 
 
 
 
 
 | 117 | #include <QFile> | 
 
 
 
 
 | 118 | static QFile debug; | 
 
 
 
 
 | 119 | #endif | 
 
 
 
 
 | 120 | #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT | 
 
 
 
 
 | 121 | #include <QFile> | 
 
 
 
 
 | 122 | static QFile indebug; | 
 
 
 
 
 | 123 | #endif | 
 
 
 
 
 | 124 |  | 
 
 
 
 
 | 125 | QuaZIODevice::QuaZIODevice(QIODevice *io, QObject *parent): | 
 
 
 
 
 | 126 | QIODevice(parent), | 
 
 
 
 
 | 127 | d(new QuaZIODevicePrivate(io)) | 
 
 
 
 
 | 128 | { | 
 
 
 
 
 | 129 | connect(io, SIGNAL(readyRead()), SIGNAL(readyRead())); | 
 
 
 
 
 | 130 | } | 
 
 
 
 
 | 131 |  | 
 
 
 
 
 | 132 | QuaZIODevice::~QuaZIODevice() | 
 
 
 
 
 | 133 | { | 
 
 
 
 
 | 134 | if (isOpen()) | 
 
 
 
 
 | 135 | close(); | 
 
 
 
 
 | 136 | delete d; | 
 
 
 
 
 | 137 | } | 
 
 
 
 
 | 138 |  | 
 
 
 
 
 | 139 | QIODevice *QuaZIODevice::getIoDevice() const | 
 
 
 
 
 | 140 | { | 
 
 
 
 
 | 141 | return d->io; | 
 
 
 
 
 | 142 | } | 
 
 
 
 
 | 143 |  | 
 
 
 
 
 | 144 | bool QuaZIODevice::open(QIODevice::OpenMode mode) | 
 
 
 
 
 | 145 | { | 
 
 
 
 
 | 146 | if ((mode & QIODevice::Append) != 0) { | 
 
 
 
 
 | 147 | setErrorString(trUtf8("QIODevice::Append is not supported for" | 
 
 
 
 
 | 148 | " QuaZIODevice")); | 
 
 
 
 
 | 149 | return false; | 
 
 
 
 
 | 150 | } | 
 
 
 
 
 | 151 | if ((mode & QIODevice::ReadWrite) == QIODevice::ReadWrite) { | 
 
 
 
 
 | 152 | setErrorString(trUtf8("QIODevice::ReadWrite is not supported for" | 
 
 
 
 
 | 153 | " QuaZIODevice")); | 
 
 
 
 
 | 154 | return false; | 
 
 
 
 
 | 155 | } | 
 
 
 
 
 | 156 | if ((mode & QIODevice::ReadOnly) != 0) { | 
 
 
 
 
 | 157 | if (inflateInit(&d->zins) != Z_OK) { | 
 
 
 
 
 | 158 | setErrorString(d->zins.msg); | 
 
 
 
 
 | 159 | return false; | 
 
 
 
 
 | 160 | } | 
 
 
 
 
 | 161 | } | 
 
 
 
 
 | 162 | if ((mode & QIODevice::WriteOnly) != 0) { | 
 
 
 
 
 | 163 | if (deflateInit(&d->zouts, Z_DEFAULT_COMPRESSION) != Z_OK) { | 
 
 
 
 
 | 164 | setErrorString(d->zouts.msg); | 
 
 
 
 
 | 165 | return false; | 
 
 
 
 
 | 166 | } | 
 
 
 
 
 | 167 | } | 
 
 
 
 
 | 168 | return QIODevice::open(mode); | 
 
 
 
 
 | 169 | } | 
 
 
 
 
 | 170 |  | 
 
 
 
 
 | 171 | void QuaZIODevice::close() | 
 
 
 
 
 | 172 | { | 
 
 
 
 
 | 173 | if ((openMode() & QIODevice::ReadOnly) != 0) { | 
 
 
 
 
 | 174 | if (inflateEnd(&d->zins) != Z_OK) { | 
 
 
 
 
 | 175 | setErrorString(d->zins.msg); | 
 
 
 
 
 | 176 | } | 
 
 
 
 
 | 177 | } | 
 
 
 
 
 | 178 | if ((openMode() & QIODevice::WriteOnly) != 0) { | 
 
 
 
 
 | 179 | flush(); | 
 
 
 
 
 | 180 | if (deflateEnd(&d->zouts) != Z_OK) { | 
 
 
 
 
 | 181 | setErrorString(d->zouts.msg); | 
 
 
 
 
 | 182 | } | 
 
 
 
 
 | 183 | } | 
 
 
 
 
 | 184 | QIODevice::close(); | 
 
 
 
 
 | 185 | } | 
 
 
 
 
 | 186 |  | 
 
 
 
 
 | 187 | qint64 QuaZIODevice::readData(char *data, qint64 maxSize) | 
 
 
 
 
 | 188 | { | 
 
 
 
 
 | 189 | int read = 0; | 
 
 
 
 
 | 190 | while (read < maxSize) { | 
 
 
 
 
 | 191 | if (d->inBufPos == d->inBufSize) { | 
 
 
 
 
 | 192 | d->inBufPos = 0; | 
 
 
 
 
 | 193 | d->inBufSize = d->io->read(d->inBuf, QUAZIO_INBUFSIZE); | 
 
 
 
 
 | 194 | if (d->inBufSize == -1) { | 
 
 
 
 
 | 195 | d->inBufSize = 0; | 
 
 
 
 
 | 196 | setErrorString(d->io->errorString()); | 
 
 
 
 
 | 197 | return -1; | 
 
 
 
 
 | 198 | } | 
 
 
 
 
 | 199 | if (d->inBufSize == 0) | 
 
 
 
 
 | 200 | break; | 
 
 
 
 
 | 201 | } | 
 
 
 
 
 | 202 | while (read < maxSize && d->inBufPos < d->inBufSize) { | 
 
 
 
 
 | 203 | d->zins.next_in = (Bytef *) (d->inBuf + d->inBufPos); | 
 
 
 
 
 | 204 | d->zins.avail_in = d->inBufSize - d->inBufPos; | 
 
 
 
 
 | 205 | d->zins.next_out = (Bytef *) (data + read); | 
 
 
 
 
 | 206 | d->zins.avail_out = (uInt) (maxSize - read); // hope it's less than 2GB | 
 
 
 
 
 | 207 | int more = 0; | 
 
 
 
 
 | 208 | switch (inflate(&d->zins, Z_SYNC_FLUSH)) { | 
 
 
 
 
 | 209 | case Z_OK: | 
 
 
 
 
 | 210 | read = (char *) d->zins.next_out - data; | 
 
 
 
 
 | 211 | d->inBufPos = (char *) d->zins.next_in - d->inBuf; | 
 
 
 
 
 | 212 | break; | 
 
 
 
 
 | 213 | case Z_STREAM_END: | 
 
 
 
 
 | 214 | read = (char *) d->zins.next_out - data; | 
 
 
 
 
 | 215 | d->inBufPos = (char *) d->zins.next_in - d->inBuf; | 
 
 
 
 
 | 216 | d->atEnd = true; | 
 
 
 
 
 | 217 | return read; | 
 
 
 
 
 | 218 | case Z_BUF_ERROR: // this should never happen, but just in case | 
 
 
 
 
 | 219 | if (!d->zBufError) { | 
 
 
 
 
 | 220 | qWarning("Z_BUF_ERROR detected with %d/%d in/out, weird", | 
 
 
 
 
 | 221 | d->zins.avail_in, d->zins.avail_out); | 
 
 
 
 
 | 222 | d->zBufError = true; | 
 
 
 
 
 | 223 | } | 
 
 
 
 
 | 224 | memmove(d->inBuf, d->inBuf + d->inBufPos, d->inBufSize - d->inBufPos); | 
 
 
 
 
 | 225 | d->inBufSize -= d->inBufPos; | 
 
 
 
 
 | 226 | d->inBufPos = 0; | 
 
 
 
 
 | 227 | more = d->io->read(d->inBuf + d->inBufSize, QUAZIO_INBUFSIZE - d->inBufSize); | 
 
 
 
 
 | 228 | if (more == -1) { | 
 
 
 
 
 | 229 | setErrorString(d->io->errorString()); | 
 
 
 
 
 | 230 | return -1; | 
 
 
 
 
 | 231 | } | 
 
 
 
 
 | 232 | if (more == 0) | 
 
 
 
 
 | 233 | return read; | 
 
 
 
 
 | 234 | d->inBufSize += more; | 
 
 
 
 
 | 235 | break; | 
 
 
 
 
 | 236 | default: | 
 
 
 
 
 | 237 | setErrorString(QString::fromLocal8Bit(d->zins.msg)); | 
 
 
 
 
 | 238 | return -1; | 
 
 
 
 
 | 239 | } | 
 
 
 
 
 | 240 | } | 
 
 
 
 
 | 241 | } | 
 
 
 
 
 | 242 | #ifdef QUAZIP_ZIODEVICE_DEBUG_INPUT | 
 
 
 
 
 | 243 | indebug.write(data, read); | 
 
 
 
 
 | 244 | #endif | 
 
 
 
 
 | 245 | return read; | 
 
 
 
 
 | 246 | } | 
 
 
 
 
 | 247 |  | 
 
 
 
 
 | 248 | qint64 QuaZIODevice::writeData(const char *data, qint64 maxSize) | 
 
 
 
 
 | 249 | { | 
 
 
 
 
 | 250 | int written = 0; | 
 
 
 
 
 | 251 | QString error; | 
 
 
 
 
 | 252 | if (d->doFlush(error) == -1) { | 
 
 
 
 
 | 253 | setErrorString(error); | 
 
 
 
 
 | 254 | return -1; | 
 
 
 
 
 | 255 | } | 
 
 
 
 
 | 256 | while (written < maxSize) { | 
 
 
 
 
 | 257 | // there is some data waiting in the output buffer | 
 
 
 
 
 | 258 | if (d->outBufPos < d->outBufSize) | 
 
 
 
 
 | 259 | return written; | 
 
 
 
 
 | 260 | d->zouts.next_in = (Bytef *) (data + written); | 
 
 
 
 
 | 261 | d->zouts.avail_in = (uInt) (maxSize - written); // hope it's less than 2GB | 
 
 
 
 
 | 262 | d->zouts.next_out = (Bytef *) d->outBuf; | 
 
 
 
 
 | 263 | d->zouts.avail_out = QUAZIO_OUTBUFSIZE; | 
 
 
 
 
 | 264 | switch (deflate(&d->zouts, Z_NO_FLUSH)) { | 
 
 
 
 
 | 265 | case Z_OK: | 
 
 
 
 
 | 266 | written = (char *) d->zouts.next_in - data; | 
 
 
 
 
 | 267 | d->outBufSize = (char *) d->zouts.next_out - d->outBuf; | 
 
 
 
 
 | 268 | break; | 
 
 
 
 
 | 269 | default: | 
 
 
 
 
 | 270 | setErrorString(QString::fromLocal8Bit(d->zouts.msg)); | 
 
 
 
 
 | 271 | return -1; | 
 
 
 
 
 | 272 | } | 
 
 
 
 
 | 273 | if (d->doFlush(error) == -1) { | 
 
 
 
 
 | 274 | setErrorString(error); | 
 
 
 
 
 | 275 | return -1; | 
 
 
 
 
 | 276 | } | 
 
 
 
 
 | 277 | } | 
 
 
 
 
 | 278 | #ifdef QUAZIP_ZIODEVICE_DEBUG_OUTPUT | 
 
 
 
 
 | 279 | debug.write(data, written); | 
 
 
 
 
 | 280 | #endif | 
 
 
 
 
 | 281 | return written; | 
 
 
 
 
 | 282 | } | 
 
 
 
 
 | 283 |  | 
 
 
 
 
 | 284 | bool QuaZIODevice::flush() | 
 
 
 
 
 | 285 | { | 
 
 
 
 
 | 286 | QString error; | 
 
 
 
 
 | 287 | if (d->doFlush(error) < 0) { | 
 
 
 
 
 | 288 | setErrorString(error); | 
 
 
 
 
 | 289 | return false; | 
 
 
 
 
 | 290 | } | 
 
 
 
 
 | 291 | // can't flush buffer, some data is still waiting | 
 
 
 
 
 | 292 | if (d->outBufPos < d->outBufSize) | 
 
 
 
 
 | 293 | return true; | 
 
 
 
 
 | 294 | Bytef c = 0; | 
 
 
 
 
 | 295 | d->zouts.next_in = &c; // fake input buffer | 
 
 
 
 
 | 296 | d->zouts.avail_in = 0; // of zero size | 
 
 
 
 
 | 297 | do { | 
 
 
 
 
 | 298 | d->zouts.next_out = (Bytef *) d->outBuf; | 
 
 
 
 
 | 299 | d->zouts.avail_out = QUAZIO_OUTBUFSIZE; | 
 
 
 
 
 | 300 | switch (deflate(&d->zouts, Z_SYNC_FLUSH)) { | 
 
 
 
 
 | 301 | case Z_OK: | 
 
 
 
 
 | 302 | d->outBufSize = (char *) d->zouts.next_out - d->outBuf; | 
 
 
 
 
 | 303 | if (d->doFlush(error) < 0) { | 
 
 
 
 
 | 304 | setErrorString(error); | 
 
 
 
 
 | 305 | return false; | 
 
 
 
 
 | 306 | } | 
 
 
 
 
 | 307 | if (d->outBufPos < d->outBufSize) | 
 
 
 
 
 | 308 | return true; | 
 
 
 
 
 | 309 | break; | 
 
 
 
 
 | 310 | case Z_BUF_ERROR: // nothing to write? | 
 
 
 
 
 | 311 | return true; | 
 
 
 
 
 | 312 | default: | 
 
 
 
 
 | 313 | setErrorString(QString::fromLocal8Bit(d->zouts.msg)); | 
 
 
 
 
 | 314 | return false; | 
 
 
 
 
 | 315 | } | 
 
 
 
 
 | 316 | } while (d->zouts.avail_out == 0); | 
 
 
 
 
 | 317 | return true; | 
 
 
 
 
 | 318 | } | 
 
 
 
 
 | 319 |  | 
 
 
 
 
 | 320 | bool QuaZIODevice::isSequential() const | 
 
 
 
 
 | 321 | { | 
 
 
 
 
 | 322 | return true; | 
 
 
 
 
 | 323 | } | 
 
 
 
 
 | 324 |  | 
 
 
 
 
 | 325 | bool QuaZIODevice::atEnd() const | 
 
 
 
 
 | 326 | { | 
 
 
 
 
 | 327 | // Here we MUST check QIODevice::bytesAvailable() because WE | 
 
 
 
 
 | 328 | // might have reached the end, but QIODevice didn't-- | 
 
 
 
 
 | 329 | // it could have simply pre-buffered all remaining data. | 
 
 
 
 
 | 330 | return (openMode() == NotOpen) || (QIODevice::bytesAvailable() == 0 && d->atEnd); | 
 
 
 
 
 | 331 | } | 
 
 
 
 
 | 332 |  | 
 
 
 
 
 | 333 | qint64 QuaZIODevice::bytesAvailable() const | 
 
 
 
 
 | 334 | { | 
 
 
 
 
 | 335 | // If we haven't recevied Z_STREAM_END, it means that | 
 
 
 
 
 | 336 | // we have at least one more input byte available. | 
 
 
 
 
 | 337 | // Plus whatever QIODevice has buffered. | 
 
 
 
 
 | 338 | return (d->atEnd ? 0 : 1) + QIODevice::bytesAvailable(); | 
 
 
 
 
 | 339 | } |