/* -*- c++ -*- * * mmconnection.cpp * * Copyright (C) 2003 Petter E. Stokke * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include "compat.h" #include "mmconnection.h" #include "mmserver.h" #include #include #include #include "version.h" MMConnection::MMConnection(KExtendedSocket* sock, MMServer* parent) : QObject(parent) , m_server(parent) , m_sock(sock) { kdDebug() << "MMConnection::MMConnection( " << m_sock->peerAddress()->pretty() << " );" << endl; connect(m_sock, SIGNAL(readyRead()), this, SLOT(readData())); connect(m_sock, SIGNAL(closed(int)), this, SLOT(socketClosed(int))); if (!(m_sock->setBufferSize(4096))) { kdDebug() << "Failed to set buffer size." << endl; deleteLater(); return; } m_sock->enableRead(true); } MMConnection::~MMConnection() { kdDebug() << "MMConnection::~MMConnection( " << m_sock->peerAddress()->pretty() << " );" << endl; delete m_sock; } void MMConnection::socketClosed(int state) { kdDebug() << "Connection " << m_sock->peerAddress()->pretty() << " was terminated by the other end: " << state << endl; deleteLater(); } QString hexify(const QByteArray& buf) { QString out(""), hex(""), asc(""), tmp; int i; for (i=0; i<(int)buf.size(); i++) { if (buf.at(i) >= 32 && buf.at(i) <= 127) asc += QChar(buf.at(i)); else asc += "."; tmp.sprintf("%02x", buf.at(i)); hex += tmp + " "; if (i % 16 == 15) { tmp.sprintf("%8d: ", i - 15); out += tmp + hex + " " + asc + "\n"; hex = ""; asc = ""; } } tmp.sprintf("%8d: ", i - (i % 16)); for (i %= 16; i < 16; i++) hex += " "; out += tmp + hex + " " + asc + "\n"; return out; } void MMConnection::readData() { char buf[1024]; kdDebug() << m_sock->bytesAvailable() << " bytes ready for reading." << endl; while (m_sock->bytesAvailable()) { int r = m_sock->readBlock((char*)buf, 1023); if (r < 0) { kdDebug() << "Read error on connection " << m_sock->peerAddress()->pretty() << endl; m_sock->closeNow(); deleteLater(); } if (r > 0) { uint pos = m_inbuf.size(); m_inbuf.resize(pos + r, QGArray::SpeedOptim); char* inbuf = m_inbuf.data(); memcpy(inbuf + pos, buf, r); } } if (m_inbuf.size()) { kdDebug() << "Connection " << m_sock->peerAddress()->pretty() << QString(" received data, inbuf is:\n") + hexify(m_inbuf) << endl; processBuffer(); } } void MMConnection::discardBuffer() { m_inbuf.resize(0, QGArray::SpeedOptim); } void MMConnection::discardBuffer(uint len) { if (len == m_inbuf.size()) discardBuffer(); else { int newSize = m_inbuf.size() - len; memmove(m_inbuf.data(), m_inbuf.data() + len, newSize); m_inbuf.resize(newSize); } } void MMConnection::processBuffer() { while (1) { // Require a reasonable minimum size before processing anything. if (m_inbuf.size() < 4) return; // Anything but the start of an HTTP POST request is not wanted. if (memcmp((void*)m_inbuf.data(), "POST", 4)) { kdDebug() << "Buffer didn't start with POST. Discarding." << endl; discardBuffer(); httpError(400, "Bad Request"); return; } // See if there's a complete HTTP header in the buffer. char* p = (char*)my_memmem((void*)m_inbuf.data(), m_inbuf.size(), (void*)"\r\n\r\n", 4); if (!p) { if (m_inbuf.size() > 16384) { kdDebug() << "Header is getting ridiculously long. Discarding." << endl; discardBuffer(); httpError(400, "Bad Request"); } return; } p += 4; uint headerLen = (uint)(p - m_inbuf.data()); QHttpRequestHeader header(QString::fromAscii(m_inbuf.data(), (int)headerLen)); // If the header is invalid, discard it from the buffer and send an error to the peer if (!header.isValid()) { kdDebug() << "Invalid HTTP request header." << endl; discardBuffer(headerLen); httpError(400, "Bad Request"); return; } kdDebug() << "HTTP request " << header.method() << " " << header.path() << " HTTP/" << header.majorVersion() << "." << header.minorVersion() << endl; kdDebug() << header.toString() << endl; if (!header.hasContentLength() || header.method() != "POST") { kdDebug() << "No content length or not POST request. Don't know how to handle that; discarding buffer." << endl; discardBuffer(); httpError(400, "Bad Request"); return; } kdDebug() << "Content length: " << header.contentLength() << endl; if (header.contentLength() < 3) { kdDebug() << "Content length is too short. Sending error packet." << endl; discardBuffer(headerLen + header.contentLength()); sendPacket(MMPacket(MMP_GENERALERROR)); return; } // See if all the document data has been received if (m_inbuf.size() < headerLen + header.contentLength()) return; MMPacket content(m_inbuf.data() + headerLen, header.contentLength()); discardBuffer(headerLen + header.contentLength()); kdDebug() << "Payload received." << endl; emit processMessage(this, &content); } } void MMConnection::httpError(int err, const QString& msg) { kdDebug() << "HTTP Error " << err << " " << msg << endl; QString out; out = QString("HTTP/1.1 %1 %2\r\n").arg(err).arg(msg); out += QString("Server: KMLDonkeyMobileMule/%1\r\n").arg(KMLDONKEY_VERSION); out += "Connection: close\r\nContent-Type: text/html; charset=utf-8\r\n\r\n"; out += "\r\n"; out += QString("%1 %2\r\n").arg(err).arg(msg); out += QString("

%1 %2

\r\n").arg(err).arg(msg); QCString data = out.utf8(); m_sock->writeBlock((const char*)data, data.length()); m_sock->flush(); deleteLater(); } void MMConnection::sendPacket(const MMPacket& p) { QString h = QString("HTTP/1.1 200 OK\r\nConnection: close\r\nContent-Type: %1\r\n").arg(m_server->getContentType()); h += QString("Content-Length: %1\r\n\r\n").arg(p.packetSize()); QCString header = h.utf8(); QByteArray buf(header.length() + p.packetSize()); memcpy(buf.data(), (const char*)header, header.length()); buf[header.length()] = p.opcode(); memcpy(buf.data() + header.length() + 1, p.data(), p.size()); m_sock->writeBlock(buf.data(), buf.size()); buf.duplicate((const char*)p.data(), p.size()); kdDebug() << "Sent message opcode " << (int)p.opcode() << QString("\n") << hexify(buf) << endl; m_sock->flush(); deleteLater(); } void MMConnection::sendPacket(MMPacket* p) { sendPacket(*p); delete p; } #include "mmconnection.moc"