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