00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #define KIO_FTP_PRIVATE_INCLUDE
00033 #include "ftp.h"
00034
00035 #include <sys/stat.h>
00036 #ifdef HAVE_SYS_TIME_H
00037 #include <sys/time.h>
00038 #endif
00039 #ifdef HAVE_SYS_SELECT_H
00040 #include <sys/select.h>
00041 #endif
00042
00043 #include <netinet/in.h>
00044 #include <arpa/inet.h>
00045
00046 #include <assert.h>
00047 #include <ctype.h>
00048 #include <errno.h>
00049 #include <fcntl.h>
00050 #include <netdb.h>
00051 #include <stdlib.h>
00052 #include <string.h>
00053 #include <unistd.h>
00054 #include <signal.h>
00055
00056 #if TIME_WITH_SYS_TIME
00057 #include <time.h>
00058 #endif
00059
00060 #include <QtCore/QDir>
00061 #include <QtNetwork/QHostAddress>
00062 #include <QtNetwork/QTcpServer>
00063
00064 #include <kdebug.h>
00065 #include <kglobal.h>
00066 #include <klocale.h>
00067 #include <kcomponentdata.h>
00068 #include <kmimetype.h>
00069 #include <kio/ioslave_defaults.h>
00070 #include <kio/slaveconfig.h>
00071 #include <kremoteencoding.h>
00072 #include <ksocketfactory.h>
00073 #include <kde_file.h>
00074 #include <kconfiggroup.h>
00075
00076 #ifdef HAVE_STRTOLL
00077 #define charToLongLong(a) strtoll(a, 0, 10)
00078 #else
00079 #define charToLongLong(a) strtol(a, 0, 10)
00080 #endif
00081
00082 #define FTP_LOGIN "anonymous"
00083 #define FTP_PASSWD "anonymous@"
//#undef kDebug
#define ENABLE_CAN_RESUME
// JPF: somebody should find a better solution for this or move this to KIO
// JPF: anyhow, in KDE 3.2.0 I found diffent MAX_IPC_SIZE definitions!
namespace KIO {
enum buffersizes
{ /**
* largest buffer size that should be used to transfer data between
* KIO slaves using the data() function
*/
maximumIpcSize = 32 * 1024,
00088 initialIpcSize = 2 * 1024,
00092 mimimumMimeSize = 1024
};
// JPF: this helper was derived from write_all in file.cc (FileProtocol).
static // JPF: in ftp.cc we make it static
00100 int WriteToFile(int fd, const char *buf, size_t len)
{
while (len > 0)
{ // JPF: shouldn't there be a KDE_write?
ssize_t written = write(fd, buf, len);
if (written >= 0)
{ buf += written;
len -= written;
continue;
}
switch(errno)
{ case EINTR: continue;
case EPIPE: return ERR_CONNECTION_BROKEN;
case ENOSPC: return ERR_DISK_FULL;
default: return ERR_COULD_NOT_WRITE;
}
}
return 0;
}
}
KIO::filesize_t Ftp::UnknownSize = (KIO::filesize_t)-1;
using namespace KIO;
extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
00101 {
00102 KComponentData componentData( "kio_ftp", "kdelibs4" );
00103 ( void ) KGlobal::locale();
00104
00105 kDebug(7102) << "Starting " << getpid();
00106
00107 if (argc != 4)
00108 {
00109 fprintf(stderr, "Usage: kio_ftp protocol domain-socket1 domain-socket2\n");
00110 exit(-1);
00111 }
00112
00113 Ftp slave(argv[2], argv[3]);
00114 slave.dispatchLoop();
00115
00116 kDebug(7102) << "Done";
00117 return 0;
00118 }
00119
00120
00121
00122
00123
00124 Ftp::Ftp( const QByteArray &pool, const QByteArray &app )
00125 : SlaveBase( "ftp", pool, app )
00126 {
00127
00128 m_data = m_control = NULL;
00129 ftpCloseControlConnection();
00130
00131
00132 m_port = 0;
00133 }
00134
00135
00136 Ftp::~Ftp()
00137 {
00138 kDebug(7102);
00139 closeConnection();
00140 }
00141
00145 void Ftp::ftpCloseDataConnection()
00146 {
00147 delete m_data;
00148 m_data = NULL;
00149 }
00150
00155 void Ftp::ftpCloseControlConnection()
00156 {
00157 m_extControl = 0;
00158 delete m_control;
00159 m_control = NULL;
00160 m_cDataMode = 0;
00161 m_bLoggedOn = false;
00162 m_bTextMode = false;
00163 m_bBusy = false;
00164 }
00165
00170 const char* Ftp::ftpResponse(int iOffset)
00171 {
00172 assert(m_control != NULL);
00173 const char *pTxt = m_lastControlLine.data();
00174
00175
00176 if(iOffset < 0)
00177 {
00178 int iMore = 0;
00179 m_iRespCode = 0;
00180
00181
00182
00183
00184
00185 do {
00186 while (!m_control->canReadLine() && m_control->waitForReadyRead()) {}
00187 m_lastControlLine = m_control->readLine();
00188 pTxt = m_lastControlLine.data();
00189 int nBytes = m_lastControlLine.size();
00190 int iCode = atoi(pTxt);
00191 if(iCode > 0) m_iRespCode = iCode;
00192
00193
00194 if(iMore != 0 && pTxt[0] == 32)
00195 ;
00196
00197 else if(nBytes < 4 || iCode < 100)
00198 iMore = 0;
00199
00200 else if(iMore == 0 && pTxt[3] == '-')
00201 iMore = iCode;
00202
00203 else if(iMore != 0 && (iMore != iCode || pTxt[3] != '-'))
00204 iMore = 0;
00205
00206 if(iMore != 0)
00207 kDebug(7102) << " > " << pTxt;
00208 } while(iMore != 0);
00209 kDebug(7102) << "resp> " << pTxt;
00210
00211 m_iRespType = (m_iRespCode > 0) ? m_iRespCode / 100 : 0;
00212 }
00213
00214
00215 while(iOffset-- > 0 && pTxt[0])
00216 pTxt++;
00217 return pTxt;
00218 }
00219
00220
00221 void Ftp::closeConnection()
00222 {
00223 if(m_control != NULL || m_data != NULL)
00224 kDebug(7102) << "m_bLoggedOn=" << m_bLoggedOn << " m_bBusy=" << m_bBusy;
00225
00226 if(m_bBusy)
00227 {
00228 kWarning(7102) << "Abandoned data stream";
00229 ftpCloseDataConnection();
00230 }
00231
00232 if(m_bLoggedOn)
00233 {
00234 if( !ftpSendCmd( "quit", 0 ) || (m_iRespType != 2) )
00235 kWarning(7102) << "QUIT returned error: " << m_iRespCode;
00236 }
00237
00238
00239 ftpCloseDataConnection();
00240 ftpCloseControlConnection();
00241 }
00242
00243 void Ftp::setHost( const QString& _host, quint16 _port, const QString& _user,
00244 const QString& _pass )
00245 {
00246 kDebug(7102) << _host << "port=" << _port;
00247
00248 m_proxyURL = metaData("UseProxy");
00249 m_bUseProxy = (m_proxyURL.isValid() && m_proxyURL.protocol() == "ftp");
00250
00251 if ( m_host != _host || m_port != _port ||
00252 m_user != _user || m_pass != _pass )
00253 closeConnection();
00254
00255 m_host = _host;
00256 m_port = _port;
00257 m_user = _user;
00258 m_pass = _pass;
00259 }
00260
00261 void Ftp::openConnection()
00262 {
00263 ftpOpenConnection(loginExplicit);
00264 }
00265
00266 bool Ftp::ftpOpenConnection (LoginMode loginMode)
00267 {
00268
00269 if(loginMode == loginImplicit && m_bLoggedOn)
00270 {
00271 assert(m_control != NULL);
00272 return true;
00273 }
00274
00275 kDebug(7102) << "ftpOpenConnection " << m_host << ":" << m_port << " "
00276 << m_user << " [password hidden]";
00277
00278 infoMessage( i18n("Opening connection to host %1", m_host) );
00279
00280 if ( m_host.isEmpty() )
00281 {
00282 error( ERR_UNKNOWN_HOST, QString() );
00283 return false;
00284 }
00285
00286 assert( !m_bLoggedOn );
00287
00288 m_initialPath.clear();
00289 m_currentPath.clear();
00290
00291 QString host = m_bUseProxy ? m_proxyURL.host() : m_host;
00292 int port = m_bUseProxy ? m_proxyURL.port() : m_port;
00293
00294 if (!ftpOpenControlConnection(host, port) )
00295 return false;
00296 infoMessage( i18n("Connected to host %1", m_host) );
00297
00298 if(loginMode != loginDefered)
00299 {
00300 m_bLoggedOn = ftpLogin();
00301 if( !m_bLoggedOn )
00302 return false;
00303 }
00304
00305 m_bTextMode = config()->readEntry("textmode", false);
00306 connected();
00307 return true;
00308 }
00309
00310
00316 bool Ftp::ftpOpenControlConnection( const QString &host, int port )
00317 {
00318
00319 closeConnection();
00320 QString sErrorMsg;
00321
00322
00323 if (port == 0)
00324 port = 21;
00325 m_control = KSocketFactory::synchronousConnectToHost("ftp", host, port, connectTimeout() * 1000);
00326 int iErrorCode = m_control->state() == QAbstractSocket::ConnectedState ? 0 : ERR_COULD_NOT_CONNECT;
00327
00328
00329 if(iErrorCode == 0)
00330 {
00331 const char* psz = ftpResponse(-1);
00332 if(m_iRespType != 2)
00333 {
00334 if(psz[0])
00335 sErrorMsg = i18n("%1.\n\nReason: %2", host, psz);
00336 iErrorCode = ERR_COULD_NOT_CONNECT;
00337 }
00338 }
00339 else
00340 {
00341 if (m_control->error() == QAbstractSocket::HostNotFoundError)
00342 iErrorCode = ERR_UNKNOWN_HOST;
00343
00344 sErrorMsg = QString("%1: %2").arg(host).arg(m_control->errorString());
00345 }
00346
00347
00348 if(iErrorCode == 0)
00349 return true;
00350 closeConnection();
00351 error(iErrorCode, sErrorMsg);
00352 return false;
00353 }
00354
00362 bool Ftp::ftpLogin()
00363 {
00364 infoMessage( i18n("Sending login information") );
00365
00366 assert( !m_bLoggedOn );
00367
00368 QString user = m_user;
00369 QString pass = m_pass;
00370
00371 if ( config()->readEntry("EnableAutoLogin", false) )
00372 {
00373 QString au = config()->readEntry("autoLoginUser");
00374 if ( !au.isEmpty() )
00375 {
00376 user = au;
00377 pass = config()->readEntry("autoLoginPass");
00378 }
00379 }
00380
00381
00382
00383 if (user.isEmpty() && pass.isEmpty())
00384 {
00385 user = FTP_LOGIN;
00386 pass = FTP_PASSWD;
00387 }
00388
00389 AuthInfo info;
00390 info.url.setProtocol( "ftp" );
00391 info.url.setHost( m_host );
00392 if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
00393 info.url.setPort( m_port );
00394 info.url.setUser( user );
00395
00396 QByteArray tempbuf;
00397 int failedAuth = 0;
00398
00399 do
00400 {
00401
00402
00403
00404 if ( failedAuth > 0 || (!user.isEmpty() && pass.isEmpty()) )
00405 {
00406 QString errorMsg;
00407 kDebug(7102) << "Prompting user for login info...";
00408
00409
00410 if( failedAuth > 0 )
00411 {
00412 errorMsg = i18n("Message sent:\nLogin using username=%1 and "
00413 "password=[hidden]\n\nServer replied:\n%2\n\n"
00414 , user, ftpResponse(0));
00415 }
00416
00417 if ( user != FTP_LOGIN )
00418 info.username = user;
00419
00420 info.prompt = i18n("You need to supply a username and a password "
00421 "to access this site.");
00422 info.commentLabel = i18n( "Site:" );
00423 info.comment = i18n("<b>%1</b>", m_host );
00424 info.keepPassword = true;
00425 info.readOnly = (!m_user.isEmpty() && m_user != FTP_LOGIN);
00426
00427 bool disablePassDlg = config()->readEntry( "DisablePassDlg", false );
00428 if ( disablePassDlg || !openPasswordDialog( info, errorMsg ) )
00429 {
00430 error( ERR_USER_CANCELED, m_host );
00431 return false;
00432 }
00433 else
00434 {
00435 user = info.username;
00436 pass = info.password;
00437 }
00438 }
00439
00440 tempbuf = "USER ";
00441 tempbuf += user.toLatin1();
00442 if ( m_bUseProxy )
00443 {
00444 tempbuf += '@';
00445 tempbuf += m_host.toLatin1();
00446 if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
00447 {
00448 tempbuf += ':';
00449 tempbuf += QString::number(m_port).toLatin1();
00450 }
00451 }
00452
00453 kDebug(7102) << "Sending Login name: " << tempbuf;
00454
00455 bool loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
00456 bool needPass = (m_iRespCode == 331);
00457
00458
00459 if ( !loggedIn && !needPass )
00460 {
00461 kDebug(7102) << "Login failed: " << ftpResponse(0);
00462 ++failedAuth;
00463 continue;
00464 }
00465
00466 if( needPass )
00467 {
00468 tempbuf = "pass ";
00469 tempbuf += pass.toLatin1();
00470 kDebug(7102) << "Sending Login password: " << "[protected]";
00471 loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
00472 }
00473
00474 if ( loggedIn )
00475 {
00476
00477 if( user != FTP_LOGIN && pass != FTP_PASSWD )
00478 cacheAuthentication( info );
00479 failedAuth = -1;
00480 }
00481
00482 } while( ++failedAuth );
00483
00484
00485 kDebug(7102) << "Login OK";
00486 infoMessage( i18n("Login OK") );
00487
00488
00489
00490 if( ftpSendCmd("SYST") && (m_iRespType == 2) )
00491 {
00492 if( !strncmp( ftpResponse(0), "215 Windows_NT", 14 ) )
00493 {
00494 ftpSendCmd( "site dirstyle" );
00495
00496
00497 if( !strncmp( ftpResponse(0), "200 MSDOS-like directory output is on", 37 ))
00498
00499 ftpSendCmd( "site dirstyle" );
00500
00501 m_extControl |= chmodUnknown;
00502 }
00503 }
00504 else
00505 kWarning(7102) << "SYST failed";
00506
00507 if ( config()->readEntry ("EnableAutoLoginMacro", false) )
00508 ftpAutoLoginMacro ();
00509
00510
00511 kDebug(7102) << "Searching for pwd";
00512 if( !ftpSendCmd("PWD") || (m_iRespType != 2) )
00513 {
00514 kDebug(7102) << "Couldn't issue pwd command";
00515 error( ERR_COULD_NOT_LOGIN, i18n("Could not login to %1.", m_host) );
00516 return false;
00517 }
00518
00519 QString sTmp = remoteEncoding()->decode( ftpResponse(3) );
00520 int iBeg = sTmp.indexOf('"');
00521 int iEnd = sTmp.lastIndexOf('"');
00522 if(iBeg > 0 && iBeg < iEnd)
00523 {
00524 m_initialPath = sTmp.mid(iBeg+1, iEnd-iBeg-1);
00525 if(m_initialPath[0] != '/') m_initialPath.prepend('/');
00526 kDebug(7102) << "Initial path set to: " << m_initialPath;
00527 m_currentPath = m_initialPath;
00528 }
00529 return true;
00530 }
00531
00532 void Ftp::ftpAutoLoginMacro ()
00533 {
00534 QString macro = metaData( "autoLoginMacro" );
00535
00536 if ( macro.isEmpty() )
00537 return;
00538
00539 QStringList list = macro.split('\n',QString::SkipEmptyParts);
00540
00541 for(QStringList::Iterator it = list.begin() ; it != list.end() ; ++it )
00542 {
00543 if ( (*it).startsWith("init") )
00544 {
00545 list = macro.split( '\\',QString::SkipEmptyParts);
00546 it = list.begin();
00547 ++it;
00548
00549 for( ; it != list.end() ; ++it )
00550 {
00551
00552
00553 if ( (*it).startsWith( "cwd" ) )
00554 ftpFolder( (*it).mid(4).trimmed(), false );
00555 }
00556
00557 break;
00558 }
00559 }
00560 }
00561
00562
00572 bool Ftp::ftpSendCmd( const QByteArray& cmd, int maxretries )
00573 {
00574 assert(m_control != NULL);
00575
00576 if ( cmd.indexOf( '\r' ) != -1 || cmd.indexOf( '\n' ) != -1)
00577 {
00578 kWarning(7102) << "Invalid command received (contains CR or LF):"
00579 << cmd.data();
00580 error( ERR_UNSUPPORTED_ACTION, m_host );
00581 return false;
00582 }
00583
00584
00585 bool isPassCmd = (cmd.left(4).toLower() == "pass");
00586 if ( !isPassCmd )
00587 kDebug(7102) << "send> " << cmd.data();
00588 else
00589 kDebug(7102) << "send> pass [protected]";
00590
00591
00592 QByteArray buf = cmd;
00593 buf += "\r\n";
00594 int num = m_control->write(buf);
00595 while (m_control->bytesToWrite() && m_control->waitForBytesWritten()) {}
00596
00597
00598
00599
00600 if( num > 0 )
00601 ftpResponse(-1);
00602 else
00603 {
00604 m_iRespType = m_iRespCode = 0;
00605 }
00606
00607
00608
00609 if( (m_iRespType <= 0) || (m_iRespCode == 421) )
00610 {
00611
00612 if (!m_bLoggedOn)
00613 {
00614
00615
00616
00617
00618 if (maxretries > 0 && !isPassCmd)
00619 {
00620 closeConnection ();
00621 if( ftpOpenConnection(loginDefered) )
00622 ftpSendCmd ( cmd, maxretries - 1 );
00623 }
00624
00625 return false;
00626 }
00627 else
00628 {
00629 if ( maxretries < 1 )
00630 return false;
00631 else
00632 {
00633 kDebug(7102) << "Was not able to communicate with " << m_host
00634 << "Attempting to re-establish connection.";
00635
00636 closeConnection();
00637 openConnection();
00638
00639 if (!m_bLoggedOn)
00640 {
00641 if (m_control != NULL)
00642 {
00643 kDebug(7102) << "Login failure, aborting";
00644 error (ERR_COULD_NOT_LOGIN, m_host);
00645 closeConnection ();
00646 }
00647 return false;
00648 }
00649
00650 kDebug(7102) << "Logged back in, re-issuing command";
00651
00652
00653 if (maxretries)
00654 maxretries--;
00655
00656 return ftpSendCmd( cmd, maxretries );
00657 }
00658 }
00659 }
00660
00661 return true;
00662 }
00663
00664
00665
00666
00667
00668
00669
00670
00671 int Ftp::ftpOpenPASVDataConnection()
00672 {
00673 assert(m_control != NULL);
00674 assert(m_data == NULL);
00675
00676
00677 QHostAddress addr = m_control->peerAddress();
00678 if (addr.protocol() != QAbstractSocket::IPv4Protocol)
00679 return ERR_INTERNAL;
00680
00681 if (m_extControl & pasvUnknown)
00682 return ERR_INTERNAL;
00683
00684 m_bPasv = true;
00685
00686
00687 if( !ftpSendCmd("PASV") || (m_iRespType != 2) )
00688 {
00689 kDebug(7102) << "PASV attempt failed";
00690
00691 if( m_iRespType == 5 )
00692 {
00693 kDebug(7102) << "disabling use of PASV";
00694 m_extControl |= pasvUnknown;
00695 }
00696 return ERR_INTERNAL;
00697 }
00698
00699
00700
00701 int i[6];
00702 const char *start = strchr(ftpResponse(3), '(');
00703 if ( !start )
00704 start = strchr(ftpResponse(3), '=');
00705 if ( !start ||
00706 ( sscanf(start, "(%d,%d,%d,%d,%d,%d)",&i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 &&
00707 sscanf(start, "=%d,%d,%d,%d,%d,%d", &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 ) )
00708 {
00709 kError(7102) << "parsing IP and port numbers failed. String parsed: " << start;
00710 return ERR_INTERNAL;
00711 }
00712
00713
00714
00715
00716
00717
00718 quint16 port = i[4] << 8 | i[5];
00719 kDebug(7102) << "Connecting to " << addr.toString() << " port " << port;
00720 m_data = KSocketFactory::synchronousConnectToHost("ftp-data", addr.toString(), port,
00721 connectTimeout() * 1000);
00722
00723 return m_data->state() == QAbstractSocket::ConnectedState ? 0 : ERR_INTERNAL;
00724 }
00725
00726
00727
00728
00729 int Ftp::ftpOpenEPSVDataConnection()
00730 {
00731 assert(m_control != NULL);
00732 assert(m_data == NULL);
00733
00734 QHostAddress address = m_control->peerAddress();
00735 int portnum;
00736
00737 if (m_extControl & epsvUnknown)
00738 return ERR_INTERNAL;
00739
00740 m_bPasv = true;
00741 if( !ftpSendCmd("EPSV") || (m_iRespType != 2) )
00742 {
00743
00744 if( m_iRespType == 5 )
00745 {
00746 kDebug(7102) << "disabling use of EPSV";
00747 m_extControl |= epsvUnknown;
00748 }
00749 return ERR_INTERNAL;
00750 }
00751
00752 const char *start = strchr(ftpResponse(3), '|');
00753 if ( !start || sscanf(start, "|||%d|", &portnum) != 1)
00754 return ERR_INTERNAL;
00755
00756 m_data = KSocketFactory::synchronousConnectToHost("ftp-data", address.toString(), portnum,
00757 connectTimeout() * 1000);
00758 return m_data->isOpen() ? 0 : ERR_INTERNAL;
00759 }
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773 int Ftp::ftpOpenDataConnection()
00774 {
00775
00776 assert( m_bLoggedOn );
00777 ftpCloseDataConnection();
00778
00779 int iErrCode = 0;
00780 int iErrCodePASV = 0;
00781
00782
00783 if( !config()->readEntry("DisablePassiveMode", false) )
00784 {
00785 iErrCode = ftpOpenPASVDataConnection();
00786 if(iErrCode == 0)
00787 return 0;
00788 iErrCodePASV = iErrCode;
00789 ftpCloseDataConnection();
00790
00791 if( !config()->readEntry("DisableEPSV", false) )
00792 {
00793 iErrCode = ftpOpenEPSVDataConnection();
00794 if(iErrCode == 0)
00795 return 0;
00796 ftpCloseDataConnection();
00797 }
00798
00799
00800
00801 if (m_extControl & epsvAllSent)
00802 return iErrCodePASV ? iErrCodePASV : iErrCode;
00803 }
00804
00805
00806 iErrCode = ftpOpenPortDataConnection();
00807 if(iErrCode == 0)
00808 return 0;
00809
00810 ftpCloseDataConnection();
00811
00812 return iErrCodePASV ? iErrCodePASV : iErrCode;
00813 }
00814
00815
00816
00817
00818
00819
00820
00821 int Ftp::ftpOpenPortDataConnection()
00822 {
00823 assert(m_control != NULL);
00824 assert(m_data == NULL);
00825
00826 m_bPasv = false;
00827 if (m_extControl & eprtUnknown)
00828 return ERR_INTERNAL;
00829
00830 QTcpServer *server = KSocketFactory::listen("ftp-data");
00831 if (!server->isListening())
00832 {
00833 delete server;
00834 return ERR_COULD_NOT_LISTEN;
00835 }
00836
00837 server->setMaxPendingConnections(1);
00838
00839 QString command;
00840 QHostAddress localAddress = m_control->localAddress();
00841 if (localAddress.protocol() == QAbstractSocket::IPv4Protocol)
00842 {
00843 struct
00844 {
00845 quint32 ip4;
00846 quint16 port;
00847 } data;
00848 data.ip4 = localAddress.toIPv4Address();
00849 data.port = server->serverPort();
00850
00851 unsigned char *pData = reinterpret_cast<unsigned char*>(&data);
00852 command.sprintf("PORT %d,%d,%d,%d,%d,%d",pData[0],pData[1],pData[2],pData[3],pData[4],pData[5]);
00853 }
00854 else if (localAddress.protocol() == QAbstractSocket::IPv6Protocol)
00855 {
00856 command = QString("EPRT |2|%2|%3|").arg(localAddress.toString()).arg(server->serverPort());
00857 }
00858
00859 if( ftpSendCmd(command.toLatin1()) && (m_iRespType == 2) )
00860 {
00861 server->waitForNewConnection(connectTimeout() * 1000);
00862 m_data = server->nextPendingConnection();
00863 delete server;
00864 return m_data ? 0 : ERR_COULD_NOT_CONNECT;
00865 }
00866
00867 delete server;
00868 return ERR_INTERNAL;
00869 }
00870
00871 bool Ftp::ftpOpenCommand( const char *_command, const QString & _path, char _mode,
00872 int errorcode, KIO::fileoffset_t _offset )
00873 {
00874 int errCode = 0;
00875 if( !ftpDataMode(_mode) )
00876 errCode = ERR_COULD_NOT_CONNECT;
00877 else
00878 errCode = ftpOpenDataConnection();
00879
00880 if(errCode != 0)
00881 {
00882 error(errCode, m_host);
00883 return false;
00884 }
00885
00886 if ( _offset > 0 ) {
00887
00888 char buf[100];
00889 sprintf(buf, "rest %lld", _offset);
00890 if ( !ftpSendCmd( buf ) )
00891 return false;
00892 if( m_iRespType != 3 )
00893 {
00894 error( ERR_CANNOT_RESUME, _path );
00895 return false;
00896 }
00897 }
00898
00899 QByteArray tmp = _command;
00900 QString errormessage;
00901
00902 if ( !_path.isEmpty() ) {
00903 tmp += ' ';
00904 tmp += remoteEncoding()->encode(_path);
00905 }
00906
00907 if( !ftpSendCmd( tmp ) || (m_iRespType != 1) )
00908 {
00909 if( _offset > 0 && strcmp(_command, "retr") == 0 && (m_iRespType == 4) )
00910 errorcode = ERR_CANNOT_RESUME;
00911
00912 errormessage = _path;
00913 }
00914
00915 else
00916 {
00917
00918 if ( _offset > 0 && strcmp(_command, "retr") == 0 )
00919 canResume();
00920
00921 m_bBusy = true;
00922 return true;
00923 }
00924
00925 error(errorcode, errormessage);
00926 return false;
00927 }
00928
00929
00930 bool Ftp::ftpCloseCommand()
00931 {
00932
00933
00934 if(m_data)
00935 {
00936 delete m_data;
00937 m_data = NULL;
00938 }
00939 if(!m_bBusy)
00940 return true;
00941
00942 kDebug(7102) << "ftpCloseCommand: reading command result";
00943 m_bBusy = false;
00944
00945 if(!ftpResponse(-1) || (m_iRespType != 2) )
00946 {
00947 kDebug(7102) << "ftpCloseCommand: no transfer complete message";
00948 return false;
00949 }
00950 return true;
00951 }
00952
00953 void Ftp::mkdir( const KUrl & url, int permissions )
00954 {
00955 if( !ftpOpenConnection(loginImplicit) )
00956 return;
00957
00958 QString path = remoteEncoding()->encode(url);
00959 QByteArray buf = "mkd ";
00960 buf += remoteEncoding()->encode(path);
00961
00962 if( !ftpSendCmd( buf ) || (m_iRespType != 2) )
00963 {
00964 QString currentPath( m_currentPath );
00965
00966
00967
00968 if( ftpFolder( path, false ) )
00969 {
00970 error( ERR_DIR_ALREADY_EXIST, path );
00971
00972 (void) ftpFolder( currentPath, false );
00973 return;
00974 }
00975
00976 error( ERR_COULD_NOT_MKDIR, path );
00977 return;
00978 }
00979
00980 if ( permissions != -1 )
00981 {
00982
00983 (void) ftpChmod( path, permissions );
00984 }
00985
00986 finished();
00987 }
00988
00989 void Ftp::rename( const KUrl& src, const KUrl& dst, KIO::JobFlags flags )
00990 {
00991 if( !ftpOpenConnection(loginImplicit) )
00992 return;
00993
00994
00995 if ( ftpRename( src.path(), dst.path(), flags ) )
00996 finished();
00997 else
00998 error( ERR_CANNOT_RENAME, src.path() );
00999 }
01000
01001 bool Ftp::ftpRename(const QString & src, const QString & dst, KIO::JobFlags jobFlags)
01002 {
01003 assert(m_bLoggedOn);
01004
01005
01006 if (!(jobFlags & KIO::Overwrite)) {
01007 if (ftpFileExists(dst)) {
01008 error(ERR_FILE_ALREADY_EXIST, dst);
01009 return false;
01010 }
01011 }
01012 if (ftpFolder(dst, false)) {
01013 error(ERR_DIR_ALREADY_EXIST, dst);
01014 return false;
01015 }
01016
01017
01018 const int pos = src.lastIndexOf('/');
01019 if (pos > 0) {
01020 if(!ftpFolder(src.left(pos+1), false))
01021 return false;
01022 }
01023
01024 QByteArray from_cmd = "RNFR ";
01025 from_cmd += remoteEncoding()->encode(src.mid(pos+1));
01026 if (!ftpSendCmd(from_cmd) || (m_iRespType != 3))
01027 return false;
01028
01029 QByteArray to_cmd = "RNTO ";
01030 to_cmd += remoteEncoding()->encode(dst);
01031 if (!ftpSendCmd(to_cmd) || (m_iRespType != 2))
01032 return false;
01033
01034 return true;
01035 }
01036
01037 void Ftp::del( const KUrl& url, bool isfile )
01038 {
01039 if( !ftpOpenConnection(loginImplicit) )
01040 return;
01041
01042
01043
01044 if ( !isfile )
01045 ftpFolder(remoteEncoding()->directory(url), false);
01046
01047 QByteArray cmd = isfile ? "DELE " : "RMD ";
01048 cmd += remoteEncoding()->encode(url);
01049
01050 if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
01051 error( ERR_CANNOT_DELETE, url.path() );
01052 else
01053 finished();
01054 }
01055
01056 bool Ftp::ftpChmod( const QString & path, int permissions )
01057 {
01058 assert( m_bLoggedOn );
01059
01060 if(m_extControl & chmodUnknown)
01061 return false;
01062
01063
01064
01065 QString cmd = QString::fromLatin1("SITE CHMOD ") + QString::number( permissions & 511, 8 ) + ' ';
01066 cmd += path;
01067
01068 ftpSendCmd(remoteEncoding()->encode(cmd));
01069 if(m_iRespType == 2)
01070 return true;
01071
01072 if(m_iRespCode == 500)
01073 {
01074 m_extControl |= chmodUnknown;
01075 kDebug(7102) << "ftpChmod: CHMOD not supported - disabling";
01076 }
01077 return false;
01078 }
01079
01080 void Ftp::chmod( const KUrl & url, int permissions )
01081 {
01082 if( !ftpOpenConnection(loginImplicit) )
01083 return;
01084
01085 if ( !ftpChmod( url.path(), permissions ) )
01086 error( ERR_CANNOT_CHMOD, url.path() );
01087 else
01088 finished();
01089 }
01090
01091 void Ftp::ftpCreateUDSEntry( const QString & filename, FtpEntry& ftpEnt, UDSEntry& entry, bool isDir )
01092 {
01093 assert(entry.count() == 0);
01094
01095 entry.insert( KIO::UDSEntry::UDS_NAME, filename );
01096 entry.insert( KIO::UDSEntry::UDS_SIZE, ftpEnt.size );
01097 entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, ftpEnt.date );
01098 entry.insert( KIO::UDSEntry::UDS_ACCESS, ftpEnt.access );
01099 entry.insert( KIO::UDSEntry::UDS_USER, ftpEnt.owner );
01100 if ( !ftpEnt.group.isEmpty() )
01101 {
01102 entry.insert( KIO::UDSEntry::UDS_GROUP, ftpEnt.group );
01103 }
01104
01105 if ( !ftpEnt.link.isEmpty() )
01106 {
01107 entry.insert( KIO::UDSEntry::UDS_LINK_DEST, ftpEnt.link );
01108
01109 KMimeType::Ptr mime = KMimeType::findByUrl( KUrl("ftp://host/" + filename ) );
01110
01111
01112
01113
01114 if ( mime->name() == KMimeType::defaultMimeType() )
01115 {
01116 kDebug(7102) << "Setting guessed mime type to inode/directory for " << filename;
01117 entry.insert( KIO::UDSEntry::UDS_GUESSED_MIME_TYPE, QString::fromLatin1( "inode/directory" ) );
01118 isDir = true;
01119 }
01120 }
01121
01122 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : ftpEnt.type );
01123
01124
01125 }
01126
01127
01128 void Ftp::ftpShortStatAnswer( const QString& filename, bool isDir )
01129 {
01130 UDSEntry entry;
01131
01132
01133 entry.insert( KIO::UDSEntry::UDS_NAME, filename );
01134 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : S_IFREG );
01135 entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
01136
01137
01138 statEntry(entry);
01139 finished();
01140 }
01141
01142 void Ftp::ftpStatAnswerNotFound( const QString & path, const QString & filename )
01143 {
01144
01145
01146
01147 QString statSide = metaData("statSide");
01148 kDebug(7102) << "statSide=" << statSide;
01149 if ( statSide == "source" )
01150 {
01151 kDebug(7102) << "Not found, but assuming found, because some servers don't allow listing";
01152
01153
01154
01155
01156
01157 ftpShortStatAnswer( filename, false );
01158
01159 return;
01160 }
01161
01162 error( ERR_DOES_NOT_EXIST, path );
01163 }
01164
01165 void Ftp::stat(const KUrl &url)
01166 {
01167 kDebug(7102) << "path=" << url.path();
01168 if( !ftpOpenConnection(loginImplicit) )
01169 return;
01170
01171 QString path = QDir::cleanPath( url.path() );
01172 kDebug(7102) << "cleaned path=" << path;
01173
01174
01175 if( path.isEmpty() || path == "/" )
01176 {
01177 UDSEntry entry;
01178
01179 entry.insert( KIO::UDSEntry::UDS_NAME, QString::fromLatin1( "." ) );
01180 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
01181 entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
01182 entry.insert( KIO::UDSEntry::UDS_USER, QString::fromLatin1( "root" ) );
01183 entry.insert( KIO::UDSEntry::UDS_GROUP, QString::fromLatin1( "root" ) );
01184
01185
01186 statEntry( entry );
01187 finished();
01188 return;
01189 }
01190
01191 KUrl tempurl( url );
01192 tempurl.setPath( path );
01193 QString listarg;
01194 QString parentDir;
01195 QString filename = tempurl.fileName();
01196 Q_ASSERT(!filename.isEmpty());
01197 QString search = filename;
01198
01199
01200
01201 bool isDir = ftpFolder(path, false);
01202
01203
01204 QString sDetails = metaData("details");
01205 int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
01206 kDebug(7102) << "details=" << details;
01207 if ( details == 0 )
01208 {
01209 if ( !isDir && !ftpFileExists(path) )
01210 {
01211 ftpStatAnswerNotFound( path, filename );
01212 return;
01213 }
01214 ftpShortStatAnswer( filename, isDir );
01215 return;
01216 }
01217
01218 if (!isDir)
01219 {
01220
01221 parentDir = tempurl.directory(KUrl::AppendTrailingSlash);
01222
01223 listarg = filename;
01224 }
01225 else
01226 {
01227
01228
01229
01230 UDSEntry entry;
01231 entry.insert( KIO::UDSEntry::UDS_NAME, filename );
01232 entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
01233 entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
01234
01235
01236 statEntry(entry);
01237 finished();
01238 return;
01239 }
01240
01241
01242 if( !ftpFolder(parentDir, true) )
01243 return;
01244
01245 if( !ftpOpenCommand( "list", listarg, 'I', ERR_DOES_NOT_EXIST ) )
01246 {
01247 kError(7102) << "COULD NOT LIST";
01248 return;
01249 }
01250 kDebug(7102) << "Starting of list was ok";
01251
01252 Q_ASSERT( !search.isEmpty() && search != "/" );
01253
01254 bool bFound = false;
01255 KUrl linkURL;
01256 FtpEntry ftpEnt;
01257 while( ftpReadDir(ftpEnt) )
01258 {
01259
01260
01261 if (!bFound) {
01262 if ( ( search == ftpEnt.name || filename == ftpEnt.name ) ) {
01263 if ( !filename.isEmpty() ) {
01264 bFound = true;
01265 UDSEntry entry;
01266 ftpCreateUDSEntry( filename, ftpEnt, entry, isDir );
01267 statEntry( entry );
01268 }
01269 }
01270 }
01271
01272
01273 }
01274
01275 ftpCloseCommand();
01276
01277 if ( !bFound )
01278 {
01279 ftpStatAnswerNotFound( path, filename );
01280 return;
01281 }
01282
01283 if ( !linkURL.isEmpty() )
01284 {
01285 if ( linkURL == url || linkURL == tempurl )
01286 {
01287 error( ERR_CYCLIC_LINK, linkURL.prettyUrl() );
01288 return;
01289 }
01290 Ftp::stat( linkURL );
01291 return;
01292 }
01293
01294 kDebug(7102) << "stat : finished successfully";
01295 finished();
01296 }
01297
01298
01299 void Ftp::listDir( const KUrl &url )
01300 {
01301 kDebug(7102) << url;
01302 if( !ftpOpenConnection(loginImplicit) )
01303 return;
01304
01305
01306 QString path = url.path();
01307 if ( path.isEmpty() )
01308 {
01309 KUrl realURL;
01310 realURL.setProtocol( "ftp" );
01311 if ( m_user != FTP_LOGIN )
01312 realURL.setUser( m_user );
01313
01314 if ( m_pass != FTP_PASSWD )
01315 realURL.setPass( m_pass );
01316 realURL.setHost( m_host );
01317 if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
01318 realURL.setPort( m_port );
01319 if ( m_initialPath.isEmpty() )
01320 m_initialPath = "/";
01321 realURL.setPath( m_initialPath );
01322 kDebug(7102) << "REDIRECTION to " << realURL.prettyUrl();
01323 redirection( realURL );
01324 finished();
01325 return;
01326 }
01327
01328 kDebug(7102) << "hunting for path" << path;
01329
01330 if (!ftpOpenDir(path)) {
01331 if (ftpFileExists(path)) {
01332 error(ERR_IS_FILE, path);
01333 } else {
01334
01335
01336 error( ERR_CANNOT_ENTER_DIRECTORY, path );
01337 }
01338 return;
01339 }
01340
01341 UDSEntry entry;
01342 FtpEntry ftpEnt;
01343 while( ftpReadDir(ftpEnt) )
01344 {
01345
01346
01347 if ( !ftpEnt.name.isEmpty() )
01348 {
01349
01350
01351
01352
01353 entry.clear();
01354 ftpCreateUDSEntry( ftpEnt.name, ftpEnt, entry, false );
01355 listEntry( entry, false );
01356 }
01357 }
01358 listEntry( entry, true );
01359 ftpCloseCommand();
01360 finished();
01361 }
01362
01363 void Ftp::slave_status()
01364 {
01365 kDebug(7102) << "Got slave_status host = " << (!m_host.toAscii().isEmpty() ? m_host.toAscii() : "[None]") << " [" << (m_bLoggedOn ? "Connected" : "Not connected") << "]";
01366 slaveStatus( m_host, m_bLoggedOn );
01367 }
01368
01369 bool Ftp::ftpOpenDir( const QString & path )
01370 {
01371
01372
01373
01374
01375 QString tmp = path.isEmpty() ? QString("/") : path;
01376
01377
01378 if( !ftpFolder(tmp, false) )
01379 return false;
01380
01381
01382
01383
01384
01385
01386
01387 if( !ftpOpenCommand( "list -la", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) )
01388 {
01389 if ( !ftpOpenCommand( "list", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) )
01390 {
01391 kWarning(7102) << "Can't open for listing";
01392 return false;
01393 }
01394 }
01395 kDebug(7102) << "Starting of list was ok";
01396 return true;
01397 }
01398
01399 bool Ftp::ftpReadDir(FtpEntry& de)
01400 {
01401 assert(m_data != NULL);
01402
01403
01404 while( true )
01405 {
01406 while (!m_data->canReadLine() && m_data->waitForReadyRead()) {}
01407 QByteArray data = m_data->readLine();
01408 if (data.size() == 0)
01409 break;
01410
01411 const char* buffer = data.data();
01412 kDebug(7102) << "dir > " << buffer;
01413
01414
01415
01416
01417
01418
01419
01420 const char *p_access, *p_junk, *p_owner, *p_group, *p_size;
01421 if( (p_access = strtok((char*)buffer," ")) == 0) continue;
01422 if( (p_junk = strtok(NULL," ")) == 0) continue;
01423 if( (p_owner = strtok(NULL," ")) == 0) continue;
01424 if( (p_group = strtok(NULL," ")) == 0) continue;
01425 if( (p_size = strtok(NULL," ")) == 0) continue;
01426
01427
01428
01429 de.access = 0;
01430 if ( strlen( p_access ) == 1 && p_junk[0] == '[' ) {
01431 de.access = S_IRWXU | S_IRWXG | S_IRWXO;
01432 }
01433
01434 const char *p_date_1, *p_date_2, *p_date_3, *p_name;
01435
01436
01437
01438
01439 if ( strchr( p_size, ',' ) != 0L )
01440 {
01441
01442 if ((p_size = strtok(NULL," ")) == 0)
01443 continue;
01444 }
01445
01446
01447
01448
01449
01450 if ( !isdigit( *p_size ) )
01451 {
01452 p_date_1 = p_size;
01453 p_size = p_group;
01454 p_group = 0;
01455
01456 }
01457 else
01458 {
01459 p_date_1 = strtok(NULL," ");
01460
01461 }
01462
01463 if ( p_date_1 != 0 &&
01464 (p_date_2 = strtok(NULL," ")) != 0 &&
01465 (p_date_3 = strtok(NULL," ")) != 0 &&
01466 (p_name = strtok(NULL,"\r\n")) != 0 )
01467 {
01468 {
01469 QByteArray tmp( p_name );
01470 if ( p_access[0] == 'l' )
01471 {
01472 int i = tmp.lastIndexOf( " -> " );
01473 if ( i != -1 ) {
01474 de.link = remoteEncoding()->decode(p_name + i + 4);
01475 tmp.truncate( i );
01476 }
01477 else
01478 de.link.clear();
01479 }
01480 else
01481 de.link.clear();
01482
01483 if ( tmp[0] == '/' )
01484 tmp.remove( 0, 1 );
01485
01486 if (tmp.indexOf('/') != -1)
01487 continue;
01488
01489
01490 de.name = remoteEncoding()->decode(tmp.trimmed());
01491 }
01492
01493 de.type = S_IFREG;
01494 switch ( p_access[0] ) {
01495 case 'd':
01496 de.type = S_IFDIR;
01497 break;
01498 case 's':
01499 de.type = S_IFSOCK;
01500 break;
01501 case 'b':
01502 de.type = S_IFBLK;
01503 break;
01504 case 'c':
01505 de.type = S_IFCHR;
01506 break;
01507 case 'l':
01508 de.type = S_IFREG;
01509
01510 break;
01511 default:
01512 break;
01513 }
01514
01515 if ( p_access[1] == 'r' )
01516 de.access |= S_IRUSR;
01517 if ( p_access[2] == 'w' )
01518 de.access |= S_IWUSR;
01519 if ( p_access[3] == 'x' || p_access[3] == 's' )
01520 de.access |= S_IXUSR;
01521 if ( p_access[4] == 'r' )
01522 de.access |= S_IRGRP;
01523 if ( p_access[5] == 'w' )
01524 de.access |= S_IWGRP;
01525 if ( p_access[6] == 'x' || p_access[6] == 's' )
01526 de.access |= S_IXGRP;
01527 if ( p_access[7] == 'r' )
01528 de.access |= S_IROTH;
01529 if ( p_access[8] == 'w' )
01530 de.access |= S_IWOTH;
01531 if ( p_access[9] == 'x' || p_access[9] == 't' )
01532 de.access |= S_IXOTH;
01533 if ( p_access[3] == 's' || p_access[3] == 'S' )
01534 de.access |= S_ISUID;
01535 if ( p_access[6] == 's' || p_access[6] == 'S' )
01536 de.access |= S_ISGID;
01537 if ( p_access[9] == 't' || p_access[9] == 'T' )
01538 de.access |= S_ISVTX;
01539
01540 de.owner = remoteEncoding()->decode(p_owner);
01541 de.group = remoteEncoding()->decode(p_group);
01542 de.size = charToLongLong(p_size);
01543
01544
01545
01546
01547
01548 time_t currentTime = time( 0L );
01549 struct tm * tmptr = gmtime( ¤tTime );
01550 int currentMonth = tmptr->tm_mon;
01551
01552
01553 tmptr->tm_sec = 0;
01554 tmptr->tm_min = 0;
01555 tmptr->tm_hour = 0;
01556
01557 tmptr->tm_mday = atoi( p_date_2 );
01558
01559
01560
01561
01562 static const char * s_months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
01563 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
01564 for ( int c = 0 ; c < 12 ; c ++ )
01565 if ( !strcmp( p_date_1, s_months[c]) )
01566 {
01567
01568 tmptr->tm_mon = c;
01569 break;
01570 }
01571
01572
01573 if ( strlen( p_date_3 ) == 4 )
01574 tmptr->tm_year = atoi( p_date_3 ) - 1900;
01575 else
01576 {
01577
01578
01579
01580
01581
01582
01583 if ( tmptr->tm_mon > currentMonth + 1 )
01584 tmptr->tm_year--;
01585
01586
01587 char * semicolon;
01588 if ( ( semicolon = (char*)strchr( p_date_3, ':' ) ) )
01589 {
01590 *semicolon = '\0';
01591 tmptr->tm_min = atoi( semicolon + 1 );
01592 tmptr->tm_hour = atoi( p_date_3 );
01593 }
01594 else
01595 kWarning(7102) << "Can't parse third field " << p_date_3;
01596 }
01597
01598
01599 de.date = mktime( tmptr );
01600 return true;
01601 }
01602 }
01603 return false;
01604 }
01605
01606
01607
01608
01609
01610 void Ftp::get( const KUrl & url )
01611 {
01612 kDebug(7102) << url;
01613 int iError = 0;
01614 ftpGet(iError, -1, url, 0);
01615 if(iError)
01616 error(iError, url.path());
01617 ftpCloseCommand();
01618 }
01619
01620 Ftp::StatusCode Ftp::ftpGet(int& iError, int iCopyFile, const KUrl& url, KIO::fileoffset_t llOffset)
01621 {
01622
01623 if( !ftpOpenConnection(loginImplicit) )
01624 return statusServerError;
01625
01626
01627
01628
01629
01630
01631 if ( !ftpSize( url.path(), '?' ) && (m_iRespCode == 550) &&
01632 ftpFolder(url.path(), false) )
01633 {
01634
01635 kDebug(7102) << "ftpGet: it is a directory in fact";
01636 iError = ERR_IS_DIRECTORY;
01637 return statusServerError;
01638 }
01639
01640 QString resumeOffset = metaData("resume");
01641 if ( !resumeOffset.isEmpty() )
01642 {
01643 llOffset = resumeOffset.toLongLong();
01644 kDebug(7102) << "ftpGet: got offset from metadata : " << llOffset;
01645 }
01646
01647 if( !ftpOpenCommand("retr", url.path(), '?', ERR_CANNOT_OPEN_FOR_READING, llOffset) )
01648 {
01649 kWarning(7102) << "ftpGet: Can't open for reading";
01650 return statusServerError;
01651 }
01652
01653
01654 if(m_size == UnknownSize)
01655 {
01656 const char* psz = strrchr( ftpResponse(4), '(' );
01657 if(psz) m_size = charToLongLong(psz+1);
01658 if (!m_size) m_size = UnknownSize;
01659 }
01660
01661 KIO::filesize_t bytesLeft = 0;
01662 if ( m_size != UnknownSize )
01663 bytesLeft = m_size - llOffset;
01664
01665 kDebug(7102) << "ftpGet: starting with offset=" << llOffset;
01666 KIO::fileoffset_t processed_size = llOffset;
01667
01668 QByteArray array;
01669 bool mimetypeEmitted = false;
01670 char buffer[maximumIpcSize];
01671
01672
01673
01674 int iBlockSize = initialIpcSize;
01675 int iBufferCur = 0;
01676
01677 while(m_size == UnknownSize || bytesLeft > 0)
01678 {
01679 if(processed_size-llOffset > 1024 * 64)
01680 iBlockSize = maximumIpcSize;
01681
01682
01683 if(iBlockSize+iBufferCur > (int)sizeof(buffer))
01684 iBlockSize = sizeof(buffer) - iBufferCur;
01685 if (m_data->bytesAvailable() == 0)
01686 m_data->waitForReadyRead();
01687 int n = m_data->read( buffer+iBufferCur, iBlockSize );
01688 if(n <= 0)
01689 {
01690 if( m_size == UnknownSize && n == 0 )
01691 break;
01692
01693 iError = ERR_COULD_NOT_READ;
01694 return statusServerError;
01695 }
01696 processed_size += n;
01697
01698
01699 if(m_size != UnknownSize)
01700 {
01701 bytesLeft -= n;
01702 iBufferCur += n;
01703 if(iBufferCur < mimimumMimeSize && bytesLeft > 0)
01704 {
01705 processedSize( processed_size );
01706 continue;
01707 }
01708 n = iBufferCur;
01709 iBufferCur = 0;
01710 }
01711
01712
01713 if(!mimetypeEmitted)
01714 {
01715 mimetypeEmitted = true;
01716 array = QByteArray::fromRawData(buffer, n);
01717 KMimeType::Ptr mime = KMimeType::findByNameAndContent(url.fileName(), array);
01718 array.clear();
01719 kDebug(7102) << "ftpGet: Emitting mimetype " << mime->name();
01720 mimeType( mime->name() );
01721 if( m_size != UnknownSize )
01722 totalSize( m_size );
01723 }
01724
01725
01726 if(iCopyFile == -1)
01727 {
01728 array = QByteArray::fromRawData(buffer, n);
01729 data( array );
01730 array.clear();
01731 }
01732 else if( (iError = WriteToFile(iCopyFile, buffer, n)) != 0)
01733 return statusClientError;
01734 processedSize( processed_size );
01735 }
01736
01737 kDebug(7102) << "ftpGet: done";
01738 if(iCopyFile == -1)
01739 data(array);
01740
01741 processedSize( m_size == UnknownSize ? processed_size : m_size );
01742 kDebug(7102) << "ftpGet: emitting finished()";
01743 finished();
01744 return statusSuccess;
01745 }
01746
01747 #if 0
01748 void Ftp::mimetype( const KUrl& url )
01749 {
01750 if( !ftpOpenConnection(loginImplicit) )
01751 return;
01752
01753 if ( !ftpOpenCommand( "retr", url.path(), 'I', ERR_CANNOT_OPEN_FOR_READING, 0 ) ) {
01754 kWarning(7102) << "Can't open for reading";
01755 return;
01756 }
01757 char buffer[ 2048 ];
01758 QByteArray array;
01759
01760
01761 int n = m_data->read( buffer, 2048 );
01762 array.setRawData(buffer, n);
01763 data( array );
01764 array.resetRawData(buffer, n);
01765
01766 kDebug(7102) << "aborting";
01767 ftpAbortTransfer();
01768
01769 kDebug(7102) << "finished";
01770 finished();
01771 kDebug(7102) << "after finished";
01772 }
01773
01774 void Ftp::ftpAbortTransfer()
01775 {
01776
01777
01778
01779 char msg[4];
01780
01781
01782 msg[0] = (char) 255;
01783 msg[1] = (char) 254;
01784 (void) send(sControl, msg, 2, 0);
01785
01786 msg[0] = (char) 255;
01787 msg[1] = (char) 242;
01788 if (send(sControl, msg, 2, MSG_OOB) != 2)
01789 ;
01790
01791
01792 kDebug(7102) << "send ABOR";
01793 QCString buf = "ABOR\r\n";
01794 if ( KSocks::self()->write( sControl, buf.data(), buf.length() ) <= 0 ) {
01795 error( ERR_COULD_NOT_WRITE, QString() );
01796 return;
01797 }
01798
01799
01800 kDebug(7102) << "read resp";
01801 if ( readresp() != '2' )
01802 {
01803 error( ERR_COULD_NOT_READ, QString() );
01804 return;
01805 }
01806
01807 kDebug(7102) << "close sockets";
01808 closeSockets();
01809 }
01810 #endif
01811
01812
01813
01814
01815
01816 void Ftp::put(const KUrl& url, int permissions, KIO::JobFlags flags)
01817 {
01818 kDebug(7102) << url;
01819 int iError = 0;
01820 ftpPut(iError, -1, url, permissions, flags);
01821 if(iError)
01822 error(iError, url.path());
01823 ftpCloseCommand();
01824 }
01825
01826 Ftp::StatusCode Ftp::ftpPut(int& iError, int iCopyFile, const KUrl& dest_url,
01827 int permissions, KIO::JobFlags flags)
01828 {
01829 if( !ftpOpenConnection(loginImplicit) )
01830 return statusServerError;
01831
01832
01833
01834 bool bMarkPartial;
01835 if (m_user.isEmpty () || m_user == FTP_LOGIN)
01836 bMarkPartial = false;
01837 else
01838 bMarkPartial = config()->readEntry("MarkPartial", true);
01839
01840 QString dest_orig = dest_url.path();
01841 QString dest_part( dest_orig );
01842 dest_part += ".part";
01843
01844 if ( ftpSize( dest_orig, 'I' ) )
01845 {
01846 if ( m_size == 0 )
01847 {
01848 QByteArray cmd = "DELE ";
01849 cmd += remoteEncoding()->encode(dest_orig);
01850 if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
01851 {
01852 iError = ERR_CANNOT_DELETE_PARTIAL;
01853 return statusServerError;
01854 }
01855 }
01856 else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
01857 {
01858 iError = ERR_FILE_ALREADY_EXIST;
01859 return statusServerError;
01860 }
01861 else if ( bMarkPartial )
01862 {
01863 if ( !ftpRename( dest_orig, dest_part, KIO::Overwrite ) )
01864 {
01865 iError = ERR_CANNOT_RENAME_PARTIAL;
01866 return statusServerError;
01867 }
01868 }
01869
01870 permissions = -1;
01871 }
01872 else if ( bMarkPartial && ftpSize( dest_part, 'I' ) )
01873 {
01874 if ( m_size == 0 )
01875 {
01876 QByteArray cmd = "DELE ";
01877 cmd += remoteEncoding()->encode(dest_part);
01878 if ( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
01879 {
01880 iError = ERR_CANNOT_DELETE_PARTIAL;
01881 return statusServerError;
01882 }
01883 }
01884 else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
01885 {
01886 flags |= canResume (m_size) ? KIO::Resume : KIO::DefaultFlags;
01887 if (!(flags & KIO::Resume))
01888 {
01889 iError = ERR_FILE_ALREADY_EXIST;
01890 return statusServerError;
01891 }
01892 }
01893 }
01894 else
01895 m_size = 0;
01896
01897 QString dest;
01898
01899
01900 if ( bMarkPartial ) {
01901 kDebug(7102) << "Adding .part extension to " << dest_orig;
01902 dest = dest_part;
01903 } else
01904 dest = dest_orig;
01905
01906 KIO::fileoffset_t offset = 0;
01907
01908
01909 if( (flags & KIO::Resume) && m_size > 0 )
01910 {
01911 offset = m_size;
01912 if(iCopyFile != -1)
01913 {
01914 if( KDE_lseek(iCopyFile, offset, SEEK_SET) < 0 )
01915 {
01916 iError = ERR_CANNOT_RESUME;
01917 return statusClientError;
01918 }
01919 }
01920 }
01921
01922 if (! ftpOpenCommand( "stor", dest, '?', ERR_COULD_NOT_WRITE, offset ) )
01923 return statusServerError;
01924
01925 kDebug(7102) << "ftpPut: starting with offset=" << offset;
01926 KIO::fileoffset_t processed_size = offset;
01927
01928 QByteArray buffer;
01929 int result;
01930 int iBlockSize = initialIpcSize;
01931
01932 do
01933 {
01934 if(iCopyFile == -1)
01935 {
01936 dataReq();
01937 result = readData( buffer );
01938 }
01939 else
01940 {
01941 if(processed_size-offset > 1024 * 64)
01942 iBlockSize = maximumIpcSize;
01943 buffer.resize(iBlockSize);
01944 result = ::read(iCopyFile, buffer.data(), buffer.size());
01945 if(result < 0)
01946 iError = ERR_COULD_NOT_WRITE;
01947 else
01948 buffer.resize(result);
01949 }
01950
01951 if (result > 0)
01952 {
01953 m_data->write( buffer );
01954 while (m_data->bytesToWrite() && m_data->waitForBytesWritten()) {}
01955 processed_size += result;
01956 processedSize (processed_size);
01957 }
01958 }
01959 while ( result > 0 );
01960
01961 if (result != 0)
01962 {
01963 ftpCloseCommand();
01964 kDebug(7102) << "Error during 'put'. Aborting.";
01965 if (bMarkPartial)
01966 {
01967
01968 if ( ftpSize( dest, 'I' ) &&
01969 ( processed_size < config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE) ) )
01970 {
01971 QByteArray cmd = "DELE ";
01972 cmd += remoteEncoding()->encode(dest);
01973 (void) ftpSendCmd( cmd );
01974 }
01975 }
01976 return statusServerError;
01977 }
01978
01979 if ( !ftpCloseCommand() )
01980 {
01981 iError = ERR_COULD_NOT_WRITE;
01982 return statusServerError;
01983 }
01984
01985
01986 if ( bMarkPartial )
01987 {
01988 kDebug(7102) << "renaming dest (" << dest << ") back to dest_orig (" << dest_orig << ")";
01989 if ( !ftpRename( dest, dest_orig, KIO::Overwrite ) )
01990 {
01991 iError = ERR_CANNOT_RENAME_PARTIAL;
01992 return statusServerError;
01993 }
01994 }
01995
01996
01997 if ( permissions != -1 )
01998 {
01999 if ( m_user == FTP_LOGIN )
02000 kDebug(7102) << "Trying to chmod over anonymous FTP ???";
02001
02002 if ( ! ftpChmod( dest_orig, permissions ) )
02003 {
02004
02005
02006
02007 }
02008 }
02009
02010
02011 finished();
02012 return statusSuccess;
02013 }
02014
02015
02018 bool Ftp::ftpSize( const QString & path, char mode )
02019 {
02020 m_size = UnknownSize;
02021 if( !ftpDataMode(mode) )
02022 return false;
02023
02024 QByteArray buf;
02025 buf = "SIZE ";
02026 buf += remoteEncoding()->encode(path);
02027 if( !ftpSendCmd( buf ) || (m_iRespType != 2) )
02028 return false;
02029
02030
02031 const char* psz = ftpResponse(4);
02032 if(!psz)
02033 return false;
02034 m_size = charToLongLong(psz);
02035 if (!m_size) m_size = UnknownSize;
02036 return true;
02037 }
02038
02039 bool Ftp::ftpFileExists(const QString& path)
02040 {
02041 QByteArray buf;
02042 buf = "SIZE ";
02043 buf += remoteEncoding()->encode(path);
02044 if( !ftpSendCmd( buf ) || (m_iRespType != 2) )
02045 return false;
02046
02047
02048 const char* psz = ftpResponse(4);
02049 return psz != 0;
02050 }
02051
02052
02053
02054
02055
02056
02057
02058
02059 bool Ftp::ftpDataMode(char cMode)
02060 {
02061 if(cMode == '?') cMode = m_bTextMode ? 'A' : 'I';
02062 else if(cMode == 'a') cMode = 'A';
02063 else if(cMode != 'A') cMode = 'I';
02064
02065 kDebug(7102) << "want" << cMode << "has" << m_cDataMode;
02066 if(m_cDataMode == cMode)
02067 return true;
02068
02069 QByteArray buf = "TYPE ";
02070 buf += cMode;
02071 if( !ftpSendCmd(buf) || (m_iRespType != 2) )
02072 return false;
02073 m_cDataMode = cMode;
02074 return true;
02075 }
02076
02077
02078 bool Ftp::ftpFolder(const QString& path, bool bReportError)
02079 {
02080 QString newPath = path;
02081 int iLen = newPath.length();
02082 if(iLen > 1 && newPath[iLen-1] == '/') newPath.truncate(iLen-1);
02083
02084
02085 if(m_currentPath == newPath)
02086 return true;
02087
02088 QByteArray tmp = "cwd ";
02089 tmp += remoteEncoding()->encode(newPath);
02090 if( !ftpSendCmd(tmp) )
02091 return false;
02092 if(m_iRespType != 2)
02093 {
02094 if(bReportError)
02095 error(ERR_CANNOT_ENTER_DIRECTORY, path);
02096 return false;
02097 }
02098 m_currentPath = newPath;
02099 return true;
02100 }
02101
02102
02103
02104
02105
02106
02107
02108 void Ftp::copy( const KUrl &src, const KUrl &dest, int permissions, KIO::JobFlags flags )
02109 {
02110 int iError = 0;
02111 int iCopyFile = -1;
02112 StatusCode cs = statusSuccess;
02113 bool bSrcLocal = src.isLocalFile();
02114 bool bDestLocal = dest.isLocalFile();
02115 QString sCopyFile;
02116
02117 if(bSrcLocal && !bDestLocal)
02118 {
02119 sCopyFile = src.toLocalFile();
02120 kDebug(7102) << "local file" << sCopyFile << "-> ftp" << dest.path();
02121 cs = ftpCopyPut(iError, iCopyFile, sCopyFile, dest, permissions, flags);
02122 if( cs == statusServerError) sCopyFile = dest.url();
02123 }
02124 else if(!bSrcLocal && bDestLocal)
02125 {
02126 sCopyFile = dest.toLocalFile();
02127 kDebug(7102) << "ftp" << src.path() << "-> local file" << sCopyFile;
02128 cs = ftpCopyGet(iError, iCopyFile, sCopyFile, src, permissions, flags);
02129 if( cs == statusServerError ) sCopyFile = src.url();
02130 }
02131 else {
02132 error( ERR_UNSUPPORTED_ACTION, QString() );
02133 return;
02134 }
02135
02136
02137 if(iCopyFile != -1)
02138 ::close(iCopyFile);
02139 if(iError)
02140 error(iError, sCopyFile);
02141 ftpCloseCommand();
02142 }
02143
02144
02145 Ftp::StatusCode Ftp::ftpCopyPut(int& iError, int& iCopyFile, const QString &sCopyFile,
02146 const KUrl& url, int permissions, KIO::JobFlags flags)
02147 {
02148
02149 KDE_struct_stat buff;
02150 QByteArray sSrc( QFile::encodeName(sCopyFile) );
02151 bool bSrcExists = (KDE_stat( sSrc.data(), &buff ) != -1);
02152 if(bSrcExists)
02153 { if(S_ISDIR(buff.st_mode))
02154 {
02155 iError = ERR_IS_DIRECTORY;
02156 return statusClientError;
02157 }
02158 }
02159 else
02160 {
02161 iError = ERR_DOES_NOT_EXIST;
02162 return statusClientError;
02163 }
02164
02165 iCopyFile = KDE_open( sSrc.data(), O_RDONLY );
02166 if(iCopyFile == -1)
02167 {
02168 iError = ERR_CANNOT_OPEN_FOR_READING;
02169 return statusClientError;
02170 }
02171
02172
02173 totalSize(buff.st_size);
02174 #ifdef ENABLE_CAN_RESUME
02175 return ftpPut(iError, iCopyFile, url, permissions, flags & ~KIO::Resume);
02176 #else
02177 return ftpPut(iError, iCopyFile, url, permissions, flags | KIO::Resume);
02178 #endif
02179 }
02180
02181
02182 Ftp::StatusCode Ftp::ftpCopyGet(int& iError, int& iCopyFile, const QString &sCopyFile,
02183 const KUrl& url, int permissions, KIO::JobFlags flags)
02184 {
02185
02186 KDE_struct_stat buff;
02187 QByteArray sDest( QFile::encodeName(sCopyFile) );
02188 bool bDestExists = (KDE_stat( sDest.data(), &buff ) != -1);
02189 if(bDestExists)
02190 { if(S_ISDIR(buff.st_mode))
02191 {
02192 iError = ERR_IS_DIRECTORY;
02193 return statusClientError;
02194 }
02195 if(!(flags & KIO::Overwrite))
02196 {
02197 iError = ERR_FILE_ALREADY_EXIST;
02198 return statusClientError;
02199 }
02200 }
02201
02202
02203 QByteArray sPart = QFile::encodeName(sCopyFile + ".part");
02204 bool bResume = false;
02205 bool bPartExists = (KDE_stat( sPart.data(), &buff ) != -1);
02206 bool bMarkPartial = config()->readEntry("MarkPartial", true);
02207 if(bMarkPartial && bPartExists && buff.st_size > 0)
02208 {
02209 if(S_ISDIR(buff.st_mode))
02210 {
02211 iError = ERR_DIR_ALREADY_EXIST;
02212 return statusClientError;
02213 }
02214
02215 #ifdef ENABLE_CAN_RESUME
02216 bResume = canResume( buff.st_size );
02217 #else
02218 bResume = true;
02219 #endif
02220 }
02221
02222 if(bPartExists && !bResume)
02223 remove(sPart.data());
02224
02225
02226
02227 if(bDestExists)
02228 remove(sDest.data());
02229
02230
02231
02232 mode_t initialMode;
02233 if (permissions != -1)
02234 initialMode = permissions | S_IWUSR;
02235 else
02236 initialMode = 0666;
02237
02238
02239 KIO::fileoffset_t hCopyOffset = 0;
02240 if(bResume)
02241 {
02242 iCopyFile = KDE_open( sPart.data(), O_RDWR );
02243 hCopyOffset = KDE_lseek(iCopyFile, 0, SEEK_END);
02244 if(hCopyOffset < 0)
02245 {
02246 iError = ERR_CANNOT_RESUME;
02247 return statusClientError;
02248 }
02249 kDebug(7102) << "copy: resuming at " << hCopyOffset;
02250 }
02251 else
02252 iCopyFile = KDE_open(sPart.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
02253
02254 if(iCopyFile == -1)
02255 {
02256 kDebug(7102) << "copy: ### COULD NOT WRITE " << sCopyFile;
02257 iError = (errno == EACCES) ? ERR_WRITE_ACCESS_DENIED
02258 : ERR_CANNOT_OPEN_FOR_WRITING;
02259 return statusClientError;
02260 }
02261
02262
02263 StatusCode iRes = ftpGet(iError, iCopyFile, url, hCopyOffset);
02264 if( ::close(iCopyFile) && iRes == statusSuccess )
02265 {
02266 iError = ERR_COULD_NOT_WRITE;
02267 iRes = statusClientError;
02268 }
02269
02270
02271 if(bMarkPartial)
02272 {
02273 if(iRes == statusSuccess)
02274 {
02275 #ifdef Q_OS_WIN
02276 if ( MoveFileExA( sPart.data(),
02277 sDest.data(),
02278 MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED ) == 0 )
02279 #else
02280 if ( KDE_rename( sPart.data(), sDest.data() ) )
02281 #endif
02282 {
02283 kDebug(7102) << "copy: cannot rename " << sPart << " to " << sDest;
02284 iError = ERR_CANNOT_RENAME_PARTIAL;
02285 iRes = statusClientError;
02286 }
02287 }
02288 else if(KDE_stat( sPart.data(), &buff ) == 0)
02289 {
02290 int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
02291 if (buff.st_size < size)
02292 remove(sPart.data());
02293 }
02294 }
02295 return iRes;
02296 }
02297