32 #define KIO_FTP_PRIVATE_INCLUDE
35 #ifdef HAVE_SYS_TIME_H
39 #if TIME_WITH_SYS_TIME
48 #include <QtCore/QCoreApplication>
49 #include <QtCore/QDir>
50 #include <QtNetwork/QHostAddress>
51 #include <QtNetwork/QTcpSocket>
52 #include <QtNetwork/QTcpServer>
53 #include <QtNetwork/QAuthenticator>
68 #define charToLongLong(a) strtoll(a, 0, 10)
70 #define charToLongLong(a) strtol(a, 0, 10)
73 #define FTP_LOGIN "anonymous"
74 #define FTP_PASSWD "anonymous@"
77 #define ENABLE_CAN_RESUME
84 return path.
left((path.
length() - qstrlen(
";type=X")));
94 if (index > -1 && (index+6) < path.
size()) {
95 const QChar mode = path.
at(index+6);
125 maximumIpcSize = 32 * 1024,
130 initialIpcSize = 2 * 1024,
134 minimumMimeSize = 1024
146 int WriteToFile(
int fd,
const char *buf,
size_t len)
150 ssize_t written = write(fd, buf, len);
157 {
case EINTR:
continue;
177 kDebug(7102) <<
"Starting " << getpid();
181 fprintf(stderr,
"Usage: kio_ftp protocol domain-socket1 domain-socket2\n");
185 Ftp slave(argv[2], argv[3]);
186 slave.dispatchLoop();
200 m_data = m_control = NULL;
202 ftpCloseControlConnection();
206 m_socketProxyAuth = 0;
219 void Ftp::ftpCloseDataConnection()
231 void Ftp::ftpCloseControlConnection()
246 const char* Ftp::ftpResponse(
int iOffset)
248 Q_ASSERT(m_control != NULL);
249 const char *pTxt = m_lastControlLine.
data();
264 m_lastControlLine = m_control->
readLine();
265 pTxt = m_lastControlLine.
data();
266 int iCode = atoi(pTxt);
269 kDebug(7102) <<
" > " << pTxt;
272 if (pTxt[3] ==
'-') {
277 kWarning(7102) <<
"Cannot parse valid code from line" << pTxt;
281 kDebug(7102) <<
" > " << pTxt;
282 if (iCode >= 100 && iCode == iMore && pTxt[3] ==
' ') {
287 kDebug(7102) <<
"resp> " << pTxt;
289 m_iRespType = (m_iRespCode > 0) ? m_iRespCode / 100 : 0;
293 while(iOffset-- > 0 && pTxt[0])
301 if(m_control != NULL || m_data != NULL)
302 kDebug(7102) <<
"m_bLoggedOn=" << m_bLoggedOn <<
" m_bBusy=" << m_bBusy;
306 kWarning(7102) <<
"Abandoned data stream";
307 ftpCloseDataConnection();
312 if( !ftpSendCmd(
"quit", 0 ) || (m_iRespType != 2) )
313 kWarning(7102) <<
"QUIT returned error: " << m_iRespCode;
317 ftpCloseDataConnection();
318 ftpCloseControlConnection();
324 kDebug(7102) << _host <<
"port=" << _port <<
"user=" << _user;
328 kDebug(7102) <<
"proxy urls:" << m_proxyUrls;
330 if ( m_host != _host || m_port != _port ||
331 m_user != _user || m_pass != _pass )
342 ftpOpenConnection(loginExplicit);
345 bool Ftp::ftpOpenConnection (LoginMode loginMode)
348 if(loginMode == loginImplicit && m_bLoggedOn)
350 Q_ASSERT(m_control != NULL);
354 kDebug(7102) <<
"host=" << m_host <<
", port=" << m_port <<
", user=" << m_user <<
"password= [password hidden]";
356 infoMessage(
i18n(
"Opening connection to host %1", m_host) );
360 error( ERR_UNKNOWN_HOST,
QString() );
364 Q_ASSERT( !m_bLoggedOn );
366 m_initialPath.
clear();
367 m_currentPath.
clear();
369 if (!ftpOpenControlConnection() )
371 infoMessage(
i18n(
"Connected to host %1", m_host) );
373 bool userNameChanged =
false;
374 if(loginMode != loginDefered)
376 m_bLoggedOn = ftpLogin(&userNameChanged);
381 m_bTextMode =
config()->readEntry(
"textmode",
false);
385 if (userNameChanged && m_bLoggedOn)
394 if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
398 realURL.
setPath( m_initialPath );
399 kDebug(7102) <<
"User name changed! Redirecting to" << realURL.
prettyUrl();
400 redirection( realURL );
414 bool Ftp::ftpOpenControlConnection()
417 return ftpOpenControlConnection(m_host, m_port);
422 Q_FOREACH (
const QString& proxyUrl, m_proxyUrls) {
423 const KUrl url (proxyUrl);
424 const QString scheme (url.protocol());
429 errorMessage = url.url();
434 kDebug(7102) <<
"Connecting to SOCKS proxy @" << url;
435 const int proxyPort = url.port();
436 QNetworkProxy proxy (QNetworkProxy::Socks5Proxy, url.host(), (proxyPort == -1 ? 0 : proxyPort));
438 if (ftpOpenControlConnection(m_host, m_port)) {
443 if (ftpOpenControlConnection(url.host(), url.port())) {
451 error(errorCode, errorMessage);
457 bool Ftp::ftpOpenControlConnection(
const QString &host,
int port )
474 const char* psz = ftpResponse(-1);
478 sErrorMsg =
i18n(
"%1.\n\nReason: %2", host, psz);
484 if (m_control->
error() == QAbstractSocket::HostNotFoundError)
485 iErrorCode = ERR_UNKNOWN_HOST;
494 error(iErrorCode, sErrorMsg);
505 bool Ftp::ftpLogin(
bool* userChanged)
507 infoMessage(
i18n(
"Sending login information") );
509 Q_ASSERT( !m_bLoggedOn );
520 pass =
config()->readEntry(
"autoLoginPass");
527 if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
535 pass.isEmpty() && checkCachedAuthentication(info))
543 if (user.isEmpty() && pass.isEmpty())
552 bool promptForRetry =
false;
562 if ( failedAuth > 0 || (!user.isEmpty() && pass.isEmpty()) )
565 kDebug(7102) <<
"Prompting user for login info...";
568 if( failedAuth > 0 && promptForRetry)
570 errorMsg =
i18n(
"Message sent:\nLogin using username=%1 and "
571 "password=[hidden]\n\nServer replied:\n%2\n\n"
572 , user, lastServerResponse);
578 info.
prompt =
i18n(
"You need to supply a username and a password "
579 "to access this site.");
585 bool disablePassDlg =
config()->readEntry(
"DisablePassDlg",
false );
586 if ( disablePassDlg || !openPasswordDialog( info, errorMsg ) )
588 error( ERR_USER_CANCELED, m_host );
604 promptForRetry =
true;
609 tempbuf += user.toLatin1();
614 if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
621 kDebug(7102) <<
"Sending Login name: " << tempbuf;
623 bool loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
624 bool needPass = (m_iRespCode == 331);
627 if ( !loggedIn && !needPass )
629 lastServerResponse = ftpResponse(0);
630 kDebug(7102) <<
"Login failed: " << lastServerResponse;
638 tempbuf += pass.toLatin1();
639 kDebug(7102) <<
"Sending Login password: " <<
"[protected]";
640 loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
647 *userChanged = (!m_user.isEmpty() && (m_user != user));
653 if (!m_user.isEmpty()) {
660 cacheAuthentication(info);
669 lastServerResponse = ftpResponse(0);
670 if (!ftpOpenControlConnection())
675 }
while( ++failedAuth );
678 kDebug(7102) <<
"Login OK";
679 infoMessage(
i18n(
"Login OK") );
683 if( ftpSendCmd(
"SYST") && (m_iRespType == 2) )
685 if( !qstrncmp( ftpResponse(0),
"215 Windows_NT", 14 ) )
687 ftpSendCmd(
"site dirstyle" );
690 if( !qstrncmp( ftpResponse(0),
"200 MSDOS-like directory output is on", 37 ))
692 ftpSendCmd(
"site dirstyle" );
694 m_extControl |= chmodUnknown;
700 if (
config()->readEntry (
"EnableAutoLoginMacro",
false) )
701 ftpAutoLoginMacro ();
704 kDebug(7102) <<
"Searching for pwd";
705 if( !ftpSendCmd(
"PWD") || (m_iRespType != 2) )
707 kDebug(7102) <<
"Couldn't issue pwd command";
708 error( ERR_COULD_NOT_LOGIN,
i18n(
"Could not login to %1.", m_host) );
712 QString sTmp = remoteEncoding()->decode( ftpResponse(3) );
715 if(iBeg > 0 && iBeg < iEnd)
717 m_initialPath = sTmp.
mid(iBeg+1, iEnd-iBeg-1);
718 if(m_initialPath[0] !=
'/') m_initialPath.
prepend(
'/');
719 kDebug(7102) <<
"Initial path set to: " << m_initialPath;
720 m_currentPath = m_initialPath;
725 void Ftp::ftpAutoLoginMacro ()
727 QString macro = metaData(
"autoLoginMacro" );
742 for( ; it != list2.
end() ; ++it )
747 (
void)ftpFolder( (*it).mid(4), false );
765 bool Ftp::ftpSendCmd(
const QByteArray& cmd,
int maxretries )
767 Q_ASSERT(m_control != NULL);
771 kWarning(7102) <<
"Invalid command received (contains CR or LF):"
773 error( ERR_UNSUPPORTED_ACTION, m_host );
778 bool isPassCmd = (cmd.
left(4).
toLower() ==
"pass");
782 kDebug(7102) <<
"send> pass [protected]";
787 int num = m_control->
write(buf);
797 m_iRespType = m_iRespCode = 0;
802 if( (m_iRespType <= 0) || (m_iRespCode == 421) )
811 if (maxretries > 0 && !isPassCmd)
814 if( ftpOpenConnection(loginDefered) )
815 ftpSendCmd ( cmd, maxretries - 1 );
822 if ( maxretries < 1 )
826 kDebug(7102) <<
"Was not able to communicate with " << m_host
827 <<
"Attempting to re-establish connection.";
834 if (m_control != NULL)
836 kDebug(7102) <<
"Login failure, aborting";
837 error (ERR_COULD_NOT_LOGIN, m_host);
843 kDebug(7102) <<
"Logged back in, re-issuing command";
849 return ftpSendCmd( cmd, maxretries );
864 int Ftp::ftpOpenPASVDataConnection()
866 Q_ASSERT(m_control != NULL);
867 Q_ASSERT(m_data == NULL);
874 if (m_extControl & pasvUnknown)
880 if( !ftpSendCmd(
"PASV") || (m_iRespType != 2) )
882 kDebug(7102) <<
"PASV attempt failed";
884 if( m_iRespType == 5 )
886 kDebug(7102) <<
"disabling use of PASV";
887 m_extControl |= pasvUnknown;
895 const char *start = strchr(ftpResponse(3),
'(');
897 start = strchr(ftpResponse(3),
'=');
899 ( sscanf(start,
"(%d,%d,%d,%d,%d,%d)",&i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 &&
900 sscanf(start,
"=%d,%d,%d,%d,%d,%d", &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 ) )
902 kError(7102) <<
"parsing IP and port numbers failed. String parsed: " << start;
911 quint16 port = i[4] << 8 | i[5];
921 int Ftp::ftpOpenEPSVDataConnection()
923 Q_ASSERT(m_control != NULL);
924 Q_ASSERT(m_data == NULL);
929 if (m_extControl & epsvUnknown)
933 if( !ftpSendCmd(
"EPSV") || (m_iRespType != 2) )
936 if( m_iRespType == 5 )
938 kDebug(7102) <<
"disabling use of EPSV";
939 m_extControl |= epsvUnknown;
944 const char *start = strchr(ftpResponse(3),
'|');
945 if ( !start || sscanf(start,
"|||%d|", &portnum) != 1)
965 int Ftp::ftpOpenDataConnection()
968 Q_ASSERT( m_bLoggedOn );
969 ftpCloseDataConnection();
972 int iErrCodePASV = 0;
977 iErrCode = ftpOpenPASVDataConnection();
980 iErrCodePASV = iErrCode;
981 ftpCloseDataConnection();
985 iErrCode = ftpOpenEPSVDataConnection();
988 ftpCloseDataConnection();
993 if (m_extControl & epsvAllSent)
998 iErrCode = ftpOpenPortDataConnection();
1002 ftpCloseDataConnection();
1004 return iErrCodePASV ? iErrCodePASV : iErrCode;
1013 int Ftp::ftpOpenPortDataConnection()
1015 Q_ASSERT(m_control != NULL);
1016 Q_ASSERT(m_data == NULL);
1019 if (m_extControl & eprtUnknown)
1035 if (localAddress.
protocol() == QAbstractSocket::IPv4Protocol)
1045 unsigned char *pData =
reinterpret_cast<unsigned char*
>(&data);
1046 command.
sprintf(
"PORT %d,%d,%d,%d,%d,%d",pData[3],pData[2],pData[1],pData[0],pData[5],pData[4]);
1048 else if (localAddress.
protocol() == QAbstractSocket::IPv6Protocol)
1053 if( ftpSendCmd(command.
toLatin1()) && (m_iRespType == 2) )
1063 bool Ftp::ftpOpenCommand(
const char *_command,
const QString & _path,
char _mode,
1070 errCode = ftpOpenDataConnection();
1074 error(errCode, m_host);
1078 if ( _offset > 0 ) {
1081 sprintf(buf,
"rest %lld", _offset);
1082 if ( !ftpSendCmd( buf ) )
1084 if( m_iRespType != 3 )
1086 error( ERR_CANNOT_RESUME, _path );
1099 if( !ftpSendCmd( tmp ) || (m_iRespType != 1) )
1101 if( _offset > 0 && qstrcmp(_command,
"retr") == 0 && (m_iRespType == 4) )
1104 errormessage = _path;
1110 if ( _offset > 0 && qstrcmp(_command,
"retr") == 0 )
1113 if(m_server && !m_data) {
1114 kDebug(7102) <<
"waiting for connection from remote.";
1120 kDebug(7102) <<
"connected with remote.";
1125 kDebug(7102) <<
"no connection received from remote.";
1127 errormessage=m_host;
1131 error(errorcode, errormessage);
1136 bool Ftp::ftpCloseCommand()
1140 ftpCloseDataConnection();
1145 kDebug(7102) <<
"ftpCloseCommand: reading command result";
1148 if(!ftpResponse(-1) || (m_iRespType != 2) )
1150 kDebug(7102) <<
"ftpCloseCommand: no transfer complete message";
1158 if( !ftpOpenConnection(loginImplicit) )
1161 const QByteArray encodedPath (remoteEncoding()->encode(url));
1164 if( !ftpSendCmd( (
QByteArray (
"mkd ") + encodedPath) ) || (m_iRespType != 2) )
1166 QString currentPath( m_currentPath );
1170 if( ftpFolder( path,
false ) )
1174 (void) ftpFolder( currentPath,
false );
1182 if ( permissions != -1 )
1185 (void) ftpChmod( path, permissions );
1193 if( !ftpOpenConnection(loginImplicit) )
1197 if ( ftpRename( src.
path(), dst.
path(), flags ) )
1201 bool Ftp::ftpRename(
const QString & src,
const QString & dst, KIO::JobFlags jobFlags)
1203 Q_ASSERT(m_bLoggedOn);
1207 if (ftpFileExists(dst)) {
1208 error(ERR_FILE_ALREADY_EXIST, dst);
1213 if (ftpFolder(dst,
false)) {
1214 error(ERR_DIR_ALREADY_EXIST, dst);
1221 if(!ftpFolder(src.
left(pos+1),
false))
1226 from_cmd += remoteEncoding()->encode(src.
mid(pos+1));
1227 if (!ftpSendCmd(from_cmd) || (m_iRespType != 3)) {
1228 error( ERR_CANNOT_RENAME, src );
1233 to_cmd += remoteEncoding()->encode(dst);
1234 if (!ftpSendCmd(to_cmd) || (m_iRespType != 2)) {
1235 error( ERR_CANNOT_RENAME, src );
1244 if( !ftpOpenConnection(loginImplicit) )
1250 ftpFolder(remoteEncoding()->directory(url),
false);
1253 cmd += remoteEncoding()->encode(url);
1255 if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
1261 bool Ftp::ftpChmod(
const QString & path,
int permissions )
1263 Q_ASSERT( m_bLoggedOn );
1265 if(m_extControl & chmodUnknown)
1273 ftpSendCmd(remoteEncoding()->encode(cmd));
1274 if(m_iRespType == 2)
1277 if(m_iRespCode == 500)
1279 m_extControl |= chmodUnknown;
1280 kDebug(7102) <<
"ftpChmod: CHMOD not supported - disabling";
1287 if( !ftpOpenConnection(loginImplicit) )
1290 if ( !ftpChmod( url.
path(), permissions ) )
1298 Q_ASSERT(entry.
count() == 0);
1314 KMimeType::Ptr mime = KMimeType::findByUrl(
KUrl(
"ftp://host/" + filename ) );
1319 if ( mime->name() == KMimeType::defaultMimeType() )
1321 kDebug(7102) <<
"Setting guessed mime type to inode/directory for " << filename;
1333 void Ftp::ftpShortStatAnswer(
const QString& filename,
bool isDir )
1350 void Ftp::ftpStatAnswerNotFound(
const QString & path,
const QString & filename )
1355 QString statSide = metaData(
"statSide");
1356 kDebug(7102) <<
"statSide=" << statSide;
1357 if ( statSide ==
"source" )
1359 kDebug(7102) <<
"Not found, but assuming found, because some servers don't allow listing";
1365 ftpShortStatAnswer( filename,
false );
1370 error( ERR_DOES_NOT_EXIST, path );
1376 if( !ftpOpenConnection(loginImplicit) )
1380 kDebug(7102) <<
"cleaned path=" << path;
1383 if( path.
isEmpty() || path ==
"/" )
1400 KUrl tempurl( url );
1405 Q_ASSERT(!filename.
isEmpty());
1410 bool isDir = ftpFolder(path,
false);
1413 QString sDetails = metaData(
"details");
1414 int details = sDetails.
isEmpty() ? 2 : sDetails.
toInt();
1415 kDebug(7102) <<
"details=" << details;
1418 if ( !isDir && !ftpFileExists(path) )
1420 ftpStatAnswerNotFound( path, filename );
1423 ftpShortStatAnswer( filename, isDir );
1451 if( !ftpFolder(parentDir,
true) )
1456 kError(7102) <<
"COULD NOT LIST";
1459 kDebug(7102) <<
"Starting of list was ok";
1461 Q_ASSERT( !search.isEmpty() && search !=
"/" );
1463 bool bFound =
false;
1467 while (ftpReadDir(ftpEnt)) {
1469 ftpValidateEntList.
append(ftpEnt);
1476 bFound = maybeEmitStatEntry(ftpEnt, search, filename, isDir);
1481 for (
int i = 0, count = ftpValidateEntList.
count(); i < count; ++i) {
1482 FtpEntry& ftpEnt = ftpValidateEntList[i];
1483 fixupEntryName(&ftpEnt);
1484 if (maybeEmitStatEntry(ftpEnt, search, filename, isDir)) {
1493 ftpStatAnswerNotFound( path, filename );
1499 if ( linkURL == url || linkURL == tempurl )
1508 kDebug(7102) <<
"stat : finished successfully";
1512 bool Ftp::maybeEmitStatEntry(
FtpEntry& ftpEnt,
const QString& search,
const QString& filename,
bool isDir)
1514 if ((search == ftpEnt.
name || filename == ftpEnt.
name) && !filename.
isEmpty()) {
1516 ftpCreateUDSEntry( filename, ftpEnt, entry, isDir );
1527 if( !ftpOpenConnection(loginImplicit) )
1541 if ( m_initialPath.
isEmpty() )
1542 m_initialPath =
'/';
1543 realURL.
setPath( m_initialPath );
1545 redirection( realURL );
1550 kDebug(7102) <<
"hunting for path" << path;
1552 if (!ftpOpenDir(path)) {
1553 if (ftpFileExists(path)) {
1566 while( ftpReadDir(ftpEnt) )
1572 ftpValidateEntList.
append(ftpEnt);
1580 ftpCreateUDSEntry( ftpEnt.
name, ftpEnt, entry,
false );
1581 listEntry( entry,
false );
1586 for (
int i = 0, count = ftpValidateEntList.
count(); i < count; ++i) {
1587 FtpEntry& ftpEnt = ftpValidateEntList[i];
1588 fixupEntryName(&ftpEnt);
1589 ftpCreateUDSEntry( ftpEnt.
name, ftpEnt, entry,
false );
1590 listEntry( entry,
false );
1594 listEntry( entry,
true );
1601 kDebug(7102) <<
"Got slave_status host = " << (!m_host.
toLatin1().
isEmpty() ? m_host.
toAscii() :
"[None]") <<
" [" << (m_bLoggedOn ?
"Connected" :
"Not connected") <<
"]";
1602 slaveStatus( m_host, m_bLoggedOn );
1605 bool Ftp::ftpOpenDir(
const QString & path )
1614 if( !ftpFolder(tmp,
false) )
1623 if( !ftpOpenCommand(
"list -la",
QString(),
'I', ERR_CANNOT_ENTER_DIRECTORY ) )
1625 if ( !ftpOpenCommand(
"list",
QString(),
'I', ERR_CANNOT_ENTER_DIRECTORY ) )
1627 kWarning(7102) <<
"Can't open for listing";
1631 kDebug(7102) <<
"Starting of list was ok";
1637 Q_ASSERT(m_data != NULL);
1644 if (data.
size() == 0)
1647 const char* buffer = data.
data();
1648 kDebug(7102) <<
"dir > " << buffer;
1656 const char *p_access, *p_junk, *p_owner, *p_group, *p_size;
1657 if( (p_access = strtok((
char*)buffer,
" ")) == 0)
continue;
1658 if( (p_junk = strtok(NULL,
" ")) == 0)
continue;
1659 if( (p_owner = strtok(NULL,
" ")) == 0)
continue;
1660 if( (p_group = strtok(NULL,
" ")) == 0)
continue;
1661 if( (p_size = strtok(NULL,
" ")) == 0)
continue;
1666 if ( qstrlen( p_access ) == 1 && p_junk[0] ==
'[' ) {
1667 de.
access = S_IRWXU | S_IRWXG | S_IRWXO;
1670 const char *p_date_1, *p_date_2, *p_date_3, *p_name;
1675 if ( strchr( p_size,
',' ) != 0L )
1678 if ((p_size = strtok(NULL,
" ")) == 0)
1695 p_date_1 = strtok(NULL,
" ");
1699 if ( p_date_1 != 0 &&
1700 (p_date_2 = strtok(NULL,
" ")) != 0 &&
1701 (p_date_3 = strtok(NULL,
" ")) != 0 &&
1702 (p_name = strtok(NULL,
"\r\n")) != 0 )
1706 if ( p_access[0] ==
'l' )
1710 de.
link = remoteEncoding()->decode(p_name + i + 4);
1719 if ( tmp[0] ==
'/' )
1725 de.
name = remoteEncoding()->decode(tmp);
1729 switch ( p_access[0] ) {
1750 if ( p_access[1] ==
'r' )
1752 if ( p_access[2] ==
'w' )
1754 if ( p_access[3] ==
'x' || p_access[3] ==
's' )
1756 if ( p_access[4] ==
'r' )
1758 if ( p_access[5] ==
'w' )
1760 if ( p_access[6] ==
'x' || p_access[6] ==
's' )
1762 if ( p_access[7] ==
'r' )
1764 if ( p_access[8] ==
'w' )
1766 if ( p_access[9] ==
'x' || p_access[9] ==
't' )
1768 if ( p_access[3] ==
's' || p_access[3] ==
'S' )
1770 if ( p_access[6] ==
's' || p_access[6] ==
'S' )
1772 if ( p_access[9] ==
't' || p_access[9] ==
'T' )
1775 de.
owner = remoteEncoding()->decode(p_owner);
1776 de.
group = remoteEncoding()->decode(p_group);
1783 time_t currentTime = time( 0L );
1784 struct tm * tmptr = gmtime( ¤tTime );
1785 int currentMonth = tmptr->tm_mon;
1788 tmptr->tm_isdst = -1;
1794 tmptr->tm_mday = atoi( p_date_2 );
1799 static const char *
const s_months[12] = {
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
1800 "Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec" };
1801 for (
int c = 0 ; c < 12 ; c ++ )
1802 if ( !qstrcmp( p_date_1, s_months[c]) )
1810 if ( qstrlen( p_date_3 ) == 4 )
1811 tmptr->tm_year = atoi( p_date_3 ) - 1900;
1820 if ( tmptr->tm_mon > currentMonth + 1 )
1825 if ( p_date_3 && ( semicolon = (
char*)strchr( p_date_3,
':' ) ) )
1828 tmptr->tm_min = atoi( semicolon + 1 );
1829 tmptr->tm_hour = atoi( p_date_3 );
1832 kWarning(7102) <<
"Can't parse third field " << p_date_3;
1836 de.
date = mktime( tmptr );
1852 const StatusCode cs = ftpGet(iError, -1, url, 0);
1855 if (cs == statusSuccess) {
1861 error(iError, url.
path());
1865 Ftp::StatusCode Ftp::ftpGet(
int& iError,
int iCopyFile,
const KUrl& url,
KIO::fileoffset_t llOffset)
1868 if( !ftpOpenConnection(loginImplicit) )
1869 return statusServerError;
1876 if ( !ftpSize( url.
path(),
'?' ) && (m_iRespCode == 550) &&
1877 ftpFolder(url.
path(),
false) )
1880 kDebug(7102) <<
"it is a directory in fact";
1882 return statusServerError;
1885 QString resumeOffset = metaData(
"resume");
1886 if ( !resumeOffset.
isEmpty() )
1889 kDebug(7102) <<
"got offset from metadata : " << llOffset;
1894 kWarning(7102) <<
"Can't open for reading";
1895 return statusServerError;
1899 if(m_size == UnknownSize)
1901 const char* psz = strrchr( ftpResponse(4),
'(' );
1903 if (!m_size) m_size = UnknownSize;
1907 if (iCopyFile == -1) {
1908 StatusCode status = ftpSendMimeType(iError, url);
1909 if (status != statusSuccess) {
1915 if ( m_size != UnknownSize ) {
1916 bytesLeft = m_size - llOffset;
1917 totalSize( m_size );
1920 kDebug(7102) <<
"starting with offset=" << llOffset;
1924 char buffer[maximumIpcSize];
1928 int iBlockSize = initialIpcSize;
1931 while(m_size == UnknownSize || bytesLeft > 0)
1933 if(processed_size-llOffset > 1024 * 64)
1934 iBlockSize = maximumIpcSize;
1937 if(iBlockSize+iBufferCur > (
int)
sizeof(buffer))
1938 iBlockSize =
sizeof(buffer) - iBufferCur;
1941 int n = m_data->
read( buffer+iBufferCur, iBlockSize );
1944 if( m_size == UnknownSize && n == 0 )
1948 return statusServerError;
1950 processed_size += n;
1953 if(m_size != UnknownSize)
1957 if(iBufferCur < minimumMimeSize && bytesLeft > 0)
1959 processedSize( processed_size );
1973 else if( (iError = WriteToFile(iCopyFile, buffer, n)) != 0)
1974 return statusClientError;
1975 processedSize( processed_size );
1982 processedSize( m_size == UnknownSize ? processed_size : m_size );
1983 return statusSuccess;
1989 if( !ftpOpenConnection(loginImplicit) )
1993 kWarning(7102) <<
"Can't open for reading";
1996 char buffer[ 2048 ];
2000 int n = m_data->
read( buffer, 2048 );
2005 kDebug(7102) <<
"aborting";
2008 kDebug(7102) <<
"finished";
2010 kDebug(7102) <<
"after finished";
2013 void Ftp::ftpAbortTransfer()
2021 msg[0] = (char) 255;
2022 msg[1] = (char) 254;
2023 (void) send(sControl, msg, 2, 0);
2025 msg[0] = (char) 255;
2026 msg[1] = (char) 242;
2027 if (send(sControl, msg, 2, MSG_OOB) != 2)
2031 kDebug(7102) <<
"send ABOR";
2032 QCString buf =
"ABOR\r\n";
2033 if ( KSocks::self()->write( sControl, buf.data(), buf.length() ) <= 0 ) {
2034 error( ERR_COULD_NOT_WRITE,
QString() );
2039 kDebug(7102) <<
"read resp";
2040 if ( readresp() !=
'2' )
2042 error( ERR_COULD_NOT_READ,
QString() );
2046 kDebug(7102) <<
"close sockets";
2060 const StatusCode cs = ftpPut(iError, -1, url, permissions, flags);
2063 if (cs == statusSuccess) {
2069 error(iError, url.
path());
2073 Ftp::StatusCode Ftp::ftpPut(
int& iError,
int iCopyFile,
const KUrl& dest_url,
2074 int permissions, KIO::JobFlags flags)
2076 if( !ftpOpenConnection(loginImplicit) )
2077 return statusServerError;
2082 if (m_user.isEmpty () || m_user ==
FTP_LOGIN)
2083 bMarkPartial =
false;
2085 bMarkPartial =
config()->readEntry(
"MarkPartial",
true);
2088 QString dest_part( dest_orig );
2089 dest_part +=
".part";
2091 if ( ftpSize( dest_orig,
'I' ) )
2096 cmd += remoteEncoding()->encode(dest_orig);
2097 if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
2100 return statusServerError;
2103 else if ( !(flags & KIO::Overwrite) && !(flags &
KIO::Resume) )
2106 return statusServerError;
2108 else if ( bMarkPartial )
2110 if ( !ftpRename( dest_orig, dest_part, KIO::Overwrite ) )
2113 return statusServerError;
2119 else if ( bMarkPartial && ftpSize( dest_part,
'I' ) )
2124 cmd += remoteEncoding()->encode(dest_part);
2125 if ( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
2128 return statusServerError;
2131 else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
2134 if (!(flags & KIO::Resume))
2137 return statusServerError;
2147 if ( bMarkPartial ) {
2148 kDebug(7102) <<
"Adding .part extension to " << dest_orig;
2156 if( (flags & KIO::Resume) && m_size > 0 )
2161 if( KDE_lseek(iCopyFile, offset, SEEK_SET) < 0 )
2164 return statusClientError;
2169 if (! ftpOpenCommand(
"stor", dest,
'?', ERR_COULD_NOT_WRITE, offset ) )
2170 return statusServerError;
2172 kDebug(7102) <<
"ftpPut: starting with offset=" << offset;
2177 int iBlockSize = initialIpcSize;
2184 result = readData( buffer );
2188 if(processed_size-offset > 1024 * 64)
2189 iBlockSize = maximumIpcSize;
2190 buffer.
resize(iBlockSize);
2191 result = ::read(iCopyFile, buffer.
data(), buffer.
size());
2200 m_data->
write( buffer );
2202 processed_size += result;
2203 processedSize (processed_size);
2206 while ( result > 0 );
2211 kDebug(7102) <<
"Error during 'put'. Aborting.";
2215 if ( ftpSize( dest,
'I' ) &&
2216 ( processed_size <
config()->
readEntry(
"MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE) ) )
2219 cmd += remoteEncoding()->encode(dest);
2220 (void) ftpSendCmd( cmd );
2223 return statusServerError;
2226 if ( !ftpCloseCommand() )
2229 return statusServerError;
2235 kDebug(7102) <<
"renaming dest (" << dest <<
") back to dest_orig (" << dest_orig <<
")";
2236 if ( !ftpRename( dest, dest_orig, KIO::Overwrite ) )
2239 return statusServerError;
2244 if ( permissions != -1 )
2247 kDebug(7102) <<
"Trying to chmod over anonymous FTP ???";
2249 if ( ! ftpChmod( dest_orig, permissions ) )
2257 return statusSuccess;
2260 const char* Ftp::ftpSendSizeCmd(
const QString& path)
2264 QString currentPath(m_currentPath);
2272 buf += remoteEncoding()->encode(path.
mid(currentPath.length()));
2274 buf += remoteEncoding()->encode(path);
2277 if (!ftpSendCmd(buf) || m_iRespType != 2) {
2282 return ftpResponse(4);
2288 bool Ftp::ftpSize(
const QString & path,
char mode)
2290 m_size = UnknownSize;
2291 if (!ftpDataMode(mode)) {
2296 if (psz.isEmpty()) {
2301 m_size = psz.trimmed().toLongLong(&ok);
2303 m_size = UnknownSize;
2309 bool Ftp::ftpFileExists(
const QString& path)
2311 return ftpSendSizeCmd(path) != 0;
2321 bool Ftp::ftpDataMode(
char cMode)
2323 if(cMode ==
'?') cMode = m_bTextMode ?
'A' :
'I';
2324 else if(cMode ==
'a') cMode =
'A';
2325 else if(cMode !=
'A') cMode =
'I';
2327 kDebug(7102) <<
"want" << cMode <<
"has" << m_cDataMode;
2328 if(m_cDataMode == cMode)
2333 if( !ftpSendCmd(buf) || (m_iRespType != 2) )
2335 m_cDataMode = cMode;
2340 bool Ftp::ftpFolder(
const QString& path,
bool bReportError)
2343 int iLen = newPath.
length();
2344 if(iLen > 1 && newPath[iLen-1] ==
'/') newPath.
truncate(iLen-1);
2347 if(m_currentPath == newPath)
2351 tmp += remoteEncoding()->encode(newPath);
2352 if( !ftpSendCmd(tmp) )
2354 if(m_iRespType != 2)
2357 error(ERR_CANNOT_ENTER_DIRECTORY, path);
2360 m_currentPath = newPath;
2374 StatusCode cs = statusSuccess;
2379 if(bSrcLocal && !bDestLocal) {
2381 kDebug(7102) <<
"local file" << sCopyFile <<
"-> ftp" << dest.
path();
2382 cs = ftpCopyPut(iError, iCopyFile, sCopyFile, dest, permissions, flags);
2383 if(cs == statusServerError) {
2384 sCopyFile = dest.
url();
2386 }
else if(!bSrcLocal && bDestLocal) {
2388 kDebug(7102) <<
"ftp" << src.
path() <<
"-> local file" << sCopyFile;
2389 cs = ftpCopyGet(iError, iCopyFile, sCopyFile, src, permissions, flags);
2390 if(cs == statusServerError) {
2391 sCopyFile = src.
url();
2399 if(iCopyFile != -1) {
2405 if (cs == statusSuccess) {
2411 error(iError, sCopyFile);
2416 Ftp::StatusCode Ftp::ftpCopyPut(
int& iError,
int& iCopyFile,
const QString &sCopyFile,
2417 const KUrl& url,
int permissions, KIO::JobFlags flags)
2420 KDE_struct_stat buff;
2421 bool bSrcExists = (
KDE::stat( sCopyFile, &buff ) != -1);
2423 {
if(S_ISDIR(buff.st_mode))
2426 return statusClientError;
2432 return statusClientError;
2435 iCopyFile =
KDE::open( sCopyFile, O_RDONLY );
2439 return statusClientError;
2443 totalSize(buff.st_size);
2444 #ifdef ENABLE_CAN_RESUME
2445 return ftpPut(iError, iCopyFile, url, permissions, flags & ~KIO::Resume);
2447 return ftpPut(iError, iCopyFile, url, permissions, flags | KIO::Resume);
2452 Ftp::StatusCode Ftp::ftpCopyGet(
int& iError,
int& iCopyFile,
const QString &sCopyFile,
2453 const KUrl& url,
int permissions, KIO::JobFlags flags)
2456 KDE_struct_stat buff;
2457 const bool bDestExists = (
KDE::stat( sCopyFile, &buff ) != -1);
2459 {
if(S_ISDIR(buff.st_mode))
2462 return statusClientError;
2464 if(!(flags & KIO::Overwrite))
2467 return statusClientError;
2473 bool bResume =
false;
2474 const bool bPartExists = (
KDE::stat( sPart, &buff ) != -1);
2475 const bool bMarkPartial =
config()->readEntry(
"MarkPartial",
true);
2476 const QString dest = bMarkPartial ? sPart : sCopyFile;
2477 if (bMarkPartial && bPartExists && buff.st_size > 0)
2479 if(S_ISDIR(buff.st_mode))
2482 return statusClientError;
2485 #ifdef ENABLE_CAN_RESUME
2486 bResume = canResume( buff.st_size );
2492 if (bPartExists && !bResume)
2498 if (permissions != -1)
2499 initialMode = permissions | S_IWUSR;
2507 hCopyOffset = KDE_lseek(iCopyFile, 0, SEEK_END);
2511 return statusClientError;
2513 kDebug(7102) <<
"resuming at " << hCopyOffset;
2516 iCopyFile =
KDE::open(dest, O_CREAT | O_TRUNC | O_WRONLY, initialMode);
2521 kDebug(7102) <<
"### COULD NOT WRITE " << sCopyFile;
2522 iError = (errno == EACCES) ? ERR_WRITE_ACCESS_DENIED
2523 : ERR_CANNOT_OPEN_FOR_WRITING;
2524 return statusClientError;
2528 StatusCode iRes = ftpGet(iError, iCopyFile, url, hCopyOffset);
2529 if( ::
close(iCopyFile) && iRes == statusSuccess )
2532 iRes = statusClientError;
2539 if(iRes == statusSuccess)
2545 kDebug(7102) <<
"cannot rename " << sPart <<
" to " << sCopyFile;
2547 iRes = statusClientError;
2553 int size =
config()->readEntry(
"MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
2554 if (buff.st_size < size)
2559 if (iRes == statusSuccess) {
2560 const QString mtimeStr = metaData(
"modified");
2564 kDebug(7102) <<
"Updating modified timestamp to" << mtimeStr;
2565 struct utimbuf utbuf;
2566 utbuf.actime = buff.st_atime;
2576 Ftp::StatusCode Ftp::ftpSendMimeType(
int& iError,
const KUrl& url)
2581 return statusSuccess;
2584 const int totalSize = ((m_size == UnknownSize || m_size > 1024) ? 1024 : m_size);
2591 return statusServerError;
2594 const int bytesRead = m_data->
peek(buffer.
data(), totalSize);
2597 if (bytesRead == -1) {
2599 return statusServerError;
2604 if (bytesRead == 0 || bytesRead == totalSize || m_size == UnknownSize) {
2610 KMimeType::Ptr mime = KMimeType::findByNameAndContent(url.
fileName(), buffer);
2611 kDebug(7102) <<
"Emitting mimetype" << mime->name();
2612 mimeType( mime->name() );
2615 return statusSuccess;
2621 kDebug(7102) <<
"Authenticator received -- realm:" << authenticator->
realm() <<
"user:"
2622 << authenticator->
user();
2625 info.
url = m_proxyURL;
2630 const bool haveCachedCredentials = checkCachedAuthentication(info);
2634 if (!haveCachedCredentials || m_socketProxyAuth) {
2637 connect(m_control, SIGNAL(connected()),
this, SLOT(saveProxyAuthentication()));
2639 info.
prompt =
i18n(
"You need to supply a username and a password for "
2640 "the proxy server listed below before you are allowed "
2641 "to access any sites.");
2645 const bool dataEntered = openPasswordDialog(info,
i18n(
"Proxy Authentication Failed."));
2647 kDebug(7102) <<
"looks like the user canceled proxy authentication.";
2648 error(ERR_USER_CANCELED, m_proxyURL.
host());
2656 if (m_socketProxyAuth) {
2657 *m_socketProxyAuth = *authenticator;
2666 void Ftp::saveProxyAuthentication()
2669 disconnect(m_control, SIGNAL(connected()),
this, SLOT(saveProxyAuthentication()));
2670 Q_ASSERT(m_socketProxyAuth);
2671 if (m_socketProxyAuth) {
2672 kDebug(7102) <<
"-- realm:" << m_socketProxyAuth->
realm() <<
"user:" << m_socketProxyAuth->
user();
2680 cacheAuthentication(a);
2682 delete m_socketProxyAuth;
2683 m_socketProxyAuth = 0;
2686 void Ftp::fixupEntryName(
FtpEntry* e)
2689 if (e->
type == S_IFDIR) {
2690 if (!ftpFolder(e->
name,
false)) {
2692 if (ftpFolder(
name,
false)) {
2694 kDebug(7102) <<
"fixing up directory name from" << e->
name <<
"to" <<
name;
2700 if (ftpFolder(
name,
false)) {
2701 kDebug(7102) <<
"fixing up directory name from" << e->
name <<
"to" <<
name;
2709 if (!ftpFileExists(e->
name)) {
2711 if (ftpFileExists(
name)) {
2713 kDebug(7102) <<
"fixing up filename from" << e->
name <<
"to" <<
name;
2719 if (ftpFileExists(
name)) {
2720 kDebug(7102) <<
"fixing up filename from" << e->
name <<
"to" <<
name;
QString i18n(const char *text)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
static QString ftpCleanPath(const QString &path)
void setPassword(const QString &password)
virtual void chmod(const KUrl &url, int permissions)
KAutostart::StartPhase readEntry(const KConfigGroup &group, const char *key, const KAutostart::StartPhase &aDefault)
QString directory(const DirectoryOptions &options=IgnoreTrailingSlash) const
virtual void mkdir(const KUrl &url, int permissions)
void truncate(int position)
QHostAddress peerAddress() const
SocketState state() const
QByteArray toLower() const
QString errorString() const
void setExtraField(const QString &fieldName, const QVariant &value)
QStringList split(const QString &sep, SplitBehavior behavior, Qt::CaseSensitivity cs) const
virtual bool canReadLine() const
void insert(uint field, const QString &value)
QString & prepend(QChar ch)
QVariant option(const QString &opt) const
SocketError error() const
int stat(const QString &path, KDE_struct_stat *buf)
int rename(const QString &in, const QString &out)
bool waitForNewConnection(int msec, bool *timedOut)
void setPassword(const QString &password)
QVariant getExtraField(const QString &fieldName) const
static QDebug kError(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
QTcpSocket * synchronousConnectToHost(const QString &protocol, const QString &host, quint16 port, int msecs=30000, QObject *parent=0)
QByteArray fromRawData(const char *data, int size)
virtual void get(const KUrl &url)
QTcpServer * listen(const QString &protocol, const QHostAddress &address=QHostAddress::Any, quint16 port=0, QObject *parent=0)
QString toLocalFile(AdjustPathOption trailing=LeaveTrailingSlash) const
#define charToLongLong(a)
QString & remove(int position, int n)
QNetworkProxy applicationProxy()
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
virtual void mimetype(const KUrl &url)
virtual void del(const KUrl &url, bool isfile)
KSharedConfigPtr config()
int lastIndexOf(QChar ch, int from, Qt::CaseSensitivity cs) const
void setPath(const QString &path)
virtual qint64 bytesToWrite() const
void setUser(const QString &user)
qint64 peek(char *data, qint64 maxSize)
int open(const QString &pathname, int flags, mode_t mode)
const char * name() const
int indexOf(char ch, int from) const
void setProtocol(const QString &proto)
QString number(int n, int base)
int count(const T &value) const
virtual void listDir(const KUrl &url)
void append(const T &value)
virtual void copy(const KUrl &src, const KUrl &dest, int permissions, KIO::JobFlags flags)
Handles the case that one side of the job is a local file.
int toInt(bool *ok, int base) const
virtual bool waitForBytesWritten(int msecs)
const char * constData() const
bool startsWith(const QString &s, Qt::CaseSensitivity cs) const
void setPass(const QString &pass)
virtual qint64 bytesAvailable() const
void resetRawData(const char *data, uint n)
bool endsWith(const QString &s, Qt::CaseSensitivity cs) const
qint64 read(char *data, qint64 maxSize)
Ftp(const QByteArray &pool, const QByteArray &app)
static char ftpModeFromPath(const QString &path, char defaultMode= '\0')
QString path(AdjustPathOption trailing=LeaveTrailingSlash) const
int kdemain(int argc, char **argv)
quint16 serverPort() const
QDateTime fromString(const QString &string, Qt::DateFormat format)
quint32 toIPv4Address() const
virtual QTcpSocket * nextPendingConnection()
QString cleanPath(const QString &path)
QByteArray left(int len) const
virtual void slave_status()
QByteArray toLatin1() const
virtual void put(const KUrl &url, int permissions, KIO::JobFlags flags)
virtual void stat(const KUrl &url)
QString mid(int position, int n) const
void setModified(bool flag)
virtual void rename(const KUrl &src, const KUrl &dst, KIO::JobFlags flags)
QHostAddress localAddress() const
QString fileName(const DirectoryOptions &options=IgnoreTrailingSlash) const
QAbstractSocket::NetworkLayerProtocol protocol() const
QString & sprintf(const char *cformat,...)
const QChar at(int position) const
virtual void setHost(const QString &host, quint16 port, const QString &user, const QString &pass)
void setMaxPendingConnections(int numConnections)
void setOption(const QString &opt, const QVariant &value)
static QDebug kWarning(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
QString left(int n) const
qint64 write(const char *data, qint64 maxSize)
void setUser(const QString &user)
QString fromLatin1(const char *str, int size)
virtual void closeConnection()
Closes the connection.
static bool supportedProxyScheme(const QString &scheme)
void setHost(const QString &host)
QString url(AdjustPathOption trailing=LeaveTrailingSlash) const
virtual void openConnection()
Connects to a ftp server and logs us in m_bLoggedOn is set to true if logging on was successful...
QByteArray & setRawData(const char *data, uint size)
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setApplicationProxy(const QNetworkProxy &networkProxy)
static bool isSocksProxy()
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
int utime(const QString &filename, struct utimbuf *buf)
virtual bool waitForReadyRead(int msecs)
qlonglong toLongLong(bool *ok, int base) const
QString prettyUrl(AdjustPathOption trailing=LeaveTrailingSlash) const
KAction * close(const QObject *recvr, const char *slot, QObject *parent)
QByteArray toAscii() const
qint64 readLine(char *data, qint64 maxSize)
QStringList list(const QString &fileClass)