📄 ftp.cpp
字号:
closeConnection(); return ftpOpenControlConnection(host, port, true); } } } // if there was a problem - report it ... if(iErrorCode == 0) // OK, return success return true; closeConnection(); // clean-up on error error(iErrorCode, sErrorMsg); return false;}/** * Called by @ref openConnection. It logs us in. * @ref m_initialPath is set to the current working directory * if logging on was successful. * * @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()->readEntry("EnableAutoLogin", false) ) { QString au = config()->readEntry("autoLoginUser"); if ( !au.isEmpty() ) { user = au; pass = config()->readEntry("autoLoginPass"); } } // Try anonymous login if both username/password // information is blank. if (user.isEmpty() && pass.isEmpty()) { user = FTP_LOGIN; pass = FTP_PASSWD; } AuthInfo info; info.url.setProtocol( "ftp" ); info.url.setHost( m_host ); if ( m_port > 0 && m_port != DEFAULT_FTP_PORT ) info.url.setPort( m_port ); info.url.setUser( user ); QByteArray tempbuf; int failedAuth = 0; do { // 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 > 0 || (!user.isEmpty() && pass.isEmpty()) ) { QString errorMsg; kDebug(7102) << "Prompting user for login info..."; // Ask user if we should retry after when login fails! if( failedAuth > 0 ) { errorMsg = i18n("Message sent:\nLogin using username=%1 and " "password=[hidden]\n\nServer replied:\n%2\n\n" , user, ftpResponse(0)); } if ( user != FTP_LOGIN ) info.username = user; 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>", m_host ); info.keepPassword = true; // Prompt the user for persistence as well. info.readOnly = (!m_user.isEmpty() && m_user != FTP_LOGIN); bool disablePassDlg = config()->readEntry( "DisablePassDlg", false ); if ( disablePassDlg || !openPasswordDialog( info, errorMsg ) ) { error( ERR_USER_CANCELED, m_host ); return false; } else { user = info.username; pass = info.password; } } tempbuf = "USER "; tempbuf += user.toLatin1(); if ( m_bUseProxy ) { tempbuf += '@'; tempbuf += m_host.toLatin1(); if ( m_port > 0 && m_port != DEFAULT_FTP_PORT ) { tempbuf += ':'; tempbuf += QString::number(m_port).toLatin1(); } } kDebug(7102) << "Sending Login name: " << tempbuf; bool loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) ); bool needPass = (m_iRespCode == 331); // Prompt user for login info if we do not // get back a "230" or "331". if ( !loggedIn && !needPass ) { kDebug(7102) << "Login failed: " << ftpResponse(0); ++failedAuth; continue; // Well we failed, prompt the user please!! } if( needPass ) { tempbuf = "pass "; tempbuf += pass.toLatin1(); kDebug(7102) << "Sending Login password: " << "[protected]"; loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) ); } if ( loggedIn ) { // Do not cache the default login!! if( user != FTP_LOGIN && pass != FTP_PASSWD ) cacheAuthentication( info ); failedAuth = -1; } } while( ++failedAuth ); kDebug(7102) << "Login OK"; 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 Sgaard) for this hint if( ftpSendCmd("SYST") && (m_iRespType == 2) ) { if( !strncmp( ftpResponse(0), "215 Windows_NT", 14 ) ) // should do for any version { ftpSendCmd( "site dirstyle" ); // Check if it was already in Unix style // Patch from Keith Refson <Keith.Refson@earth.ox.ac.uk> if( !strncmp( ftpResponse(0), "200 MSDOS-like directory output is on", 37 )) //It was in Unix style already! ftpSendCmd( "site dirstyle" ); // windows won't support chmod before KDE konquers their desktop... m_extControl |= chmodUnknown; } } else kWarning(7102) << "SYST failed"; if ( config()->readEntry ("EnableAutoLoginMacro", false) ) ftpAutoLoginMacro (); // Get the current working directory kDebug(7102) << "Searching for pwd"; if( !ftpSendCmd("PWD") || (m_iRespType != 2) ) { kDebug(7102) << "Couldn't issue pwd command"; error( ERR_COULD_NOT_LOGIN, i18n("Could not login to %1.", m_host) ); // or anything better ? return false; } QString sTmp = remoteEncoding()->decode( ftpResponse(3) ); int iBeg = sTmp.indexOf('"'); int iEnd = sTmp.lastIndexOf('"'); if(iBeg > 0 && iBeg < iEnd) { m_initialPath = sTmp.mid(iBeg+1, iEnd-iBeg-1); if(m_initialPath[0] != '/') m_initialPath.prepend('/'); kDebug(7102) << "Initial path set to: " << m_initialPath; m_currentPath = m_initialPath; } return true;}void Ftp::ftpAutoLoginMacro (){ QString macro = metaData( "autoLoginMacro" ); if ( macro.isEmpty() ) return; QStringList list = macro.split('\n',QString::SkipEmptyParts); for(QStringList::Iterator it = list.begin() ; it != list.end() ; ++it ) { if ( (*it).startsWith("init") ) { list = macro.split( '\\',QString::SkipEmptyParts); 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" ) ) ftpFolder( (*it).mid(4).trimmed(), false ); } break; } }}/** * ftpSendCmd - send a command (@p cmd) and read response * * @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 any response received, false on error */bool Ftp::ftpSendCmd( const QByteArray& cmd, int maxretries ){ assert(m_control != NULL); // must have control connection socket if ( cmd.indexOf( '\r' ) != -1 || cmd.indexOf( '\n' ) != -1) { kWarning(7102) << "Invalid command received (contains CR or LF):" << cmd.data(); error( ERR_UNSUPPORTED_ACTION, m_host ); return false; } // Don't print out the password... bool isPassCmd = (cmd.left(4).toLower() == "pass"); if ( !isPassCmd ) kDebug(7102) << "send> " << cmd.data(); else kDebug(7102) << "send> pass [protected]"; // Send the message... QByteArray buf = cmd; buf += "\r\n"; // Yes, must use CR/LF - see http://cr.yp.to/ftp/request.html int num = m_control->write(buf); while (m_control->bytesToWrite() && m_control->waitForBytesWritten()) {} // If we were able to successfully send the command, then we will // attempt to read the response. Otherwise, take action to re-attempt // the login based on the maximum number of retires specified... if( num > 0 ) ftpResponse(-1); else { m_iRespType = m_iRespCode = 0; } // If respCh is NULL or the response is 421 (Timed-out), we try to re-send // the command based on the value of maxretries. if( (m_iRespType <= 0) || (m_iRespCode == 421) ) { // We have not yet logged on... if (!m_bLoggedOn) { // The command was sent from the ftpLogin function, i.e. we are actually // attempting to login in. NOTE: If we already sent the username, we // return false and let the user decide whether (s)he wants to start from // the beginning... if (maxretries > 0 && !isPassCmd) { closeConnection (); if( ftpOpenConnection(loginDefered) ) ftpSendCmd ( cmd, maxretries - 1 ); } return false; } else { if ( maxretries < 1 ) return false; else { kDebug(7102) << "Was not able to communicate with " << m_host << "Attempting to re-establish connection."; closeConnection(); // Close the old connection... openConnection(); // Attempt to re-establish a new connection... if (!m_bLoggedOn) { if (m_control != NULL) // if openConnection succeeded ... { kDebug(7102) << "Login failure, aborting"; error (ERR_COULD_NOT_LOGIN, m_host); closeConnection (); } return false; } kDebug(7102) << "Logged back in, re-issuing command"; // If we were able to login, resend the command... if (maxretries) maxretries--; return ftpSendCmd( cmd, maxretries ); } } } return true;}/* * ftpOpenPASVDataConnection - set up data connection, using PASV mode * * return 0 if successful, ERR_INTERNAL otherwise * doesn't set error message, since non-pasv mode will always be tried if * this one fails */int Ftp::ftpOpenPASVDataConnection(){ assert(m_control != NULL); // must have control connection socket assert(m_data == NULL); // ... but no data connection // Check that we can do PASV QHostAddress addr = m_control->peerAddress(); if (addr.protocol() != QAbstractSocket::IPv4Protocol) return ERR_INTERNAL; // no PASV for non-PF_INET connections if (m_extControl & pasvUnknown) return ERR_INTERNAL; // already tried and got "unknown command" m_bPasv = true; /* Let's PASsiVe*/ if( !ftpSendCmd("PASV") || (m_iRespType != 2) ) { kDebug(7102) << "PASV attempt failed"; // unknown command? if( m_iRespType == 5 ) { kDebug(7102) << "disabling use of PASV"; m_extControl |= pasvUnknown; } return ERR_INTERNAL; } // The usual answer is '227 Entering Passive Mode. (160,39,200,55,6,245)' // but anonftpd gives '227 =160,39,200,55,6,245' int i[6]; const char *start = strchr(ftpResponse(3), '('); if ( !start ) start = strchr(ftpResponse(3), '='); 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 ) ) { kError(7102) << "parsing IP and port numbers failed. String parsed: " << start; return ERR_INTERNAL; } // we ignore the host part on purpose for two reasons // a) it might be wrong anyway // b) it would make us being suceptible to a port scanning attack // now connect the data socket ... quint16 port = i[4] << 8 | i[5]; kDebug(7102) << "Connecting to " << addr.toString() << " port " << port; m_data = new QSslSocket(); KSocketFactory::synchronousConnectToHost(m_data, "ftp-data", addr.toString(), port, connectTimeout() * 1000); return m_data->state() == QAbstractSocket::ConnectedState ? 0 : ERR_INTERNAL;}/* * ftpOpenEPSVDataConnection - opens a data connection via EPSV */int Ftp::ftpOpenEPSVDataConnection(){ assert(m_control != NULL); // must have control connection socket assert(m_data == NULL); // ... but no data connection QHostAddress address = m_control->peerAddress(); int portnum; if (m_extControl & epsvUnknown) return ERR_INTERNAL; m_bPasv = true; if( !ftpSendCmd("EPSV") || (m_iRespType != 2) ) { // unknown command? if( m_iRespType == 5 ) { kDebug(7102) << "disabling use of EPSV"; m_extControl |= epsvUnknown; } return ERR_INTERNAL; } const char *start = strchr(ftpResponse(3), '|'); if ( !start || sscanf(start, "|||%d|", &portnum) != 1) return ERR_INTERNAL; m_data = new QSslSocket(); KSocketFactory::synchronousConnectToHost(m_data, "ftp-data", address.toString(), portnum, connectTimeout() * 1000); return m_data->isOpen() ? 0 : ERR_INTERNAL;}int Ftp::encryptDataChannel(){ if (m_bIgnoreSslErrors) m_data->ignoreSslErrors(); if (m_bPasv) m_data->startClientEncryption(); else m_data->startServerEncryption(); if (!m_data->waitForEncrypted(connectTimeout() * 1000)) return ERR_SLAVE_DEFINED; return 0;}bool Ftp::requestDataEncryption(){ // initate tls transfer for data chanel on the control channel bool pbszSucc = (ftpSendCmd("PBSZ 0") && (m_iRespType == 2)); if (!pbszSucc) return false; // try protected data transfer first
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -