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