2
* httpconnect.cpp - HTTP "CONNECT" 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
#include "httpconnect.h"
23
#include <qstringlist.h>
37
QString escapeOutput(const QByteArray &in)
40
for(int n = 0; n < in.size(); ++n) {
42
out += QString("\\\\");
44
else if(in[n] >= 32 && in[n] < 127) {
45
out += QChar::fromLatin1(in[n]);
48
out += QString().sprintf("\\x%02x", (unsigned char)in[n]);
55
static QString extractLine(QByteArray *buf, bool *found)
58
int index = buf->indexOf ("\r\n");
67
QString s = QString::fromAscii(buf->left(index));
68
buf->remove(0, index + 2);
76
static bool extractMainHeader(const QString &line, QString *proto, int *code, QString *msg)
78
int n = line.indexOf(' ');
82
*proto = line.mid(0, n);
84
int n2 = line.indexOf(' ', n);
88
*code = line.mid(n, n2-n).toInt();
95
class HttpConnect::Private
98
Private(HttpConnect *_q) :
113
QStringList headerLines;
119
HttpConnect::HttpConnect(QObject *parent)
122
d = new Private(this);
123
connect(&d->sock, SIGNAL(connected()), SLOT(sock_connected()));
124
connect(&d->sock, SIGNAL(connectionClosed()), SLOT(sock_connectionClosed()));
125
connect(&d->sock, SIGNAL(delayedCloseFinished()), SLOT(sock_delayedCloseFinished()));
126
connect(&d->sock, SIGNAL(readyRead()), SLOT(sock_readyRead()));
127
connect(&d->sock, SIGNAL(bytesWritten(int)), SLOT(sock_bytesWritten(int)));
128
connect(&d->sock, SIGNAL(error(int)), SLOT(sock_error(int)));
133
HttpConnect::~HttpConnect()
139
void HttpConnect::reset(bool clear)
141
if(d->sock.state() != BSocket::Idle)
145
d->recvBuf.resize(0);
150
void HttpConnect::setAuth(const QString &user, const QString &pass)
156
void HttpConnect::connectToHost(const QString &proxyHost, int proxyPort, const QString &host, int port)
166
fprintf(stderr, "HttpConnect: Connecting to %s:%d", qPrintable(proxyHost), proxyPort);
167
if(d->user.isEmpty())
168
fprintf(stderr, "\n");
170
fprintf(stderr, ", auth {%s,%s}\n", qPrintable(d->user), qPrintable(d->pass));
172
d->sock.connectToHost(d->host, d->port);
175
bool HttpConnect::isOpen() const
180
void HttpConnect::close()
183
if(d->sock.bytesToWrite() == 0)
187
void HttpConnect::write(const QByteArray &buf)
193
QByteArray HttpConnect::read(int bytes)
195
return ByteStream::read(bytes);
198
int HttpConnect::bytesAvailable() const
200
return ByteStream::bytesAvailable();
203
int HttpConnect::bytesToWrite() const
206
return d->sock.bytesToWrite();
211
void HttpConnect::sock_connected()
214
fprintf(stderr, "HttpConnect: Connected\n");
217
d->headerLines.clear();
219
// connected, now send the request
221
s += QString("CONNECT ") + d->real_host + ':' + QString::number(d->real_port) + " HTTP/1.0\r\n";
222
if(!d->user.isEmpty()) {
223
QString str = d->user + ':' + d->pass;
224
s += QString("Proxy-Authorization: Basic ") + QCA::Base64().encodeString(str) + "\r\n";
226
s += "Pragma: no-cache\r\n";
229
QByteArray block = s.toUtf8();
231
fprintf(stderr, "HttpConnect: writing: {%s}\n", qPrintable(escapeOutput(block)));
233
d->toWrite = block.size();
234
d->sock.write(block);
237
void HttpConnect::sock_connectionClosed()
248
void HttpConnect::sock_delayedCloseFinished()
252
delayedCloseFinished();
256
void HttpConnect::sock_readyRead()
258
QByteArray block = d->sock.read();
261
ByteStream::appendArray(&d->recvBuf, block);
264
// grab available lines
267
QString line = extractLine(&d->recvBuf, &found);
274
d->headerLines += line;
277
// done with grabbing the header?
279
QString str = d->headerLines.first();
280
d->headerLines.takeFirst();
285
if(!extractMainHeader(str, &proto, &code, &msg)) {
287
fprintf(stderr, "HttpConnect: invalid header!\n");
295
fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", qPrintable(proto), code, qPrintable(msg));
296
for(QStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it)
297
fprintf(stderr, "HttpConnect: * [%s]\n", qPrintable(*it));
301
if(code == 200) { // OK
303
fprintf(stderr, "HttpConnect: << Success >>\n");
308
if(!d->recvBuf.isEmpty()) {
309
appendRead(d->recvBuf);
310
d->recvBuf.resize(0);
318
if(code == 407) { // Authentication failed
320
errStr = tr("Authentication failed");
322
else if(code == 404) { // Host not found
323
err = ErrHostNotFound;
324
errStr = tr("Host not found");
326
else if(code == 403) { // Access denied
328
errStr = tr("Access denied");
330
else if(code == 503) { // Connection refused
331
err = ErrConnectionRefused;
332
errStr = tr("Connection refused");
334
else { // invalid reply
336
errStr = tr("Invalid reply");
340
fprintf(stderr, "HttpConnect: << Error >> [%s]\n", qPrintable(errStr));
356
void HttpConnect::sock_bytesWritten(int x)
366
if(d->active && x > 0)
370
void HttpConnect::sock_error(int x)
378
if(x == BSocket::ErrHostNotFound)
379
error(ErrProxyConnect);
380
else if(x == BSocket::ErrConnectionRefused)
381
error(ErrProxyConnect);
382
else if(x == BSocket::ErrRead)