2
* httppoll.cpp - HTTP polling proxy
3
* Copyright (C) 2003 Justin Karneges
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.
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.
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
#include<qstringlist.h>
26
#include<qguardedptr.h>
40
static QByteArray randomArray(int size)
43
for(int n = 0; n < size; ++n)
44
a[n] = (char)(256.0*rand()/(RAND_MAX+1.0));
48
//----------------------------------------------------------------------------
50
//----------------------------------------------------------------------------
51
static QString hpk(int n, const QString &s)
56
return Base64::arrayToString( QCA::SHA1::hash( QCString(hpk(n - 1, s).latin1()) ) );
59
class HttpPoll::Private
79
QString key[POLL_KEYS];
85
HttpPoll::HttpPoll(QObject *parent)
92
connect(d->t, SIGNAL(timeout()), SLOT(do_sync()));
94
connect(&d->http, SIGNAL(result()), SLOT(http_result()));
95
connect(&d->http, SIGNAL(error(int)), SLOT(http_error(int)));
100
HttpPoll::~HttpPoll()
107
void HttpPoll::reset(bool clear)
109
if(d->http.isActive())
120
void HttpPoll::setAuth(const QString &user, const QString &pass)
126
void HttpPoll::connectToUrl(const QString &url)
128
connectToHost("", 0, url);
131
void HttpPoll::connectToHost(const QString &proxyHost, int proxyPort, const QString &url)
136
if(!proxyHost.isEmpty()) {
149
d->url = u.encodedPathAndQuery();
150
d->use_proxy = false;
155
QString key = getKey(&last);
158
fprintf(stderr, "HttpPoll: Connecting to %s:%d [%s]", d->host.latin1(), d->port, d->url.latin1());
159
if(d->user.isEmpty())
160
fprintf(stderr, "\n");
162
fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
164
QGuardedPtr<QObject> self = this;
170
d->http.setAuth(d->user, d->pass);
171
d->http.post(d->host, d->port, d->url, makePacket("0", key, "", QByteArray()), d->use_proxy);
174
QByteArray HttpPoll::makePacket(const QString &ident, const QString &key, const QString &newkey, const QByteArray &block)
181
if(!newkey.isEmpty()) {
186
QCString cs = str.latin1();
187
int len = cs.length();
189
QByteArray a(len + block.size());
190
memcpy(a.data(), cs.data(), len);
191
memcpy(a.data() + len, block.data(), block.size());
195
int HttpPoll::pollInterval() const
200
void HttpPoll::setPollInterval(int seconds)
202
d->polltime = seconds;
205
bool HttpPoll::isOpen() const
207
return (d->state == 2 ? true: false);
210
void HttpPoll::close()
212
if(d->state == 0 || d->closing)
215
if(bytesToWrite() == 0)
221
void HttpPoll::http_result()
223
// check for death :)
224
QGuardedPtr<QObject> self = this;
231
QString cookie = d->http.getHeader("Set-Cookie");
232
int n = cookie.find("ID=");
239
int n2 = cookie.find(';', n);
241
id = cookie.mid(n, n2-n);
244
QByteArray block = d->http.body();
247
if(id.right(2) == ":0") {
248
if(id == "0:0" && d->state == 2) {
261
bool justNowConnected = false;
264
justNowConnected = true;
267
// sync up again soon
268
if(bytesToWrite() > 0 || !d->closing)
269
d->t->start(d->polltime * 1000, true);
272
if(justNowConnected) {
276
if(!d->out.isEmpty()) {
277
int x = d->out.size();
287
if(!block.isEmpty()) {
295
if(bytesToWrite() > 0) {
301
delayedCloseFinished();
307
void HttpPoll::http_error(int x)
310
if(x == HttpProxyPost::ErrConnectionRefused)
311
error(ErrConnectionRefused);
312
else if(x == HttpProxyPost::ErrHostNotFound)
313
error(ErrHostNotFound);
314
else if(x == HttpProxyPost::ErrSocket)
316
else if(x == HttpProxyPost::ErrProxyConnect)
317
error(ErrProxyConnect);
318
else if(x == HttpProxyPost::ErrProxyNeg)
320
else if(x == HttpProxyPost::ErrProxyAuth)
324
int HttpPoll::tryWrite()
326
if(!d->http.isActive())
331
void HttpPoll::do_sync()
333
if(d->http.isActive())
337
d->out = takeWrite(0, false);
340
QString key = getKey(&last);
344
newkey = getKey(&last);
347
QGuardedPtr<QObject> self = this;
352
d->http.post(d->host, d->port, d->url, makePacket(d->ident, key, newkey, d->out), d->use_proxy);
355
void HttpPoll::resetKey()
358
fprintf(stderr, "HttpPoll: reset key!\n");
360
QByteArray a = randomArray(64);
361
QString str = QString::fromLatin1(a.data(), a.size());
363
d->key_n = POLL_KEYS;
364
for(int n = 0; n < POLL_KEYS; ++n)
365
d->key[n] = hpk(n+1, str);
368
const QString & HttpPoll::getKey(bool *last)
374
return d->key[d->key_n];
378
//----------------------------------------------------------------------------
380
//----------------------------------------------------------------------------
381
static QString extractLine(QByteArray *buf, bool *found)
385
for(n = 0; n < (int)buf->size()-1; ++n) {
386
if(buf->at(n) == '\r' && buf->at(n+1) == '\n') {
389
memcpy(cstr.data(), buf->data(), n);
390
n += 2; // hack off CR/LF
392
memmove(buf->data(), buf->data() + n, buf->size() - n);
393
buf->resize(buf->size() - n);
394
QString s = QString::fromUtf8(cstr);
407
static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
409
int n = line.find(' ');
413
*proto = line.mid(0, n);
415
int n2 = line.find(' ', n);
419
*code = line.mid(n, n2-n).toInt();
426
class HttpProxyPost::Private
432
QByteArray postdata, recvBuf, body;
436
QStringList headerLines;
441
HttpProxyPost::HttpProxyPost(QObject *parent)
445
connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
446
connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
447
connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
448
connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
452
HttpProxyPost::~HttpProxyPost()
458
void HttpProxyPost::reset(bool clear)
460
if(d->sock.state() != BSocket::Idle)
462
d->recvBuf.resize(0);
467
void HttpProxyPost::setAuth(const QString &user, const QString &pass)
473
bool HttpProxyPost::isActive() const
475
return (d->sock.state() == BSocket::Idle ? false: true);
478
void HttpProxyPost::post(const QString &proxyHost, int proxyPort, const QString &url, const QByteArray &data, bool asProxy)
485
d->asProxy = asProxy;
488
fprintf(stderr, "HttpProxyPost: Connecting to %s:%d", proxyHost.latin1(), proxyPort);
489
if(d->user.isEmpty())
490
fprintf(stderr, "\n");
492
fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1());
494
d->sock.connectToHost(proxyHost, proxyPort);
497
void HttpProxyPost::stop()
502
QByteArray HttpProxyPost::body() const
507
QString HttpProxyPost::getHeader(const QString &var) const
509
for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) {
510
const QString &s = *it;
511
int n = s.find(": ");
514
QString v = s.mid(0, n);
521
void HttpProxyPost::sock_connected()
524
fprintf(stderr, "HttpProxyPost: Connected\n");
527
d->headerLines.clear();
531
// connected, now send the request
533
s += QString("POST ") + d->url + " HTTP/1.0\r\n";
535
if(!d->user.isEmpty()) {
536
QString str = d->user + ':' + d->pass;
537
s += QString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n";
539
s += "Proxy-Connection: Keep-Alive\r\n";
540
s += "Pragma: no-cache\r\n";
541
s += QString("Host: ") + u.host() + "\r\n";
544
s += QString("Host: ") + d->host + "\r\n";
546
s += "Content-Type: application/x-www-form-urlencoded\r\n";
547
s += QString("Content-Length: ") + QString::number(d->postdata.size()) + "\r\n";
551
QCString cs = s.utf8();
552
QByteArray block(cs.length());
553
memcpy(block.data(), cs.data(), block.size());
554
d->sock.write(block);
557
d->sock.write(d->postdata);
560
void HttpProxyPost::sock_connectionClosed()
562
d->body = d->recvBuf.copy();
567
void HttpProxyPost::sock_readyRead()
569
QByteArray block = d->sock.read();
570
ByteStream::appendArray(&d->recvBuf, block);
573
// grab available lines
576
QString line = extractLine(&d->recvBuf, &found);
583
d->headerLines += line;
586
// done with grabbing the header?
588
QString str = d->headerLines.first();
589
d->headerLines.remove(d->headerLines.begin());
594
if(!extractMainHeader(str, &proto, &code, &msg)) {
596
fprintf(stderr, "HttpProxyPost: invalid header!\n");
604
fprintf(stderr, "HttpProxyPost: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1());
605
for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
606
fprintf(stderr, "HttpProxyPost: * [%s]\n", (*it).latin1());
610
if(code == 200) { // OK
612
fprintf(stderr, "HttpProxyPost: << Success >>\n");
618
if(code == 407) { // Authentication failed
620
errStr = tr("Authentication failed");
622
else if(code == 404) { // Host not found
623
err = ErrHostNotFound;
624
errStr = tr("Host not found");
626
else if(code == 403) { // Access denied
628
errStr = tr("Access denied");
630
else if(code == 503) { // Connection refused
631
err = ErrConnectionRefused;
632
errStr = tr("Connection refused");
634
else { // invalid reply
636
errStr = tr("Invalid reply");
640
fprintf(stderr, "HttpProxyPost: << Error >> [%s]\n", errStr.latin1());
650
void HttpProxyPost::sock_error(int x)
653
fprintf(stderr, "HttpProxyPost: socket error: %d\n", x);
656
if(x == BSocket::ErrHostNotFound)
657
error(ErrProxyConnect);
658
else if(x == BSocket::ErrConnectionRefused)
659
error(ErrProxyConnect);
660
else if(x == BSocket::ErrRead)