1 /*
2  * httppoll.cpp - HTTP polling proxy
3  * Copyright (C) 2003  Justin Karneges
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * either version 2
9    of the License, or (at your option) any later version.1 of the License, or (at your option) any later version.
10  *
11  * This library 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 GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 
22 #include "httppoll.h"
23 
24 #include <QUrl>
25 #include <QStringList>
26 #include <QTimer>
27 #include <QPointer>
28 #include <QtCrypto>
29 #include <QByteArray>
30 #include <stdlib.h>
31 #include "bsocket.h"
32 
33 #ifdef PROX_DEBUG
34 #include <stdio.h>
35 #endif
36 
37 #define POLL_KEYS 64
38 
39 // CS_NAMESPACE_BEGIN
40 
randomArray(int size)41 static QByteArray randomArray(int size)
42 {
43 	QByteArray a;
44   a.resize(size);
45 	for(int n = 0; n < size; ++n)
46 		a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
47 	return a;
48 }
49 
50 //----------------------------------------------------------------------------
51 // HttpPoll
52 //----------------------------------------------------------------------------
hpk(int n,const QString & s)53 static QString hpk(int n, const QString &s)
54 {
55 	if(n == 0)
56 		return s;
57 	else
58 		return QCA::Base64().arrayToString( QCA::Hash("sha1").hash( hpk(n - 1, s).toLatin1() ).toByteArray() );
59 }
60 
61 class HttpPoll::Private
62 {
63 public:
Private()64 	Private() {}
65 
66 	HttpProxyPost http;
67 	QString host;
68 	int port;
69 	QString user, pass;
70 	QString url;
71 	bool use_proxy;
72 
73 	QByteArray out;
74 
75 	int state;
76 	bool closing;
77 	QString ident;
78 
79 	QTimer *t;
80 
81 	QString key[POLL_KEYS];
82 	int key_n;
83 
84 	int polltime;
85 };
86 
HttpPoll(QObject * parent)87 HttpPoll::HttpPoll(QObject *parent)
88 :ByteStream(parent)
89 {
90 	d = new Private;
91 
92 	d->polltime = 30;
93 	d->t = new QTimer;
94   d->t->setSingleShot(true);
95 	connect(d->t, &QTimer::timeout, this, &HttpPoll::do_sync);
96 
97 	connect(&d->http, &HttpProxyPost::result, this, &HttpPoll::http_result);
98 	connect(&d->http, &HttpProxyPost::error, this, &HttpPoll::http_error);
99 
100 	reset(true);
101 }
102 
~HttpPoll()103 HttpPoll::~HttpPoll()
104 {
105 	reset(true);
106 	delete d->t;
107 	delete d;
108 }
109 
reset(bool clear)110 void HttpPoll::reset(bool clear)
111 {
112 	if(d->http.isActive())
113 		d->http.stop();
114 	if(clear)
115 		clearReadBuffer();
116 	clearWriteBuffer();
117 	d->out.resize(0);
118 	d->state = 0;
119 	d->closing = false;
120 	d->t->stop();
121 }
122 
setAuth(const QString & user,const QString & pass)123 void HttpPoll::setAuth(const QString &user, const QString &pass)
124 {
125 	d->user = user;
126 	d->pass = pass;
127 }
128 
connectToUrl(const QString & url)129 void HttpPoll::connectToUrl(const QString &url)
130 {
131 	connectToHost("", 0, url);
132 }
133 
connectToHost(const QString & proxyHost,int proxyPort,const QString & url)134 void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url)
135 {
136 	reset(true);
137 
138 	// using proxy?
139 	if(!proxyHost.isEmpty()) {
140 		d->host = proxyHost;
141 		d->port = proxyPort;
142 		d->url = url;
143 		d->use_proxy = true;
144 	}
145 	else {
146 		QUrl u(url);
147 		d->host = u.host();
148 		if(u.port() != -1)
149 			d->port = u.port();
150 		else
151 			d->port = 80;
152 		d->url = u.path() + "?" + u.query(QUrl::FullyEncoded);
153 		d->use_proxy = false;
154 	}
155 
156 	resetKey();
157 	bool last;
158 	QString key = getKey(&last);
159 
160 #ifdef PROX_DEBUG
161 	fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1());
162 	if(d->user.isEmpty())
163 		fprintf(stderr, "\n");
164 	else
165 		fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
166 #endif
167 	QPointer<QObject> self = this;
168 	syncStarted();
169 	if(!self)
170 		return;
171 
172 	d->state = 1;
173 	d->http.setAuth(d->user, d->pass);
174 	d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy);
175 }
176 
makePacket(const QString & ident,const QString & key,const QString & newkey,const QByteArray & block)177 QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block)
178 {
179 	QString str = ident;
180 	if(!key.isEmpty()) {
181 		str += ';';
182 		str += key;
183 	}
184 	if(!newkey.isEmpty()) {
185 		str += ';';
186 		str += newkey;
187 	}
188 	str += ',';
189 	QByteArray cs = str.toLatin1();
190 	int len = cs.length();
191 
192 	QByteArray a;
193 	a.resize(len + block.size());
194 	memcpy(a.data(), cs.data(), len);
195 	memcpy(a.data() + len, block.data(), block.size());
196 	return a;
197 }
198 
pollInterval() const199 int HttpPoll::pollInterval() const
200 {
201 	return d->polltime;
202 }
203 
setPollInterval(int seconds)204 void HttpPoll::setPollInterval(int seconds)
205 {
206 	d->polltime = seconds;
207 }
208 
isOpen() const209 bool HttpPoll::isOpen() const
210 {
211 	return (d->state == 2 ? true: false);
212 }
213 
close()214 void HttpPoll::close()
215 {
216 	if(d->state == 0 || d->closing)
217 		return;
218 
219 	if(bytesToWrite() == 0)
220 		reset();
221 	else
222 		d->closing = true;
223 }
224 
http_result()225 void HttpPoll::http_result()
226 {
227 	// check for death :)
228 	QPointer<QObject> self = this;
229 	syncFinished();
230 	if(!self)
231 		return;
232 
233 	// get id and packet
234 	QString id;
235 	QString cookie = d->http.getHeader("Set-Cookie");
236 	int n = cookie.indexOf("ID=");
237 	if(n == -1) {
238 		reset();
239 		error(ErrRead);
240 		return;
241 	}
242 	n += 3;
243 	int n2 = cookie.indexOf(';', n);
244 	if(n2 != -1)
245 		id = cookie.mid(n, n2-n);
246 	else
247 		id = cookie.mid(n);
248 	QByteArray block = d->http.body();
249 
250 	// session error?
251 	if(id.right(2) == ":0") {
252 		if(id == "0:0" && d->state == 2) {
253 			reset();
254 			connectionClosed();
255 			return;
256 		}
257 		else {
258 			reset();
259 			error(ErrRead);
260 			return;
261 		}
262 	}
263 
264 	d->ident = id;
265 	bool justNowConnected = false;
266 	if(d->state == 1) {
267 		d->state = 2;
268 		justNowConnected = true;
269 	}
270 
271 	// sync up again soon
272 	if(bytesToWrite() > 0 || !d->closing) {
273 		d->t->start(d->polltime * 1000);
274   }
275 
276 	// connecting
277 	if(justNowConnected) {
278 		connected();
279 	}
280 	else {
281 		if(!d->out.isEmpty()) {
282 			int x = d->out.size();
283 			d->out.resize(0);
284 			takeWrite(x);
285 			bytesWritten(x);
286 		}
287 	}
288 
289 	if(!self)
290 		return;
291 
292 	if(!block.isEmpty()) {
293 		appendRead(block);
294 		readyRead();
295 	}
296 
297 	if(!self)
298 		return;
299 
300 	if(bytesToWrite() > 0) {
301 		do_sync();
302 	}
303 	else {
304 		if(d->closing) {
305 			reset();
306 			delayedCloseFinished();
307 			return;
308 		}
309 	}
310 }
311 
http_error(int x)312 void HttpPoll::http_error(int x)
313 {
314 	reset();
315 	if(x == HttpProxyPost::ErrConnectionRefused)
316 		error(ErrConnectionRefused);
317 	else if(x == HttpProxyPost::ErrHostNotFound)
318 		error(ErrHostNotFound);
319 	else if(x == HttpProxyPost::ErrSocket)
320 		error(ErrRead);
321 	else if(x == HttpProxyPost::ErrProxyConnect)
322 		error(ErrProxyConnect);
323 	else if(x == HttpProxyPost::ErrProxyNeg)
324 		error(ErrProxyNeg);
325 	else if(x == HttpProxyPost::ErrProxyAuth)
326 		error(ErrProxyAuth);
327 }
328 
tryWrite()329 int HttpPoll::tryWrite()
330 {
331 	if(!d->http.isActive())
332 		do_sync();
333 	return 0;
334 }
335 
do_sync()336 void HttpPoll::do_sync()
337 {
338 	if(d->http.isActive())
339 		return;
340 
341 	d->t->stop();
342 	d->out = takeWrite(0, false);
343 
344 	bool last;
345 	QString key = getKey(&last);
346 	QString newkey;
347 	if(last) {
348 		resetKey();
349 		newkey = getKey(&last);
350 	}
351 
352 	QPointer<QObject> self = this;
353 	syncStarted();
354 	if(!self)
355 		return;
356 
357 	d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy);
358 }
359 
resetKey()360 void HttpPoll::resetKey()
361 {
362 #ifdef PROX_DEBUG
363 	fprintf(stderr, "HttpPoll: reset key!\n");
364 #endif
365 	QByteArray a = randomArray(64);
366 	QString str = QString::fromLatin1(a.data(), a.size());
367 
368 	d->key_n = POLL_KEYS;
369 	for(int n = 0; n < POLL_KEYS; ++n)
370 		d->key[n] = hpk(n+1, str);
371 }
372 
getKey(bool * last)373 const QString & HttpPoll::getKey(bool *last)
374 {
375 	*last = false;
376 	--(d->key_n);
377 	if(d->key_n == 0)
378 		*last = true;
379 	return d->key[d->key_n];
380 }
381 
382 
383 //----------------------------------------------------------------------------
384 // HttpProxyPost
385 //----------------------------------------------------------------------------
extractLine(QByteArray * buf,bool * found)386 static QString extractLine(QByteArray *buf, bool *found)
387 {
388 	// scan for newline
389 	int n;
390 	for(n = 0; n < (int)buf->size()-1; ++n) {
391 		if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
392 			QByteArray cstr;
393 			cstr.resize(n);
394 			memcpy(cstr.data(), buf->data(), n);
395 			n += 2; // hack off CR/LF
396 
397 			memmove(buf->data(), buf->data() + n, buf->size() - n);
398 			buf->resize(buf->size() - n);
399 			QString s = QString::fromUtf8(cstr);
400 
401 			if(found)
402 				*found = true;
403 			return s;
404 		}
405 	}
406 
407 	if(found)
408 		*found = false;
409 	return "";
410 }
411 
extractMainHeader(const QString & line,QString * proto,int * code,QString * msg)412 static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
413 {
414 	int n = line.indexOf(' ');
415 	if(n == -1)
416 		return false;
417 	if(proto)
418 		*proto = line.mid(0, n);
419 	++n;
420 	int n2 = line.indexOf(' ', n);
421 	if(n2 == -1)
422 		return false;
423 	if(code)
424 		*code = line.midRef(n, n2-n).toInt();
425 	n = n2+1;
426 	if(msg)
427 		*msg = line.mid(n);
428 	return true;
429 }
430 
431 class HttpProxyPost::Private
432 {
433 public:
Private()434 	Private() {}
435 
436 	BSocket sock;
437 	QByteArray postdata, recvBuf, body;
438 	QString url;
439 	QString user, pass;
440 	bool inHeader;
441 	QStringList headerLines;
442 	bool asProxy;
443 	QString host;
444 };
445 
HttpProxyPost(QObject * parent)446 HttpProxyPost::HttpProxyPost(QObject *parent)
447 :QObject(parent)
448 {
449 	d = new Private;
450 	connect(&d->sock, &BSocket::connected, this, &HttpProxyPost::sock_connected);
451 	connect(&d->sock, &ByteStream::connectionClosed, this, &HttpProxyPost::sock_connectionClosed);
452 	connect(&d->sock, &ByteStream::readyRead, this, &HttpProxyPost::sock_readyRead);
453 	connect(&d->sock, &ByteStream::error, this, &HttpProxyPost::sock_error);
454 	reset(true);
455 }
456 
~HttpProxyPost()457 HttpProxyPost::~HttpProxyPost()
458 {
459 	reset(true);
460 	delete d;
461 }
462 
reset(bool clear)463 void HttpProxyPost::reset(bool clear)
464 {
465 	if(d->sock.state() != BSocket::Idle)
466 		d->sock.close();
467 	d->recvBuf.resize(0);
468 	if(clear)
469 		d->body.resize(0);
470 }
471 
setAuth(const QString & user,const QString & pass)472 void HttpProxyPost::setAuth(const QString &user, const QString &pass)
473 {
474 	d->user = user;
475 	d->pass = pass;
476 }
477 
isActive() const478 bool HttpProxyPost::isActive() const
479 {
480 	return (d->sock.state() == BSocket::Idle ? false: true);
481 }
482 
post(const QString & proxyHost,int proxyPort,const QString & url,const QByteArray & data,bool asProxy)483 void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy)
484 {
485 	reset(true);
486 
487 	d->host = proxyHost;
488 	d->url = url;
489 	d->postdata = data;
490 	d->asProxy = asProxy;
491 
492 #ifdef PROX_DEBUG
493 	fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
494 	if(d->user.isEmpty())
495 		fprintf(stderr, "\n");
496 	else
497 		fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
498 #endif
499 	d->sock.connectToHost(proxyHost, proxyPort);
500 }
501 
stop()502 void HttpProxyPost::stop()
503 {
504 	reset();
505 }
506 
body() const507 QByteArray HttpProxyPost::body() const
508 {
509 	return d->body;
510 }
511 
getHeader(const QString & var) const512 QString HttpProxyPost::getHeader(const QString &var) const
513 {
514 	for(QStringList::ConstIterator it = d->headerLines.constBegin(); it != d->headerLines.constEnd(); ++it) {
515 		const QString &s = *it;
516 		int n = s.indexOf(": ");
517 		if(n == -1)
518 			continue;
519 		QString v = s.mid(0, n);
520 		if(v.toLower() == var.toLower())
521 			return s.mid(n+2);
522 	}
523 	return "";
524 }
525 
sock_connected()526 void HttpProxyPost::sock_connected()
527 {
528 #ifdef PROX_DEBUG
529 	fprintf(stderr, "HttpProxyPost: Connected\n");
530 #endif
531 	d->inHeader = true;
532 	d->headerLines.clear();
533 
534 	QUrl u(d->url);
535 
536 	// connected, now send the request
537 	QString s;
538 	s += QString("POST ") + d->url + " HTTP/1.0\r\n";
539 	if(d->asProxy) {
540 		if(!d->user.isEmpty()) {
541 			QString str = d->user + ':' + d->pass;
542 			s += QString("Proxy-Authorization: Basic ") + QCA::Base64().encodeString(str) + "\r\n";
543 		}
544 		s += "Pragma: no-cache\r\n";
545 		s += QString("Host: ") + u.host() + "\r\n";
546 	}
547 	else {
548 		s += QString("Host: ") + d->host + "\r\n";
549 	}
550 	s += "Content-Type: application/x-www-form-urlencoded\r\n";
551 	s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n";
552 	s += "\r\n";
553 
554 	// write request
555 	d->sock.write(s.toUtf8());
556 
557 	// write postdata
558 	d->sock.write(d->postdata);
559 }
560 
sock_connectionClosed()561 void HttpProxyPost::sock_connectionClosed()
562 {
563 	d->body = d->recvBuf;
564 	reset();
565 	result();
566 }
567 
sock_readyRead()568 void HttpProxyPost::sock_readyRead()
569 {
570 	QByteArray block = d->sock.read();
571 	ByteStream::appendArray(&d->recvBuf, block);
572 
573 	if(d->inHeader) {
574 		// grab available lines
575 		while(1) {
576 			bool found;
577 			QString line = extractLine(&d->recvBuf, &found);
578 			if(!found)
579 				break;
580 			if(line.isEmpty()) {
581 				d->inHeader = false;
582 				break;
583 			}
584 			d->headerLines += line;
585 		}
586 
587 		// done with grabbing the header?
588 		if(!d->inHeader) {
589 			QString str = d->headerLines.first();
590 			d->headerLines.takeFirst();
591 
592 			QString proto;
593 			int code;
594 			QString msg;
595 			if(!extractMainHeader(str, &proto, &code, &msg)) {
596 #ifdef PROX_DEBUG
597 				fprintf(stderr, "HttpProxyPost: invalid header!\n");
598 #endif
599 				reset(true);
600 				error(ErrProxyNeg);
601 				return;
602 			}
603 			else {
604 #ifdef PROX_DEBUG
605 				fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
606 				for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
607 					fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1());
608 #endif
609 			}
610 
611 			if(code == 200) { // OK
612 #ifdef PROX_DEBUG
613 				fprintf(stderr, "HttpProxyPost: << Success >>\n");
614 #endif
615 			}
616 			else {
617 				int err;
618 				QString errStr;
619 				if(code == 407) { // Authentication failed
620 					err = ErrProxyAuth;
621 					errStr = tr("Authentication failed");
622 				}
623 				else if(code == 404) { // Host not found
624 					err = ErrHostNotFound;
625 					errStr = tr("Host not found");
626 				}
627 				else if(code == 403) { // Access denied
628 					err = ErrProxyNeg;
629 					errStr = tr("Access denied");
630 				}
631 				else if(code == 503) { // Connection refused
632 					err = ErrConnectionRefused;
633 					errStr = tr("Connection refused");
634 				}
635 				else { // invalid reply
636 					err = ErrProxyNeg;
637 					errStr = tr("Invalid reply");
638 				}
639 
640 #ifdef PROX_DEBUG
641 				fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1());
642 #endif
643 				reset(true);
644 				error(err);
645 				return;
646 			}
647 		}
648 	}
649 }
650 
sock_error(int x)651 void HttpProxyPost::sock_error(int x)
652 {
653 #ifdef PROX_DEBUG
654 	fprintf(stderr, "HttpProxyPost: socket error: %d\n", x);
655 #endif
656 	reset(true);
657 	if(x == BSocket::ErrHostNotFound)
658 		error(ErrProxyConnect);
659 	else if(x == BSocket::ErrConnectionRefused)
660 		error(ErrProxyConnect);
661 	else if(x == BSocket::ErrRead)
662 		error(ErrProxyNeg);
663 }
664 
665 //----------------------------------------------------------------------------
666 // HttpProxyGetStream
667 //----------------------------------------------------------------------------
668 class HttpProxyGetStream::Private
669 {
670 public:
Private()671 	Private() {}
672 
673 	BSocket sock;
674 	QByteArray recvBuf;
675 	QString url;
676 	QString user, pass;
677 	bool inHeader;
678 	QStringList headerLines;
679 	bool use_ssl;
680 	bool asProxy;
681 	QString host;
682 	int length;
683 
684 	QCA::TLS *tls;
685 };
686 
HttpProxyGetStream(QObject * parent)687 HttpProxyGetStream::HttpProxyGetStream(QObject *parent)
688 :QObject(parent)
689 {
690 	d = new Private;
691 	d->tls = 0;
692 	connect(&d->sock, &BSocket::connected, this, &HttpProxyGetStream::sock_connected);
693 	connect(&d->sock, &ByteStream::connectionClosed, this, &HttpProxyGetStream::sock_connectionClosed);
694 	connect(&d->sock, &ByteStream::readyRead, this, &HttpProxyGetStream::sock_readyRead);
695 	connect(&d->sock, &ByteStream::error, this, &HttpProxyGetStream::sock_error);
696 	reset(true);
697 }
698 
~HttpProxyGetStream()699 HttpProxyGetStream::~HttpProxyGetStream()
700 {
701 	reset(true);
702 	delete d;
703 }
704 
reset(bool)705 void HttpProxyGetStream::reset(bool /*clear*/)
706 {
707 	if(d->tls) {
708 		delete d->tls;
709 		d->tls = 0;
710 	}
711 	if(d->sock.state() != BSocket::Idle)
712 		d->sock.close();
713 	d->recvBuf.resize(0);
714 	//if(clear)
715 	//	d->body.resize(0);
716 	d->length = -1;
717 }
718 
setAuth(const QString & user,const QString & pass)719 void HttpProxyGetStream::setAuth(const QString &user, const QString &pass)
720 {
721 	d->user = user;
722 	d->pass = pass;
723 }
724 
isActive() const725 bool HttpProxyGetStream::isActive() const
726 {
727 	return (d->sock.state() == BSocket::Idle ? false: true);
728 }
729 
get(const QString & proxyHost,int proxyPort,const QString & url,bool ssl,bool asProxy)730 void HttpProxyGetStream::get(const QString &proxyHost, int proxyPort, const QString &url, bool ssl, bool asProxy)
731 {
732 	reset(true);
733 
734 	d->host = proxyHost;
735 	d->url = url;
736 	d->use_ssl = ssl;
737 	d->asProxy = asProxy;
738 
739 #ifdef PROX_DEBUG
740 	fprintf(stderr, "HttpProxyGetStream: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
741 	if(d->user.isEmpty())
742 		fprintf(stderr, "\n");
743 	else
744 		fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
745 #endif
746 	d->sock.connectToHost(proxyHost, proxyPort);
747 }
748 
stop()749 void HttpProxyGetStream::stop()
750 {
751 	reset();
752 }
753 
getHeader(const QString & var) const754 QString HttpProxyGetStream::getHeader(const QString &var) const
755 {
756 	for(QStringList::ConstIterator it = d->headerLines.constBegin(); it != d->headerLines.constEnd(); ++it) {
757 		const QString &s = *it;
758 		int n = s.indexOf(": ");
759 		if(n == -1)
760 			continue;
761 		QString v = s.mid(0, n);
762 		if(v.toLower() == var.toLower())
763 			return s.mid(n+2);
764 	}
765 	return "";
766 }
767 
length() const768 int HttpProxyGetStream::length() const
769 {
770 	return d->length;
771 }
772 
sock_connected()773 void HttpProxyGetStream::sock_connected()
774 {
775 #ifdef PROX_DEBUG
776 	fprintf(stderr, "HttpProxyGetStream: Connected\n");
777 #endif
778 	if(d->use_ssl) {
779 		d->tls = new QCA::TLS;
780 		connect(d->tls, &QCA::SecureLayer::readyRead, this, &HttpProxyGetStream::tls_readyRead);
781 		connect(d->tls, &QCA::SecureLayer::readyReadOutgoing, this, &HttpProxyGetStream::tls_readyReadOutgoing);
782 		connect(d->tls, &QCA::SecureLayer::error, this, &HttpProxyGetStream::tls_error);
783 		d->tls->startClient();
784 	}
785 
786 	d->inHeader = true;
787 	d->headerLines.clear();
788 
789 	QUrl u(d->url);
790 
791 	// connected, now send the request
792 	QString s;
793 	s += QString("GET ") + d->url + " HTTP/1.0\r\n";
794 	if(d->asProxy) {
795 		if(!d->user.isEmpty()) {
796 			QString str = d->user + ':' + d->pass;
797 			s += QString("Proxy-Authorization: Basic ") + QCA::Base64().encodeString(str) + "\r\n";
798 		}
799 		s += "Pragma: no-cache\r\n";
800 		s += QString("Host: ") + u.host() + "\r\n";
801 	}
802 	else {
803 		s += QString("Host: ") + d->host + "\r\n";
804 	}
805 	s += "\r\n";
806 
807 	// write request
808 	if(d->use_ssl)
809 		d->tls->write(s.toUtf8());
810 	else
811 		d->sock.write(s.toUtf8());
812 }
813 
sock_connectionClosed()814 void HttpProxyGetStream::sock_connectionClosed()
815 {
816 	//d->body = d->recvBuf;
817 	reset();
818 	emit finished();
819 }
820 
sock_readyRead()821 void HttpProxyGetStream::sock_readyRead()
822 {
823 	QByteArray block = d->sock.read();
824 
825 	if(d->use_ssl)
826 		d->tls->writeIncoming(block);
827 	else
828 		processData(block);
829 }
830 
processData(const QByteArray & block)831 void HttpProxyGetStream::processData(const QByteArray &block)
832 {
833 	printf("processData: %d bytes\n", block.size());
834 	if(!d->inHeader) {
835 		emit dataReady(block);
836 		return;
837 	}
838 
839 	ByteStream::appendArray(&d->recvBuf, block);
840 
841 	if(d->inHeader) {
842 		// grab available lines
843 		while(1) {
844 			bool found;
845 			QString line = extractLine(&d->recvBuf, &found);
846 			if(!found)
847 				break;
848 			if(line.isEmpty()) {
849 				printf("empty line\n");
850 				d->inHeader = false;
851 				break;
852 			}
853 			d->headerLines += line;
854 			printf("headerLine: [%s]\n", qPrintable(line));
855 		}
856 
857 		// done with grabbing the header?
858 		if(!d->inHeader) {
859 			QString str = d->headerLines.first();
860 			d->headerLines.takeFirst();
861 
862 			QString proto;
863 			int code;
864 			QString msg;
865 			if(!extractMainHeader(str, &proto, &code, &msg)) {
866 #ifdef PROX_DEBUG
867 				fprintf(stderr, "HttpProxyGetStream: invalid header!\n");
868 #endif
869 				reset(true);
870 				error(ErrProxyNeg);
871 				return;
872 			}
873 			else {
874 #ifdef PROX_DEBUG
875 				fprintf(stderr, "HttpProxyGetStream: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
876 				for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
877 					fprintf(stderr, "HttpProxyGetStream: * [%s]\n", (*it).latin1());
878 #endif
879 			}
880 
881 			if(code == 200) { // OK
882 #ifdef PROX_DEBUG
883 				fprintf(stderr, "HttpProxyGetStream: << Success >>\n");
884 #endif
885 
886 				bool ok;
887 				int x = getHeader("Content-Length").toInt(&ok);
888 				if(ok)
889 					d->length = x;
890 
891 				QPointer<QObject> self = this;
892 				emit handshaken();
893 				if(!self)
894 					return;
895 			}
896 			else {
897 				int err;
898 				QString errStr;
899 				if(code == 407) { // Authentication failed
900 					err = ErrProxyAuth;
901 					errStr = tr("Authentication failed");
902 				}
903 				else if(code == 404) { // Host not found
904 					err = ErrHostNotFound;
905 					errStr = tr("Host not found");
906 				}
907 				else if(code == 403) { // Access denied
908 					err = ErrProxyNeg;
909 					errStr = tr("Access denied");
910 				}
911 				else if(code == 503) { // Connection refused
912 					err = ErrConnectionRefused;
913 					errStr = tr("Connection refused");
914 				}
915 				else { // invalid reply
916 					err = ErrProxyNeg;
917 					errStr = tr("Invalid reply");
918 				}
919 
920 #ifdef PROX_DEBUG
921 				fprintf(stderr, "HttpProxyGetStream: << Error >> [%s]\n", errStr.latin1());
922 #endif
923 				reset(true);
924 				error(err);
925 				return;
926 			}
927 
928 			if(!d->recvBuf.isEmpty()) {
929 				QByteArray a = d->recvBuf;
930 				d->recvBuf.clear();
931 				emit dataReady(a);
932 			}
933 		}
934 	}
935 }
936 
sock_error(int x)937 void HttpProxyGetStream::sock_error(int x)
938 {
939 #ifdef PROX_DEBUG
940 	fprintf(stderr, "HttpProxyGetStream: socket error: %d\n", x);
941 #endif
942 	reset(true);
943 	if(x == BSocket::ErrHostNotFound)
944 		error(ErrProxyConnect);
945 	else if(x == BSocket::ErrConnectionRefused)
946 		error(ErrProxyConnect);
947 	else if(x == BSocket::ErrRead)
948 		error(ErrProxyNeg);
949 }
950 
tls_readyRead()951 void HttpProxyGetStream::tls_readyRead()
952 {
953 	//printf("tls_readyRead\n");
954 	processData(d->tls->read());
955 }
956 
tls_readyReadOutgoing()957 void HttpProxyGetStream::tls_readyReadOutgoing()
958 {
959 	//printf("tls_readyReadOutgoing\n");
960 	d->sock.write(d->tls->readOutgoing());
961 }
962 
tls_error()963 void HttpProxyGetStream::tls_error()
964 {
965 #ifdef PROX_DEBUG
966 	fprintf(stderr, "HttpProxyGetStream: ssl error: %d\n", d->tls->errorCode());
967 #endif
968 	reset(true);
969 	error(ErrConnectionRefused); // FIXME: bogus error
970 }
971 
972 // CS_NAMESPACE_END
973