📄 ftp.cc
字号:
// Get the current working directory kdDebug(7102) << "Searching for pwd" << endl; if ( !ftpSendCmd( "pwd", '2' ) ) { kdDebug(7102) << "Couldn't issue pwd command" << endl; error( ERR_COULD_NOT_LOGIN, i18n("Could not login to %1.").arg(m_host) ); // or anything better ? return false; } kdDebug(7102) << "2> " << rspbuf << endl; char *p = strchr( rspbuf+3, '"' ); // Look for first " if ( p != 0 ) { char *p2 = strchr( p + 1, '"' ); // Look for second " if ( p2 != 0 ) { *p2 = '\0'; m_initialPath = p + 1; if ( *(p+1) != '/' ) // safety check, for servers that return C:/TEMP/ m_initialPath.prepend('/'); kdDebug(7102) << "Initial path set to: " << m_initialPath << endl; } } return true;}/** * ftpSendCmd - send a command (@p cmd) and read response * * @param expresp the expected first char. '\001' for no check * @param maxretries number of time it should retry. Since it recursively * calls itself if it can't read the answer (this happens especially after * timeouts), we need to limit the recursiveness ;-) * * return true if proper response received, false on error * or if @p expresp doesn't match */bool Ftp::ftpSendCmd( const QCString& cmd, char expresp, int maxretries ){ assert( sControl > 0 ); QCString buf = cmd; buf += "\r\n"; if ( cmd.left(4).lower() != "pass" ) // don't print out the password kdDebug(7102) << cmd.data() << endl; if ( KSocks::self()->write( sControl, buf.data(), buf.length() ) <= 0 ) { error( ERR_COULD_NOT_WRITE, QString::null ); return false; } char rsp = readresp(); if (!rsp || ( rsp == '4' && rspbuf[1] == '2' && rspbuf[2] == '1' )) { kdDebug() << "got 421 -> timeout" << endl; // 421 is "421 No Transfer Timeout (300 seconds): closing control connection" if ( cmd=="list" && maxretries > 0 ) // Only retry for "list". retr/stor/... need to redo the whole thing { // It might mean a timeout occured, let's try logging in again m_bLoggedOn = false; kdDebug(7102) << "Couldn't read answer - perhaps timeout - trying logging in again" << endl; openConnection(); if (!m_bLoggedOn) { kdDebug(7102) << "Login failure, aborting" << endl; return false; } kdDebug(7102) << "Logged back in, reissuing command" << endl; // On success, try the command again return ftpSendCmd( cmd, expresp, maxretries - 1 ); } else { error( ERR_SERVER_TIMEOUT, m_host ); return false; } } return (expresp == 0) || (rsp == expresp);}/* * ftpOpenPASVDataConnection - set up data connection, using PASV mode * * return 1 if successful, 0 otherwise * doesn't set error message, since non-pasv mode will always be tried if * this one fails */bool Ftp::ftpOpenPASVDataConnection(){ int i[6], j; unsigned char n[6]; int on=1; struct linger lng = { 1, 120 }; KExtendedSocket ks; const KSocketAddress *sa = ksControl->peerAddress(); QString host; // Check that we can do PASV if (sa != NULL && sa->family() != PF_INET) return false; // no PASV for non-PF_INET connections m_bPasv = true; /* Let's PASsiVe*/ if (!(ftpSendCmd("PASV",'2'))) { return false; } // The usual answer is '227 Entering Passive Mode. (160,39,200,55,6,245)' // but anonftpd gives '227 =160,39,200,55,6,245' char *start = strchr(rspbuf,'('); if ( !start ) start = strchr(rspbuf,'='); if ( !start || ( sscanf(start, "(%d,%d,%d,%d,%d,%d)",&i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 && sscanf(start, "=%d,%d,%d,%d,%d,%d", &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 ) ) { kdError(7102) << "parsing IP and port numbers failed. String parsed: " << start << endl; return false; } for (j=0; j<6; j++) { n[j] = (unsigned char) (i[j] & 0xff); } // Make hostname host.sprintf("%d.%d.%d.%d", i[0], i[1], i[2], i[3]); // port number is given in network byte order ks.setAddress(host, ntohs(i[5] << 8 | i[4])); ks.setSocketFlags(KExtendedSocket::noResolve); if (ks.connect() < 0) { return false; } sDatal = ks.fd(); if ( (setsockopt( sDatal,SOL_SOCKET,SO_REUSEADDR,(char*)&on, sizeof(on) ) == -1) || (sDatal < 0) ) { return false; } if ( setsockopt(sDatal, SOL_SOCKET,SO_KEEPALIVE, (char *) &on, (int) sizeof(on)) < 0 ) kdError(7102) << "Keepalive not allowed" << endl; if ( setsockopt(sDatal, SOL_SOCKET,SO_LINGER, (char *) &lng,(int) sizeof (lng)) < 0 ) kdError(7102) << "Linger mode was not allowed." << endl; ks.release(); return true;}/* * ftpOpenEPSVDataConnection - opens a data connection via EPSV */bool Ftp::ftpOpenEPSVDataConnection(){ // for SO_LINGER int on=1; struct linger lng = { 1, 120 }; KExtendedSocket ks; const KSocketAddress *sa = ksControl->peerAddress(); int portnum; // we are sure sa is a KInetSocketAddress, because we asked for KExtendedSocket::inetSocket // when we connected const KInetSocketAddress *sin = static_cast<const KInetSocketAddress*>(sa); if (m_extControl & epsvUnknown || sa == NULL) return false; m_bPasv = true; if (!(ftpSendCmd("EPSV", '2'))) { // unknown command? if (rspbuf[0] == '5') { kdDebug(7102) << "disabling use of EPSV" << endl; m_extControl |= epsvUnknown; } return false; } char *start = strchr(rspbuf,'|'); if ( !start || sscanf(rspbuf, "|||%d|", &portnum) != 1) { // invalid response? return false; } ks.setSocketFlags(KExtendedSocket::noResolve); ks.setAddress(sin->nodeName(), portnum); if (ks.connect() < 0) { return false; } sDatal = ks.fd(); if ( (setsockopt( sDatal,SOL_SOCKET,SO_REUSEADDR,(char*)&on, sizeof(on) ) == -1) || (sDatal < 0) ) { return false; } if ( setsockopt(sDatal, SOL_SOCKET,SO_KEEPALIVE, (char *) &on, (int) sizeof(on)) < 0 ) kdError(7102) << "Keepalive not allowed" << endl; if ( setsockopt(sDatal, SOL_SOCKET,SO_LINGER, (char *) &lng,(int) sizeof (lng)) < 0 ) kdError(7102) << "Linger mode was not allowed." << endl; ks.release(); return true;}/* * ftpOpenEPRTDataConnection */bool Ftp::ftpOpenEPRTDataConnection(){ KExtendedSocket ks; // yes, we are sure this is a KInetSocketAddress const KInetSocketAddress *sin = static_cast<const KInetSocketAddress*>(ksControl->localAddress()); m_bPasv = false; if (m_extControl & eprtUnknown || sin == NULL) return false; ks.setHost(sin->nodeName()); ks.setPort(0); // setting port to 0 will make us bind to a random, free port ks.setSocketFlags(KExtendedSocket::noResolve | KExtendedSocket::passiveSocket | KExtendedSocket::inetSocket); if (ks.listen(1) < 0) { error(ERR_COULD_NOT_LISTEN, m_host); return false; } sin = static_cast<const KInetSocketAddress*>(ks.localAddress()); if (sin == NULL) // error ? return false; // QString command = QString::fromLatin1("eprt |%1|%2|%3|").arg(sin->ianaFamily()) // .arg(sin->nodeName()) // .arg(sin->port()); QCString command; command.sprintf("eprt |%d|%s|%d|", sin->ianaFamily(), sin->nodeName().latin1(), sin->port()); // FIXME! Encoding for hostnames? if (!ftpSendCmd(command, '2')) { // unknown command? if (rspbuf[0] == '5') { kdDebug(7102) << "disabling use of EPRT" << endl; m_extControl |= eprtUnknown; } return false; } sDatal = ks.fd(); ks.release(); return true;}/* * ftpOpenDataConnection - set up data connection * * return 1 if successful, 0 otherwise */bool Ftp::ftpOpenDataConnection(){ assert( m_bLoggedOn ); union { struct sockaddr sa; struct sockaddr_in in; } sin; struct linger lng = { 0, 0 }; socklen_t l; char buf[64]; int on = 1; ////////////// First try passive (EPSV & PASV) modes if ( config()->readBoolEntry( "DisablePassiveMode", false ) == false ) { if (ftpOpenEPSVDataConnection()) return true; if (ftpOpenPASVDataConnection()) return true; // if we sent EPSV ALL already and it was accepted, then we can't // use active connections any more if (m_extControl & epsvAllSent) return false; if (ftpOpenEPRTDataConnection()) return true; } ////////////// Fallback : PORT mode m_bPasv = false; l = sizeof(sin); if ( getsockname( sControl, &sin.sa, &l ) < 0 ) return false; if (sin.sa.sa_family != PF_INET) return false; // wrong family sDatal = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP ); if ( sDatal == 0 ) { error( ERR_COULD_NOT_CREATE_SOCKET, QString::null ); return false; } if ( setsockopt( sDatal, SOL_SOCKET, SO_REUSEADDR, (char*)&on, sizeof(on) ) == -1 ) { ::close( sDatal ); error( ERR_COULD_NOT_CREATE_SOCKET, QString::null ); return false; } if ( setsockopt( sDatal, SOL_SOCKET, SO_LINGER, (char*)&lng, sizeof(lng) ) == -1 ) { ::close( sDatal ); error( ERR_COULD_NOT_CREATE_SOCKET, QString::null ); return false; } sin.in.sin_port = 0; if ( bind( sDatal, &sin.sa, sizeof(sin) ) == -1 ) { ::close( sDatal ); sDatal = 0; error( ERR_COULD_NOT_BIND, m_host ); return false; } if ( listen( sDatal, 1 ) < 0 ) { error( ERR_COULD_NOT_LISTEN, m_host ); ::close( sDatal ); sDatal = 0; return 0; } if ( getsockname( sDatal, &sin.sa, &l ) < 0 ) // error ? return false; sprintf(buf,"port %d,%d,%d,%d,%d,%d", (unsigned char)sin.sa.sa_data[2],(unsigned char)sin.sa.sa_data[3], (unsigned char)sin.sa.sa_data[4],(unsigned char)sin.sa.sa_data[5], (unsigned char)sin.sa.sa_data[0],(unsigned char)sin.sa.sa_data[1]); return ftpSendCmd( buf, '2' );}/* * ftpAcceptConnect - wait for incoming connection * Used by @ref ftpOpenCommand * * return -2 on error or timeout * otherwise returns socket descriptor */int Ftp::ftpAcceptConnect(){ struct sockaddr addr; int sData; socklen_t l; fd_set mask; FD_ZERO(&mask); FD_SET(sDatal,&mask); if ( m_bPasv ) return sDatal; if ( select( sDatal + 1, &mask, NULL, NULL, 0L ) == 0) { ::close( sDatal ); return -2; } l = sizeof(addr); if ( ( sData = accept( sDatal, &addr, &l ) ) > 0 ) return sData; ::close( sDatal ); return -2;}bool Ftp::ftpOpenCommand( const char *_command, const QString & _path, char _mode, int errorcode, unsigned long _offset ){ QCString buf = "type "; buf += _mode; if ( !ftpSendCmd( buf, '2' ) ) { error( ERR_COULD_NOT_CONNECT, QString::null ); return false; } if ( !ftpOpenDataConnection() ) { error( ERR_COULD_NOT_CONNECT, QString::null ); return false; } if ( _offset > 0 ) { // send rest command if offset > 0, this applies to retr and stor commands char buf[100]; sprintf(buf, "rest %ld", _offset); if ( !ftpSendCmd( buf, '3' ) ) { if ( rspbuf[0] != '3' ) // other errors were already emitted { error( ERR_CANNOT_RESUME, _path ); // should never happen return false; } } } QCString tmp = _command; if ( !_path.isEmpty() ) { tmp += " "; tmp += _path.ascii(); } if ( !ftpSendCmd( tmp, '1' ) ) { if ( _offset > 0 && strcmp(_command, "retr") == 0 && rspbuf[0] == '4') { // Failed to resume errorcode = ERR_CANNOT_RESUME; } // The error here depends on the command error( errorcode, _path ); return false; } // Only now we know for sure that we can resume if ( _offset > 0 && strcmp(_command, "retr") == 0 ) canResume(); if ( ( sData = ftpAcceptConnect() ) < 0 ) { error( ERR_COULD_NOT_ACCEPT, QString::null ); return false; } return true;}void Ftp::closeSockets(){ if( sData != 0 ) { shutdown( sData, 2 ); ::close( sData ); sData = 0; } if( sDatal != 0 ) { ::close( sDatal ); sDatal = 0; }}bool Ftp::ftpCloseCommand(){ kdDebug(7102) << "Ftp::ftpCloseCommand" << endl; // first close data sockets (if opened), then read response that // we got for whatever was used in ftpOpenCommand ( should be 226 ) closeSockets(); if ( readresp() != '2' ) { kdDebug(7102) << "Did not get transfer complete message" << endl; return false; } return true;}void Ftp::mkdir( const KURL & url, int permissions ){ QString path = url.path(); if (!m_bLoggedOn) { openConnection(); if (!m_bLoggedOn) { kdDebug(7102) << "Login failure, aborting" << endl; return; } } assert( m_bLoggedOn ); QCString buf = "mkd "; buf += path.latin1(); if ( ! ftpSendCmd( buf, '2' ) ) { 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, bool overwrite ){ if (!m_bLoggedOn) { openConnection(); if (!m_bLoggedOn) { kdDebug(7102) << "Login failure, aborting" << endl; return; } } // The actual functionality is in ftpRename because put needs it if ( ftpRename( src.path(), dst.path(), overwrite ) ) finished(); else error( ERR_CANNOT_RENAME, src.path() );}bool Ftp::ftpRename( const QString & src, const QString & dst, bool /* overwrite */ ){ // TODO honor overwrite assert( m_bLoggedOn ); QCString cmd; cmd = "RNFR "; cmd += src.ascii(); if ( !ftpSendCmd( cmd, '3') ) return false; cmd = "RNTO ";
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -