httpfilter.cc
00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 2002 Waldo Bastian <bastian@kde.org> 00004 00005 This library is free software; you can redistribute it and/or 00006 modify it under the terms of the GNU Library General Public 00007 License version 2 as published by the Free Software Foundation. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include <kio/global.h> 00021 00022 #include <klocale.h> 00023 00024 #include "httpfilter.h" 00025 00026 HTTPFilterBase::HTTPFilterBase() 00027 : last(0) 00028 { 00029 } 00030 00031 HTTPFilterBase::~HTTPFilterBase() 00032 { 00033 delete last; 00034 } 00035 00036 void 00037 HTTPFilterBase::chain(HTTPFilterBase *previous) 00038 { 00039 last = previous; 00040 connect(last, TQT_SIGNAL(output(const TQByteArray &)), 00041 this, TQT_SLOT(slotInput(const TQByteArray &))); 00042 } 00043 00044 HTTPFilterChain::HTTPFilterChain() 00045 : first(0) 00046 { 00047 } 00048 00049 void 00050 HTTPFilterChain::addFilter(HTTPFilterBase *filter) 00051 { 00052 if (!last) 00053 { 00054 first = filter; 00055 } 00056 else 00057 { 00058 disconnect(last, TQT_SIGNAL(output(const TQByteArray &)), 0, 0); 00059 filter->chain(last); 00060 } 00061 last = filter; 00062 connect(filter, TQT_SIGNAL(output(const TQByteArray &)), 00063 this, TQT_SIGNAL(output(const TQByteArray &))); 00064 connect(filter, TQT_SIGNAL(error(int, const TQString &)), 00065 this, TQT_SIGNAL(error(int, const TQString &))); 00066 } 00067 00068 void 00069 HTTPFilterChain::slotInput(const TQByteArray &d) 00070 { 00071 if (first) 00072 first->slotInput(d); 00073 else 00074 emit output(d); 00075 } 00076 00077 HTTPFilterMD5::HTTPFilterMD5() 00078 { 00079 } 00080 00081 TQString 00082 HTTPFilterMD5::md5() 00083 { 00084 return TQString::fromLatin1(context.base64Digest()); 00085 } 00086 00087 void 00088 HTTPFilterMD5::slotInput(const TQByteArray &d) 00089 { 00090 context.update(d); 00091 emit output(d); 00092 } 00093 00094 00095 HTTPFilterGZip::HTTPFilterGZip() 00096 { 00097 #ifdef DO_GZIP 00098 bHasHeader = false; 00099 bHasFinished = false; 00100 bPlainText = false; 00101 bEatTrailer = false; 00102 bEof = false; 00103 zstr.next_in = (Bytef *) Z_NULL; 00104 zstr.avail_in = 0; 00105 zstr.zalloc = Z_NULL; 00106 zstr.zfree = Z_NULL; 00107 zstr.opaque = Z_NULL; 00108 00109 inflateInit2(&zstr, -MAX_WBITS); 00110 00111 iTrailer = 8; 00112 #endif 00113 } 00114 00115 HTTPFilterGZip::~HTTPFilterGZip() 00116 { 00117 #ifdef DO_GZIP 00118 inflateEnd(&zstr); 00119 #endif 00120 00121 } 00122 00123 /* The get_byte() and checkHeader() functions are modified version from */ 00124 /* the correpsonding functions that can be found in zlib, the following */ 00125 /* copyright notice applies to these functions: */ 00126 00127 /* zlib.h -- interface of the 'zlib' general purpose compression library 00128 version 1.1.3, July 9th, 1998 00129 00130 Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler 00131 00132 This software is provided 'as-is', without any express or implied 00133 warranty. In no event will the authors be held liable for any damages 00134 arising from the use of this software. 00135 00136 Permission is granted to anyone to use this software for any purpose, 00137 including commercial applications, and to alter it and redistribute it 00138 freely, subject to the following restrictions: 00139 00140 1. The origin of this software must not be misrepresented; you must not 00141 claim that you wrote the original software. If you use this software 00142 in a product, an acknowledgment in the product documentation would be 00143 appreciated but is not required. 00144 2. Altered source versions must be plainly marked as such, and must not be 00145 misrepresented as being the original software. 00146 3. This notice may not be removed or altered from any source distribution. 00147 00148 Jean-loup Gailly Mark Adler 00149 jloup@gzip.org madler@alumni.caltech.edu 00150 00151 00152 The data format used by the zlib library is described by RFCs (Request for 00153 Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt 00154 (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). 00155 */ 00156 00157 int 00158 HTTPFilterGZip::get_byte() 00159 { 00160 #ifdef DO_GZIP 00161 if (bEof) return EOF; 00162 if (zstr.avail_in == 0) 00163 { 00164 bEof = true; 00165 return EOF; 00166 } 00167 zstr.avail_in--; 00168 zstr.total_in++; 00169 return *(zstr.next_in)++; 00170 #else 00171 return 0; 00172 #endif 00173 } 00174 00175 #ifdef DO_GZIP 00176 00177 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */ 00178 00179 /* gzip flag byte */ 00180 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */ 00181 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */ 00182 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */ 00183 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */ 00184 #define COMMENT 0x10 /* bit 4 set: file comment present */ 00185 #define RESERVED 0xE0 /* bits 5..7: reserved */ 00186 #endif 00187 00188 // 0 : ok 00189 // 1 : not gzip 00190 // 2 : no header 00191 int 00192 HTTPFilterGZip::checkHeader() 00193 { 00194 #ifdef DO_GZIP 00195 uInt len; 00196 int c; 00197 00198 /* Check the gzip magic header */ 00199 for (len = 0; len < 2; len++) { 00200 c = get_byte(); 00201 if (c != gz_magic[len]) { 00202 if (len != 0) 00203 { 00204 zstr.avail_in++; 00205 zstr.next_in--; 00206 } 00207 if (c != EOF) { 00208 zstr.avail_in++; 00209 zstr.next_in--; 00210 return 1; 00211 } 00212 return 2; 00213 } 00214 } 00215 00216 int method = get_byte(); /* method byte */ 00217 int flags = get_byte(); /* flags byte */ 00218 00219 if (method != Z_DEFLATED || (flags & RESERVED) != 0) { 00220 return bEof ? 2 : 1; 00221 } 00222 00223 /* Discard time, xflags and OS code: */ 00224 for (len = 0; len < 6; len++) (void)get_byte(); 00225 00226 if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */ 00227 len = (uInt)get_byte(); 00228 len += ((uInt)get_byte())<<8; 00229 /* len is garbage if EOF but the loop below will quit anyway */ 00230 while (len-- != 0 && get_byte() != EOF) ; 00231 } 00232 if ((flags & ORIG_NAME) != 0) { /* skip the original file name */ 00233 while ((c = get_byte()) != 0 && c != EOF) ; 00234 } 00235 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */ 00236 while ((c = get_byte()) != 0 && c != EOF) ; 00237 } 00238 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */ 00239 for (len = 0; len < 2; len++) (void)get_byte(); 00240 } 00241 00242 return bEof ? 2 : 0; 00243 #else 00244 return 0; 00245 #endif 00246 } 00247 00248 void 00249 HTTPFilterGZip::slotInput(const TQByteArray &d) 00250 { 00251 #ifdef DO_GZIP 00252 if (bPlainText) 00253 { 00254 emit output(d); 00255 return; 00256 } 00257 if (d.size() == 0) 00258 { 00259 if (bEatTrailer) 00260 bHasFinished = true; 00261 if (!bHasFinished) 00262 { 00263 // Make sure we get the last bytes still in the pipe. 00264 // Needed with "deflate". 00265 TQByteArray flush(4); 00266 flush.fill(0); 00267 slotInput(flush); 00268 if (!bHasFinished && !bHasHeader) 00269 { 00270 // Send as-is 00271 emit output(headerData); 00272 bHasFinished = true; 00273 // End of data 00274 emit output(TQByteArray()); 00275 } 00276 } 00277 if (!bHasFinished) 00278 emit error( KIO::ERR_SLAVE_DEFINED, i18n("Unexpected end of data, some information may be lost.")); 00279 return; 00280 } 00281 if (bHasFinished) 00282 return; 00283 00284 if (bEatTrailer) 00285 { 00286 iTrailer -= d.size(); 00287 if (iTrailer <= 0) 00288 { 00289 bHasFinished = true; 00290 // End of data 00291 emit output(TQByteArray()); 00292 } 00293 return; 00294 } 00295 00296 if (!bHasHeader) 00297 { 00298 bEof = false; 00299 00300 // Add data to header. 00301 int orig_size = headerData.size(); 00302 headerData.resize(orig_size+d.size()); 00303 memcpy(headerData.data()+orig_size, d.data(), d.size()); 00304 00305 zstr.avail_in = headerData.size(); 00306 zstr.next_in = (Bytef *) headerData.data(); 00307 00308 int result = checkHeader(); 00309 if (result == 1) 00310 { 00311 bPlainText = true; 00312 output(headerData); 00313 return; 00314 } 00315 00316 if (result != 0) 00317 return; // next time better 00318 00319 bHasHeader = true; 00320 } 00321 else 00322 { 00323 zstr.avail_in = d.size(); 00324 zstr.next_in = (Bytef *) d.data(); 00325 } 00326 00327 while( zstr.avail_in ) 00328 { 00329 char buf[8192]; 00330 zstr.next_out = (Bytef *) buf; 00331 zstr.avail_out = 8192; 00332 int result = inflate( &zstr, Z_NO_FLUSH ); 00333 if ((result != Z_OK) && (result != Z_STREAM_END)) 00334 { 00335 emit error( KIO::ERR_SLAVE_DEFINED, i18n("Receiving corrupt data.")); 00336 break; 00337 } 00338 int bytesOut = 8192 - zstr.avail_out; 00339 if (bytesOut) 00340 { 00341 TQByteArray d; 00342 d.setRawData( buf, bytesOut ); 00343 emit output(d); 00344 d.resetRawData( buf, bytesOut ); 00345 } 00346 if (result == Z_STREAM_END) 00347 { 00348 if (iTrailer) 00349 { 00350 bEatTrailer = true; 00351 } 00352 else 00353 { 00354 bHasFinished = true; 00355 // End of data 00356 emit output(TQByteArray()); 00357 } 00358 return; 00359 } 00360 } 00361 #endif 00362 } 00363 00364 HTTPFilterDeflate::HTTPFilterDeflate() 00365 { 00366 #ifdef DO_GZIP 00367 bHasHeader = true; 00368 iTrailer = 0; 00369 #endif 00370 } 00371 00372 #include "httpfilter.moc"