Vidalia 0.2.10
|
00001 /* 00002 ** This file is part of Vidalia, and is subject to the license terms in the 00003 ** LICENSE file, found in the top level directory of this distribution. If you 00004 ** did not receive the LICENSE file with this file, you may obtain it from the 00005 ** Vidalia source package distributed by the Vidalia Project at 00006 ** http://www.vidalia-project.net/. No part of Vidalia, including this file, 00007 ** may be copied, modified, propagated, or distributed except according to the 00008 ** terms described in the LICENSE file. 00009 ** 00010 ** * * * 00011 ** 00012 ** Zlib support in this class is derived from Tor's torgzip.[ch]. 00013 ** Tor is distributed under this license: 00014 ** 00015 ** Copyright (c) 2001-2004, Roger Dingledine 00016 ** Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson 00017 ** 00018 ** Redistribution and use in source and binary forms, with or without 00019 ** modification, are permitted provided that the following conditions are 00020 ** met: 00021 ** 00022 ** * Redistributions of source code must retain the above copyright 00023 ** notice, this list of conditions and the following disclaimer. 00024 ** 00025 ** * Redistributions in binary form must reproduce the above 00026 ** copyright notice, this list of conditions and the following disclaimer 00027 ** in the documentation and/or other materials provided with the 00028 ** distribution. 00029 ** 00030 ** * Neither the names of the copyright owners nor the names of its 00031 ** contributors may be used to endorse or promote products derived from 00032 ** this software without specific prior written permission. 00033 ** 00034 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 00035 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 00036 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 00037 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 00038 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00039 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 00040 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 00041 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 00042 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 00043 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 00044 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00045 */ 00046 00047 /* 00048 ** \file ZlibByteArray.cpp 00049 ** \version $Id: ZlibByteArray.cpp 4203 2010-01-22 18:49:00Z edmanm $ 00050 ** \brief Wrapper around QByteArray that adds compression capabilities 00051 */ 00052 00053 #include "config.h" 00054 00055 #include <QString> 00056 00057 #ifdef HAVE_LIMITS_H 00058 #include <limits.h> 00059 #elif defined(HAVE_SYS_LIMITS_H) 00060 #include <sys/limits.h> 00061 #endif 00062 00063 /* The following check for UINT_MAX is derived from Tor's torint.h. See 00064 * the top of this file for details on Tor's license. */ 00065 #ifndef UINT_MAX 00066 #if (SIZEOF_INT == 2) 00067 #define UINT_MAX 0xffffu 00068 #elif (SIZEOF_INT == 4) 00069 #define UINT_MAX 0xffffffffu 00070 #elif (SIZEOF_INT == 8) 00071 #define UINT_MAX 0xffffffffffffffffu 00072 #else 00073 #error "Your platform uses a sizeof(int) that we don't understand." 00074 #endif 00075 #endif 00076 00077 #include "zlib.h" 00078 #include <ZlibByteArray.h> 00079 00080 00081 /** Constructor */ 00082 ZlibByteArray::ZlibByteArray(QByteArray data) 00083 : QByteArray(data) 00084 { 00085 } 00086 00087 /** Return the 'bits' value to tell zlib to use <b>method</b>.*/ 00088 int 00089 ZlibByteArray::methodBits(CompressionMethod method) 00090 { 00091 /* Bits+16 means "use gzip" in zlib >= 1.2 */ 00092 return (method == Gzip ? 15+16 : 15); 00093 } 00094 00095 /** Returns a string description of <b>method</b>. */ 00096 QString 00097 ZlibByteArray::methodString(CompressionMethod method) 00098 { 00099 switch (method) { 00100 case None: return "None"; 00101 case Zlib: return "Zlib"; 00102 case Gzip: return "Gzip"; 00103 default: return "Unknown"; 00104 } 00105 } 00106 00107 /** Returns true if the Zlib compression library is available and usable. */ 00108 bool 00109 ZlibByteArray::isZlibAvailable() 00110 { 00111 static int isZlibAvailable = -1; 00112 if (isZlibAvailable >= 0) 00113 return isZlibAvailable; 00114 00115 /* From zlib.h: 00116 * "The application can compare zlibVersion and ZLIB_VERSION for consistency. 00117 * If the first character differs, the library code actually used is 00118 * not compatible with the zlib.h header file used by the application." */ 00119 QString libVersion(zlibVersion()); 00120 QString headerVersion(ZLIB_VERSION); 00121 if (libVersion.isEmpty() || headerVersion.isEmpty() || 00122 libVersion.at(0) != headerVersion.at(0)) 00123 isZlibAvailable = 0; 00124 else 00125 isZlibAvailable = 1; 00126 00127 return isZlibAvailable; 00128 } 00129 00130 /** Returns true iff we support gzip-based compression. Otherwise, we need to 00131 * use zlib. */ 00132 bool 00133 ZlibByteArray::isGzipSupported() 00134 { 00135 static int isGzipSupported = -1; 00136 if (isGzipSupported >= 0) 00137 return isGzipSupported; 00138 00139 QString version(zlibVersion()); 00140 if (version.startsWith("0.") || 00141 version.startsWith("1.0") || 00142 version.startsWith("1.1")) 00143 isGzipSupported = 0; 00144 else 00145 isGzipSupported = 1; 00146 00147 return isGzipSupported; 00148 } 00149 00150 /** Compresses the current contents of this object using <b>method</b>. 00151 * Returns the compressed data if successful. If an error occurs, this will 00152 * return an empty QByteArray and set the optional <b>errmsg</b> to a string 00153 * describing the failure. */ 00154 QByteArray 00155 ZlibByteArray::compress(const CompressionMethod method, 00156 QString *errmsg) const 00157 { 00158 return compress(QByteArray(data()), method, errmsg); 00159 } 00160 00161 /** Compresses <b>in</b> using <b>method</b>. Returns the compressed data 00162 * if successful. If an error occurs, this will return an empty QByteArray and 00163 * set the optional <b>errmsg</b> to a string describing the failure. */ 00164 QByteArray 00165 ZlibByteArray::compress(const QByteArray in, 00166 const CompressionMethod method, 00167 QString *errmsg) 00168 { 00169 QByteArray out; 00170 QString errorstr; 00171 struct z_stream_s *stream = NULL; 00172 size_t out_size; 00173 size_t out_len; 00174 size_t in_len = in.length(); 00175 off_t offset; 00176 00177 if (method == None) 00178 return in; 00179 if (method == Gzip && !isGzipSupported()) { 00180 /* Old zlib versions don't support gzip in deflateInit2 */ 00181 if (errmsg) 00182 *errmsg = QString("Gzip not supported with zlib %1") 00183 .arg(ZLIB_VERSION); 00184 return QByteArray(); 00185 } 00186 00187 stream = new struct z_stream_s; 00188 stream->zalloc = Z_NULL; 00189 stream->zfree = Z_NULL; 00190 stream->opaque = NULL; 00191 stream->next_in = (unsigned char*)in.data(); 00192 stream->avail_in = in_len; 00193 00194 if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED, 00195 methodBits(method), 00196 8, Z_DEFAULT_STRATEGY) != Z_OK) { 00197 errorstr = QString("Error from deflateInit2: %1") 00198 .arg(stream->msg ? stream->msg : "<no message>"); 00199 goto err; 00200 } 00201 00202 /* Guess 50% compression. */ 00203 out_size = in_len / 2; 00204 if (out_size < 1024) out_size = 1024; 00205 00206 out.resize(out_size); 00207 stream->next_out = (unsigned char*)out.data(); 00208 stream->avail_out = out_size; 00209 00210 while (1) { 00211 switch (deflate(stream, Z_FINISH)) 00212 { 00213 case Z_STREAM_END: 00214 goto done; 00215 case Z_OK: 00216 /* In case zlib doesn't work as I think .... */ 00217 if (stream->avail_out >= stream->avail_in+16) 00218 break; 00219 case Z_BUF_ERROR: 00220 offset = stream->next_out - ((unsigned char*)out.data()); 00221 out_size *= 2; 00222 out.resize(out_size); 00223 stream->next_out = (unsigned char*)(out.data() + offset); 00224 if (out_size - offset > UINT_MAX) { 00225 errorstr = 00226 "Ran over unsigned int limit of zlib while uncompressing"; 00227 goto err; 00228 } 00229 stream->avail_out = (unsigned int)(out_size - offset); 00230 break; 00231 default: 00232 errorstr = QString("%1 compression didn't finish: %2") 00233 .arg(methodString(method)) 00234 .arg(stream->msg ? stream->msg : "<no message>"); 00235 goto err; 00236 } 00237 } 00238 done: 00239 out_len = stream->total_out; 00240 if (deflateEnd(stream)!=Z_OK) { 00241 errorstr = "Error freeing zlib structures"; 00242 goto err; 00243 } 00244 out.resize(out_len); 00245 delete stream; 00246 return out; 00247 err: 00248 if (stream) { 00249 deflateEnd(stream); 00250 delete stream; 00251 } 00252 if (errmsg) 00253 *errmsg = errorstr; 00254 return QByteArray(); 00255 } 00256 00257 /** Uncompresses the current contents of this object using <b>method</b>. 00258 * Returns the uncompressed data if successful. If an error occurs, this will 00259 * return an empty QByteArray and set the optional <b>errmsg</b> to a string 00260 * describing the failure. */ 00261 QByteArray 00262 ZlibByteArray::uncompress(const CompressionMethod method, 00263 QString *errmsg) const 00264 { 00265 return uncompress(QByteArray(data()), method, errmsg); 00266 } 00267 00268 /** Uncompresses <b>in</b> using <b>method</b>. Returns the uncompressed data 00269 * if successful. If an error occurs, this will return an empty QByteArray and 00270 * set the optional <b>errmsg</b> to a string describing the failure. */ 00271 QByteArray 00272 ZlibByteArray::uncompress(const QByteArray in, 00273 const CompressionMethod method, 00274 QString *errmsg) 00275 { 00276 QByteArray out; 00277 QString errorstr; 00278 struct z_stream_s *stream = NULL; 00279 size_t out_size; 00280 size_t out_len; 00281 size_t in_len = in.length(); 00282 off_t offset; 00283 int r; 00284 00285 if (method == None) 00286 return in; 00287 if (method == Gzip && !isGzipSupported()) { 00288 /* Old zlib versions don't support gzip in inflateInit2 */ 00289 if (errmsg) 00290 *errmsg = QString("Gzip not supported with zlib %1") 00291 .arg(ZLIB_VERSION); 00292 return QByteArray(); 00293 } 00294 00295 stream = new struct z_stream_s; 00296 stream->zalloc = Z_NULL; 00297 stream->zfree = Z_NULL; 00298 stream->opaque = NULL; 00299 stream->msg = NULL; 00300 stream->next_in = (unsigned char*) in.data(); 00301 stream->avail_in = in_len; 00302 00303 if (inflateInit2(stream, 00304 methodBits(method)) != Z_OK) { 00305 errorstr = QString("Error from inflateInit2: %1") 00306 .arg(stream->msg ? stream->msg : "<no message>"); 00307 goto err; 00308 } 00309 00310 out_size = in_len * 2; /* guess 50% compression. */ 00311 if (out_size < 1024) out_size = 1024; 00312 00313 out.resize(out_size); 00314 stream->next_out = (unsigned char*)out.data(); 00315 stream->avail_out = out_size; 00316 00317 while (1) { 00318 switch (inflate(stream, Z_FINISH)) 00319 { 00320 case Z_STREAM_END: 00321 if (stream->avail_in == 0) 00322 goto done; 00323 /* There may be more compressed data here. */ 00324 if ((r = inflateEnd(stream)) != Z_OK) { 00325 errorstr = "Error freeing zlib structures"; 00326 goto err; 00327 } 00328 if (inflateInit2(stream, methodBits(method)) != Z_OK) { 00329 errorstr = QString("Error from second inflateInit2: %1") 00330 .arg(stream->msg ? stream->msg : "<no message>"); 00331 goto err; 00332 } 00333 break; 00334 case Z_OK: 00335 if (stream->avail_in == 0) 00336 goto done; 00337 /* In case zlib doesn't work as I think.... */ 00338 if (stream->avail_out >= stream->avail_in+16) 00339 break; 00340 case Z_BUF_ERROR: 00341 if (stream->avail_out > 0) { 00342 errorstr = QString("Possible truncated or corrupt %1 data") 00343 .arg(methodString(method)); 00344 goto err; 00345 } 00346 offset = stream->next_out - (unsigned char*)out.data(); 00347 out_size *= 2; 00348 out.resize(out_size); 00349 stream->next_out = (unsigned char*)(out.data() + offset); 00350 if (out_size - offset > UINT_MAX) { 00351 errorstr = 00352 "Ran over unsigned int limit of zlib while uncompressing"; 00353 goto err; 00354 } 00355 stream->avail_out = (unsigned int)(out_size - offset); 00356 break; 00357 default: 00358 errorstr = QString("%1 decompression returned an error: %2") 00359 .arg(methodString(method)) 00360 .arg(stream->msg ? stream->msg : "<no message>"); 00361 goto err; 00362 } 00363 } 00364 done: 00365 out_len = stream->next_out - (unsigned char*)out.data(); 00366 r = inflateEnd(stream); 00367 delete stream; 00368 if (r != Z_OK) { 00369 errorstr = "Error freeing zlib structure"; 00370 goto err; 00371 } 00372 out.resize(out_len); 00373 return out; 00374 err: 00375 if (stream) { 00376 inflateEnd(stream); 00377 delete stream; 00378 } 00379 if (errmsg) 00380 *errmsg = errorstr; 00381 return QByteArray(); 00382 } 00383