📄 q3ftp.cpp
字号:
p |= QUrlInfo::WriteGroup; if ( perms[ group ][ executable ] ) p |= QUrlInfo::ExeGroup; if ( perms[ other ][ readable ] ) p |= QUrlInfo::ReadOther; if ( perms[ other ][ writable ] ) p |= QUrlInfo::WriteOther; if ( perms[ other ][ executable ] ) p |= QUrlInfo::ExeOther; info->setPermissions( p ); // size tmp = lst[ 4 ]; info->setSize( tmp.toInt() ); // date and time QTime time; QString dateStr; dateStr += QLatin1String("Sun "); lst[ 5 ][ 0 ] = lst[ 5 ][ 0 ].upper(); dateStr += lst[ 5 ]; dateStr += QLatin1Char(' '); dateStr += lst[ 6 ]; dateStr += QLatin1Char(' '); if ( lst[ 7 ].contains( QLatin1String(":") ) ) { time = QTime( lst[ 7 ].left( 2 ).toInt(), lst[ 7 ].right( 2 ).toInt() ); dateStr += QString::number( QDate::currentDate().year() ); } else { dateStr += lst[ 7 ]; } QDate date = QDate::fromString( dateStr ); info->setLastModified( QDateTime( date, time ) ); if ( lst[ 7 ].contains( QLatin1String(":") ) ) { const int futureTolerance = 600; if( info->lastModified().secsTo( QDateTime::currentDateTime() ) < -futureTolerance ) { QDateTime dt = info->lastModified(); QDate d = dt.date(); d.setYMD(d.year()-1, d.month(), d.day()); dt.setDate(d); info->setLastModified(dt); } } // name if ( info->isSymLink() ) info->setName( lst[ 8 ].stripWhiteSpace() ); else { QString n; for ( uint i = 8; i < (uint) lst.count(); ++i ) n += lst[ i ] + QLatin1String(" "); n = n.stripWhiteSpace(); info->setName( n ); } return true;}void Q3FtpDTP::socketConnected(){#if !defined (Q_WS_QWS) // Use a large send buffer to reduce the number // of writeBlocks when download and uploading files. // The actual size used here (128k) is default on most // Unixes. socket.socketDevice()->setSendBufferSize(128 * 1024); socket.socketDevice()->setReceiveBufferSize(128 * 1024);#endif bytesDone = 0;#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP::connectState( CsConnected )" );#endif emit connectState( Q3FtpDTP::CsConnected );}void Q3FtpDTP::socketReadyRead(){ if ( pi->currentCommand().isEmpty() ) { socket.close();#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP::connectState( CsClosed )" );#endif emit connectState( Q3FtpDTP::CsClosed ); return; } if ( pi->currentCommand().startsWith(QLatin1String("LIST")) ) { while ( socket.canReadLine() ) { QUrlInfo i; QString line = QLatin1String(socket.readLine());#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP read (list): '%s'", line.latin1() );#endif if ( parseDir( line, QLatin1String(""), &i ) ) { emit listInfo( i ); } else { // some FTP servers don't return a 550 if the file or directory // does not exist, but rather write a text to the data socket // -- try to catch these cases if ( line.endsWith( QLatin1String("No such file or directory\r\n") ) ) err = line; } } } else { if ( !is_ba && data.dev ) { QByteArray ba( socket.bytesAvailable() ); Q_LONG bytesRead = socket.readBlock( ba.data(), ba.size() ); if ( bytesRead < 0 ) { // ### error handling return; } ba.resize( bytesRead ); bytesDone += bytesRead;#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP read: %d bytes (total %d bytes)", (int)bytesRead, bytesDone );#endif emit dataTransferProgress( bytesDone, bytesTotal ); if (data.dev) // make sure it wasn't deleted in the slot data.dev->writeBlock( ba ); } else {#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP readyRead: %d bytes available (total %d bytes read)", (int)bytesAvailable(), bytesDone );#endif emit dataTransferProgress( bytesDone+socket.bytesAvailable(), bytesTotal ); emit readyRead(); } }}void Q3FtpDTP::socketError( int e ){ if ( e == Q3Socket::ErrHostNotFound ) {#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP::connectState( CsHostNotFound )" );#endif emit connectState( Q3FtpDTP::CsHostNotFound ); } else if ( e == Q3Socket::ErrConnectionRefused ) {#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP::connectState( CsConnectionRefused )" );#endif emit connectState( Q3FtpDTP::CsConnectionRefused ); }}void Q3FtpDTP::socketConnectionClosed(){ if ( !is_ba && data.dev ) { clearData(); }#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP::connectState( CsClosed )" );#endif emit connectState( Q3FtpDTP::CsClosed );}void Q3FtpDTP::socketBytesWritten( int bytes ){ bytesDone += bytes;#if defined(Q3FTPDTP_DEBUG) qDebug( "Q3FtpDTP::bytesWritten( %d )", bytesDone );#endif emit dataTransferProgress( bytesDone, bytesTotal ); if ( callWriteData ) writeData();}/********************************************************************** * * Q3FtpPI implemenatation * *********************************************************************/Q3FtpPI::Q3FtpPI( QObject *parent ) : QObject( parent ), rawCommand(false), dtp( this ), commandSocket( 0, "Q3FtpPI_socket" ), state( Begin ), abortState( None ), currentCmd( QString() ), waitForDtpToConnect( false ), waitForDtpToClose( false ){ connect( &commandSocket, SIGNAL(hostFound()), SLOT(hostFound()) ); connect( &commandSocket, SIGNAL(connected()), SLOT(connected()) ); connect( &commandSocket, SIGNAL(connectionClosed()), SLOT(connectionClosed()) ); connect( &commandSocket, SIGNAL(delayedCloseFinished()), SLOT(delayedCloseFinished()) ); connect( &commandSocket, SIGNAL(readyRead()), SLOT(readyRead()) ); connect( &commandSocket, SIGNAL(error(int)), SLOT(error(int)) ); connect( &dtp, SIGNAL(connectState(int)), SLOT(dtpConnectState(int)) );}void Q3FtpPI::connectToHost( const QString &host, Q_UINT16 port ){ emit connectState( Q3Ftp::HostLookup ); commandSocket.connectToHost( host, port );}/* Sends the sequence of commands \a cmds to the FTP server. When the commands are all done the finished() signal is emitted. When an error occurs, the error() signal is emitted. If there are pending commands in the queue this functions returns false and the \a cmds are not added to the queue; otherwise it returns true.*/bool Q3FtpPI::sendCommands( const QStringList &cmds ){ if ( !pendingCommands.isEmpty() ) return false; if ( commandSocket.state()!=Q3Socket::Connected || state!=Idle ) { emit error( Q3Ftp::NotConnected, QFtp::tr( "Not connected" ) ); return true; // there are no pending commands } pendingCommands = cmds; startNextCmd(); return true;}void Q3FtpPI::clearPendingCommands(){ pendingCommands.clear(); dtp.abortConnection(); currentCmd.clear(); state = Idle;}void Q3FtpPI::abort(){ pendingCommands.clear(); if ( abortState != None ) // ABOR already sent return; abortState = AbortStarted;#if defined(Q3FTPPI_DEBUG) qDebug( "Q3FtpPI send: ABOR" );#endif commandSocket.writeBlock( "ABOR\r\n", 6 ); if ( currentCmd.startsWith(QLatin1String("STOR ")) ) dtp.abortConnection();}void Q3FtpPI::hostFound(){ emit connectState( Q3Ftp::Connecting );}void Q3FtpPI::connected(){ state = Begin;#if defined(Q3FTPPI_DEBUG)// qDebug( "Q3FtpPI state: %d [connected()]", state );#endif emit connectState( Q3Ftp::Connected );}void Q3FtpPI::connectionClosed(){ commandSocket.close(); emit connectState( Q3Ftp::Unconnected );}void Q3FtpPI::delayedCloseFinished(){ emit connectState( Q3Ftp::Unconnected );}void Q3FtpPI::error( int e ){ if ( e == Q3Socket::ErrHostNotFound ) { emit connectState( Q3Ftp::Unconnected ); emit error( Q3Ftp::HostNotFound, QFtp::tr( "Host %1 not found" ).arg( commandSocket.peerName() ) ); } else if ( e == Q3Socket::ErrConnectionRefused ) { emit connectState( Q3Ftp::Unconnected ); emit error( Q3Ftp::ConnectionRefused, QFtp::tr( "Connection refused to host %1" ).arg( commandSocket.peerName() ) ); }}void Q3FtpPI::readyRead(){ if ( waitForDtpToClose ) return; while ( commandSocket.canReadLine() ) { // read line with respect to line continuation QString line = QLatin1String(commandSocket.readLine()); if ( replyText.isEmpty() ) { if ( line.length() < 3 ) { // ### protocol error return; } const int lowerLimit[3] = {1,0,0}; const int upperLimit[3] = {5,5,9}; for ( int i=0; i<3; i++ ) { replyCode[i] = line[i].digitValue(); if ( replyCode[i]<lowerLimit[i] || replyCode[i]>upperLimit[i] ) { // ### protocol error return; } } } QString endOfMultiLine; endOfMultiLine[0] = '0' + replyCode[0]; endOfMultiLine[1] = '0' + replyCode[1]; endOfMultiLine[2] = '0' + replyCode[2]; endOfMultiLine[3] = ' '; QString lineCont( endOfMultiLine ); lineCont[3] = '-'; QString lineLeft4 = line.left(4); while ( lineLeft4 != endOfMultiLine ) { if ( lineLeft4 == lineCont ) replyText += line.mid( 4 ); // strip 'xyz-' else replyText += line; if ( !commandSocket.canReadLine() ) return; line = QLatin1String(commandSocket.readLine()); lineLeft4 = line.left(4); } replyText += line.mid( 4 ); // strip reply code 'xyz ' if ( replyText.endsWith(QLatin1String("\r\n")) ) replyText.truncate( replyText.length()-2 ); if ( processReply() ) replyText = QLatin1String(""); }}/* Process a reply from the FTP server. Returns true if the reply was processed or false if the reply has to be processed at a later point.*/bool Q3FtpPI::processReply(){#if defined(Q3FTPPI_DEBUG)// qDebug( "Q3FtpPI state: %d [processReply() begin]", state ); if ( replyText.length() < 400 ) qDebug( "Q3FtpPI recv: %d %s", 100*replyCode[0]+10*replyCode[1]+replyCode[2], replyText.latin1() ); else qDebug( "Q3FtpPI recv: %d (text skipped)", 100*replyCode[0]+10*replyCode[1]+replyCode[2] );#endif // process 226 replies ("Closing Data Connection") only when the data // connection is really closed to avoid short reads of the DTP if ( 100*replyCode[0]+10*replyCode[1]+replyCode[2] == 226 ) { if ( dtp.socketState() != Q3Socket::Idle ) { waitForDtpToClose = true; return false; } } switch ( abortState ) { case AbortStarted: abortState = WaitForAbortToFinish; break; case WaitForAbortToFinish: abortState = None; return true; default: break; } // get new state static const State table[5] = { /* 1yz 2yz 3yz 4yz 5yz */ Waiting, Success, Idle, Failure, Failure }; switch ( state ) { case Begin: if ( replyCode[0] == 1 ) { return true; } else if ( replyCode[0] == 2 ) { state = Idle; emit finished( QFtp::tr( "Connected to host %1" ).arg( commandSocket.peerName() ) ); break; } // ### error handling return true; case Waiting: if ( replyCode[0]<0 || replyCode[0]>5 ) state = Failure; else#if defined(Q_OS_IRIX) && defined(Q_CC_GNU) { // work around a crash on 64 bit gcc IRIX State *t = (State *) table; state = t[replyCode[0] - 1]; }#else state = table[replyCode[0] - 1];#endif break; default: // ### spontaneous message return true; }#if defined(Q3FTPPI_DEBUG)// qDebug( "Q3FtpPI state: %d [processReply() intermediate]", state );#endif // special actions on certain replies int replyCodeInt = 100*replyCode[0] + 10*replyCode[1] + replyCode[2]; emit rawFtpReply( replyCodeInt, replyText ); if ( rawCommand ) { rawCommand = false; } else if ( replyCodeInt == 227 ) { // 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2) // rfc959 does not define this response precisely, and gives // both examples where the parenthesis are used, and where // they are missing. We need to scan for the address and host // info. QRegExp addrPortPattern(QLatin1String("(\\d+),(\\d+),(\\d+),(\\d+),(\\d+),(\\d+)")); if (addrPortPattern.search(replyText) == -1) {#if defined(Q3FTPPI_DEBUG) qDebug( "Q3Ftp: bad 227 response -- address and port information missing" );#endif // ### error handling } else { QStringList lst = addrPortPattern.capturedTexts(); QString host = lst[1] + QLatin1String(".") + lst[2] + QLatin1String(".") + lst[3] + QLatin1String(".") + lst[4]; Q_UINT16 port = ( lst[5].toUInt() << 8 ) + lst[6].toUInt(); waitForDtpToConnect = true; dtp.connectToHost( host, port ); } } else if ( replyCodeInt == 230 ) { if ( currentCmd.startsWith(QLatin1String("USER ")) && pendingCommands.count()>0 && pendingCommands.first().startsWith(QLatin1String("PASS ")) ) { // no need to send the PASS -- we are already logged in pendingCommands.pop_front(); } // 230 User logged in, proceed. emit connectState( Q3Ftp::LoggedIn ); } else if ( replyCodeInt == 213 ) { // 213 File status. if ( currentCmd.startsWith(QLatin1String("SIZE ")) ) dtp.setBytesTotal( replyText.simplifyWhiteSpace().toInt() ); } else if ( replyCode[0]==1 && currentCmd.startsWith(QLatin1String("STOR ")) ) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -