/* * httpconnect.cpp - HTTP "CONNECT" proxy * Copyright (C) 2003 Justin Karneges * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * */ #include "httpconnect.h" #include #include "bsocket.h" #include "base64.h" #ifdef PROX_DEBUG #include #endif // CS_NAMESPACE_BEGIN static TQString extractLine(TQByteArray *buf, bool *found) { // scan for newline int n; for(n = 0; n < (int)buf->size()-1; ++n) { if(buf->at(n) == '\r' && buf->at(n+1) == '\n') { TQCString cstr; cstr.resize(n+1); memcpy(cstr.data(), buf->data(), n); n += 2; // hack off CR/LF memmove(buf->data(), buf->data() + n, buf->size() - n); buf->resize(buf->size() - n); TQString s = TQString::fromUtf8(cstr); if(found) *found = true; return s; } } if(found) *found = false; return ""; } static bool extractMainHeader(const TQString &line, TQString *proto, int *code, TQString *msg) { int n = line.find(' '); if(n == -1) return false; if(proto) *proto = line.mid(0, n); ++n; int n2 = line.find(' ', n); if(n2 == -1) return false; if(code) *code = line.mid(n, n2-n).toInt(); n = n2+1; if(msg) *msg = line.mid(n); return true; } class HttpConnect::Private { public: Private() {} BSocket sock; TQString host; int port; TQString user, pass; TQString real_host; int real_port; TQByteArray recvBuf; bool inHeader; TQStringList headerLines; int toWrite; bool active; }; HttpConnect::HttpConnect(TQObject *parent) :ByteStream(parent) { d = new Private; connect(&d->sock, TQT_SIGNAL(connected()), TQT_SLOT(sock_connected())); connect(&d->sock, TQT_SIGNAL(connectionClosed()), TQT_SLOT(sock_connectionClosed())); connect(&d->sock, TQT_SIGNAL(delayedCloseFinished()), TQT_SLOT(sock_delayedCloseFinished())); connect(&d->sock, TQT_SIGNAL(readyRead()), TQT_SLOT(sock_readyRead())); connect(&d->sock, TQT_SIGNAL(bytesWritten(int)), TQT_SLOT(sock_bytesWritten(int))); connect(&d->sock, TQT_SIGNAL(error(int)), TQT_SLOT(sock_error(int))); reset(true); } HttpConnect::~HttpConnect() { reset(true); delete d; } void HttpConnect::reset(bool clear) { if(d->sock.state() != BSocket::Idle) d->sock.close(); if(clear) { clearReadBuffer(); d->recvBuf.resize(0); } d->active = false; } void HttpConnect::setAuth(const TQString &user, const TQString &pass) { d->user = user; d->pass = pass; } void HttpConnect::connectToHost(const TQString &proxyHost, int proxyPort, const TQString &host, int port) { reset(true); d->host = proxyHost; d->port = proxyPort; d->real_host = host; d->real_port = port; #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: Connecting to %s:%d", proxyHost.latin1(), proxyPort); if(d->user.isEmpty()) fprintf(stderr, "\n"); else fprintf(stderr, ", auth {%s,%s}\n", d->user.latin1(), d->pass.latin1()); #endif d->sock.connectToHost(d->host, d->port); } bool HttpConnect::isOpen() const { return d->active; } void HttpConnect::close() { d->sock.close(); if(d->sock.bytesToWrite() == 0) reset(); } void HttpConnect::write(const TQByteArray &buf) { if(d->active) d->sock.write(buf); } TQByteArray HttpConnect::read(int bytes) { return ByteStream::read(bytes); } int HttpConnect::bytesAvailable() const { return ByteStream::bytesAvailable(); } int HttpConnect::bytesToWrite() const { if(d->active) return d->sock.bytesToWrite(); else return 0; } void HttpConnect::sock_connected() { #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: Connected\n"); #endif d->inHeader = true; d->headerLines.clear(); // connected, now send the request TQString s; s += TQString("CONNECT ") + d->real_host + ':' + TQString::number(d->real_port) + " HTTP/1.0\r\n"; if(!d->user.isEmpty()) { TQString str = d->user + ':' + d->pass; s += TQString("Proxy-Authorization: Basic ") + Base64::encodeString(str) + "\r\n"; } s += "Proxy-Connection: Keep-Alive\r\n"; s += "Pragma: no-cache\r\n"; s += "\r\n"; TQCString cs = s.utf8(); TQByteArray block(cs.length()); memcpy(block.data(), cs.data(), block.size()); d->toWrite = block.size(); d->sock.write(block); } void HttpConnect::sock_connectionClosed() { if(d->active) { reset(); connectionClosed(); } else { error(ErrProxyNeg); } } void HttpConnect::sock_delayedCloseFinished() { if(d->active) { reset(); delayedCloseFinished(); } } void HttpConnect::sock_readyRead() { TQByteArray block = d->sock.read(); if(!d->active) { ByteStream::appendArray(&d->recvBuf, block); if(d->inHeader) { // grab available lines while(1) { bool found; TQString line = extractLine(&d->recvBuf, &found); if(!found) break; if(line.isEmpty()) { d->inHeader = false; break; } d->headerLines += line; } // done with grabbing the header? if(!d->inHeader) { TQString str = d->headerLines.first(); d->headerLines.remove(d->headerLines.begin()); TQString proto; int code; TQString msg; if(!extractMainHeader(str, &proto, &code, &msg)) { #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: invalid header!\n"); #endif reset(true); error(ErrProxyNeg); return; } else { #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: header proto=[%s] code=[%d] msg=[%s]\n", proto.latin1(), code, msg.latin1()); for(TQStringList::ConstIterator it = d->headerLines.begin(); it != d->headerLines.end(); ++it) fprintf(stderr, "HttpConnect: * [%s]\n", (*it).latin1()); #endif } if(code == 200) { // OK #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: << Success >>\n"); #endif d->active = true; connected(); if(!d->recvBuf.isEmpty()) { appendRead(d->recvBuf); d->recvBuf.resize(0); readyRead(); return; } } else { int err; TQString errStr; if(code == 407) { // Authentication failed err = ErrProxyAuth; errStr = tr("Authentication failed"); } else if(code == 404) { // Host not found err = ErrHostNotFound; errStr = tr("Host not found"); } else if(code == 403) { // Access denied err = ErrProxyNeg; errStr = tr("Access denied"); } else if(code == 503) { // Connection refused err = ErrConnectionRefused; errStr = tr("Connection refused"); } else { // invalid reply err = ErrProxyNeg; errStr = tr("Invalid reply"); } #ifdef PROX_DEBUG fprintf(stderr, "HttpConnect: << Error >> [%s]\n", errStr.latin1()); #endif reset(true); error(err); return; } } } } else { appendRead(block); readyRead(); return; } } void HttpConnect::sock_bytesWritten(int x) { if(d->toWrite > 0) { int size = x; if(d->toWrite < x) size = d->toWrite; d->toWrite -= size; x -= size; } if(d->active && x > 0) bytesWritten(x); } void HttpConnect::sock_error(int x) { if(d->active) { reset(); error(ErrRead); } else { reset(true); if(x == BSocket::ErrHostNotFound) error(ErrProxyConnect); else if(x == BSocket::ErrConnectionRefused) error(ErrProxyConnect); else if(x == BSocket::ErrRead) error(ErrProxyNeg); } } // CS_NAMESPACE_END #include "httpconnect.moc"