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