summaryrefslogtreecommitdiffstats
path: root/kopete/protocols/msn/incomingtransfer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kopete/protocols/msn/incomingtransfer.cpp')
-rw-r--r--kopete/protocols/msn/incomingtransfer.cpp381
1 files changed, 381 insertions, 0 deletions
diff --git a/kopete/protocols/msn/incomingtransfer.cpp b/kopete/protocols/msn/incomingtransfer.cpp
new file mode 100644
index 00000000..99422ef7
--- /dev/null
+++ b/kopete/protocols/msn/incomingtransfer.cpp
@@ -0,0 +1,381 @@
+/*
+ incomingtransfer.cpp - msn p2p protocol
+
+ Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@ kde.org>
+ Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
+
+ *************************************************************************
+ * *
+ * 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. *
+ * *
+ *************************************************************************
+*/
+
+#include "incomingtransfer.h"
+using P2P::TransferContext;
+using P2P::IncomingTransfer;
+using P2P::Message;
+
+// Kde includes
+#include <kbufferedsocket.h>
+#include <kdebug.h>
+#include <klocale.h>
+#include <kserversocket.h>
+#include <kstandarddirs.h>
+#include <ktempfile.h>
+using namespace KNetwork;
+
+// Qt includes
+#include <qfile.h>
+#include <qregexp.h>
+
+// Kopete includes
+#include <kopetetransfermanager.h>
+
+IncomingTransfer::IncomingTransfer(const QString& from, P2P::Dispatcher *dispatcher, Q_UINT32 sessionId)
+: TransferContext(from,dispatcher,sessionId)
+{
+ m_direction = P2P::Incoming;
+ m_listener = 0l;
+}
+
+IncomingTransfer::~IncomingTransfer()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+ if(m_listener)
+ {
+ delete m_listener;
+ m_listener = 0l;
+ }
+
+ if(m_socket)
+ {
+ delete m_socket;
+ m_socket = 0l;
+ }
+}
+
+
+void IncomingTransfer::slotTransferAccepted(Kopete::Transfer* transfer, const QString& /*fileName*/)
+{
+ Q_UINT32 sessionId = transfer->info().internalId().toUInt();
+ if(sessionId!=m_sessionId)
+ return;
+
+ QObject::connect(transfer , SIGNAL(transferCanceled()), this, SLOT(abort()));
+ m_transfer = transfer;
+
+ QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
+ sendMessage(OK, content);
+
+ QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
+}
+
+void IncomingTransfer::slotTransferRefused(const Kopete::FileTransferInfo& info)
+{
+ Q_UINT32 sessionId = info.internalId().toUInt();
+ if(sessionId!=m_sessionId)
+ return;
+
+ QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
+ // Send the sending client a cancelation message.
+ sendMessage(DECLINE, content);
+ m_state=Finished;
+
+ QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
+}
+
+
+
+void IncomingTransfer::acknowledged()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+
+ switch(m_state)
+ {
+ case Invitation:
+ // NOTE UDI: base identifier acknowledge message, ignore.
+ // UDI: 200 OK message should follow.
+ if(m_type == File)
+ {
+ // FT: 200 OK acknowledged message.
+ // If this is the first connection between the two clients, a direct connection invitation
+ // should follow. Otherwise, the file transfer may start right away.
+ if(m_transfer)
+ {
+ QFile *destination = new QFile(m_transfer->destinationURL().path());
+ if(!destination->open(IO_WriteOnly))
+ {
+ m_transfer->slotError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Cannot open file for writing"));
+ m_transfer = 0l;
+
+ error();
+ return;
+ }
+ m_file = destination;
+ }
+ m_state = Negotiation;
+ }
+ break;
+
+ case Negotiation:
+ // 200 OK acknowledge message.
+ break;
+
+ case DataTransfer:
+ break;
+
+ case Finished:
+ // UDI: Bye acknowledge message.
+ m_dispatcher->detach(this);
+ break;
+ }
+}
+
+void IncomingTransfer::processMessage(const Message& message)
+{
+ if(m_file && (message.header.flag == 0x20 || message.header.flag == 0x01000030))
+ {
+ // UserDisplayIcon data or File data is in this message.
+ // Write the recieved data to the file.
+ kdDebug(14140) << k_funcinfo << QString("Received, %1 bytes").arg(message.header.dataSize) << endl;
+
+ m_file->writeBlock(message.body.data(), message.header.dataSize);
+ if(m_transfer){
+ m_transfer->slotProcessed(message.header.dataOffset + message.header.dataSize);
+ }
+
+ if((message.header.dataOffset + message.header.dataSize) == message.header.totalDataSize)
+ {
+ // Transfer is complete.
+ if(m_type == UserDisplayIcon){
+ m_tempFile->close();
+ m_dispatcher->displayIconReceived(m_tempFile, m_object);
+ m_tempFile = 0l;
+ m_file = 0l;
+ }
+ else
+ {
+ m_file->close();
+ }
+
+ m_isComplete = true;
+ // Send data acknowledge message.
+ acknowledge(message);
+
+ if(m_type == UserDisplayIcon)
+ {
+ m_state = Finished;
+ // Send BYE message.
+ sendMessage(BYE, "\r\n");
+ }
+ }
+ }
+ else if(message.header.dataSize == 4 && message.applicationIdentifier == 1)
+ {
+ // Data preparation message.
+ m_tempFile = new KTempFile(locateLocal("tmp", "msnpicture--"), ".png");
+ m_tempFile->setAutoDelete(true);
+ m_file = m_tempFile->file();
+ m_state = DataTransfer;
+ // Send data preparation acknowledge message.
+ acknowledge(message);
+ }
+ else
+ {
+ QString body =
+ QCString(message.body.data(), message.header.dataSize);
+// kdDebug(14140) << k_funcinfo << "received, " << body << endl;
+
+ if(body.startsWith("INVITE"))
+ {
+ // Retrieve some MSNSLP headers used when
+ // replying to this INVITE message.
+ QRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ m_branch = regex.cap(1);
+ // NOTE Call-ID never changes.
+ regex = QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
+ regex.search(body);
+ m_callId = regex.cap(1);
+ regex = QRegExp("Bridges: ([^\r\n]*)\r\n");
+ regex.search(body);
+ QString bridges = regex.cap(1);
+ // The NetID field is 0 if the Conn-Type is
+ // Direct-Connect or Firewall, otherwise, it is
+ // a randomly generated number.
+ regex = QRegExp("NetID: (\\-?\\d+)\r\n");
+ regex.search(body);
+ QString netId = regex.cap(1);
+ kdDebug(14140) << "net id, " << netId << endl;
+ // Connection Types
+ // - Direct-Connect
+ // - Port-Restrict-NAT
+ // - IP-Restrict-NAT
+ // - Symmetric-NAT
+ // - Firewall
+ regex = QRegExp("Conn-Type: ([^\r\n]+)\r\n");
+ regex.search(body);
+ QString connType = regex.cap(1);
+
+ bool wouldListen = false;
+ if(netId.toUInt() == 0 && connType == "Direct-Connect"){
+ wouldListen = true;
+
+ }
+ else if(connType == "IP-Restrict-NAT"){
+ wouldListen = true;
+ }
+#if 1
+ wouldListen = false; // TODO Direct connection support
+#endif
+ QString content;
+
+ if(wouldListen)
+ {
+ // Create a listening socket for direct file transfer.
+ m_listener = new KServerSocket("", "");
+ m_listener->setResolutionEnabled(true);
+ // Create the callback that will try to accept incoming connections.
+ QObject::connect(m_listener, SIGNAL(readyAccept()), SLOT(slotAccept()));
+ QObject::connect(m_listener, SIGNAL(gotError(int)), this, SLOT(slotListenError(int)));
+ // Listen for incoming connections.
+ bool isListening = m_listener->listen(1);
+ kdDebug(14140) << k_funcinfo << (isListening ? "listening" : "not listening") << endl;
+ kdDebug(14140) << k_funcinfo
+ << "local endpoint, " << m_listener->localAddress().nodeName()
+ << endl;
+
+ content = "Bridge: TCPv1\r\n"
+ "Listening: true\r\n" +
+ QString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) +
+ QString("IPv4Internal-Addrs: %1\r\n").arg(m_listener->localAddress().nodeName()) +
+ QString("IPv4Internal-Port: %1\r\n").arg(m_listener->localAddress().serviceName()) +
+ "\r\n";
+ }
+ else
+ {
+ content =
+ "Bridge: TCPv1\r\n"
+ "Listening: false\r\n"
+ "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
+ "\r\n";
+ }
+
+ m_state = DataTransfer;
+
+ if (m_type != File)
+ {
+ // NOTE For file transfers, the connection invite *must not* be acknowledged in any way
+ // as this trips MSN 7.5
+
+ acknowledge(message);
+ // Send 200 OK message to the sending client.
+ sendMessage(OK, content);
+ }
+ }
+ else if(body.startsWith("BYE"))
+ {
+ m_state = Finished;
+ // Send the sending client an acknowledge message.
+ acknowledge(message);
+
+ if(m_file && m_transfer)
+ {
+ if(m_isComplete){
+ // The transfer is complete.
+ m_transfer->slotComplete();
+ }
+ else
+ {
+ // The transfer has been canceled remotely.
+ if(m_transfer){
+ // Inform the user of the file transfer cancelation.
+ m_transfer->slotError(KIO::ERR_ABORTED, i18n("File transfer canceled."));
+ }
+ // Remove the partially received file.
+ m_file->remove();
+ }
+ }
+
+ // Dispose of this transfer context.
+ m_dispatcher->detach(this);
+ }
+ else if(body.startsWith("MSNSLP/1.0 200 OK"))
+ {
+ if(m_type == UserDisplayIcon){
+ m_state = Negotiation;
+ // Acknowledge the 200 OK message.
+ acknowledge(message);
+ }
+ }
+ }
+}
+
+void IncomingTransfer::slotListenError(int /*errorCode*/)
+{
+ kdDebug(14140) << k_funcinfo << m_listener->errorString() << endl;
+}
+
+void IncomingTransfer::slotAccept()
+{
+ // Try to accept an incoming connection from the sending client.
+ m_socket = static_cast<KBufferedSocket*>(m_listener->accept());
+ if(!m_socket)
+ {
+ // NOTE If direct connection fails, the sending
+ // client wil transfer the file data through the
+ // existing session.
+ kdDebug(14140) << k_funcinfo << "Direct connection failed." << endl;
+ // Close the listening endpoint.
+ m_listener->close();
+ return;
+ }
+
+ kdDebug(14140) << k_funcinfo << "Direct connection established." << endl;
+
+ // Set the socket to non blocking,
+ // enable the ready read signal and disable
+ // ready write signal.
+ // NOTE readyWrite consumes too much cpu usage.
+ m_socket->setBlocking(false);
+ m_socket->enableRead(true);
+ m_socket->enableWrite(false);
+
+ // Create the callback that will try to read bytes from the accepted socket.
+ QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
+ // Create the callback that will try to handle the socket close event.
+ QObject::connect(m_socket, SIGNAL(closed()), this, SLOT(slotSocketClosed()));
+ // Create the callback that will try to handle the socket error event.
+ QObject::connect(m_socket, SIGNAL(gotError(int)), this, SLOT(slotSocketError(int)));
+}
+
+void IncomingTransfer::slotSocketRead()
+{
+ int available = m_socket->bytesAvailable();
+ kdDebug(14140) << k_funcinfo << available << ", bytes available." << endl;
+ if(available > 0)
+ {
+ QByteArray buffer(available);
+ m_socket->readBlock(buffer.data(), buffer.size());
+
+ if(QString(buffer) == "foo"){
+ kdDebug(14140) << "Connection Check." << endl;
+ }
+ }
+}
+
+void IncomingTransfer::slotSocketClosed()
+{
+ kdDebug(14140) << k_funcinfo << endl;
+}
+
+void IncomingTransfer::slotSocketError(int errorCode)
+{
+ kdDebug(14140) << k_funcinfo << errorCode << endl;
+}
+
+#include "incomingtransfer.moc"