1 // oftmetatransfer.cpp
2 
3 // Copyright (C)  2006
4 
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 // 02110-1301  USA
19 
20 #include "oftmetatransfer.h"
21 
22 #include <QFileInfo>
23 #include <QTcpSocket>
24 
25 #include <kdebug.h>
26 
27 #include "ofttransfer.h"
28 #include "oftprotocol.h"
29 
30 #define BUFFER_SIZE 32768
31 
OftMetaTransfer(const QByteArray & cookie,const QStringList & files,const QString & dir,QTcpSocket * socket)32 OftMetaTransfer::OftMetaTransfer( const QByteArray& cookie, const QStringList &files, const QString &dir, QTcpSocket *socket )
33 : m_file( this ), m_socket( socket ), m_state( SetupReceive )
34 {
35 	//filetransfertask is responsible for hooking us up to the ui
36 	//we're responsible for hooking up the socket and timer
37 	connect( m_socket, SIGNAL(readyRead()), this, SLOT(socketRead()) );
38 	connect( m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
39 	         this, SLOT(socketError(QAbstractSocket::SocketError)) );
40 
41 	initOft();
42 	m_oft.cookie = cookie;
43 	m_files = files;
44 	m_dir = dir;
45 }
46 
OftMetaTransfer(const QByteArray & cookie,const QStringList & files,QTcpSocket * socket)47 OftMetaTransfer::OftMetaTransfer( const QByteArray& cookie, const QStringList& files, QTcpSocket *socket )
48 : m_file( this ), m_socket( socket ), m_state( SetupSend )
49 {
50 	//filetransfertask is responsible for hooking us up to the ui
51 	//we're responsible for hooking up the socket and timer
52 	connect( m_socket, SIGNAL(readyRead()), this, SLOT(socketRead()) );
53 	connect( m_socket, SIGNAL(error(QAbstractSocket::SocketError)),
54 	         this, SLOT(socketError(QAbstractSocket::SocketError)) );
55 
56 	initOft();
57 	m_oft.cookie = cookie;
58 	for ( int i = 0; i < files.size(); ++i )
59 	{
60 		QFileInfo fileInfo( files.at(i) );
61 		m_oft.totalSize += fileInfo.size();
62 	}
63 	m_oft.fileCount = files.size();
64 	m_files = files;
65 }
66 
start()67 void OftMetaTransfer::start()
68 {
69 	if ( m_files.count() == 0 )
70 	{
71 		doCancel();
72 		return;
73 	}
74 
75 	//filesLeft is decremented in prompt
76 	m_oft.filesLeft = m_oft.fileCount + 1;
77 	prompt();
78 }
79 
~OftMetaTransfer()80 OftMetaTransfer::~OftMetaTransfer()
81 {
82 	if( m_socket )
83 	{
84 		m_socket->close();
85 		delete m_socket;
86 		m_socket = 0;
87 	}
88 	kDebug(OSCAR_RAW_DEBUG) << "really done";
89 }
90 /*
91 bool OftMetaTransfer::validFile()
92 {
93 	if ( m_action == Send )
94 	{
95 		if ( ! m_file.exists() )
96 		{
97 			emit error( KIO::ERR_DOES_NOT_EXIST, m_file.fileName() );
98 			return 0;
99 		}
100 		if ( m_file.size() == 0 )
101 		{
102 			emit error( KIO::ERR_COULD_NOT_READ, i18n("file is empty: ") + m_file.fileName() );
103 			return 0;
104 		}
105 		if ( ! QFileInfo( m_file ).isReadable() )
106 		{
107 			emit error( KIO::ERR_CANNOT_OPEN_FOR_READING, m_file.fileName() );
108 			return 0;
109 		}
110 	}
111 	else //receive
112 	{ //note: opening for writing clobbers the file
113 		if ( m_file.exists() )
114 		{
115 			if ( ! QFileInfo( m_file ).isWritable() )
116 			{ //it's there and readonly
117 				emit error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, m_file.fileName() );
118 				return 0;
119 			}
120 		}
121 		else if ( ! QFileInfo( QFileInfo( m_file ).toLocalFile() ).isWritable() )
122 		{ //not allowed to create it
123 				emit error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, m_file.fileName() );
124 				return 0;
125 		}
126 	}
127 	return true;
128 }
129 */
130 
socketError(QAbstractSocket::SocketError e)131 void OftMetaTransfer::socketError( QAbstractSocket::SocketError e )
132 {
133 	QString desc = m_socket->errorString();
134 	kWarning(OSCAR_RAW_DEBUG) << "socket error: " << e << " : " << desc;
135 	emit transferError( e, desc );
136 }
137 
socketRead()138 void OftMetaTransfer::socketRead()
139 {
140 	if ( m_state == Receiving ) //raw file data
141 		saveData();
142 	else //oft packet
143 		readOft();
144 }
145 
readOft()146 void OftMetaTransfer::readOft()
147 {
148 	kDebug(OSCAR_RAW_DEBUG) ;
149 	QByteArray raw = m_socket->readAll(); //is this safe?
150 	OftProtocol p;
151 	uint b=0;
152 	//remember we're responsible for freeing this!
153 	OftTransfer *t = static_cast<OftTransfer*>( p.parse( raw, b ) );
154 	OFT data = t->data();
155 	kDebug(OSCAR_RAW_DEBUG) << "checksum: " << data.checksum;
156 	kDebug(OSCAR_RAW_DEBUG) << "sentChecksum: " << data.sentChecksum;
157 
158 	switch( data.type )
159 	{
160 	case 0x101:
161 		handleReceiveSetup( data );
162 		break;
163 	case 0x202:
164 		handleSendSetup( data );
165 		break;
166 	case 0x204:
167 		handleSendDone( data );
168 		break;
169 	case 0x205:
170 		handleSendResumeRequest( data );
171 		break;
172 	case 0x106:
173 		handleReceiveResumeSetup( data );
174 		break;
175 	case 0x207:
176 		handleSendResumeSetup( data );
177 		break;
178 	default:
179 		kWarning(OSCAR_RAW_DEBUG) << "unknown type " << data.type;
180 	}
181 
182 	delete t;
183 }
184 
initOft()185 void OftMetaTransfer::initOft()
186 {
187 	//set up the default values for the oft
188 	m_oft.type = 0; //invalid
189 	m_oft.cookie = 0;
190 	m_oft.fileSize = 0;
191 	m_oft.modTime = 0;
192 	m_oft.checksum = 0xFFFF0000; //file checksum
193 	m_oft.bytesSent = 0;
194 	m_oft.sentChecksum = 0xFFFF0000; //checksum of transmitted bytes
195 	m_oft.flags = 0x20; //flags; 0x20=not done, 1=done
196 	m_oft.fileName.clear();
197 	m_oft.fileCount = 1;
198 	m_oft.filesLeft = 1;
199 	m_oft.partCount = 1;
200 	m_oft.partsLeft = 1;
201 	m_oft.totalSize = 0;
202 }
203 
handleReceiveSetup(const OFT & oft)204 void OftMetaTransfer::handleReceiveSetup( const OFT &oft )
205 {
206 	if ( m_state != SetupReceive )
207 		return;
208 
209 	kDebug(OSCAR_RAW_DEBUG) << "prompt" << endl
210 		<< "\tmysize " <<  m_file.size() << endl
211 		<< "\tsendersize " << oft.fileSize << endl;
212 	//do we care about anything *in* the prompt?
213 	//just the checksum.
214 
215 	m_oft.checksum = oft.checksum;
216 	m_oft.modTime = oft.modTime;
217 	m_oft.fileCount = oft.fileCount;
218 	m_oft.filesLeft = oft.filesLeft;
219 	m_oft.partCount = oft.partCount;
220 	m_oft.partsLeft = oft.partsLeft;
221 	m_oft.totalSize = oft.totalSize;
222 	m_oft.fileName = oft.fileName;
223 	m_oft.bytesSent = oft.bytesSent;
224 	m_oft.fileSize = oft.fileSize;
225 
226 	int currentFileIndex = oft.fileCount - oft.filesLeft;
227 	if ( currentFileIndex < m_files.count() )
228 		m_file.setFileName( m_files.at( currentFileIndex ) );
229 	else
230 		m_file.setFileName( m_dir + oft.fileName );
231 
232 	emit fileStarted( m_oft.fileName, m_file.fileName() );
233 	emit fileStarted( m_file.fileName(), m_oft.fileSize );
234 	if ( m_file.size() > 0 && m_file.size() <= oft.fileSize )
235 	{
236 		m_oft.sentChecksum = fileChecksum( m_file );
237 		if ( m_file.size() < oft.fileSize )
238 		{ //could be a partial file
239 			resume();
240 			return;
241 		}
242 		else if ( m_oft.checksum == m_oft.sentChecksum )
243 		{ //apparently we've already got it
244 			//TODO: set bytesSent?
245 			done(); //don't redo checksum
246 			return;
247 		}
248 
249 		//if we didn't break then we need the whole file
250 		m_oft.sentChecksum = 0xffff0000;
251 	}
252 
253 	m_file.open( QIODevice::WriteOnly );
254 	//TODO what if open failed?
255 	ack();
256 }
257 
handleReceiveResumeSetup(const OFT & oft)258 void OftMetaTransfer::handleReceiveResumeSetup( const OFT &oft )
259 {
260 	if ( m_state != SetupReceive )
261 		return;
262 
263 	kDebug(OSCAR_RAW_DEBUG) << "sender resume" << endl
264 		<< "\tfilesize\t" << oft.fileSize << endl
265 		<< "\tmodTime\t" << oft.modTime << endl
266 		<< "\tbytesSent\t" << oft.bytesSent << endl
267 		<< "\tflags\t" << oft.flags << endl;
268 
269 	QIODevice::OpenMode flags;
270 	if ( oft.bytesSent ) //yay, we can resume
271 	{
272 		flags = QIODevice::WriteOnly | QIODevice::Append;
273 	}
274 	else
275 	{ //they insist on sending the whole file :(
276 		flags = QIODevice::WriteOnly;
277 		m_oft.sentChecksum = 0xffff0000;
278 		m_oft.bytesSent = 0;
279 	}
280 
281 	m_file.open( flags );
282 	//TODO what if open failed?
283 	rAck();
284 }
285 
handleSendSetup(const OFT & oft)286 void OftMetaTransfer::handleSendSetup( const OFT &oft )
287 {
288 	if ( m_state != SetupSend )
289 		return;
290 
291 	kDebug(OSCAR_RAW_DEBUG) << "ack";
292 	emit fileStarted( m_file.fileName(), oft.fileName );
293 	emit fileStarted( m_file.fileName(), oft.fileSize );
294 
295 	//time to send real data
296 	//TODO: validate file again, just to be sure
297 	m_file.open( QIODevice::ReadOnly );
298 	m_state = Sending;
299 
300 	//use bytesWritten to trigger writes
301 	connect( m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(write()) );
302 	write();
303 }
304 
handleSendResumeSetup(const OFT & oft)305 void OftMetaTransfer::handleSendResumeSetup( const OFT &oft )
306 {
307 	Q_UNUSED(oft);
308 
309 	if ( m_state != SetupSend )
310 		return;
311 
312 	kDebug(OSCAR_RAW_DEBUG) << "resume ack";
313 	//TODO: validate file again, just to be sure
314 	m_file.open( QIODevice::ReadOnly );
315 	m_file.seek( m_oft.bytesSent );
316 	m_state = Sending;
317 
318 	//use bytesWritten to trigger writes
319 	connect( m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(write()) );
320 	write();
321 }
322 
handleSendResumeRequest(const OFT & oft)323 void OftMetaTransfer::handleSendResumeRequest( const OFT &oft )
324 {
325 	if ( m_state != SetupSend )
326 		return;
327 
328 	kDebug(OSCAR_RAW_DEBUG) << "receiver resume" << endl
329 		<< "\tfilesize\t" << oft.fileSize << endl
330 		<< "\tmodTime\t" << oft.modTime << endl
331 		<< "\tbytesSent\t" << oft.bytesSent << endl
332 		<< "\tflags\t" << oft.flags << endl;
333 
334 	if ( fileChecksum( m_file, oft.bytesSent ) == oft.sentChecksum )
335 	{
336 		m_oft.sentChecksum = oft.sentChecksum;
337 		m_oft.bytesSent = oft.bytesSent; //ok, we can resume this
338 	}
339 
340 	rAgree();
341 }
342 
handleSendDone(const OFT & oft)343 void OftMetaTransfer::handleSendDone( const OFT &oft )
344 {
345 	kDebug(OSCAR_RAW_DEBUG) << "done";
346 	emit fileFinished( m_file.fileName(), oft.bytesSent );
347 
348 	disconnect( m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(write()) );
349 	if ( oft.sentChecksum != m_oft.checksum )
350 		kDebug(OSCAR_RAW_DEBUG) << "checksums do not match!";
351 
352 	if ( m_oft.filesLeft > 1 )
353 	{ // Ready for next file
354 		m_state = SetupSend;
355 		prompt();
356 	}
357 	else
358 	{ // Last file, ending connection
359 		connect( m_socket, SIGNAL(disconnected()), this, SLOT(emitTransferCompleted()) );
360 		m_socket->disconnectFromHost();
361 	}
362 }
363 
write()364 void OftMetaTransfer::write()
365 {
366 	if ( m_socket->bytesToWrite() )
367 		return; //give hte socket time to catch up
368 
369 	//an arbitrary amount to send each time.
370 	char data[BUFFER_SIZE];
371 
372 	m_file.seek( m_oft.bytesSent );
373 	int read = m_file.read( data, BUFFER_SIZE );
374 	if( read == -1 )
375 	{ //FIXME: handle this properly
376 		kWarning(OSCAR_RAW_DEBUG) << "failed to read :(";
377 		return;
378 	}
379 
380 	int written = m_socket->write( data, read );
381 	if( written == -1 )
382 	{ //FIXME: handle this properly
383 		kWarning(OSCAR_RAW_DEBUG) << "failed to write :(";
384 		return;
385 	}
386 
387 	m_oft.sentChecksum = chunkChecksum( data, written,
388 	                                    m_oft.sentChecksum, m_oft.bytesSent & 1);
389 	m_oft.bytesSent += written;
390 
391 	//tell the ui
392 	emit fileProcessed( m_oft.bytesSent, m_oft.fileSize );
393 	if ( m_oft.bytesSent >= m_oft.fileSize )
394 	{
395 		m_file.close();
396 		disconnect( m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(write()) );
397 		//now we sit and do nothing until either an OFT Done
398 		//arrives or the user cancels.
399 		//we *should* always get the OFT done right away.
400 	}
401 }
402 
saveData()403 void OftMetaTransfer::saveData()
404 {
405 	QByteArray raw = m_socket->readAll(); //is this safe?
406 	int written = m_file.write( raw );
407 	if( written == -1 )
408 	{ //FIXME: handle this properly
409 		kWarning(OSCAR_RAW_DEBUG) << "failed to write :(";
410 		return;
411 	}
412 
413 	m_oft.sentChecksum = chunkChecksum( raw.constData(), raw.size(),
414 	                                    m_oft.sentChecksum, m_oft.bytesSent & 1);
415 	m_oft.bytesSent += written;
416 	if ( written != raw.size() )
417 	{	//FIXME: handle this properly
418 		kWarning(OSCAR_RAW_DEBUG) << "didn't write everything we read";
419 		doCancel();
420 	}
421 
422 	//tell the ui
423 	emit fileProcessed( m_oft.bytesSent, m_oft.fileSize );
424 	if ( m_oft.bytesSent >= m_oft.fileSize )
425 	{
426 		m_file.close();
427 		done();
428 	}
429 
430 }
431 
sendOft()432 void OftMetaTransfer::sendOft()
433 {
434 	//now make a transfer out of it
435 	OftTransfer t( m_oft );
436 	int written = m_socket->write( t.toWire() );
437 
438 	if( written == -1 ) //FIXME: handle this properly
439 		kDebug(OSCAR_RAW_DEBUG) << "failed to write :(";
440 }
441 
prompt()442 void OftMetaTransfer::prompt()
443 {
444 	kDebug(OSCAR_RAW_DEBUG) ;
445 	m_oft.type = 0x0101; //type = prompt
446 
447 	m_oft.filesLeft--;
448 	const int index = m_oft.fileCount - m_oft.filesLeft;
449 
450 	m_file.setFileName( m_files.at( index ) );
451 	QFileInfo fileInfo( m_file );
452 
453 	m_oft.modTime = fileInfo.lastModified().toTime_t();
454 	m_oft.fileSize = fileInfo.size();
455 	m_oft.fileName = fileInfo.fileName();
456 	m_oft.checksum = fileChecksum( m_file );
457 	m_oft.sentChecksum = 0xFFFF0000;
458 	m_oft.bytesSent = 0;
459 	sendOft();
460 	//now we wait for the other side to ack
461 }
462 
ack()463 void OftMetaTransfer::ack()
464 {
465 	kDebug(OSCAR_RAW_DEBUG) ;
466 	m_oft.type = 0x0202; //type = ack
467 	sendOft();
468 	m_state = Receiving;
469 }
470 
rAck()471 void OftMetaTransfer::rAck()
472 {
473 	kDebug(OSCAR_RAW_DEBUG) ;
474 	m_oft.type = 0x0207; //type = resume ack
475 	sendOft();
476 	m_state = Receiving;
477 }
478 
done()479 void OftMetaTransfer::done()
480 {
481 	kDebug(OSCAR_RAW_DEBUG) ;
482 	m_oft.type = 0x0204; //type = done
483 	if ( m_oft.sentChecksum != m_oft.checksum )
484 		kDebug(OSCAR_RAW_DEBUG) << "checksums do not match!";
485 
486 	emit fileFinished( m_file.fileName(), m_oft.bytesSent );
487 	if ( m_oft.filesLeft == 1 )
488 		m_oft.flags = 1;
489 
490 	sendOft();
491 
492 	if ( m_oft.filesLeft > 1 )
493 	{ //Ready for next file
494 		m_state = SetupReceive;
495 	}
496 	else
497 	{ //Last file, ending connection
498 		m_state = Done;
499 
500 		connect( m_socket, SIGNAL(disconnected()), this, SLOT(emitTransferCompleted()) );
501 		m_socket->disconnectFromHost();
502 	}
503 }
504 
resume()505 void OftMetaTransfer::resume()
506 {
507 	kDebug(OSCAR_RAW_DEBUG) ;
508 	m_oft.type = 0x0205; //type = resume
509 	m_oft.bytesSent = m_file.size();
510 	sendOft();
511 }
512 
rAgree()513 void OftMetaTransfer::rAgree()
514 {
515 	kDebug(OSCAR_RAW_DEBUG) ;
516 	m_oft.type = 0x0106; //type = sender resume
517 	sendOft();
518 }
519 
doCancel()520 void OftMetaTransfer::doCancel()
521 {
522 	kDebug(OSCAR_RAW_DEBUG) ;
523 	//stop our timer in case we were sending stuff
524 	disconnect( m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(write()) );
525 	m_socket->close();
526 	deleteLater();
527 }
528 
emitTransferCompleted()529 void OftMetaTransfer::emitTransferCompleted()
530 {
531 	kDebug(OSCAR_RAW_DEBUG) ;
532 
533 	emit transferCompleted();
534 	deleteLater(); //yay, it's ok to kill everything now
535 }
536 
fileChecksum(QFile & file,int bytes) const537 Oscar::DWORD OftMetaTransfer::fileChecksum( QFile& file, int bytes ) const
538 {
539 	Oscar::DWORD checksum = 0xFFFF0000;
540 	char data[BUFFER_SIZE];
541 	int read;
542 	int totalRead = 0;
543 
544 	file.open( QIODevice::ReadOnly );
545 	while ( (read = file.read( data, BUFFER_SIZE )) > 0 )
546 	{
547 		if ( bytes != -1 && (totalRead + read) >= bytes )
548 		{
549 			read = bytes - totalRead;
550 			checksum = chunkChecksum( data, read, checksum, totalRead & 1);
551 			break;
552 		}
553 		else
554 		{
555 			checksum = chunkChecksum( data, read, checksum, totalRead & 1);
556 		}
557 		totalRead += read;
558 	}
559 	file.close();
560 
561 	if ( read == -1 )
562 		return 0xFFFF0000;
563 
564 	return checksum;
565 }
566 
chunkChecksum(const char * buffer,int bufferSize,Oscar::DWORD checksum,bool shiftIndex) const567 Oscar::DWORD OftMetaTransfer::chunkChecksum( const char *buffer, int bufferSize,
568                                              Oscar::DWORD checksum, bool shiftIndex ) const
569 {
570 	//code adapted from Miranda's oft_calc_checksum
571 	const int evenIndex = (shiftIndex) ? 1 : 0;
572 
573 	checksum = (checksum >> 16) & 0xffff;
574 	for ( int i = 0; i < bufferSize; i++ )
575 	{
576 		quint16 val = (uchar)buffer[i];
577 
578 		if ( (i & 1) == evenIndex )
579 			val = val << 8;
580 
581 		if (checksum < val)
582 			checksum -= val + 1;
583 		else // simulate carry
584 			checksum -= val;
585 	}
586 	checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
587 	checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
588 	return checksum << 16;
589 }
590 
591