Port things from MSN to WLM plugin:
[kdenetwork.git] / kopete / protocols / msn / incomingtransfer.cpp
blobea846e736a31752377442386ee8f4b1b7c3f9ea4
1 /*
2 incomingtransfer.cpp - msn p2p protocol
4 Copyright (c) 2003-2005 by Olivier Goffart <ogoffart@kde.org>
5 Copyright (c) 2005 by Gregg Edghill <gregg.edghill@gmail.com>
7 Kopete (c) 2002-2007 by the Kopete developers <kopete-devel@kde.org>
9 *************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 *************************************************************************
19 #include "incomingtransfer.h"
20 //Added by qt3to4:
21 #include <QByteArray>
22 using P2P::TransferContext;
23 using P2P::IncomingTransfer;
24 using P2P::Message;
26 // Kde includes
27 #include <kdebug.h>
28 #include <klocale.h>
29 #include <kstandarddirs.h>
30 #include <ktemporaryfile.h>
32 // Qt includes
33 #include <qfile.h>
34 #include <qregexp.h>
35 #include <qtcpserver.h>
36 #include <qtcpsocket.h>
38 // Kopete includes
39 #include <kopetetransfermanager.h>
41 IncomingTransfer::IncomingTransfer(const QString& from, P2P::Dispatcher *dispatcher, quint32 sessionId)
42 : TransferContext(from,dispatcher,sessionId)
44 m_direction = P2P::Incoming;
45 m_listener = 0l;
48 IncomingTransfer::~IncomingTransfer()
50 kDebug(14140) ;
51 if(m_listener)
53 delete m_listener;
54 m_listener = 0l;
57 if(m_socket)
59 delete m_socket;
60 m_socket = 0l;
65 void IncomingTransfer::slotTransferAccepted(Kopete::Transfer* transfer, const QString& /*fileName*/)
67 quint32 sessionId = transfer->info().internalId().toUInt();
68 if(sessionId!=m_sessionId)
69 return;
71 QObject::connect(transfer , SIGNAL(transferCanceled()), this, SLOT(abort()));
72 m_transfer = transfer;
74 QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
75 sendMessage(OK, content);
77 QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
80 void IncomingTransfer::slotTransferRefused(const Kopete::FileTransferInfo& info)
82 quint32 sessionId = info.internalId().toUInt();
83 if(sessionId!=m_sessionId)
84 return;
86 QString content = QString("SessionID: %1\r\n\r\n").arg(sessionId);
87 // Send the sending client a cancellation message.
88 sendMessage(DECLINE, content);
89 m_state=Finished;
91 QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
96 void IncomingTransfer::acknowledged()
98 kDebug(14140) ;
100 switch(m_state)
102 case Invitation:
103 // NOTE UDI: base identifier acknowledge message, ignore.
104 // UDI: 200 OK message should follow.
105 if(m_type == File)
107 // FT: 200 OK acknowledged message.
108 // If this is the first connection between the two clients, a direct connection invitation
109 // should follow. Otherwise, the file transfer may start right away.
110 if(m_transfer)
112 QFile *destination = new QFile(m_transfer->destinationURL().path());
113 if(!destination->open(QIODevice::WriteOnly))
115 m_transfer->slotError(KIO::ERR_CANNOT_OPEN_FOR_WRITING, i18n("Cannot open file for writing"));
116 m_transfer = 0l;
118 error();
119 return;
121 m_file = destination;
123 m_state = Negotiation;
125 break;
127 case Negotiation:
128 // 200 OK acknowledge message.
129 break;
131 case DataTransfer:
132 break;
134 case Finished:
135 // UDI: Bye acknowledge message.
136 m_dispatcher->detach(this);
137 break;
141 void IncomingTransfer::processMessage(const Message& message)
143 if(m_file && (message.header.flag == 0x20 || message.header.flag == 0x01000030))
145 if(m_state == Finished) {
146 return;
148 // UserDisplayIcon data or File data is in this message.
149 // Write the received data to the file.
150 kDebug(14140) << QString("Received, %1 bytes").arg(message.header.dataSize);
152 m_file->write(message.body.data(), message.header.dataSize);
153 if(m_transfer){
154 m_transfer->slotProcessed(message.header.dataOffset + message.header.dataSize);
157 if((message.header.dataOffset + message.header.dataSize) == message.header.totalDataSize)
159 // Transfer is complete.
160 if(m_type == UserDisplayIcon){
161 m_tempFile->close();
162 m_dispatcher->displayIconReceived(m_tempFile, m_object);
163 m_tempFile = 0l;
164 m_file = 0l;
166 else
168 m_file->close();
171 m_isComplete = true;
172 // Send data acknowledge message.
173 acknowledge(message);
175 if(m_type == UserDisplayIcon)
177 m_state = Finished;
178 // Send BYE message.
179 sendMessage(BYE, "\r\n");
183 //pidgin is probably broken since it sends 0 as appid but we don't need this check anyway
184 else if(message.header.dataSize == 4 /*&& message.applicationIdentifier == 1*/)
186 // Data preparation message.
187 m_tempFile = new KTemporaryFile();
188 m_tempFile->setPrefix("msnpicture--");
189 // m_tempFile->setSuffix(".png");
190 m_tempFile->open();
191 m_file = m_tempFile;
192 m_state = DataTransfer;
193 // Send data preparation acknowledge message.
194 acknowledge(message);
196 else
198 QString body =
199 QByteArray(message.body.data(), message.header.dataSize);
200 // kDebug(14140) << "received, " << body;
202 if(body.startsWith("INVITE"))
204 // Retrieve some MSNSLP headers used when
205 // replying to this INVITE message.
206 QRegExp regex(";branch=\\{([0-9A-F\\-]*)\\}\r\n");
207 regex.indexIn(body);
208 m_branch = regex.cap(1);
209 // NOTE Call-ID never changes.
210 regex = QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
211 regex.indexIn(body);
212 m_callId = regex.cap(1);
213 regex = QRegExp("Bridges: ([^\r\n]*)\r\n");
214 regex.indexIn(body);
215 QString bridges = regex.cap(1);
216 // The NetID field is 0 if the Conn-Type is
217 // Direct-Connect or Firewall, otherwise, it is
218 // a randomly generated number.
219 regex = QRegExp("NetID: (\\-?\\d+)\r\n");
220 regex.indexIn(body);
221 QString netId = regex.cap(1);
222 kDebug(14140) << "net id, " << netId;
223 // Connection Types
224 // - Direct-Connect
225 // - Port-Restrict-NAT
226 // - IP-Restrict-NAT
227 // - Symmetric-NAT
228 // - Firewall
229 regex = QRegExp("Conn-Type: ([^\r\n]+)\r\n");
230 regex.indexIn(body);
231 QString connType = regex.cap(1);
233 bool wouldListen = false;
234 if(netId.toUInt() == 0 && connType == "Direct-Connect"){
235 wouldListen = true;
238 else if(connType == "IP-Restrict-NAT"){
239 wouldListen = true;
241 #if 1
242 wouldListen = false; // TODO Direct connection support
243 #endif
244 QString content;
246 if(wouldListen)
248 // Create a listening socket for direct file transfer.
249 m_listener = new QTcpServer();
250 // Create the callback that will try to accept incoming connections.
251 QObject::connect(m_listener, SIGNAL(newConnection()), SLOT(slotAccept()));
253 // Listen for incoming connections.
254 bool isListening = m_listener->listen();
255 kDebug(14140) << (isListening ? "listening" : "not listening");
256 kDebug(14140) << "local endpoint, " << m_listener->serverAddress().toString();
258 content = "Bridge: TCPv1\r\n"
259 "Listening: true\r\n" +
260 QString("Hashed-Nonce: {%1}\r\n").arg(P2P::Uid::createUid()) +
261 QString("IPv4Internal-Addrs: %1\r\n").arg(m_listener->serverAddress().toString()) +
262 QString("IPv4Internal-Port: %1\r\n").arg(m_listener->serverPort()) +
263 "\r\n";
265 else
267 content =
268 "Bridge: TCPv1\r\n"
269 "Listening: false\r\n"
270 "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
271 "\r\n";
274 m_state = DataTransfer;
276 if (m_type != File)
278 // NOTE For file transfers, the connection invite *must not* be acknowledged in any way
279 // as this trips MSN 7.5
281 acknowledge(message);
282 // Send 200 OK message to the sending client.
283 sendMessage(OK, content);
286 else if(body.startsWith("BYE"))
288 m_state = Finished;
289 // Send the sending client an acknowledge message.
290 acknowledge(message);
292 if(m_file && m_transfer)
294 if(m_isComplete){
295 // The transfer is complete.
296 m_transfer->slotComplete();
298 else
300 // The transfer has been canceled remotely.
301 if(m_transfer){
302 // Inform the user of the file transfer cancellation.
303 m_transfer->slotError(KIO::ERR_ABORTED, i18n("File transfer cancelled."));
305 // Remove the partially received file.
306 m_file->remove();
310 // Dispose of this transfer context.
311 m_dispatcher->detach(this);
313 else if(body.startsWith("MSNSLP/1.0 200 OK"))
315 if(m_type == UserDisplayIcon){
316 m_state = Negotiation;
317 // Acknowledge the 200 OK message.
318 acknowledge(message);
324 void IncomingTransfer::slotAccept()
326 // Try to accept an incoming connection from the sending client.
327 m_socket = m_listener->nextPendingConnection();
328 if(!m_socket)
330 // NOTE If direct connection fails, the sending
331 // client wil transfer the file data through the
332 // existing session.
333 kDebug(14140) << "Direct connection failed.";
334 // Close the listening endpoint.
335 m_listener->close();
336 return;
339 kDebug(14140) << "Direct connection established.";
341 // Create the callback that will try to read bytes from the accepted socket.
342 QObject::connect(m_socket, SIGNAL(readyRead()), this, SLOT(slotSocketRead()));
343 // Create the callback that will try to handle the socket close event.
344 QObject::connect(m_socket, SIGNAL(disconnected()), this, SLOT(slotSocketClosed()));
345 // Create the callback that will try to handle the socket error event.
346 QObject::connect(m_socket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(slotSocketError(QAbstractSocket::SocketError)));
349 void IncomingTransfer::slotSocketRead()
351 int available = m_socket->bytesAvailable();
352 kDebug(14140) << available << ", bytes available.";
353 if(available > 0)
355 QByteArray buffer = m_socket->read(available);
356 if(QString(buffer) == "foo"){
357 kDebug(14140) << "Connection Check.";
362 void IncomingTransfer::slotSocketClosed()
364 kDebug(14140) ;
367 void IncomingTransfer::slotSocketError(QAbstractSocket::SocketError errorCode)
369 kDebug(14140) << errorCode;
372 #include "incomingtransfer.moc"