📄 ftp.cc
字号:
/* This file is part of the KDE libraries Copyright (C) 2000 David Faure <faure@kde.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.*/// $Id: ftp.cc,v 1.147.2.2 2002/12/11 22:03:47 waba Exp $#include "ftp.h"#include <sys/types.h>#include <sys/stat.h>#include <sys/socket.h>#ifdef HAVE_SYS_TIME_H#include <sys/time.h>#endif#ifdef HAVE_SYS_SELECT_H#include <sys/select.h>#endif#include <netinet/in.h>#include <assert.h>#include <ctype.h>#include <errno.h>#include <netdb.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#if TIME_WITH_SYS_TIME#include <time.h>#endif#include <qdir.h>#include <kdebug.h>#include <klocale.h>#include <kinstance.h>#include <kmimemagic.h>#include <kmimetype.h>#include <kextsock.h>#include <ksockaddr.h>#include <ksocks.h>#include <kio/ioslave_defaults.h>#include <kio/slaveconfig.h>#define FTP_LOGIN QString::fromLatin1("anonymous")#define FTP_PASSWD QString::fromLatin1("kde-user@kde.org")using namespace KIO;extern "C" { int ftp_kdemain(int argc, char **argv); }int ftp_kdemain( int argc, char **argv ){ KLocale::setMainCatalogue("kdelibs"); KInstance instance( "kio_ftp" ); ( void ) KGlobal::locale(); kdDebug(7102) << "Starting " << getpid() << endl; if (argc != 4) { fprintf(stderr, "Usage: kio_ftp protocol domain-socket1 domain-socket2\n"); exit(-1); } Ftp slave(argv[2], argv[3]); slave.dispatchLoop(); kdDebug(7102) << "Done" << endl; return 0;}Ftp::Ftp( const QCString &pool, const QCString &app ) : SlaveBase( "ftp", pool, app ){ dirfile = 0L; m_extControl = sData = sDatal = 0; sControl = -1; ksControl = NULL; m_bLoggedOn = false; m_bFtpStarted = false; setMultipleAuthCaching( true ); kdDebug(7102) << "Ftp::Ftp()" << endl;}Ftp::~Ftp(){ closeConnection();}/* memccpy appeared first in BSD4.4 */void *mymemccpy(void *dest, const void *src, int c, size_t n){ char *d = (char*)dest; const char *s = (const char*)src; while (n-- > 0) if ((*d++ = *s++) == c) return d; return NULL;}/* * read a line of text * * return -1 on error, bytecount otherwise */int Ftp::ftpReadline(char *buf,int max,netbuf *ctl){ int x,retval = 0; char *end; int eof = 0; if ( max == 0 ) return 0; do { if (ctl->cavail > 0) { x = (max >= ctl->cavail) ? ctl->cavail : max-1; end = (char*)mymemccpy(buf,ctl->cget,'\n',x); if (end != NULL) x = end - buf; retval += x; buf += x; *buf = '\0'; max -= x; ctl->cget += x; ctl->cavail -= x; if (end != NULL) break; } if (max == 1) { *buf = '\0'; break; } if (ctl->cput == ctl->cget) { ctl->cput = ctl->cget = ctl->buf; ctl->cavail = 0; ctl->cleft = FTP_BUFSIZ; } if (eof) { if (retval == 0) retval = -1; break; } if ((x = KSocks::self()->read(ctl->handle,ctl->cput,ctl->cleft)) == -1) { kdError(7102) << "read failed: " << strerror(errno) << endl; retval = -1; break; } if (x == 0) eof = 1; ctl->cleft -= x; ctl->cavail += x; ctl->cput += x; } while (1); return retval;}/** * read a response from the server, into rspbuf * @return first char of response (rspbuf[0]), '\0' if we couldn't read the response */char Ftp::readresp(){ char match[5]; if ( ftpReadline( rspbuf, 256, nControl ) == -1 ) { // This can happen after the server closed the connection (after a timeout) kdWarning(7102) << "Could not read" << endl; //error( ERR_COULD_NOT_READ, QString::null ); return '\0'; } kdDebug(7102) << "resp> " << rspbuf << endl; if ( rspbuf[3] == '-' ) { strncpy( match, rspbuf, 3 ); match[3] = ' '; match[4] = '\0'; do { if ( ftpReadline( rspbuf, 256, nControl ) == -1 ) { kdWarning(7102) << "Could not read" << endl; //error( ERR_COULD_NOT_READ, QString::null ); return '\0'; } kdDebug(7102) << rspbuf << endl; } while ( strncmp( rspbuf, match, 4 ) ); } return rspbuf[0];}void Ftp::closeConnection(){ kdDebug(7102) << "Ftp::closeConnection() m_bLoggedOn=" << m_bLoggedOn << " m_bFtpStarted=" << m_bFtpStarted << endl; if ( m_bLoggedOn || m_bFtpStarted ) { ASSERT( m_bFtpStarted ); // can't see how it could be false is loggedon is true if( sControl != 0 ) { kdDebug(7102) << "Ftp::closeConnection() sending quit" << endl; if ( !ftpSendCmd( "quit", '2' ) ) kdWarning(7102) << "Ftp::closeConnection() 'quit' failed with err=" << rspbuf[0] << rspbuf[1] << rspbuf[2] << endl; free( nControl ); if (ksControl != NULL) delete ksControl; // ::close( sControl ); sControl = 0; } } m_extControl = 0; m_bLoggedOn = false; m_bFtpStarted = false; //ready()}void Ftp::setHost( const QString& _host, int _port, const QString& _user, const QString& _pass ){ kdDebug(7102) << "Ftp::setHost " << _host << endl; QString user = _user; QString pass = _pass; if ( !_user.isEmpty() ) { user = _user; pass = _pass.isEmpty() ? QString::null:_pass; } else { user = FTP_LOGIN; pass = FTP_PASSWD; } m_proxyURL = metaData("UseProxy"); kdDebug(7102) << "Proxy URL: " << m_proxyURL.url() << endl; m_bUseProxy = ( m_proxyURL.isValid() && m_proxyURL.protocol() == QString::fromLatin1("ftp") ); if ( m_host != _host || m_port != _port || m_user != user || m_pass != pass ) closeConnection( ); m_host = _host; m_port = _port; m_user = user; m_pass = pass;}void Ftp::openConnection(){ kdDebug(7102) << "openConnection " << m_host << ":" << m_port << " " << m_user << " [password hidden]" << endl; infoMessage( i18n("Opening connection to host <b>%1</b>").arg(m_host) ); if ( m_host.isEmpty() ) { error( ERR_UNKNOWN_HOST, QString::null ); return; } assert( !m_bLoggedOn ); m_initialPath = QString::null; QString host = m_bUseProxy ? m_proxyURL.host() : m_host; unsigned short int port = m_bUseProxy ? m_proxyURL.port() : m_port; if (!connect( host, port )) return; // error emitted by connect m_bFtpStarted = true; infoMessage( i18n("Connected to host <b>%1</b>").arg(m_host) ); kdDebug(7102) << "Connected ...." << endl; m_bLoggedOn = ftpLogin(); if ( !m_bLoggedOn ) return; // error emitted by ftpLogin connected();}/** * Called by @ref openConnection. It opens the control connection to the ftp server. * * @return true on success. */bool Ftp::connect( const QString &host, unsigned short int port ){ if ( port == 0 ) { struct servent *pse; if ( ( pse = getservbyname( "ftp", "tcp" ) ) == NULL ) port = 21; else port = ntohs(pse->s_port); } int on = 1; // require an Internet Socket ksControl = new KExtendedSocket(host, port, KExtendedSocket::inetSocket); if (ksControl == NULL) { error( ERR_OUT_OF_MEMORY, QString::null ); return false; } if (ksControl->connect() < 0) { if (ksControl->status() == IO_LookupError) error(ERR_UNKNOWN_HOST, host); else error(ERR_COULD_NOT_CONNECT, host); delete ksControl; ksControl = NULL; return false; } sControl = ksControl->fd(); if ( setsockopt( sControl, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on) ) == -1 ) { // ::close( sControl ); delete ksControl; ksControl = NULL; error( ERR_COULD_NOT_CREATE_SOCKET, host ); return false; } nControl = (netbuf*)calloc(1,sizeof(netbuf)); if (nControl == NULL) { // ::close( sControl ); delete ksControl; ksControl = NULL; error( ERR_OUT_OF_MEMORY, QString::null ); return false; } nControl->handle = sControl; if ( readresp() != '2' ) { // ::close( sControl ); delete ksControl; ksControl = NULL; free( nControl ); error( ERR_COULD_NOT_CONNECT, host ); return false; } return true;}/** * Called by @ref openConnection. It logs us in. * @ref m_initialPath is set to the current working directory * if logging on was successfull. * * @return true on success. */bool Ftp::ftpLogin(){ infoMessage( i18n("Sending login information") ); assert( !m_bLoggedOn ); QString user = m_user; QString pass = m_pass; if ( config()->readBoolEntry("EnableAutoLogin") ) { QString au = config()->readEntry("autoLoginUser"); if ( !au.isEmpty() ) { user = au; pass = config()->readEntry("autoLoginPass"); } } kdDebug(7102) << "ftpLogin " << user << endl; if ( !user.isEmpty() ) { AuthInfo info; QCString tempbuf; int failedAuth = 0; // Construct the URL to be used as key for caching. info.url.setProtocol( QString::fromLatin1("ftp") ); info.url.setHost( m_host ); info.url.setPort( m_port ); while ( ++failedAuth ) { // Ask user if we should retry after login failure. if( failedAuth > 2 ) { info.prompt = i18n("Login Failed! Do you want to retry ?"); if( messageBox(QuestionYesNo, info.prompt, i18n("Authorization")) != 3 ) { kdDebug(7102) << "Login aborted by user!" << endl; error( ERR_USER_CANCELED, QString::null); return false; } } // Check the cache and/or prompt user for password if 1st // login attempt failed OR the user supplied a login name, // but no password. if ( failedAuth > 1 || (!user.isEmpty() && pass.isEmpty()) ) { if ( user != FTP_LOGIN && pass != FTP_PASSWD ) info.username = m_user; if ( failedAuth < 3 && checkCachedAuthentication( info ) ) { user = info.username; pass = info.password; } else { info.prompt = i18n("You need to supply a username and a password " "to access this site."); info.commentLabel = i18n( "Site:" ); info.comment = i18n("<b>%1</b>").arg( m_host ); bool disablePassDlg = config()->readBoolEntry( "DisablePassDlg", false ); if ( disablePassDlg || !openPassDlg( info ) ) { error( ERR_USER_CANCELED, m_host ); return false; } else { user = info.username; pass = info.password; } } } tempbuf = "user "; tempbuf += user.latin1(); if ( m_bUseProxy ) { tempbuf += '@'; tempbuf += m_host.latin1(); if ( m_port > 0 && m_port != DEFAULT_FTP_PORT ) { tempbuf += ':'; tempbuf += QString::number(m_port).latin1(); } } kdDebug(7102) << "Sending Login name: " << user << endl; bool loggedIn = (ftpSendCmd( tempbuf, '2' ) && !strncmp( rspbuf, "230", 3)); bool needPass = !strncmp( rspbuf, "331", 3); // Prompt user for login info if we do not // get back a "230" or "331". if ( !loggedIn && !needPass ) { kdDebug(7102) << "1> " << rspbuf << endl; continue; // Well we failed, prompt the user please!! } if( needPass ) { tempbuf = "pass "; tempbuf += pass.latin1(); kdDebug(7102) << "Sending Login password: " << "[protected]" << endl; loggedIn = (ftpSendCmd( tempbuf, '2' ) && !strncmp(rspbuf, "230", 3)); } if ( loggedIn ) { // Do not cache the default login!! if( user != FTP_LOGIN && pass != FTP_PASSWD ) cacheAuthentication( info ); failedAuth = -1; } } } kdDebug(7102) << "Login OK" << endl; infoMessage( i18n("Login OK") ); // Okay, we're logged in. If this is IIS 4, switch dir listing style to Unix: // Thanks to jk@soegaard.net (Jens Kristian S鴊aard) for this hint if( ftpSendCmd( "syst", '2' ) ) { if( !strncmp( rspbuf, "215 Windows_NT version", 22 ) ) // should do for any version { (void)ftpSendCmd( "site dirstyle", '2' ); // Check if it was already in Unix style // Patch from Keith Refson <Keith.Refson@earth.ox.ac.uk> if( !strncmp( rspbuf, "200 MSDOS-like directory output is on", 37 )) //It was in Unix style already! (void)ftpSendCmd( "site dirstyle", '2' ); } } else kdWarning(7102) << "syst failed" << endl; QString macro = metaData( "autoLoginMacro" ); if ( !macro.isEmpty() && config()->readBoolEntry("EnableAutoLoginMacro") ) { QStringList list = QStringList::split('\n', macro); if ( !list.isEmpty() ) { QStringList::Iterator it = list.begin(); for( ; it != list.end() ; ++it ) { if ( (*it).find("init") == 0 ) { list = QStringList::split( '\\', macro); it = list.begin(); ++it; // ignore the macro name for( ; it != list.end() ; ++it ) { // TODO: Add support for arbitrary commands // besides simply changing directory!! if ( (*it).startsWith( "cwd" ) ) ftpSendCmd( (*it).latin1(), '2' ); } break; } } } }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -