Vidalia
0.2.17
|
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.torproject.org/projects/vidalia.html. No part of Vidalia, 00007 ** including this file, may be copied, modified, propagated, or distributed 00008 ** except according to the 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 ** \brief Wrapper around QByteArray that adds compression capabilities 00050 */ 00051 00052 #include "config.h" 00053 00054 #include <QString> 00055 00056 #ifdef HAVE_LIMITS_H 00057 #include <limits.h> 00058 #elif defined(HAVE_SYS_LIMITS_H) 00059 #include <sys/limits.h> 00060 #endif 00061 00062 /* The following check for UINT_MAX is derived from Tor's torint.h. See 00063 * the top of this file for details on Tor's license. */ 00064 #ifndef UINT_MAX 00065 #if (SIZEOF_INT == 2) 00066 #define UINT_MAX 0xffffu 00067 #elif (SIZEOF_INT == 4) 00068 #define UINT_MAX 0xffffffffu 00069 #elif (SIZEOF_INT == 8) 00070 #define UINT_MAX 0xffffffffffffffffu 00071 #else 00072 #error "Your platform uses a sizeof(int) that we don't understand." 00073 #endif 00074 #endif 00075 00076 #include "zlib.h" 00077 #include <ZlibByteArray.h> 00078 00079 00080 /** Constructor */ 00081 ZlibByteArray::ZlibByteArray(QByteArray data) 00082 : QByteArray(data) 00083 { 00084 } 00085 00086 /** Return the 'bits' value to tell zlib to use <b>method</b>.*/ 00087 int 00088 ZlibByteArray::methodBits(CompressionMethod method) 00089 { 00090 /* Bits+16 means "use gzip" in zlib >= 1.2 */ 00091 return (method == Gzip ? 15+16 : 15); 00092 } 00093 00094 /** Returns a string description of <b>method</b>. */ 00095 QString 00096 ZlibByteArray::methodString(CompressionMethod method) 00097 { 00098 switch (method) { 00099 case None: return "None"; 00100 case Zlib: return "Zlib"; 00101 case Gzip: return "Gzip"; 00102 default: return "Unknown"; 00103 } 00104 } 00105 00106 /** Returns true if the Zlib compression library is available and usable. */ 00107 bool 00108 ZlibByteArray::isZlibAvailable() 00109 { 00110 static int isZlibAvailable = -1; 00111 if (isZlibAvailable >= 0) 00112 return isZlibAvailable; 00113 00114 /* From zlib.h: 00115 * "The application can compare zlibVersion and ZLIB_VERSION for consistency. 00116 * If the first character differs, the library code actually used is 00117 * not compatible with the zlib.h header file used by the application." */ 00118 QString libVersion(zlibVersion()); 00119 QString headerVersion(ZLIB_VERSION); 00120 if (libVersion.isEmpty() || headerVersion.isEmpty() || 00121 libVersion.at(0) != headerVersion.at(0)) 00122 isZlibAvailable = 0; 00123 else 00124 isZlibAvailable = 1; 00125 00126 return isZlibAvailable; 00127 } 00128 00129 /** Returns true iff we support gzip-based compression. Otherwise, we need to 00130 * use zlib. */ 00131 bool 00132 ZlibByteArray::isGzipSupported() 00133 { 00134 static int isGzipSupported = -1; 00135 if (isGzipSupported >= 0) 00136 return isGzipSupported; 00137 00138 QString version(zlibVersion()); 00139 if (version.startsWith("0.") || 00140 version.startsWith("1.0") || 00141 version.startsWith("1.1")) 00142 isGzipSupported = 0; 00143 else 00144 isGzipSupported = 1; 00145 00146 return isGzipSupported; 00147 } 00148 00149 /** Compresses the current contents of this object using <b>method</b>. 00150 * Returns the compressed data if successful. If an error occurs, this will 00151 * return an empty QByteArray and set the optional <b>errmsg</b> to a string 00152 * describing the failure. */ 00153 QByteArray 00154 ZlibByteArray::compress(const CompressionMethod method, 00155 QString *errmsg) const 00156 { 00157 return compress(QByteArray(data()), method, errmsg); 00158 } 00159 00160 /** Compresses <b>in</b> using <b>method</b>. Returns the compressed data 00161 * if successful. If an error occurs, this will return an empty QByteArray and 00162 * set the optional <b>errmsg</b> to a string describing the failure. */ 00163 QByteArray 00164 ZlibByteArray::compress(const QByteArray in, 00165 const CompressionMethod method, 00166 QString *errmsg) 00167 { 00168 QByteArray out; 00169 QString errorstr; 00170 struct z_stream_s *stream = NULL; 00171 size_t out_size; 00172 size_t out_len; 00173 size_t in_len = in.length(); 00174 off_t offset; 00175 00176 if (method == None) 00177 return in; 00178 if (method == Gzip && !isGzipSupported()) { 00179 /* Old zlib versions don't support gzip in deflateInit2 */ 00180 if (errmsg) 00181 *errmsg = QString("Gzip not supported with zlib %1") 00182 .arg(ZLIB_VERSION); 00183 return QByteArray(); 00184 } 00185 00186 stream = new struct z_stream_s; 00187 stream->zalloc = Z_NULL; 00188 stream->zfree = Z_NULL; 00189 stream->opaque = NULL; 00190 stream->next_in = (unsigned char*)in.data(); 00191 stream->avail_in = in_len; 00192 00193 if (deflateInit2(stream, Z_BEST_COMPRESSION, Z_DEFLATED, 00194 methodBits(method), 00195 8, Z_DEFAULT_STRATEGY) != Z_OK) { 00196 errorstr = QString("Error from deflateInit2: %1") 00197 .arg(stream->msg ? stream->msg : "<no message>"); 00198 goto err; 00199 } 00200 00201 /* Guess 50% compression. */ 00202 out_size = in_len / 2; 00203 if (out_size < 1024) out_size = 1024; 00204 00205 out.resize(out_size); 00206 stream->next_out = (unsigned char*)out.data(); 00207 stream->avail_out = out_size; 00208 00209 while (1) { 00210 switch (deflate(stream, Z_FINISH)) 00211 { 00212 case Z_STREAM_END: 00213 goto done; 00214 case Z_OK: 00215 /* In case zlib doesn't work as I think .... */ 00216 if (stream->avail_out >= stream->avail_in+16) 00217 break; 00218 case Z_BUF_ERROR: 00219 offset = stream->next_out - ((unsigned char*)out.data()); 00220 out_size *= 2; 00221 out.resize(out_size); 00222 stream->next_out = (unsigned char*)(out.data() + offset); 00223 if (out_size - offset > UINT_MAX) { 00224 errorstr = 00225 "Ran over unsigned int limit of zlib while uncompressing"; 00226 goto err; 00227 } 00228 stream->avail_out = (unsigned int)(out_size - offset); 00229 break; 00230 default: 00231 errorstr = QString("%1 compression didn't finish: %2") 00232 .arg(methodString(method)) 00233 .arg(stream->msg ? stream->msg : "<no message>"); 00234 goto err; 00235 } 00236 } 00237 done: 00238 out_len = stream->total_out; 00239 if (deflateEnd(stream)!=Z_OK) { 00240 errorstr = "Error freeing zlib structures"; 00241 goto err; 00242 } 00243 out.resize(out_len); 00244 delete stream; 00245 return out; 00246 err: 00247 if (stream) { 00248 deflateEnd(stream); 00249 delete stream; 00250 } 00251 if (errmsg) 00252 *errmsg = errorstr; 00253 return QByteArray(); 00254 } 00255 00256 /** Uncompresses the current contents of this object using <b>method</b>. 00257 * Returns the uncompressed data if successful. If an error occurs, this will 00258 * return an empty QByteArray and set the optional <b>errmsg</b> to a string 00259 * describing the failure. */ 00260 QByteArray 00261 ZlibByteArray::uncompress(const CompressionMethod method, 00262 QString *errmsg) const 00263 { 00264 return uncompress(QByteArray(data()), method, errmsg); 00265 } 00266 00267 /** Uncompresses <b>in</b> using <b>method</b>. Returns the uncompressed data 00268 * if successful. If an error occurs, this will return an empty QByteArray and 00269 * set the optional <b>errmsg</b> to a string describing the failure. */ 00270 QByteArray 00271 ZlibByteArray::uncompress(const QByteArray in, 00272 const CompressionMethod method, 00273 QString *errmsg) 00274 { 00275 QByteArray out; 00276 QString errorstr; 00277 struct z_stream_s *stream = NULL; 00278 size_t out_size; 00279 size_t out_len; 00280 size_t in_len = in.length(); 00281 off_t offset; 00282 int r; 00283 00284 if (method == None) 00285 return in; 00286 if (method == Gzip && !isGzipSupported()) { 00287 /* Old zlib versions don't support gzip in inflateInit2 */ 00288 if (errmsg) 00289 *errmsg = QString("Gzip not supported with zlib %1") 00290 .arg(ZLIB_VERSION); 00291 return QByteArray(); 00292 } 00293 00294 stream = new struct z_stream_s; 00295 stream->zalloc = Z_NULL; 00296 stream->zfree = Z_NULL; 00297 stream->opaque = NULL; 00298 stream->msg = NULL; 00299 stream->next_in = (unsigned char*) in.data(); 00300 stream->avail_in = in_len; 00301 00302 if (inflateInit2(stream, 00303 methodBits(method)) != Z_OK) { 00304 errorstr = QString("Error from inflateInit2: %1") 00305 .arg(stream->msg ? stream->msg : "<no message>"); 00306 goto err; 00307 } 00308 00309 out_size = in_len * 2; /* guess 50% compression. */ 00310 if (out_size < 1024) out_size = 1024; 00311 00312 out.resize(out_size); 00313 stream->next_out = (unsigned char*)out.data(); 00314 stream->avail_out = out_size; 00315 00316 while (1) { 00317 switch (inflate(stream, Z_FINISH)) 00318 { 00319 case Z_STREAM_END: 00320 if (stream->avail_in == 0) 00321 goto done; 00322 /* There may be more compressed data here. */ 00323 if ((r = inflateEnd(stream)) != Z_OK) { 00324 errorstr = "Error freeing zlib structures"; 00325 goto err; 00326 } 00327 if (inflateInit2(stream, methodBits(method)) != Z_OK) { 00328 errorstr = QString("Error from second inflateInit2: %1") 00329 .arg(stream->msg ? stream->msg : "<no message>"); 00330 goto err; 00331 } 00332 break; 00333 case Z_OK: 00334 if (stream->avail_in == 0) 00335 goto done; 00336 /* In case zlib doesn't work as I think.... */ 00337 if (stream->avail_out >= stream->avail_in+16) 00338 break; 00339 case Z_BUF_ERROR: 00340 if (stream->avail_out > 0) { 00341 errorstr = QString("Possible truncated or corrupt %1 data") 00342 .arg(methodString(method)); 00343 goto err; 00344 } 00345 offset = stream->next_out - (unsigned char*)out.data(); 00346 out_size *= 2; 00347 out.resize(out_size); 00348 stream->next_out = (unsigned char*)(out.data() + offset); 00349 if (out_size - offset > UINT_MAX) { 00350 errorstr = 00351 "Ran over unsigned int limit of zlib while uncompressing"; 00352 goto err; 00353 } 00354 stream->avail_out = (unsigned int)(out_size - offset); 00355 break; 00356 default: 00357 errorstr = QString("%1 decompression returned an error: %2") 00358 .arg(methodString(method)) 00359 .arg(stream->msg ? stream->msg : "<no message>"); 00360 goto err; 00361 } 00362 } 00363 done: 00364 out_len = stream->next_out - (unsigned char*)out.data(); 00365 r = inflateEnd(stream); 00366 delete stream; 00367 if (r != Z_OK) { 00368 errorstr = "Error freeing zlib structure"; 00369 goto err; 00370 } 00371 out.resize(out_len); 00372 return out; 00373 err: 00374 if (stream) { 00375 inflateEnd(stream); 00376 delete stream; 00377 } 00378 if (errmsg) 00379 *errmsg = errorstr; 00380 return QByteArray(); 00381 } 00382