/* -*- C++ -*- * Copyright (C) 2003 Thiago Macieira * * * 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 #include #include #include #include #include "kresolver.h" #include "ksocketaddress.h" #include "ksocketdevice.h" #include "khttpproxysocketdevice.h" using namespace KNetwork; KResolverEntry KHttpProxySocketDevice::defaultProxy; class KNetwork::KHttpProxySocketDevicePrivate { public: KResolverEntry proxy; TQCString request; TQCString 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 = TQCString(); 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 TQString& node, const TQString& 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 TQString request = TQString::fromLatin1("CONNECT %1:%2 HTTP/1.1\r\n" "Cache-Control: no-cache\r\n" "Host: \r\n" "\r\n"); TQString node2 = node; if (node.contains(':')) node2 = '[' + node + ']'; d->request = TQString(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 TQ_LONG written = tqwriteBlock(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()) { TQ_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! TQByteArray buf(avail); if (peekBlock(buf.data(), avail) < 0) return false; // error! TQCString 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 tqreadBlock(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 tqreadBlock(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; }