📄 ftp.cpp
字号:
// Parse third field if ( strlen( p_date_3 ) == 4 ) // 4 digits, looks like a year tmptr->tm_year = atoi( p_date_3 ) - 1900; else { // otherwise, the year is implicit // according to man ls, this happens when it is between than 6 months // old and 1 hour in the future. // So the year is : current year if tm_mon <= currentMonth+1 // otherwise current year minus one // (The +1 is a security for the "+1 hour" at the end of the month issue) if ( tmptr->tm_mon > currentMonth + 1 ) tmptr->tm_year--; // and p_date_3 contains probably a time char * semicolon; if ( ( semicolon = (char*)strchr( p_date_3, ':' ) ) ) { *semicolon = '\0'; tmptr->tm_min = atoi( semicolon + 1 ); tmptr->tm_hour = atoi( p_date_3 ); } else kWarning(7102) << "Can't parse third field " << p_date_3; } //kDebug(7102) << asctime( tmptr ); de.date = mktime( tmptr ); return true; } } // line invalid, loop to get another line return false;}//===============================================================================// public: get download file from server// helper: ftpGet called from get() and copy()//===============================================================================void Ftp::get( const KUrl & url ){ kDebug(7102) << "Ftp::get " << url.url(); int iError = 0; ftpGet(iError, -1, url, 0); // iError gets status if(iError) // can have only server side errs error(iError, url.path()); ftpCloseCommand(); // must close command!}Ftp::StatusCode Ftp::ftpGet(int& iError, int iCopyFile, const KUrl& url, KIO::fileoffset_t llOffset){ // Calls error() by itself! if( !ftpOpenConnection(loginImplicit) ) return statusServerError; // Try to find the size of the file (and check that it exists at // the same time). If we get back a 550, "File does not exist" // or "not a plain file", check if it is a directory. If it is a // directory, return an error; otherwise simply try to retrieve // the request... if ( !ftpSize( url.path(), '?' ) && (m_iRespCode == 550) && ftpFolder(url.path(), false) ) { // Ok it's a dir in fact kDebug(7102) << "ftpGet: it is a directory in fact"; iError = ERR_IS_DIRECTORY; return statusServerError; } QString resumeOffset = metaData("resume"); if ( !resumeOffset.isEmpty() ) { llOffset = resumeOffset.toLongLong(); kDebug(7102) << "ftpGet: got offset from metadata : " << llOffset; } if( !ftpOpenCommand("retr", url.path(), '?', ERR_CANNOT_OPEN_FOR_READING, llOffset) ) { kWarning(7102) << "ftpGet: Can't open for reading"; return statusServerError; } // Read the size from the response string if(m_size == UnknownSize) { const char* psz = strrchr( ftpResponse(4), '(' ); if(psz) m_size = charToLongLong(psz+1); if (!m_size) m_size = UnknownSize; } KIO::filesize_t bytesLeft = 0; if ( m_size != UnknownSize ) bytesLeft = m_size - llOffset; kDebug(7102) << "ftpGet: starting with offset=" << llOffset; KIO::fileoffset_t processed_size = llOffset; QByteArray array; bool mimetypeEmitted = false; char buffer[maximumIpcSize]; // start with small data chunks in case of a slow data source (modem) // - unfortunately this has a negative impact on performance for large // - files - so we will increase the block size after a while ... int iBlockSize = initialIpcSize; int iBufferCur = 0; while(m_size == UnknownSize || bytesLeft > 0) { // let the buffer size grow if the file is larger 64kByte ... if(processed_size-llOffset > 1024 * 64) iBlockSize = maximumIpcSize; // read the data and detect EOF or error ... if(iBlockSize+iBufferCur > (int)sizeof(buffer)) iBlockSize = sizeof(buffer) - iBufferCur; if (m_data->bytesAvailable() == 0) m_data->waitForReadyRead(); int n = m_data->read( buffer+iBufferCur, iBlockSize ); if(n <= 0) { // this is how we detect EOF in case of unknown size if( m_size == UnknownSize && n == 0 ) break; // unexpected eof. Happens when the daemon gets killed. iError = ERR_COULD_NOT_READ; return statusServerError; } processed_size += n; // collect very small data chunks in buffer before processing ... if(m_size != UnknownSize) { bytesLeft -= n; iBufferCur += n; if(iBufferCur < mimimumMimeSize && bytesLeft > 0) { processedSize( processed_size ); continue; } n = iBufferCur; iBufferCur = 0; } // get the mime type and set the total size ... if(!mimetypeEmitted) { mimetypeEmitted = true; array = QByteArray::fromRawData(buffer, n); KMimeType::Ptr mime = KMimeType::findByNameAndContent(url.fileName(), array); array.clear(); kDebug(7102) << "ftpGet: Emitting mimetype " << mime->name(); mimeType( mime->name() ); if( m_size != UnknownSize ) // Emit total size AFTER mimetype totalSize( m_size ); } // write output file or pass to data pump ... if(iCopyFile == -1) { array = QByteArray::fromRawData(buffer, n); data( array ); array.clear(); } else if( (iError = WriteToFile(iCopyFile, buffer, n)) != 0) return statusClientError; // client side error processedSize( processed_size ); } kDebug(7102) << "ftpGet: done"; if(iCopyFile == -1) // must signal EOF to data pump ... data(array); // array is empty and must be empty! processedSize( m_size == UnknownSize ? processed_size : m_size ); kDebug(7102) << "ftpGet: emitting finished()"; finished(); return statusSuccess;}#if 0 void Ftp::mimetype( const KUrl& url ) { if( !ftpOpenConnection(loginImplicit) ) return; if ( !ftpOpenCommand( "retr", url.path(), 'I', ERR_CANNOT_OPEN_FOR_READING, 0 ) ) { kWarning(7102) << "Can't open for reading"; return; } char buffer[ 2048 ]; QByteArray array; // Get one chunk of data only and send it, KIO::Job will determine the // mimetype from it using KMimeMagic int n = m_data->read( buffer, 2048 ); array.setRawData(buffer, n); data( array ); array.resetRawData(buffer, n); kDebug(7102) << "aborting"; ftpAbortTransfer(); kDebug(7102) << "finished"; finished(); kDebug(7102) << "after finished"; } void Ftp::ftpAbortTransfer() { // RFC 959, page 34-35 // IAC (interpret as command) = 255 ; IP (interrupt process) = 254 // DM = 242 (data mark) char msg[4]; // 1. User system inserts the Telnet "Interrupt Process" (IP) signal // in the Telnet stream. msg[0] = (char) 255; //IAC msg[1] = (char) 254; //IP (void) send(sControl, msg, 2, 0); // 2. User system sends the Telnet "Sync" signal. msg[0] = (char) 255; //IAC msg[1] = (char) 242; //DM if (send(sControl, msg, 2, MSG_OOB) != 2) ; // error... // Send ABOR kDebug(7102) << "send ABOR"; QCString buf = "ABOR\r\n"; if ( KSocks::self()->write( sControl, buf.data(), buf.length() ) <= 0 ) { error( ERR_COULD_NOT_WRITE, QString() ); return; } // kDebug(7102) << "read resp"; if ( readresp() != '2' ) { error( ERR_COULD_NOT_READ, QString() ); return; } kDebug(7102) << "close sockets"; closeSockets(); }#endif//===============================================================================// public: put upload file to server// helper: ftpPut called from put() and copy()//===============================================================================void Ftp::put(const KUrl& url, int permissions, KIO::JobFlags flags){ kDebug(7102) << "Ftp::put " << url.url(); int iError = 0; // iError gets status ftpPut(iError, -1, url, permissions, flags); if(iError) // can have only server side errs error(iError, url.path()); ftpCloseCommand(); // must close command!}Ftp::StatusCode Ftp::ftpPut(int& iError, int iCopyFile, const KUrl& dest_url, int permissions, KIO::JobFlags flags){ if( !ftpOpenConnection(loginImplicit) ) return statusServerError; // Don't use mark partial over anonymous FTP. // My incoming dir allows put but not rename... bool bMarkPartial; if (m_user.isEmpty () || m_user == FTP_LOGIN) bMarkPartial = false; else bMarkPartial = config()->readEntry("MarkPartial", true); QString dest_orig = dest_url.path(); QString dest_part( dest_orig ); dest_part += ".part"; if ( ftpSize( dest_orig, 'I' ) ) { if ( m_size == 0 ) { // delete files with zero size QByteArray cmd = "DELE "; cmd += remoteEncoding()->encode(dest_orig); if( !ftpSendCmd( cmd ) || (m_iRespType != 2) ) { iError = ERR_CANNOT_DELETE_PARTIAL; return statusServerError; } } else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) ) { iError = ERR_FILE_ALREADY_EXIST; return statusServerError; } else if ( bMarkPartial ) { // when using mark partial, append .part extension if ( !ftpRename( dest_orig, dest_part, KIO::Overwrite ) ) { iError = ERR_CANNOT_RENAME_PARTIAL; return statusServerError; } } // Don't chmod an existing file permissions = -1; } else if ( bMarkPartial && ftpSize( dest_part, 'I' ) ) { // file with extension .part exists if ( m_size == 0 ) { // delete files with zero size QByteArray cmd = "DELE "; cmd += remoteEncoding()->encode(dest_part); if ( !ftpSendCmd( cmd ) || (m_iRespType != 2) ) { iError = ERR_CANNOT_DELETE_PARTIAL; return statusServerError; } } else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) ) { flags |= canResume (m_size) ? KIO::Resume : KIO::DefaultFlags; if (!(flags & KIO::Resume)) { iError = ERR_FILE_ALREADY_EXIST; return statusServerError; } } } else m_size = 0; QString dest; // if we are using marking of partial downloads -> add .part extension if ( bMarkPartial ) { kDebug(7102) << "Adding .part extension to " << dest_orig; dest = dest_part; } else dest = dest_orig; KIO::fileoffset_t offset = 0; // set the mode according to offset if( (flags & KIO::Resume) && m_size > 0 ) { offset = m_size; if(iCopyFile != -1) { if( KDE_lseek(iCopyFile, offset, SEEK_SET) < 0 ) { iError = ERR_CANNOT_RESUME; return statusClientError; } } } if (! ftpOpenCommand( "stor", dest, '?', ERR_COULD_NOT_WRITE, offset ) ) return statusServerError; kDebug(7102) << "ftpPut: starting with offset=" << offset; KIO::fileoffset_t processed_size = offset; QByteArray buffer; int result; int iBlockSize = initialIpcSize; // Loop until we got 'dataEnd' do { if(iCopyFile == -1) { dataReq(); // Request for data result = readData( buffer ); } else { // let the buffer size grow if the file is larger 64kByte ... if(processed_size-offset > 1024 * 64) iBlockSize = maximumIpcSize; buffer.resize(iBlockSize); result = ::read(iCopyFile, buffer.data(), buffer.size()); if(result < 0) iError = ERR_COULD_NOT_WRITE; else buffer.resize(result); } if (result > 0) { m_data->write( buffer ); while (m_data->bytesToWrite() && m_data->waitForBytesWritten()) {} processed_size += result; processedSize (processed_size); } } while ( result > 0 ); if (result != 0) // error { ftpCloseCommand(); // don't care about errors kDebug(7102) << "Error during 'put'. Aborting."; if (bMarkPartial) { // Remove if smaller than minimum size if ( ftpSize( dest, 'I' ) && ( processed_size < config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE) ) ) { QByteArray cmd = "DELE "; cmd += remoteEncoding()->encode(dest); (void) ftpSendCmd( cmd ); } } return statusServerError; } if ( !ftpCloseCommand() ) { iError = ERR_COULD_NOT_WRITE; return statusServerError; } // after full download rename the file back to original name if ( bMarkPartial ) { kDebug(7102) << "renaming dest (" << dest << ") back to dest_orig (" << dest_orig << ")"; if ( !ftpRename( dest, dest_orig, KIO::Overwrite ) ) { iError = ERR_CANNOT_RENAME_PARTIAL; return statusServerError; } } // set final permissions if ( permissions != -1 ) { if ( m_user == FTP_LOGIN ) kDebug(7102) << "Trying to chmod over anonymous FTP ???"; // chmod the file we just put if ( ! ftpChmod( dest_orig, permissions ) ) { // To be tested //if ( m_user != FTP_LOGIN ) // warning( i18n( "Could not change permissions for\n%1" ).arg( dest_orig ) ); } } /
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -