📄 ftp.cpp
字号:
bool protpSucc = (ftpSendCmd("PROT P") && (m_iRespType == 2)); if (!protpSucc) { // Set the data channel to clear (should not be necessary, just in case). ftpSendCmd("PROT C"); return false; } return true;}/* * ftpOpenDataConnection - set up data connection * * The routine calls several ftpOpenXxxxConnection() helpers to find * the best connection mode. If a helper cannot connect if returns * ERR_INTERNAL - so this is not really an error! All other error * codes are treated as fatal, e.g. they are passed back to the caller * who is responsible for calling error(). ftpOpenPortDataConnection * can be called as last try and it does never return ERR_INTERNAL. * * @return 0 if successful, err code otherwise */int Ftp::ftpOpenDataConnection(){ // make sure that we are logged on and have no data connection... assert( m_bLoggedOn ); ftpCloseDataConnection(); int iErrCode = 0; int iErrCodePASV = 0; // Remember error code from PASV // First try passive (EPSV & PASV) modes if ( !config()->readEntry("DisablePassiveMode", false) ) { iErrCode = ftpOpenPASVDataConnection(); if(iErrCode == 0) { // success requestDataEncryption(); return 0; } iErrCodePASV = iErrCode; ftpCloseDataConnection(); if ( !config()->readEntry("DisableEPSV", false) ) { iErrCode = ftpOpenEPSVDataConnection(); if(iErrCode == 0) { // success requestDataEncryption(); return 0; } ftpCloseDataConnection(); } // if we sent EPSV ALL already and it was accepted, then we can't // use active connections any more if (m_extControl & epsvAllSent) return iErrCodePASV ? iErrCodePASV : iErrCode; } // fall back to port mode iErrCode = ftpOpenPortDataConnection(); if(iErrCode == 0) { // success requestDataEncryption(); return 0; } ftpCloseDataConnection(); // prefer to return the error code from PASV if any, since that's what should have worked in the first place return iErrCodePASV ? iErrCodePASV : iErrCode;}/* * ftpOpenPortDataConnection - set up data connection * * @return 0 if successful, err code otherwise (but never ERR_INTERNAL * because this is the last connection mode that is tried) */int Ftp::ftpOpenPortDataConnection(){ assert(m_control != NULL); // must have control connection socket assert(m_data == NULL); // ... but no data connection m_bPasv = false; if (m_extControl & eprtUnknown) return ERR_INTERNAL; //QTcpServer *server = new SslServer(); SslServer *server = new SslServer(); //KSocketFactory::listen("ftp-data"); server = new SslServer(); server->setProxy(KSocketFactory::proxyForListening("ftp-data")); server->listen(); if (!server->isListening()) { delete server; return ERR_COULD_NOT_LISTEN; } server->setMaxPendingConnections(1); QString command; QHostAddress localAddress = m_control->localAddress(); if (localAddress.protocol() == QAbstractSocket::IPv4Protocol) { struct { quint32 ip4; quint16 port; } data; data.ip4 = localAddress.toIPv4Address(); data.port = server->serverPort(); unsigned char *pData = reinterpret_cast<unsigned char*>(&data); command.sprintf("PORT %d,%d,%d,%d,%d,%d",pData[0],pData[1],pData[2],pData[3],pData[4],pData[5]); } else if (localAddress.protocol() == QAbstractSocket::IPv6Protocol) { command = QString("EPRT |2|%2|%3|").arg(localAddress.toString()).arg(server->serverPort()); } if( ftpSendCmd(command.toLatin1()) && (m_iRespType == 2) ) return 0; { server->waitForNewConnection(connectTimeout() * 1000); m_data = server->socket(); //m_data = server->nextPendingConnection(); delete server; return m_data ? 0 : ERR_COULD_NOT_CONNECT; } delete server; return ERR_INTERNAL;}bool Ftp::ftpOpenCommand( const char *_command, const QString & _path, char _mode, int errorcode, KIO::fileoffset_t _offset ){ int errCode = 0; if( !ftpDataMode(_mode) ) errCode = ERR_COULD_NOT_CONNECT; else errCode = ftpOpenDataConnection(); if(errCode != 0) { error(errCode, m_host); return false; } bool useDataEnc = requestDataEncryption(); if ( _offset > 0 ) { // send rest command if offset > 0, this applies to retr and stor commands char buf[100]; sprintf(buf, "rest %lld", _offset); if ( !ftpSendCmd( buf ) ) return false; if( m_iRespType != 3 ) { error( ERR_CANNOT_RESUME, _path ); // should never happen return false; } } QByteArray tmp = _command; QString errormessage; if ( !_path.isEmpty() ) { tmp += ' '; tmp += remoteEncoding()->encode(_path); } if( !ftpSendCmd( tmp ) || (m_iRespType != 1) ) { if( _offset > 0 && strcmp(_command, "retr") == 0 && (m_iRespType == 4) ) errorcode = ERR_CANNOT_RESUME; // The error here depends on the command errormessage = _path; } else { // Only now we know for sure that we can resume if ( _offset > 0 && strcmp(_command, "retr") == 0 ) canResume(); m_bBusy = true; // cleared in ftpCloseCommand if (useDataEnc) { int result = encryptDataChannel(); if (result != 0) { error(result, "TLS Negotiation failed on the data channel."); return false; } } return true; } error(errorcode, errormessage); return false;}bool Ftp::ftpCloseCommand(){ // first close data sockets (if opened), then read response that // we got for whatever was used in ftpOpenCommand ( should be 226 ) if(m_data) { delete m_data; m_data = NULL; } if(!m_bBusy) return true; kDebug(7102) << "ftpCloseCommand: reading command result"; m_bBusy = false; if(!ftpResponse(-1) || (m_iRespType != 2) ) { kDebug(7102) << "ftpCloseCommand: no transfer complete message"; return false; } return true;}void Ftp::mkdir( const KUrl & url, int permissions ){ if( !ftpOpenConnection(loginImplicit) ) return; QString path = remoteEncoding()->encode(url); QByteArray buf = "mkd "; buf += remoteEncoding()->encode(path); if( !ftpSendCmd( buf ) || (m_iRespType != 2) ) { QString currentPath( m_currentPath ); // Check whether or not mkdir failed because // the directory already exists... if( ftpFolder( path, false ) ) { error( ERR_DIR_ALREADY_EXIST, path ); // Change the directory back to what it was... (void) ftpFolder( currentPath, false ); return; } error( ERR_COULD_NOT_MKDIR, path ); return; } if ( permissions != -1 ) { // chmod the dir we just created, ignoring errors. (void) ftpChmod( path, permissions ); } finished();}void Ftp::rename( const KUrl& src, const KUrl& dst, KIO::JobFlags flags ){ if( !ftpOpenConnection(loginImplicit) ) return; // The actual functionality is in ftpRename because put needs it if ( ftpRename( src.path(), dst.path(), flags ) ) finished(); else error( ERR_CANNOT_RENAME, src.path() );}bool Ftp::ftpRename( const QString & src, const QString & dst, KIO::JobFlags ){ // TODO honor overwrite assert( m_bLoggedOn ); int pos = src.lastIndexOf("/"); if( !ftpFolder(src.left(pos+1), false) ) return false; QByteArray from_cmd = "RNFR "; from_cmd += remoteEncoding()->encode(src.mid(pos+1)); if( !ftpSendCmd( from_cmd ) || (m_iRespType != 3) ) return false; QByteArray to_cmd = "RNTO "; to_cmd += remoteEncoding()->encode(dst); if( !ftpSendCmd( to_cmd ) || (m_iRespType != 2) ) return false; return true;}void Ftp::del( const KUrl& url, bool isfile ){ if( !ftpOpenConnection(loginImplicit) ) return; // When deleting a directory, we must exit from it first // The last command probably went into it (to stat it) if ( !isfile ) ftpFolder(remoteEncoding()->directory(url), false); // ignore errors QByteArray cmd = isfile ? "DELE " : "RMD "; cmd += remoteEncoding()->encode(url); if( !ftpSendCmd( cmd ) || (m_iRespType != 2) ) error( ERR_CANNOT_DELETE, url.path() ); else finished();}bool Ftp::ftpChmod( const QString & path, int permissions ){ assert( m_bLoggedOn ); if(m_extControl & chmodUnknown) // previous errors? return false; // we need to do bit AND 777 to get permissions, in case // we were sent a full mode (unlikely) QString cmd = QString::fromLatin1("SITE CHMOD ") + QString::number( permissions & 511, 8 /*octal*/ ) + ' '; cmd += path; ftpSendCmd(remoteEncoding()->encode(cmd)); if(m_iRespType == 2) return true; if(m_iRespCode == 500) { m_extControl |= chmodUnknown; kDebug(7102) << "ftpChmod: CHMOD not supported - disabling"; } return false;}void Ftp::chmod( const KUrl & url, int permissions ){ if( !ftpOpenConnection(loginImplicit) ) return; if ( !ftpChmod( url.path(), permissions ) ) error( ERR_CANNOT_CHMOD, url.path() ); else finished();}void Ftp::ftpCreateUDSEntry( const QString & filename, FtpEntry& ftpEnt, UDSEntry& entry, bool isDir ){ assert(entry.count() == 0); // by contract :-) entry.insert( KIO::UDSEntry::UDS_NAME, filename ); entry.insert( KIO::UDSEntry::UDS_SIZE, ftpEnt.size ); entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, ftpEnt.date ); entry.insert( KIO::UDSEntry::UDS_ACCESS, ftpEnt.access ); entry.insert( KIO::UDSEntry::UDS_USER, ftpEnt.owner ); if ( !ftpEnt.group.isEmpty() ) { entry.insert( KIO::UDSEntry::UDS_GROUP, ftpEnt.group ); } if ( !ftpEnt.link.isEmpty() ) { entry.insert( KIO::UDSEntry::UDS_LINK_DEST, ftpEnt.link ); KMimeType::Ptr mime = KMimeType::findByUrl( KUrl("ftps://host/" + filename ) ); // Links on ftp sites are often links to dirs, and we have no way to check // that. Let's do like Netscape : assume dirs generally. // But we do this only when the mimetype can't be known from the filename. // --> we do better than Netscape :-) if ( mime->name() == KMimeType::defaultMimeType() ) { kDebug(7102) << "Setting guessed mime type to inode/directory for " << filename; entry.insert( KIO::UDSEntry::UDS_GUESSED_MIME_TYPE, QString::fromLatin1( "inode/directory" ) ); isDir = true; } } entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : ftpEnt.type ); // entry.insert KIO::UDSEntry::UDS_ACCESS_TIME,buff.st_atime); // entry.insert KIO::UDSEntry::UDS_CREATION_TIME,buff.st_ctime);}void Ftp::ftpShortStatAnswer( const QString& filename, bool isDir ){ UDSEntry entry; entry.insert( KIO::UDSEntry::UDS_NAME, filename ); entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : S_IFREG ); entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH ); // No details about size, ownership, group, etc. statEntry(entry); finished();}void Ftp::ftpStatAnswerNotFound( const QString & path, const QString & filename ){ // Only do the 'hack' below if we want to download an existing file (i.e. when looking at the "source") // When e.g. uploading a file, we still need stat() to return "not found" // when the file doesn't exist. QString statSide = metaData("statSide"); kDebug(7102) << "Ftp::stat statSide=" << statSide; if ( statSide == "source" ) { kDebug(7102) << "Not found, but assuming found, because some servers don't allow listing"; // MS Server is incapable of handling "list <blah>" in a case insensitive way // But "retr <blah>" works. So lie in stat(), to get going... // // There's also the case of ftp://ftp2.3ddownloads.com/90380/linuxgames/loki/patches/ut/ut-patch-436.run // where listing permissions are denied, but downloading is still possible. ftpShortStatAnswer( filename, false /*file, not dir*/ ); return; } error( ERR_DOES_NOT_EXIST, path );}void Ftp::stat( const KUrl &url){ kDebug(7102) << "Ftp::stat : path='" << url.path() << "'"; if( !ftpOpenConnection(loginImplicit) ) return; QString path = QDir::cleanPath( url.path() );
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -