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 *************************************************************************
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. *
16 *************************************************************************
19 #include "incomingtransfer.h"
22 using P2P::TransferContext
;
23 using P2P::IncomingTransfer
;
29 #include <kstandarddirs.h>
30 #include <ktemporaryfile.h>
35 #include <qtcpserver.h>
36 #include <qtcpsocket.h>
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
;
48 IncomingTransfer::~IncomingTransfer()
65 void IncomingTransfer::slotTransferAccepted(Kopete::Transfer
* transfer
, const QString
& /*fileName*/)
67 quint32 sessionId
= transfer
->info().internalId().toUInt();
68 if(sessionId
!=m_sessionId
)
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
)
86 QString content
= QString("SessionID: %1\r\n\r\n").arg(sessionId
);
87 // Send the sending client a cancellation message.
88 sendMessage(DECLINE
, content
);
91 QObject::disconnect(Kopete::TransferManager::transferManager(), 0l, this, 0l);
96 void IncomingTransfer::acknowledged()
103 // NOTE UDI: base identifier acknowledge message, ignore.
104 // UDI: 200 OK message should follow.
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.
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"));
121 m_file
= destination
;
123 m_state
= Negotiation
;
128 // 200 OK acknowledge message.
135 // UDI: Bye acknowledge message.
136 m_dispatcher
->detach(this);
141 void IncomingTransfer::processMessage(const Message
& message
)
143 if(m_file
&& (message
.header
.flag
== 0x20 || message
.header
.flag
== 0x01000030))
145 if(m_state
== Finished
) {
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
);
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
){
162 m_dispatcher
->displayIconReceived(m_tempFile
, m_object
);
172 // Send data acknowledge message.
173 acknowledge(message
);
175 if(m_type
== UserDisplayIcon
)
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");
192 m_state
= DataTransfer
;
193 // Send data preparation acknowledge message.
194 acknowledge(message
);
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");
208 m_branch
= regex
.cap(1);
209 // NOTE Call-ID never changes.
210 regex
= QRegExp("Call-ID: \\{([0-9A-F\\-]*)\\}\r\n");
212 m_callId
= regex
.cap(1);
213 regex
= QRegExp("Bridges: ([^\r\n]*)\r\n");
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");
221 QString netId
= regex
.cap(1);
222 kDebug(14140) << "net id, " << netId
;
225 // - Port-Restrict-NAT
229 regex
= QRegExp("Conn-Type: ([^\r\n]+)\r\n");
231 QString connType
= regex
.cap(1);
233 bool wouldListen
= false;
234 if(netId
.toUInt() == 0 && connType
== "Direct-Connect"){
238 else if(connType
== "IP-Restrict-NAT"){
242 wouldListen
= false; // TODO Direct connection support
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()) +
269 "Listening: false\r\n"
270 "Hashed-Nonce: {00000000-0000-0000-0000-000000000000}\r\n"
274 m_state
= DataTransfer
;
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"))
289 // Send the sending client an acknowledge message.
290 acknowledge(message
);
292 if(m_file
&& m_transfer
)
295 // The transfer is complete.
296 m_transfer
->slotComplete();
300 // The transfer has been canceled remotely.
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.
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();
330 // NOTE If direct connection fails, the sending
331 // client wil transfer the file data through the
333 kDebug(14140) << "Direct connection failed.";
334 // Close the listening endpoint.
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.";
355 QByteArray buffer
= m_socket
->read(available
);
356 if(QString(buffer
) == "foo"){
357 kDebug(14140) << "Connection Check.";
362 void IncomingTransfer::slotSocketClosed()
367 void IncomingTransfer::slotSocketError(QAbstractSocket::SocketError errorCode
)
369 kDebug(14140) << errorCode
;
372 #include "incomingtransfer.moc"