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