fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kio / httpfilter / httpfilter.cc
blob500b33935022055d2e984b295ef2450c1a8dcdf6
1 /*
2 This file is part of the KDE libraries
3 Copyright (c) 2002 Waldo Bastian <bastian@kde.org>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "httpfilter.h"
22 #include <kio/global.h>
24 #include <klocale.h>
26 #include <stdio.h>
28 HTTPFilterBase::HTTPFilterBase()
29 : last(0)
33 HTTPFilterBase::~HTTPFilterBase()
35 delete last;
38 void
39 HTTPFilterBase::chain(HTTPFilterBase *previous)
41 last = previous;
42 connect(last, SIGNAL(output(const QByteArray &)),
43 this, SLOT(slotInput(const QByteArray &)));
46 HTTPFilterChain::HTTPFilterChain()
47 : first(0)
51 void
52 HTTPFilterChain::addFilter(HTTPFilterBase *filter)
54 if (!last)
56 first = filter;
58 else
60 disconnect(last, SIGNAL(output(const QByteArray &)), 0, 0);
61 filter->chain(last);
63 last = filter;
64 connect(filter, SIGNAL(output(const QByteArray &)),
65 this, SIGNAL(output(const QByteArray &)));
66 connect(filter, SIGNAL(error(int, const QString &)),
67 this, SIGNAL(error(int, const QString &)));
70 void
71 HTTPFilterChain::slotInput(const QByteArray &d)
73 if (first)
74 first->slotInput(d);
75 else
76 emit output(d);
79 HTTPFilterMD5::HTTPFilterMD5()
83 QString
84 HTTPFilterMD5::md5()
86 return QLatin1String(context.base64Digest());
89 void
90 HTTPFilterMD5::slotInput(const QByteArray &d)
92 context.update(d);
93 emit output(d);
97 HTTPFilterGZip::HTTPFilterGZip()
99 #ifdef DO_GZIP
100 bHasHeader = false;
101 bHasFinished = false;
102 bPlainText = false;
103 bEatTrailer = false;
104 bEof = false;
105 zstr.next_in = (Bytef *) Z_NULL;
106 zstr.avail_in = 0;
107 zstr.zalloc = Z_NULL;
108 zstr.zfree = Z_NULL;
109 zstr.opaque = Z_NULL;
110 inflateInit2(&zstr, -MAX_WBITS);
111 iTrailer = 8;
112 #endif
115 HTTPFilterGZip::~HTTPFilterGZip()
117 #ifdef DO_GZIP
118 inflateEnd(&zstr);
119 #endif
123 /* The get_byte() and checkHeader() functions are modified version from */
124 /* the correpsonding functions that can be found in zlib, the following */
125 /* copyright notice applies to these functions: */
127 /* zlib.h -- interface of the 'zlib' general purpose compression library
128 version 1.1.3, July 9th, 1998
130 Copyright (C) 1995-1998 Jean-loup Gailly and Mark Adler
132 This software is provided 'as-is', without any express or implied
133 warranty. In no event will the authors be held liable for any damages
134 arising from the use of this software.
136 Permission is granted to anyone to use this software for any purpose,
137 including commercial applications, and to alter it and redistribute it
138 freely, subject to the following restrictions:
140 1. The origin of this software must not be misrepresented; you must not
141 claim that you wrote the original software. If you use this software
142 in a product, an acknowledgment in the product documentation would be
143 appreciated but is not required.
144 2. Altered source versions must be plainly marked as such, and must not be
145 misrepresented as being the original software.
146 3. This notice may not be removed or altered from any source distribution.
148 Jean-loup Gailly Mark Adler
149 jloup@gzip.org madler@alumni.caltech.edu
152 The data format used by the zlib library is described by RFCs (Request for
153 Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt
154 (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format).
158 HTTPFilterGZip::get_byte()
160 #ifdef DO_GZIP
161 if (bEof) return EOF;
162 if (zstr.avail_in == 0)
164 bEof = true;
165 return EOF;
167 zstr.avail_in--;
168 zstr.total_in++;
169 return *(zstr.next_in)++;
170 #else
171 return 0;
172 #endif
175 #ifdef DO_GZIP
177 static int gz_magic[2] = {0x1f, 0x8b}; /* gzip magic header */
179 /* gzip flag byte */
180 #define ASCII_FLAG 0x01 /* bit 0 set: file probably ascii text */
181 #define HEAD_CRC 0x02 /* bit 1 set: header CRC present */
182 #define EXTRA_FIELD 0x04 /* bit 2 set: extra field present */
183 #define ORIG_NAME 0x08 /* bit 3 set: original file name present */
184 #define COMMENT 0x10 /* bit 4 set: file comment present */
185 #define RESERVED 0xE0 /* bits 5..7: reserved */
186 #endif
188 // 0 : ok
189 // 1 : not gzip
190 // 2 : no header
192 HTTPFilterGZip::checkHeader()
194 #ifdef DO_GZIP
195 uInt len;
196 int c;
198 /* Check the gzip magic header */
199 for (len = 0; len < 2; ++len) {
200 c = get_byte();
201 if (c != gz_magic[len]) {
202 if (len != 0)
204 zstr.avail_in++;
205 zstr.next_in--;
207 if (c != EOF) {
208 zstr.avail_in++;
209 zstr.next_in--;
210 return 1;
212 return 2;
215 int method = get_byte(); /* method byte */
216 int flags = get_byte(); /* flags byte */
218 if (method != Z_DEFLATED || (flags & RESERVED) != 0) {
219 return bEof ? 2 : 1;
222 /* Discard time, xflags and OS code: */
223 for (len = 0; len < 6; ++len) (void)get_byte();
225 if ((flags & EXTRA_FIELD) != 0) { /* skip the extra field */
226 len = (uInt)get_byte();
227 len += ((uInt)get_byte())<<8;
228 /* len is garbage if EOF but the loop below will quit anyway */
229 while (len-- != 0 && get_byte() != EOF) ;
231 if ((flags & ORIG_NAME) != 0) { /* skip the original file name */
232 while ((c = get_byte()) != 0 && c != EOF) ;
234 if ((flags & COMMENT) != 0) { /* skip the .gz file comment */
235 while ((c = get_byte()) != 0 && c != EOF) ;
237 if ((flags & HEAD_CRC) != 0) { /* skip the header crc */
238 for (len = 0; len < 2; ++len) (void)get_byte();
241 return bEof ? 2 : 0;
242 #else
243 return 0;
244 #endif
247 void
248 HTTPFilterGZip::slotInput(const QByteArray &d)
250 #ifdef DO_GZIP
251 if (bPlainText)
253 emit output(d);
254 return;
256 if (d.size() == 0)
258 if (bEatTrailer)
259 bHasFinished = true;
260 if (!bHasFinished)
262 // Make sure we get the last bytes still in the pipe.
263 // Needed with "deflate".
264 QByteArray flush(4, 0);
265 slotInput(flush);
266 if (!bHasFinished && !bHasHeader)
268 // Send as-is
269 emit output(headerData);
270 bHasFinished = true;
271 // End of data
272 emit output(QByteArray());
275 if (!bHasFinished)
276 emit error( KIO::ERR_SLAVE_DEFINED, i18n("Unexpected end of data, some information may be lost."));
277 return;
279 if (bHasFinished)
280 return;
282 if (bEatTrailer)
284 iTrailer -= d.size();
285 if (iTrailer <= 0)
287 bHasFinished = true;
288 // End of data
289 emit output(QByteArray());
291 return;
294 if (!bHasHeader)
296 bEof = false;
298 // Add data to header.
299 int orig_size = headerData.size();
300 headerData.resize(orig_size+d.size());
301 memcpy(headerData.data()+orig_size, d.data(), d.size());
303 zstr.avail_in = headerData.size();
304 zstr.next_in = (Bytef *) headerData.data();
306 int result = checkHeader();
307 if (result == 1)
309 bPlainText = true;
310 output(headerData);
311 return;
314 if (result != 0)
315 return; // next time better
317 bHasHeader = true;
319 else
321 zstr.avail_in = d.size();
322 zstr.next_in = (Bytef *) d.data();
325 while( zstr.avail_in )
327 char buf[8192];
328 zstr.next_out = (Bytef *) buf;
329 zstr.avail_out = 8192;
330 int result = inflate( &zstr, Z_NO_FLUSH );
331 if ((result != Z_OK) && (result != Z_STREAM_END))
333 emit error( KIO::ERR_SLAVE_DEFINED, i18n("Receiving corrupt data."));
334 break;
336 int bytesOut = 8192 - zstr.avail_out;
337 if (bytesOut)
339 QByteArray d( buf, bytesOut );
340 emit output(d);
342 if (result == Z_STREAM_END)
344 if (iTrailer)
346 bEatTrailer = true;
348 else
350 bHasFinished = true;
351 // End of data
352 emit output(QByteArray());
354 return;
357 #endif
360 HTTPFilterDeflate::HTTPFilterDeflate()
362 #ifdef DO_GZIP
363 bHasHeader = true;
364 iTrailer = 0;
365 #endif
368 #include "httpfilter.moc"