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.
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.
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
20
#include "oftmetatransfer.h"
22
#include <QtCore/QFileInfo>
23
#include <QtNetwork/QTcpSocket>
27
#include "ofttransfer.h"
28
#include "oftprotocol.h"
30
#define BUFFER_SIZE 32768
32
OftMetaTransfer::OftMetaTransfer( const QByteArray& cookie, const QStringList &files, const QString &dir, QTcpSocket *socket )
33
: m_file( this ), m_socket( socket ), m_state( SetupReceive )
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)) );
42
m_oft.cookie = cookie;
47
OftMetaTransfer::OftMetaTransfer( const QByteArray& cookie, const QStringList& files, QTcpSocket *socket )
48
: m_file( this ), m_socket( socket ), m_state( SetupSend )
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)) );
57
m_oft.cookie = cookie;
58
for ( int i = 0; i < files.size(); ++i )
60
QFileInfo fileInfo( files.at(i) );
61
m_oft.totalSize += fileInfo.size();
63
m_oft.fileCount = files.size();
67
void OftMetaTransfer::start()
69
if ( m_files.count() == 0 )
75
//filesLeft is decremented in prompt
76
m_oft.filesLeft = m_oft.fileCount + 1;
80
OftMetaTransfer::~OftMetaTransfer()
88
kDebug(OSCAR_RAW_DEBUG) << "really done";
91
bool OftMetaTransfer::validFile()
93
if ( m_action == Send )
95
if ( ! m_file.exists() )
97
emit error( KIO::ERR_DOES_NOT_EXIST, m_file.fileName() );
100
if ( m_file.size() == 0 )
102
emit error( KIO::ERR_COULD_NOT_READ, i18n("file is empty: ") + m_file.fileName() );
105
if ( ! QFileInfo( m_file ).isReadable() )
107
emit error( KIO::ERR_CANNOT_OPEN_FOR_READING, m_file.fileName() );
112
{ //note: opening for writing clobbers the file
113
if ( m_file.exists() )
115
if ( ! QFileInfo( m_file ).isWritable() )
116
{ //it's there and readonly
117
emit error( KIO::ERR_CANNOT_OPEN_FOR_WRITING, m_file.fileName() );
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() );
131
void OftMetaTransfer::socketError( QAbstractSocket::SocketError e )
133
QString desc = m_socket->errorString();
134
kWarning(OSCAR_RAW_DEBUG) << "socket error: " << e << " : " << desc;
135
emit transferError( e, desc );
138
void OftMetaTransfer::socketRead()
140
if ( m_state == Receiving ) //raw file data
146
void OftMetaTransfer::readOft()
148
kDebug(OSCAR_RAW_DEBUG) ;
149
QByteArray raw = m_socket->readAll(); //is this safe?
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;
161
handleReceiveSetup( data );
164
handleSendSetup( data );
167
handleSendDone( data );
170
handleSendResumeRequest( data );
173
handleReceiveResumeSetup( data );
176
handleSendResumeSetup( data );
179
kWarning(OSCAR_RAW_DEBUG) << "unknown type " << data.type;
185
void OftMetaTransfer::initOft()
187
//set up the default values for the oft
188
m_oft.type = 0; //invalid
192
m_oft.checksum = 0xFFFF0000; //file checksum
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();
204
void OftMetaTransfer::handleReceiveSetup( const OFT &oft )
206
if ( m_state != SetupReceive )
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?
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;
226
int currentFileIndex = oft.fileCount - oft.filesLeft;
227
if ( currentFileIndex < m_files.count() )
228
m_file.setFileName( m_files.at( currentFileIndex ) );
230
m_file.setFileName( m_dir + oft.fileName );
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 )
236
m_oft.sentChecksum = fileChecksum( m_file );
237
if ( m_file.size() < oft.fileSize )
238
{ //could be a partial file
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
249
//if we didn't break then we need the whole file
250
m_oft.sentChecksum = 0xffff0000;
253
m_file.open( QIODevice::WriteOnly );
254
//TODO what if open failed?
258
void OftMetaTransfer::handleReceiveResumeSetup( const OFT &oft )
260
if ( m_state != SetupReceive )
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;
269
QIODevice::OpenMode flags;
270
if ( oft.bytesSent ) //yay, we can resume
272
flags = QIODevice::WriteOnly | QIODevice::Append;
275
{ //they insist on sending the whole file :(
276
flags = QIODevice::WriteOnly;
277
m_oft.sentChecksum = 0xffff0000;
281
m_file.open( flags );
282
//TODO what if open failed?
286
void OftMetaTransfer::handleSendSetup( const OFT &oft )
288
if ( m_state != SetupSend )
291
kDebug(OSCAR_RAW_DEBUG) << "ack";
292
emit fileStarted( m_file.fileName(), oft.fileName );
293
emit fileStarted( m_file.fileName(), oft.fileSize );
295
//time to send real data
296
//TODO: validate file again, just to be sure
297
m_file.open( QIODevice::ReadOnly );
300
//use bytesWritten to trigger writes
301
connect( m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(write()) );
305
void OftMetaTransfer::handleSendResumeSetup( const OFT &oft )
309
if ( m_state != SetupSend )
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 );
318
//use bytesWritten to trigger writes
319
connect( m_socket, SIGNAL(bytesWritten(qint64)), this, SLOT(write()) );
323
void OftMetaTransfer::handleSendResumeRequest( const OFT &oft )
325
if ( m_state != SetupSend )
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;
334
if ( fileChecksum( m_file, oft.bytesSent ) == oft.sentChecksum )
336
m_oft.sentChecksum = oft.sentChecksum;
337
m_oft.bytesSent = oft.bytesSent; //ok, we can resume this
343
void OftMetaTransfer::handleSendDone( const OFT &oft )
345
kDebug(OSCAR_RAW_DEBUG) << "done";
346
emit fileFinished( m_file.fileName(), oft.bytesSent );
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!";
352
if ( m_oft.filesLeft > 1 )
353
{ // Ready for next file
358
{ // Last file, ending connection
359
connect( m_socket, SIGNAL(disconnected()), this, SLOT(emitTransferCompleted()) );
360
m_socket->disconnectFromHost();
364
void OftMetaTransfer::write()
366
if ( m_socket->bytesToWrite() )
367
return; //give hte socket time to catch up
369
//an arbitrary amount to send each time.
370
char data[BUFFER_SIZE];
372
m_file.seek( m_oft.bytesSent );
373
int read = m_file.read( data, BUFFER_SIZE );
375
{ //FIXME: handle this properly
376
kWarning(OSCAR_RAW_DEBUG) << "failed to read :(";
380
int written = m_socket->write( data, read );
382
{ //FIXME: handle this properly
383
kWarning(OSCAR_RAW_DEBUG) << "failed to write :(";
387
m_oft.sentChecksum = chunkChecksum( data, written,
388
m_oft.sentChecksum, m_oft.bytesSent & 1);
389
m_oft.bytesSent += written;
392
emit fileProcessed( m_oft.bytesSent, m_oft.fileSize );
393
if ( m_oft.bytesSent >= m_oft.fileSize )
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.
403
void OftMetaTransfer::saveData()
405
QByteArray raw = m_socket->readAll(); //is this safe?
406
int written = m_file.write( raw );
408
{ //FIXME: handle this properly
409
kWarning(OSCAR_RAW_DEBUG) << "failed to write :(";
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";
423
emit fileProcessed( m_oft.bytesSent, m_oft.fileSize );
424
if ( m_oft.bytesSent >= m_oft.fileSize )
432
void OftMetaTransfer::sendOft()
434
//now make a transfer out of it
435
OftTransfer t( m_oft );
436
int written = m_socket->write( t.toWire() );
438
if( written == -1 ) //FIXME: handle this properly
439
kDebug(OSCAR_RAW_DEBUG) << "failed to write :(";
442
void OftMetaTransfer::prompt()
444
kDebug(OSCAR_RAW_DEBUG) ;
445
m_oft.type = 0x0101; //type = prompt
448
const int index = m_oft.fileCount - m_oft.filesLeft;
450
m_file.setFileName( m_files.at( index ) );
451
QFileInfo fileInfo( m_file );
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;
460
//now we wait for the other side to ack
463
void OftMetaTransfer::ack()
465
kDebug(OSCAR_RAW_DEBUG) ;
466
m_oft.type = 0x0202; //type = ack
471
void OftMetaTransfer::rAck()
473
kDebug(OSCAR_RAW_DEBUG) ;
474
m_oft.type = 0x0207; //type = resume ack
479
void OftMetaTransfer::done()
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!";
486
emit fileFinished( m_file.fileName(), m_oft.bytesSent );
487
if ( m_oft.filesLeft == 1 )
492
if ( m_oft.filesLeft > 1 )
493
{ //Ready for next file
494
m_state = SetupReceive;
497
{ //Last file, ending connection
500
connect( m_socket, SIGNAL(disconnected()), this, SLOT(emitTransferCompleted()) );
501
m_socket->disconnectFromHost();
505
void OftMetaTransfer::resume()
507
kDebug(OSCAR_RAW_DEBUG) ;
508
m_oft.type = 0x0205; //type = resume
509
m_oft.bytesSent = m_file.size();
513
void OftMetaTransfer::rAgree()
515
kDebug(OSCAR_RAW_DEBUG) ;
516
m_oft.type = 0x0106; //type = sender resume
520
void OftMetaTransfer::doCancel()
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()) );
529
void OftMetaTransfer::emitTransferCompleted()
531
kDebug(OSCAR_RAW_DEBUG) ;
533
emit transferCompleted();
534
deleteLater(); //yay, it's ok to kill everything now
537
Oscar::DWORD OftMetaTransfer::fileChecksum( QFile& file, int bytes ) const
539
Oscar::DWORD checksum = 0xFFFF0000;
540
char data[BUFFER_SIZE];
544
file.open( QIODevice::ReadOnly );
545
while ( (read = file.read( data, BUFFER_SIZE )) > 0 )
547
if ( bytes != -1 && (totalRead + read) >= bytes )
549
read = bytes - totalRead;
550
checksum = chunkChecksum( data, read, checksum, totalRead & 1);
555
checksum = chunkChecksum( data, read, checksum, totalRead & 1);
567
Oscar::DWORD OftMetaTransfer::chunkChecksum( const char *buffer, int bufferSize,
568
Oscar::DWORD checksum, bool shiftIndex ) const
570
//code adapted from Miranda's oft_calc_checksum
571
const int evenIndex = (shiftIndex) ? 1 : 0;
573
checksum = (checksum >> 16) & 0xffff;
574
for ( int i = 0; i < bufferSize; i++ )
576
quint16 val = (uchar)buffer[i];
578
if ( (i & 1) == evenIndex )
583
else // simulate carry
586
checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
587
checksum = ((checksum & 0x0000ffff) + (checksum >> 16));
588
return checksum << 16;
591
#include "oftmetatransfer.moc"