diff options
Diffstat (limited to 'kdecore/network/khttpproxysocketdevice.cpp')
-rw-r--r-- | kdecore/network/khttpproxysocketdevice.cpp | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/kdecore/network/khttpproxysocketdevice.cpp b/kdecore/network/khttpproxysocketdevice.cpp new file mode 100644 index 000000000..73e433a2e --- /dev/null +++ b/kdecore/network/khttpproxysocketdevice.cpp @@ -0,0 +1,281 @@ +/* -*- C++ -*- + * Copyright (C) 2003 Thiago Macieira <thiago.macieira@kdemail.net> + * + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <config.h> + +#include <sys/types.h> +#include <sys/socket.h> + +#include <qsocketnotifier.h> +#include <qcstring.h> + +#include "kresolver.h" +#include "ksocketaddress.h" +#include "ksocketdevice.h" +#include "khttpproxysocketdevice.h" + +using namespace KNetwork; + +KResolverEntry KHttpProxySocketDevice::defaultProxy; + +class KNetwork::KHttpProxySocketDevicePrivate +{ +public: + KResolverEntry proxy; + QCString request; + QCString reply; + KSocketAddress peer; + + KHttpProxySocketDevicePrivate() + : proxy(KHttpProxySocketDevice::defaultProxy) + { } +}; + +KHttpProxySocketDevice::KHttpProxySocketDevice(const KSocketBase* parent) + : KSocketDevice(parent), d(new KHttpProxySocketDevicePrivate) +{ +} + +KHttpProxySocketDevice::KHttpProxySocketDevice(const KResolverEntry& proxy) + : d(new KHttpProxySocketDevicePrivate) +{ + d->proxy = proxy; +} + +KHttpProxySocketDevice::~KHttpProxySocketDevice() +{ + // nothing special to be done during closing + // KSocketDevice::~KSocketDevice closes the socket + + delete d; +} + +int KHttpProxySocketDevice::capabilities() const +{ + return CanConnectString | CanNotBind | CanNotListen | CanNotUseDatagrams; +} + +const KResolverEntry& +KHttpProxySocketDevice::proxyServer() const +{ + return d->proxy; +} + +void KHttpProxySocketDevice::setProxyServer(const KResolverEntry& proxy) +{ + d->proxy = proxy; +} + +void KHttpProxySocketDevice::close() +{ + d->reply = d->request = QCString(); + d->peer = KSocketAddress(); + KSocketDevice::close(); +} + +KSocketAddress KHttpProxySocketDevice::peerAddress() const +{ + if (isOpen()) + return d->peer; + return KSocketAddress(); +} + +KSocketAddress KHttpProxySocketDevice::externalAddress() const +{ + return KSocketAddress(); +} + +bool KHttpProxySocketDevice::connect(const KResolverEntry& address) +{ + if (d->proxy.family() == AF_UNSPEC) + // no proxy server set ! + return KSocketDevice::connect(address); + + if (isOpen()) + { + // socket is already open + resetError(); + return true; + } + + if (m_sockfd == -1) + // socket isn't created yet + return connect(address.address().nodeName(), + address.address().serviceName()); + + d->peer = address.address(); + return parseServerReply(); +} + +bool KHttpProxySocketDevice::connect(const QString& node, const QString& service) +{ + // same safety checks as above + if (m_sockfd == -1 && (d->proxy.family() == AF_UNSPEC || + node.isEmpty() || service.isEmpty())) + { + // no proxy server set ! + setError(IO_ConnectError, NotSupported); + return false; + } + + if (isOpen()) + { + // socket is already open + return true; + } + + if (m_sockfd == -1) + { + // must create the socket + if (!KSocketDevice::connect(d->proxy)) + return false; // also unable to contact proxy server + setState(0); // unset open flag + + // prepare the request + QString request = QString::fromLatin1("CONNECT %1:%2 HTTP/1.1\r\n" + "Cache-Control: no-cache\r\n" + "Host: \r\n" + "\r\n"); + QString node2 = node; + if (node.contains(':')) + node2 = '[' + node + ']'; + + d->request = request.arg(node2).arg(service).latin1(); + } + + return parseServerReply(); +} + +bool KHttpProxySocketDevice::parseServerReply() +{ + // make sure we're connected + if (!KSocketDevice::connect(d->proxy)) + if (error() == InProgress) + return true; + else if (error() != NoError) + return false; + + if (!d->request.isEmpty()) + { + // send request + Q_LONG written = writeBlock(d->request, d->request.length()); + if (written < 0) + { + qDebug("KHttpProxySocketDevice: would block writing request!"); + if (error() == WouldBlock) + setError(IO_ConnectError, InProgress); + return error() == WouldBlock; // error + } + qDebug("KHttpProxySocketDevice: request written"); + + d->request.remove(0, written); + + if (!d->request.isEmpty()) + { + setError(IO_ConnectError, InProgress); + return true; // still in progress + } + } + + // request header is sent + // must parse reply, but must also be careful not to read too much + // from the buffer + + int index; + if (!blocking()) + { + Q_LONG avail = bytesAvailable(); + qDebug("KHttpProxySocketDevice: %ld bytes available", avail); + setState(0); + if (avail == 0) + { + setError(IO_ConnectError, InProgress); + return true; + } + else if (avail < 0) + return false; // error! + + QByteArray buf(avail); + if (peekBlock(buf.data(), avail) < 0) + return false; // error! + + QCString fullHeaders = d->reply + buf.data(); + // search for the end of the headers + index = fullHeaders.find("\r\n\r\n"); + if (index == -1) + { + // no, headers not yet finished... + // consume data from socket + readBlock(buf.data(), avail); + d->reply += buf.data(); + setError(IO_ConnectError, InProgress); + return true; + } + + // headers are finished + index -= d->reply.length(); + d->reply += fullHeaders.mid(d->reply.length(), index + 4); + + // consume from socket + readBlock(buf.data(), index + 4); + } + else + { + int state = 0; + if (d->reply.right(3) == "\r\n\r") + state = 3; + else if (d->reply.right(2) == "\r\n") + state = 2; + else if (d->reply.right(1) == "\r") + state = 1; + while (state != 4) + { + char c = getch(); + d->reply += c; + + if ((state == 3 && c == '\n') || + (state == 1 && c == '\n') || + c == '\r') + ++state; + else + state = 0; + } + } + + // now really parse the reply + qDebug("KHttpProxySocketDevice: get reply: %s\n", + d->reply.left(d->reply.find('\r')).data()); + if (d->reply.left(7) != "HTTP/1." || + (index = d->reply.find(' ')) == -1 || + d->reply[index + 1] != '2') + { + setError(IO_ConnectError, NetFailure); + return false; + } + + // we've got it + resetError(); + setState(IO_Open); + return true; +} |