ftp.cpp
上传用户:hbyfgg
上传日期:2015-04-03
资源大小:74k
文件大小:73k
源码类别:

Linux/Unix编程

开发平台:

Unix_Linux

  1. // -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2; c-file-style: "stroustrup" -*-
  2. /*  This file is part of the KDE libraries
  3.     Copyright (C) 2000-2006 David Faure <faure@kde.org>
  4.     This library is free software; you can redistribute it and/or
  5.     modify it under the terms of the GNU Library General Public
  6.     License as published by the Free Software Foundation; either
  7.     version 2 of the License, or (at your option) any later version.
  8.     This library is distributed in the hope that it will be useful,
  9.     but WITHOUT ANY WARRANTY; without even the implied warranty of
  10.     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  11.     Library General Public License for more details.
  12.     You should have received a copy of the GNU Library General Public License
  13.     along with this library; see the file COPYING.LIB.  If not, write to
  14.     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15.     Boston, MA 02110-1301, USA.
  16. */
  17. /*
  18.     Recommended reading explaining FTP details and quirks:
  19.       http://cr.yp.to/ftp.html  (by D.J. Bernstein)
  20.     RFC:
  21.       RFC  959 "File Transfer Protocol (FTP)"
  22.       RFC 1635 "How to Use Anonymous FTP"
  23.       RFC 2428 "FTP Extensions for IPv6 and NATs" (defines EPRT and EPSV)
  24. */
  25. #define  KIO_FTP_PRIVATE_INCLUDE
  26. #include "ftp.h"
  27. #include <sys/stat.h>
  28. #ifdef HAVE_SYS_TIME_H
  29. #include <sys/time.h>
  30. #endif
  31. #ifdef HAVE_SYS_SELECT_H
  32. #include <sys/select.h>
  33. #endif
  34. #include <netinet/in.h>
  35. #include <arpa/inet.h>
  36. #include <assert.h>
  37. #include <ctype.h>
  38. #include <errno.h>
  39. #include <fcntl.h>
  40. #include <netdb.h>
  41. #include <stdlib.h>
  42. #include <string.h>
  43. #include <unistd.h>
  44. #include <signal.h>
  45. #if TIME_WITH_SYS_TIME
  46. #include <time.h>
  47. #endif
  48. #include <QtCore/QDir>
  49. #include <QtNetwork/QHostAddress>
  50. #include <QtNetwork/QTcpServer>
  51. #include <kdebug.h>
  52. #include <kglobal.h>
  53. #include <klocale.h>
  54. #include <kcomponentdata.h>
  55. #include <kmimetype.h>
  56. #include <kio/ioslave_defaults.h>
  57. #include <kio/slaveconfig.h>
  58. #include <kremoteencoding.h>
  59. #include <ksocketfactory.h>
  60. #include <kde_file.h>
  61. #include <kconfiggroup.h>
  62. #include <kmessagebox.h>
  63. #ifdef HAVE_STRTOLL
  64.   #define charToLongLong(a) strtoll(a, 0, 10)
  65. #else
  66.   #define charToLongLong(a) strtol(a, 0, 10)
  67. #endif
  68. #define FTP_LOGIN   "anonymous"
  69. #define FTP_PASSWD  "anonymous@"
  70. //#undef  kDebug
  71. #define ENABLE_CAN_RESUME
  72. // JPF: somebody should find a better solution for this or move this to KIO
  73. // JPF: anyhow, in KDE 3.2.0 I found diffent MAX_IPC_SIZE definitions!
  74. namespace KIO {
  75.     enum buffersizes
  76.     {  /**
  77.         * largest buffer size that should be used to transfer data between
  78.         * KIO slaves using the data() function
  79.         */
  80.         maximumIpcSize = 32 * 1024,
  81.         /**
  82.          * this is a reasonable value for an initial read() that a KIO slave
  83.          * can do to obtain data via a slow network connection.
  84.          */
  85.         initialIpcSize =  2 * 1024,
  86.         /**
  87.          * recommended size of a data block passed to findBufferFileType()
  88.          */
  89.         mimimumMimeSize =     1024
  90.     };
  91.     // JPF: this helper was derived from write_all in file.cc (FileProtocol).
  92.     static // JPF: in ftp.cc we make it static
  93.     /**
  94.      * This helper handles some special issues (blocking and interrupted
  95.      * system call) when writing to a file handle.
  96.      *
  97.      * @return 0 on success or an error code on failure (ERR_COULD_NOT_WRITE,
  98.      * ERR_DISK_FULL, ERR_CONNECTION_BROKEN).
  99.      */
  100.    int WriteToFile(int fd, const char *buf, size_t len)
  101.    {
  102.       while (len > 0)
  103.       {  // JPF: shouldn't there be a KDE_write?
  104.          ssize_t written = write(fd, buf, len);
  105.          if (written >= 0)
  106.          {   buf += written;
  107.              len -= written;
  108.              continue;
  109.          }
  110.          switch(errno)
  111.          {   case EINTR:   continue;
  112.              case EPIPE:   return ERR_CONNECTION_BROKEN;
  113.              case ENOSPC:  return ERR_DISK_FULL;
  114.              default:      return ERR_COULD_NOT_WRITE;
  115.          }
  116.       }
  117.       return 0;
  118.    }
  119. }
  120. KIO::filesize_t Ftp::UnknownSize = (KIO::filesize_t)-1;
  121. using namespace KIO;
  122. extern "C" int KDE_EXPORT kdemain( int argc, char **argv )
  123. {
  124.   KComponentData componentData( "kio_ftpc", "kdelibs4" );
  125.   ( void ) KGlobal::locale();
  126.   kDebug(7102) << "Starting " << getpid();
  127.   if (argc != 4)
  128.   {
  129.      fprintf(stderr, "Usage: kio_ftps protocol domain-socket1 domain-socket2n");
  130.      exit(-1);
  131.   }
  132.   Ftp slave(argv[2], argv[3]);
  133.   slave.dispatchLoop();
  134.   kDebug(7102) << "Done";
  135.   return 0;
  136. }
  137. //===============================================================================
  138. // Ftp
  139. //===============================================================================
  140. Ftp::Ftp( const QByteArray &pool, const QByteArray &app )
  141.     : SlaveBase( "ftps", pool, app )
  142. {
  143.   // init the socket data
  144.   m_data = m_control = NULL;
  145.   ftpCloseControlConnection();
  146.   // init other members
  147.   m_port = 0;
  148.   kDebug(7102) << "Ftp::Ftp()";
  149. }
  150. Ftp::~Ftp()
  151. {
  152.   kDebug(7102) << "Ftp::~Ftp()";
  153.   closeConnection();
  154. }
  155. /**
  156.  * This closes a data connection opened by ftpOpenDataConnection().
  157.  */
  158. void Ftp::ftpCloseDataConnection()
  159. {
  160.   delete m_data;
  161.   m_data = NULL;
  162. }
  163. /**
  164.  * This closes a control connection opened by ftpOpenControlConnection() and reinits the
  165.  * related states.  This method gets called from the constructor with m_control = NULL.
  166.  */
  167. void Ftp::ftpCloseControlConnection()
  168. {
  169.   m_extControl = 0;
  170.   delete m_control;
  171.   m_control = NULL;
  172.   m_cDataMode = 0;
  173.   m_bLoggedOn = false;    // logon needs control connction
  174.   m_bTextMode = false;
  175.   m_bBusy = false;
  176. }
  177. /**
  178.  * Returns the last response from the server (iOffset >= 0)  -or-  reads a new response
  179.  * (iOffset < 0). The result is returned (with iOffset chars skipped for iOffset > 0).
  180.  */
  181. const char* Ftp::ftpResponse(int iOffset)
  182. {
  183.   assert(m_control != NULL);    // must have control connection socket
  184.   const char *pTxt = m_lastControlLine.data();
  185.   // read the next line ...
  186.   if(iOffset < 0)
  187.   {
  188.     int  iMore = 0;
  189.     m_iRespCode = 0;
  190.     // If the server sends multiline responses "nnn-text" we loop here until
  191.     // a final "nnn text" line is reached. Only data from the final line will
  192.     // be stored. Some servers (OpenBSD) send a single "nnn-" followed by
  193.     // optional lines that start with a space and a final "nnn text" line.
  194.     do {
  195.       while (!m_control->canReadLine() && m_control->waitForReadyRead()) {}
  196.       m_lastControlLine = m_control->readLine();
  197.       pTxt = m_lastControlLine.data();
  198.       int nBytes = m_lastControlLine.size();
  199.       int iCode  = atoi(pTxt);
  200.       if(iCode > 0) m_iRespCode = iCode;
  201.       // ignore lines starting with a space in multiline response
  202.       if(iMore != 0 && pTxt[0] == 32)
  203.         ;
  204.       // otherwise the line should start with "nnn-" or "nnn "
  205.       else if(nBytes < 4 || iCode < 100)
  206.         iMore = 0;
  207.       // we got a valid line, now check for multiline responses ...
  208.       else if(iMore == 0 && pTxt[3] == '-')
  209.         iMore = iCode;
  210.       // "nnn " ends multiline mode ...
  211.       else if(iMore != 0 && (iMore != iCode || pTxt[3] != '-'))
  212.         iMore = 0;
  213.       if(iMore != 0)
  214.          kDebug(7102) << "    > " << pTxt;
  215.     } while(iMore != 0);
  216.     kDebug(7102) << "resp> " << pTxt;
  217.     m_iRespType = (m_iRespCode > 0) ? m_iRespCode / 100 : 0;
  218.   }
  219.   // return text with offset ...
  220.   while(iOffset-- > 0 && pTxt[0])
  221.     pTxt++;
  222.   return pTxt;
  223. }
  224. void Ftp::closeConnection()
  225. {
  226.   if(m_control != NULL || m_data != NULL)
  227.     kDebug(7102) << "Ftp::closeConnection m_bLoggedOn=" << m_bLoggedOn << " m_bBusy=" << m_bBusy;
  228.   if(m_bBusy)              // ftpCloseCommand not called
  229.   {
  230.     kWarning(7102) << "Ftp::closeConnection Abandoned data stream";
  231.     ftpCloseDataConnection();
  232.   }
  233.   if(m_bLoggedOn)           // send quit
  234.   {
  235.     if( !ftpSendCmd( "quit", 0 ) || (m_iRespType != 2) )
  236.       kWarning(7102) << "Ftp::closeConnection QUIT returned error: " << m_iRespCode;
  237.   }
  238.   // close the data and control connections ...
  239.   ftpCloseDataConnection();
  240.   ftpCloseControlConnection();
  241. }
  242. void Ftp::setHost( const QString& _host, quint16 _port, const QString& _user,
  243.                    const QString& _pass )
  244. {
  245.   kDebug(7102) << "Ftp::setHost (" << getpid() << "): " << _host << " port=" << _port;
  246.   m_proxyURL = metaData("UseProxy");
  247.   m_bUseProxy = (m_proxyURL.isValid() && m_proxyURL.protocol() == "ftp");
  248.   if ( m_host != _host || m_port != _port ||
  249.        m_user != _user || m_pass != _pass )
  250.     closeConnection();
  251.   m_host = _host;
  252.   m_port = _port;
  253.   m_user = _user;
  254.   m_pass = _pass;
  255. }
  256. void Ftp::openConnection()
  257. {
  258.   ftpOpenConnection(loginExplicit);
  259. }
  260. bool Ftp::ftpOpenConnection (LoginMode loginMode)
  261. {
  262.   // check for implicit login if we are already logged on ...
  263.   if(loginMode == loginImplicit && m_bLoggedOn)
  264.   {
  265.     assert(m_control != NULL);    // must have control connection socket
  266.     return true;
  267.   }
  268.   kDebug(7102) << "ftpOpenConnection " << m_host << ":" << m_port << " "
  269.                 << m_user << " [password hidden]";
  270.   infoMessage( i18n("Opening connection to host %1", m_host) );
  271.   if ( m_host.isEmpty() )
  272.   {
  273.     error( ERR_UNKNOWN_HOST, QString() );
  274.     return false;
  275.   }
  276.   assert( !m_bLoggedOn );
  277.   m_initialPath.clear();
  278.   m_currentPath.clear();
  279.   QString host = m_bUseProxy ? m_proxyURL.host() : m_host;
  280.   int port = m_bUseProxy ? m_proxyURL.port() : m_port;
  281.   if (!ftpOpenControlConnection(host, port) )
  282.     return false;          // error emitted by ftpOpenControlConnection
  283.   infoMessage( i18n("Connected to host %1", m_host) );
  284.   if(loginMode != loginDefered)
  285.   {
  286.     m_bLoggedOn = ftpLogin();
  287.     if( !m_bLoggedOn )
  288.       return false;       // error emitted by ftpLogin
  289.   }
  290.   m_bTextMode = config()->readEntry("textmode", false);
  291.   connected();
  292.   return true;
  293. }
  294. /**
  295.  * Called by @ref openConnection. It opens the control connection to the ftp server.
  296.  *
  297.  * @return true on success.
  298.  */
  299. bool Ftp::ftpOpenControlConnection( const QString &host, int port, bool ignoreSslErrors )
  300. {
  301.   m_bIgnoreSslErrors = ignoreSslErrors;
  302.   // implicitly close, then try to open a new connection ...
  303.   closeConnection();
  304.   QString sErrorMsg;
  305.   // now connect to the server and read the login message ...
  306.   if (port == 0)
  307.     port = 21;                  // default FTP port
  308. m_control = new QSslSocket();
  309. KSocketFactory::synchronousConnectToHost(m_control, "ftps", host, port, connectTimeout() * 1000);
  310.   int iErrorCode = m_control->state() == QAbstractSocket::ConnectedState ? 0 : ERR_COULD_NOT_CONNECT;
  311.   // on connect success try to read the server message...
  312.   if(iErrorCode == 0)
  313.   {
  314.     const char* psz = ftpResponse(-1);
  315.     if(m_iRespType != 2)
  316.     { // login not successful, do we have an message text?
  317.       if(psz[0])
  318.         sErrorMsg = i18n("%1.nnReason: %2", host, psz);
  319.       iErrorCode = ERR_COULD_NOT_CONNECT;
  320.     }
  321.   }
  322.   else
  323.   {
  324.     if (m_control->error() == QAbstractSocket::HostNotFoundError)
  325.       iErrorCode = ERR_UNKNOWN_HOST;
  326.     sErrorMsg = QString("%1: %2").arg(host).arg(m_control->errorString());
  327.   }
  328.   // Send unencrypted "AUTH TLS" request.
  329.   // TODO: redirect to FTP fallback on negative response.
  330.   if(iErrorCode == 0) 
  331.   {
  332.     bool authSucc = (ftpSendCmd("AUTH TLS") && (m_iRespCode == 234));
  333.     if (!authSucc)
  334.     {
  335.       iErrorCode = ERR_SLAVE_DEFINED;
  336.       sErrorMsg = i18n("The FTP server does not seem to support ftps-encryption.");
  337.     }
  338.   }
  339.   // Starts the encryption
  340.   if(iErrorCode == 0) 
  341.   {
  342.     // If the method has been called with ignoreSslErrors, make the ssl socket
  343.     // ignore the errors during handshakes. 
  344.     if (ignoreSslErrors) m_control->ignoreSslErrors();
  345.     m_control->startClientEncryption();
  346.     if (!m_control->waitForEncrypted(connectTimeout() * 1000)) 
  347.     {
  348.       // It is quite common, that the TLS handshake fails, as the majority 
  349.       // of certificates are self signed, and thus the host cannot be verified.
  350.       // If the user wants to continue nevertheless, this method is called
  351.       // again, with the "ignoreSslErrors" flag.
  352.       bool doNotIgnore = false;
  353.       QList<QSslError> errors = m_control->sslErrors();
  354.       for (int i = 0; i < errors.size(); ++i) 
  355.       {
  356. if (messageBox(WarningContinueCancel, errors.at(i).errorString(), 
  357. "TLS Handshake Error", 
  358. i18n("&Continue"), 
  359. i18n("&Cancel")) == KMessageBox::Cancel) doNotIgnore = false; 
  360.       }
  361.       if (doNotIgnore) 
  362.       {
  363. iErrorCode = ERR_SLAVE_DEFINED;
  364. sErrorMsg = i18n("TLS Handshake Error.");
  365.       }
  366.       else
  367.       {
  368. closeConnection();
  369. return ftpOpenControlConnection(host, port, true);
  370.       }
  371.     }
  372.   }
  373.   // if there was a problem - report it ...
  374.   if(iErrorCode == 0)             // OK, return success
  375.     return true;
  376.   closeConnection();              // clean-up on error
  377.   error(iErrorCode, sErrorMsg);
  378.   return false;
  379. }
  380. /**
  381.  * Called by @ref openConnection. It logs us in.
  382.  * @ref m_initialPath is set to the current working directory
  383.  * if logging on was successful.
  384.  *
  385.  * @return true on success.
  386.  */
  387. bool Ftp::ftpLogin()
  388. {
  389.   infoMessage( i18n("Sending login information") );
  390.   assert( !m_bLoggedOn );
  391.   QString user = m_user;
  392.   QString pass = m_pass;
  393.   if ( config()->readEntry("EnableAutoLogin", false) )
  394.   {
  395.     QString au = config()->readEntry("autoLoginUser");
  396.     if ( !au.isEmpty() )
  397.     {
  398.         user = au;
  399.         pass = config()->readEntry("autoLoginPass");
  400.     }
  401.   }
  402.   // Try anonymous login if both username/password
  403.   // information is blank.
  404.   if (user.isEmpty() && pass.isEmpty())
  405.   {
  406.     user = FTP_LOGIN;
  407.     pass = FTP_PASSWD;
  408.   }
  409.   AuthInfo info;
  410.   info.url.setProtocol( "ftp" );
  411.   info.url.setHost( m_host );
  412.   if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
  413.       info.url.setPort( m_port );
  414.   info.url.setUser( user );
  415.   QByteArray tempbuf;
  416.   int failedAuth = 0;
  417.   do
  418.   {
  419.     // Check the cache and/or prompt user for password if 1st
  420.     // login attempt failed OR the user supplied a login name,
  421.     // but no password.
  422.     if ( failedAuth > 0 || (!user.isEmpty() && pass.isEmpty()) )
  423.     {
  424.       QString errorMsg;
  425.       kDebug(7102) << "Prompting user for login info...";
  426.       // Ask user if we should retry after when login fails!
  427.       if( failedAuth > 0 )
  428.       {
  429.         errorMsg = i18n("Message sent:nLogin using username=%1 and "
  430.                         "password=[hidden]nnServer replied:n%2nn"
  431.                         , user, ftpResponse(0));
  432.       }
  433.       if ( user != FTP_LOGIN )
  434.         info.username = user;
  435.       info.prompt = i18n("You need to supply a username and a password "
  436.                           "to access this site.");
  437.       info.commentLabel = i18n( "Site:" );
  438.       info.comment = i18n("<b>%1</b>",  m_host );
  439.       info.keepPassword = true; // Prompt the user for persistence as well.
  440.       info.readOnly = (!m_user.isEmpty() && m_user != FTP_LOGIN);
  441.       bool disablePassDlg = config()->readEntry( "DisablePassDlg", false );
  442.       if ( disablePassDlg || !openPasswordDialog( info, errorMsg ) )
  443.       {
  444.         error( ERR_USER_CANCELED, m_host );
  445.         return false;
  446.       }
  447.       else
  448.       {
  449.         user = info.username;
  450.         pass = info.password;
  451.       }
  452.     }
  453.     tempbuf = "USER ";
  454.     tempbuf += user.toLatin1();
  455.     if ( m_bUseProxy )
  456.     {
  457.       tempbuf += '@';
  458.       tempbuf += m_host.toLatin1();
  459.       if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
  460.       {
  461.         tempbuf += ':';
  462.         tempbuf += QString::number(m_port).toLatin1();
  463.       }
  464.     }
  465.     kDebug(7102) << "Sending Login name: " << tempbuf;
  466.     bool loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
  467.     bool needPass = (m_iRespCode == 331);
  468.     // Prompt user for login info if we do not
  469.     // get back a "230" or "331".
  470.     if ( !loggedIn && !needPass )
  471.     {
  472.       kDebug(7102) << "Login failed: " << ftpResponse(0);
  473.       ++failedAuth;
  474.       continue;  // Well we failed, prompt the user please!!
  475.     }
  476.     if( needPass )
  477.     {
  478.       tempbuf = "pass ";
  479.       tempbuf += pass.toLatin1();
  480.       kDebug(7102) << "Sending Login password: " << "[protected]";
  481.       loggedIn = ( ftpSendCmd(tempbuf) && (m_iRespCode == 230) );
  482.     }
  483.     if ( loggedIn )
  484.     {
  485.       // Do not cache the default login!!
  486.       if( user != FTP_LOGIN && pass != FTP_PASSWD )
  487.         cacheAuthentication( info );
  488.       failedAuth = -1;
  489.     }
  490.   } while( ++failedAuth );
  491.   kDebug(7102) << "Login OK";
  492.   infoMessage( i18n("Login OK") );
  493.   // Okay, we're logged in. If this is IIS 4, switch dir listing style to Unix:
  494.   // Thanks to jk@soegaard.net (Jens Kristian Sgaard) for this hint
  495.   if( ftpSendCmd("SYST") && (m_iRespType == 2) )
  496.   {
  497.     if( !strncmp( ftpResponse(0), "215 Windows_NT", 14 ) ) // should do for any version
  498.     {
  499.       ftpSendCmd( "site dirstyle" );
  500.       // Check if it was already in Unix style
  501.       // Patch from Keith Refson <Keith.Refson@earth.ox.ac.uk>
  502.       if( !strncmp( ftpResponse(0), "200 MSDOS-like directory output is on", 37 ))
  503.          //It was in Unix style already!
  504.          ftpSendCmd( "site dirstyle" );
  505.       // windows won't support chmod before KDE konquers their desktop...
  506.       m_extControl |= chmodUnknown;
  507.     }
  508.   }
  509.   else
  510.     kWarning(7102) << "SYST failed";
  511.   if ( config()->readEntry ("EnableAutoLoginMacro", false) )
  512.     ftpAutoLoginMacro ();
  513.   // Get the current working directory
  514.   kDebug(7102) << "Searching for pwd";
  515.   if( !ftpSendCmd("PWD") || (m_iRespType != 2) )
  516.   {
  517.     kDebug(7102) << "Couldn't issue pwd command";
  518.     error( ERR_COULD_NOT_LOGIN, i18n("Could not login to %1.", m_host) ); // or anything better ?
  519.     return false;
  520.   }
  521.   QString sTmp = remoteEncoding()->decode( ftpResponse(3) );
  522.   int iBeg = sTmp.indexOf('"');
  523.   int iEnd = sTmp.lastIndexOf('"');
  524.   if(iBeg > 0 && iBeg < iEnd)
  525.   {
  526.     m_initialPath = sTmp.mid(iBeg+1, iEnd-iBeg-1);
  527.     if(m_initialPath[0] != '/') m_initialPath.prepend('/');
  528.     kDebug(7102) << "Initial path set to: " << m_initialPath;
  529.     m_currentPath = m_initialPath;
  530.   }
  531.   return true;
  532. }
  533. void Ftp::ftpAutoLoginMacro ()
  534. {
  535.   QString macro = metaData( "autoLoginMacro" );
  536.   if ( macro.isEmpty() )
  537.     return;
  538.   QStringList list = macro.split('n',QString::SkipEmptyParts);
  539.   for(QStringList::Iterator it = list.begin() ; it != list.end() ; ++it )
  540.   {
  541.     if ( (*it).startsWith("init") )
  542.     {
  543.       list = macro.split( '\',QString::SkipEmptyParts);
  544.       it = list.begin();
  545.       ++it;  // ignore the macro name
  546.       for( ; it != list.end() ; ++it )
  547.       {
  548.         // TODO: Add support for arbitrary commands
  549.         // besides simply changing directory!!
  550.         if ( (*it).startsWith( "cwd" ) )
  551.           ftpFolder( (*it).mid(4).trimmed(), false );
  552.       }
  553.       break;
  554.     }
  555.   }
  556. }
  557. /**
  558.  * ftpSendCmd - send a command (@p cmd) and read response
  559.  *
  560.  * @param maxretries number of time it should retry. Since it recursively
  561.  * calls itself if it can't read the answer (this happens especially after
  562.  * timeouts), we need to limit the recursiveness ;-)
  563.  *
  564.  * return true if any response received, false on error
  565.  */
  566. bool Ftp::ftpSendCmd( const QByteArray& cmd, int maxretries )
  567. {
  568.   assert(m_control != NULL);    // must have control connection socket
  569.   if ( cmd.indexOf( 'r' ) != -1 || cmd.indexOf( 'n' ) != -1)
  570.   {
  571.     kWarning(7102) << "Invalid command received (contains CR or LF):"
  572.                     << cmd.data();
  573.     error( ERR_UNSUPPORTED_ACTION, m_host );
  574.     return false;
  575.   }
  576.   // Don't print out the password...
  577.   bool isPassCmd = (cmd.left(4).toLower() == "pass");
  578.   if ( !isPassCmd )
  579.     kDebug(7102) << "send> " << cmd.data();
  580.   else
  581.     kDebug(7102) << "send> pass [protected]";
  582.   // Send the message...
  583.   QByteArray buf = cmd;
  584.   buf += "rn";      // Yes, must use CR/LF - see http://cr.yp.to/ftp/request.html
  585.   int num = m_control->write(buf);
  586.   while (m_control->bytesToWrite() && m_control->waitForBytesWritten()) {}
  587.   // If we were able to successfully send the command, then we will
  588.   // attempt to read the response. Otherwise, take action to re-attempt
  589.   // the login based on the maximum number of retires specified...
  590.   if( num > 0 )
  591.     ftpResponse(-1);
  592.   else
  593.   {
  594.     m_iRespType = m_iRespCode = 0;
  595.   }
  596.   // If respCh is NULL or the response is 421 (Timed-out), we try to re-send
  597.   // the command based on the value of maxretries.
  598.   if( (m_iRespType <= 0) || (m_iRespCode == 421) )
  599.   {
  600.     // We have not yet logged on...
  601.     if (!m_bLoggedOn)
  602.     {
  603.       // The command was sent from the ftpLogin function, i.e. we are actually
  604.       // attempting to login in. NOTE: If we already sent the username, we
  605.       // return false and let the user decide whether (s)he wants to start from
  606.       // the beginning...
  607.       if (maxretries > 0 && !isPassCmd)
  608.       {
  609.         closeConnection ();
  610.         if( ftpOpenConnection(loginDefered) )
  611.           ftpSendCmd ( cmd, maxretries - 1 );
  612.       }
  613.       return false;
  614.     }
  615.     else
  616.     {
  617.       if ( maxretries < 1 )
  618.         return false;
  619.       else
  620.       {
  621.         kDebug(7102) << "Was not able to communicate with " << m_host
  622.                       << "Attempting to re-establish connection.";
  623.         closeConnection(); // Close the old connection...
  624.         openConnection();  // Attempt to re-establish a new connection...
  625.         if (!m_bLoggedOn)
  626.         {
  627.           if (m_control != NULL)  // if openConnection succeeded ...
  628.           {
  629.             kDebug(7102) << "Login failure, aborting";
  630.             error (ERR_COULD_NOT_LOGIN, m_host);
  631.             closeConnection ();
  632.           }
  633.           return false;
  634.         }
  635.         kDebug(7102) << "Logged back in, re-issuing command";
  636.         // If we were able to login, resend the command...
  637.         if (maxretries)
  638.           maxretries--;
  639.         return ftpSendCmd( cmd, maxretries );
  640.       }
  641.     }
  642.   }
  643.   return true;
  644. }
  645. /*
  646.  * ftpOpenPASVDataConnection - set up data connection, using PASV mode
  647.  *
  648.  * return 0 if successful, ERR_INTERNAL otherwise
  649.  * doesn't set error message, since non-pasv mode will always be tried if
  650.  * this one fails
  651.  */
  652. int Ftp::ftpOpenPASVDataConnection()
  653. {
  654.   assert(m_control != NULL);    // must have control connection socket
  655.   assert(m_data == NULL);       // ... but no data connection
  656.   // Check that we can do PASV
  657.   QHostAddress addr = m_control->peerAddress();
  658.   if (addr.protocol() != QAbstractSocket::IPv4Protocol)
  659.     return ERR_INTERNAL;       // no PASV for non-PF_INET connections
  660.  if (m_extControl & pasvUnknown)
  661.     return ERR_INTERNAL;       // already tried and got "unknown command"
  662.   m_bPasv = true;
  663.   /* Let's PASsiVe*/
  664.   if( !ftpSendCmd("PASV") || (m_iRespType != 2) )
  665.   {
  666.     kDebug(7102) << "PASV attempt failed";
  667.     // unknown command?
  668.     if( m_iRespType == 5 )
  669.     {
  670.         kDebug(7102) << "disabling use of PASV";
  671.         m_extControl |= pasvUnknown;
  672.     }
  673.     return ERR_INTERNAL;
  674.   }
  675.   // The usual answer is '227 Entering Passive Mode. (160,39,200,55,6,245)'
  676.   // but anonftpd gives '227 =160,39,200,55,6,245'
  677.   int i[6];
  678.   const char *start = strchr(ftpResponse(3), '(');
  679.   if ( !start )
  680.     start = strchr(ftpResponse(3), '=');
  681.   if ( !start ||
  682.        ( sscanf(start, "(%d,%d,%d,%d,%d,%d)",&i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 &&
  683.          sscanf(start, "=%d,%d,%d,%d,%d,%d", &i[0], &i[1], &i[2], &i[3], &i[4], &i[5]) != 6 ) )
  684.   {
  685.     kError(7102) << "parsing IP and port numbers failed. String parsed: " << start;
  686.     return ERR_INTERNAL;
  687.   }
  688.   // we ignore the host part on purpose for two reasons
  689.   // a) it might be wrong anyway
  690.   // b) it would make us being suceptible to a port scanning attack
  691.   // now connect the data socket ...
  692.   quint16 port = i[4] << 8 | i[5];
  693.   kDebug(7102) << "Connecting to " << addr.toString() << " port " << port;
  694.   m_data = new QSslSocket();
  695.   KSocketFactory::synchronousConnectToHost(m_data, "ftp-data", addr.toString(), port, connectTimeout() * 1000);
  696.   return m_data->state() == QAbstractSocket::ConnectedState ? 0 : ERR_INTERNAL;
  697. }
  698. /*
  699.  * ftpOpenEPSVDataConnection - opens a data connection via EPSV
  700.  */
  701. int Ftp::ftpOpenEPSVDataConnection()
  702. {
  703.   assert(m_control != NULL);    // must have control connection socket
  704.   assert(m_data == NULL);       // ... but no data connection
  705.   QHostAddress address = m_control->peerAddress();
  706.   int portnum;
  707.   if (m_extControl & epsvUnknown)
  708.     return ERR_INTERNAL;
  709.   m_bPasv = true;
  710.   if( !ftpSendCmd("EPSV") || (m_iRespType != 2) )
  711.   {
  712.     // unknown command?
  713.     if( m_iRespType == 5 )
  714.     {
  715.        kDebug(7102) << "disabling use of EPSV";
  716.        m_extControl |= epsvUnknown;
  717.     }
  718.     return ERR_INTERNAL;
  719.   }
  720.   const char *start = strchr(ftpResponse(3), '|');
  721.   if ( !start || sscanf(start, "|||%d|", &portnum) != 1)
  722.     return ERR_INTERNAL;
  723.   m_data = new QSslSocket();
  724.   KSocketFactory::synchronousConnectToHost(m_data, "ftp-data", address.toString(), portnum,
  725.                                                     connectTimeout() * 1000);
  726.   return m_data->isOpen() ? 0 : ERR_INTERNAL;
  727. }
  728. int Ftp::encryptDataChannel()
  729. {
  730.   if (m_bIgnoreSslErrors) m_data->ignoreSslErrors();
  731.   if (m_bPasv) m_data->startClientEncryption();
  732.   else m_data->startServerEncryption();
  733.   if (!m_data->waitForEncrypted(connectTimeout() * 1000)) return ERR_SLAVE_DEFINED;
  734.   return 0;
  735. }
  736. bool Ftp::requestDataEncryption()
  737. {
  738.   // initate tls transfer for data chanel on the control channel 
  739.   bool pbszSucc = (ftpSendCmd("PBSZ 0") && (m_iRespType == 2));
  740.   if (!pbszSucc) return false;
  741.     
  742.   // try protected data transfer first
  743.   bool protpSucc = (ftpSendCmd("PROT P") && (m_iRespType == 2));
  744.   if (!protpSucc)
  745.   {
  746.     // Set the data channel to clear (should not be necessary, just in case).
  747.     ftpSendCmd("PROT C");
  748.     return false;
  749.   }
  750.   
  751.   return true;
  752. }
  753. /*
  754.  * ftpOpenDataConnection - set up data connection
  755.  *
  756.  * The routine calls several ftpOpenXxxxConnection() helpers to find
  757.  * the best connection mode. If a helper cannot connect if returns
  758.  * ERR_INTERNAL - so this is not really an error! All other error
  759.  * codes are treated as fatal, e.g. they are passed back to the caller
  760.  * who is responsible for calling error(). ftpOpenPortDataConnection
  761.  * can be called as last try and it does never return ERR_INTERNAL.
  762.  *
  763.  * @return 0 if successful, err code otherwise
  764.  */
  765. int Ftp::ftpOpenDataConnection()
  766. {
  767.   // make sure that we are logged on and have no data connection...
  768.   assert( m_bLoggedOn );
  769.   ftpCloseDataConnection();
  770.   int  iErrCode = 0;
  771.   int  iErrCodePASV = 0;  // Remember error code from PASV
  772.   // First try passive (EPSV & PASV) modes
  773.   if ( !config()->readEntry("DisablePassiveMode", false) )
  774.   {
  775.     iErrCode = ftpOpenPASVDataConnection();
  776.     if(iErrCode == 0)
  777.     {
  778.       // success
  779.       requestDataEncryption();
  780.       return 0; 
  781.     }
  782.     iErrCodePASV = iErrCode;
  783.     ftpCloseDataConnection();
  784.     if ( !config()->readEntry("DisableEPSV", false) )
  785.     {
  786.       iErrCode = ftpOpenEPSVDataConnection();
  787.       if(iErrCode == 0)
  788.       {
  789. // success
  790. requestDataEncryption();
  791. return 0; 
  792.       }
  793.       ftpCloseDataConnection();
  794.     }
  795.     // if we sent EPSV ALL already and it was accepted, then we can't
  796.     // use active connections any more
  797.     if (m_extControl & epsvAllSent)
  798.       return iErrCodePASV ? iErrCodePASV : iErrCode;
  799.   }
  800.   // fall back to port mode
  801.   iErrCode = ftpOpenPortDataConnection();
  802.   if(iErrCode == 0) 
  803.   {
  804.     // success
  805.     requestDataEncryption();
  806.     return 0; 
  807.   }
  808.   ftpCloseDataConnection();
  809.   // prefer to return the error code from PASV if any, since that's what should have worked in the first place
  810.   return iErrCodePASV ? iErrCodePASV : iErrCode;
  811. }
  812. /*
  813.  * ftpOpenPortDataConnection - set up data connection
  814.  *
  815.  * @return 0 if successful, err code otherwise (but never ERR_INTERNAL
  816.  *         because this is the last connection mode that is tried)
  817.  */
  818. int Ftp::ftpOpenPortDataConnection()
  819. {
  820.   assert(m_control != NULL);    // must have control connection socket
  821.   assert(m_data == NULL);       // ... but no data connection
  822.   m_bPasv = false;
  823.   if (m_extControl & eprtUnknown)
  824.     return ERR_INTERNAL;
  825.   //QTcpServer *server = new SslServer();
  826.   SslServer *server = new SslServer(); //KSocketFactory::listen("ftp-data");
  827.   server = new SslServer();
  828.   server->setProxy(KSocketFactory::proxyForListening("ftp-data"));
  829.   server->listen();
  830.   if (!server->isListening())
  831.   {
  832.     delete server;
  833.     return ERR_COULD_NOT_LISTEN;
  834.   }
  835.   server->setMaxPendingConnections(1);
  836.   QString command;
  837.   QHostAddress localAddress = m_control->localAddress();
  838.   if (localAddress.protocol() == QAbstractSocket::IPv4Protocol)
  839.   {
  840.     struct
  841.     {
  842.       quint32 ip4;
  843.       quint16 port;
  844.     } data;
  845.     data.ip4 = localAddress.toIPv4Address();
  846.     data.port = server->serverPort();
  847.     unsigned char *pData = reinterpret_cast<unsigned char*>(&data);
  848.     command.sprintf("PORT %d,%d,%d,%d,%d,%d",pData[0],pData[1],pData[2],pData[3],pData[4],pData[5]);
  849.   }
  850.   else if (localAddress.protocol() == QAbstractSocket::IPv6Protocol)
  851.   {
  852.     command = QString("EPRT |2|%2|%3|").arg(localAddress.toString()).arg(server->serverPort());
  853.   }
  854.   if( ftpSendCmd(command.toLatin1()) && (m_iRespType == 2) ) return 0;
  855.   {
  856.     server->waitForNewConnection(connectTimeout() * 1000);
  857.     m_data = server->socket();
  858.     //m_data = server->nextPendingConnection();
  859.     delete server;
  860.     return m_data ? 0 : ERR_COULD_NOT_CONNECT;
  861.   }
  862.   delete server;
  863.   return ERR_INTERNAL;
  864. }
  865. bool Ftp::ftpOpenCommand( const char *_command, const QString & _path, char _mode,
  866.                           int errorcode, KIO::fileoffset_t _offset )
  867. {
  868.   int errCode = 0;
  869.   if( !ftpDataMode(_mode) )
  870.     errCode = ERR_COULD_NOT_CONNECT;
  871.   else
  872.     errCode = ftpOpenDataConnection();
  873.   if(errCode != 0)
  874.   {
  875.     error(errCode, m_host);
  876.     return false;
  877.   }
  878.   bool useDataEnc = requestDataEncryption();
  879.   if ( _offset > 0 ) {
  880.     // send rest command if offset > 0, this applies to retr and stor commands
  881.     char buf[100];
  882.     sprintf(buf, "rest %lld", _offset);
  883.     if ( !ftpSendCmd( buf ) )
  884.        return false;
  885.     if( m_iRespType != 3 )
  886.     {
  887.       error( ERR_CANNOT_RESUME, _path ); // should never happen
  888.       return false;
  889.     }
  890.   }
  891.   QByteArray tmp = _command;
  892.   QString errormessage;
  893.   if ( !_path.isEmpty() ) {
  894.     tmp += ' ';
  895.     tmp += remoteEncoding()->encode(_path);
  896.   }
  897.   if( !ftpSendCmd( tmp ) || (m_iRespType != 1) )
  898.   {
  899.     if( _offset > 0 && strcmp(_command, "retr") == 0 && (m_iRespType == 4) )
  900.       errorcode = ERR_CANNOT_RESUME;
  901.     // The error here depends on the command
  902.     errormessage = _path;
  903.   }
  904.   else
  905.   {
  906.     // Only now we know for sure that we can resume
  907.     if ( _offset > 0 && strcmp(_command, "retr") == 0 )
  908.       canResume();
  909.     m_bBusy = true;              // cleared in ftpCloseCommand
  910.     if (useDataEnc) 
  911.     {
  912.       int result = encryptDataChannel();
  913.       if (result != 0) 
  914.       {
  915. error(result, "TLS Negotiation failed on the data channel."); 
  916. return false;
  917.       }
  918.     }
  919.     return true;
  920.   }
  921.   error(errorcode, errormessage);
  922.   return false;
  923. }
  924. bool Ftp::ftpCloseCommand()
  925. {
  926.   // first close data sockets (if opened), then read response that
  927.   // we got for whatever was used in ftpOpenCommand ( should be 226 )
  928.   if(m_data)
  929.   {
  930.     delete  m_data;
  931.     m_data = NULL;
  932.   }
  933.   if(!m_bBusy)
  934.     return true;
  935.   kDebug(7102) << "ftpCloseCommand: reading command result";
  936.   m_bBusy = false;
  937.   if(!ftpResponse(-1) || (m_iRespType != 2) )
  938.   {
  939.     kDebug(7102) << "ftpCloseCommand: no transfer complete message";
  940.     return false;
  941.   }
  942.   return true;
  943. }
  944. void Ftp::mkdir( const KUrl & url, int permissions )
  945. {
  946.   if( !ftpOpenConnection(loginImplicit) )
  947.         return;
  948.   QString path = remoteEncoding()->encode(url);
  949.   QByteArray buf = "mkd ";
  950.   buf += remoteEncoding()->encode(path);
  951.   if( !ftpSendCmd( buf ) || (m_iRespType != 2) )
  952.   {
  953.     QString currentPath( m_currentPath );
  954.     // Check whether or not mkdir failed because
  955.     // the directory already exists...
  956.     if( ftpFolder( path, false ) )
  957.     {
  958.       error( ERR_DIR_ALREADY_EXIST, path );
  959.       // Change the directory back to what it was...
  960.       (void) ftpFolder( currentPath, false );
  961.       return;
  962.     }
  963.     error( ERR_COULD_NOT_MKDIR, path );
  964.     return;
  965.   }
  966.   if ( permissions != -1 )
  967.   {
  968.     // chmod the dir we just created, ignoring errors.
  969.     (void) ftpChmod( path, permissions );
  970.   }
  971.   finished();
  972. }
  973. void Ftp::rename( const KUrl& src, const KUrl& dst, KIO::JobFlags flags )
  974. {
  975.   if( !ftpOpenConnection(loginImplicit) )
  976.         return;
  977.   // The actual functionality is in ftpRename because put needs it
  978.   if ( ftpRename( src.path(), dst.path(), flags ) )
  979.     finished();
  980.   else
  981.     error( ERR_CANNOT_RENAME, src.path() );
  982. }
  983. bool Ftp::ftpRename( const QString & src, const QString & dst, KIO::JobFlags )
  984. {
  985.   // TODO honor overwrite
  986.   assert( m_bLoggedOn );
  987.   int pos = src.lastIndexOf("/");
  988.   if( !ftpFolder(src.left(pos+1), false) )
  989.       return false;
  990.   QByteArray from_cmd = "RNFR ";
  991.   from_cmd += remoteEncoding()->encode(src.mid(pos+1));
  992.   if( !ftpSendCmd( from_cmd ) || (m_iRespType != 3) )
  993.       return false;
  994.   QByteArray to_cmd = "RNTO ";
  995.   to_cmd += remoteEncoding()->encode(dst);
  996.   if( !ftpSendCmd( to_cmd ) || (m_iRespType != 2) )
  997.       return false;
  998.   return true;
  999. }
  1000. void Ftp::del( const KUrl& url, bool isfile )
  1001. {
  1002.   if( !ftpOpenConnection(loginImplicit) )
  1003.         return;
  1004.   // When deleting a directory, we must exit from it first
  1005.   // The last command probably went into it (to stat it)
  1006.   if ( !isfile )
  1007.     ftpFolder(remoteEncoding()->directory(url), false); // ignore errors
  1008.   QByteArray cmd = isfile ? "DELE " : "RMD ";
  1009.   cmd += remoteEncoding()->encode(url);
  1010.   if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
  1011.     error( ERR_CANNOT_DELETE, url.path() );
  1012.   else
  1013.     finished();
  1014. }
  1015. bool Ftp::ftpChmod( const QString & path, int permissions )
  1016. {
  1017.   assert( m_bLoggedOn );
  1018.   if(m_extControl & chmodUnknown)      // previous errors?
  1019.     return false;
  1020.   // we need to do bit AND 777 to get permissions, in case
  1021.   // we were sent a full mode (unlikely)
  1022.   QString cmd = QString::fromLatin1("SITE CHMOD ") + QString::number( permissions & 511, 8 /*octal*/ ) + ' ';
  1023.   cmd += path;
  1024.   ftpSendCmd(remoteEncoding()->encode(cmd));
  1025.   if(m_iRespType == 2)
  1026.      return true;
  1027.   if(m_iRespCode == 500)
  1028.   {
  1029.     m_extControl |= chmodUnknown;
  1030.     kDebug(7102) << "ftpChmod: CHMOD not supported - disabling";
  1031.   }
  1032.   return false;
  1033. }
  1034. void Ftp::chmod( const KUrl & url, int permissions )
  1035. {
  1036.   if( !ftpOpenConnection(loginImplicit) )
  1037.         return;
  1038.   if ( !ftpChmod( url.path(), permissions ) )
  1039.     error( ERR_CANNOT_CHMOD, url.path() );
  1040.   else
  1041.     finished();
  1042. }
  1043. void Ftp::ftpCreateUDSEntry( const QString & filename, FtpEntry& ftpEnt, UDSEntry& entry, bool isDir )
  1044. {
  1045.   assert(entry.count() == 0); // by contract :-)
  1046.   entry.insert( KIO::UDSEntry::UDS_NAME, filename );
  1047.   entry.insert( KIO::UDSEntry::UDS_SIZE, ftpEnt.size );
  1048.   entry.insert( KIO::UDSEntry::UDS_MODIFICATION_TIME, ftpEnt.date );
  1049.   entry.insert( KIO::UDSEntry::UDS_ACCESS, ftpEnt.access );
  1050.   entry.insert( KIO::UDSEntry::UDS_USER, ftpEnt.owner );
  1051.   if ( !ftpEnt.group.isEmpty() )
  1052.   {
  1053.     entry.insert( KIO::UDSEntry::UDS_GROUP, ftpEnt.group );
  1054.   }
  1055.   if ( !ftpEnt.link.isEmpty() )
  1056.   {
  1057.     entry.insert( KIO::UDSEntry::UDS_LINK_DEST, ftpEnt.link );
  1058.     KMimeType::Ptr mime = KMimeType::findByUrl( KUrl("ftps://host/" + filename ) );
  1059.     // Links on ftp sites are often links to dirs, and we have no way to check
  1060.     // that. Let's do like Netscape : assume dirs generally.
  1061.     // But we do this only when the mimetype can't be known from the filename.
  1062.     // --> we do better than Netscape :-)
  1063.     if ( mime->name() == KMimeType::defaultMimeType() )
  1064.     {
  1065.       kDebug(7102) << "Setting guessed mime type to inode/directory for " << filename;
  1066.       entry.insert( KIO::UDSEntry::UDS_GUESSED_MIME_TYPE, QString::fromLatin1( "inode/directory" ) );
  1067.       isDir = true;
  1068.     }
  1069.   }
  1070.   entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : ftpEnt.type );
  1071.   // entry.insert KIO::UDSEntry::UDS_ACCESS_TIME,buff.st_atime);
  1072.   // entry.insert KIO::UDSEntry::UDS_CREATION_TIME,buff.st_ctime);
  1073. }
  1074. void Ftp::ftpShortStatAnswer( const QString& filename, bool isDir )
  1075. {
  1076.     UDSEntry entry;
  1077.     entry.insert( KIO::UDSEntry::UDS_NAME, filename );
  1078.     entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : S_IFREG );
  1079.     entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
  1080.     // No details about size, ownership, group, etc.
  1081.     statEntry(entry);
  1082.     finished();
  1083. }
  1084. void Ftp::ftpStatAnswerNotFound( const QString & path, const QString & filename )
  1085. {
  1086.     // Only do the 'hack' below if we want to download an existing file (i.e. when looking at the "source")
  1087.     // When e.g. uploading a file, we still need stat() to return "not found"
  1088.     // when the file doesn't exist.
  1089.     QString statSide = metaData("statSide");
  1090.     kDebug(7102) << "Ftp::stat statSide=" << statSide;
  1091.     if ( statSide == "source" )
  1092.     {
  1093.         kDebug(7102) << "Not found, but assuming found, because some servers don't allow listing";
  1094.         // MS Server is incapable of handling "list <blah>" in a case insensitive way
  1095.         // But "retr <blah>" works. So lie in stat(), to get going...
  1096.         //
  1097.         // There's also the case of ftp://ftp2.3ddownloads.com/90380/linuxgames/loki/patches/ut/ut-patch-436.run
  1098.         // where listing permissions are denied, but downloading is still possible.
  1099.         ftpShortStatAnswer( filename, false /*file, not dir*/ );
  1100.         return;
  1101.     }
  1102.     error( ERR_DOES_NOT_EXIST, path );
  1103. }
  1104. void Ftp::stat( const KUrl &url)
  1105. {
  1106.   kDebug(7102) << "Ftp::stat : path='" << url.path() << "'";
  1107.   if( !ftpOpenConnection(loginImplicit) )
  1108.         return;
  1109.   QString path = QDir::cleanPath( url.path() );
  1110.   kDebug(7102) << "Ftp::stat : cleaned path='" << path << "'";
  1111.   // We can't stat root, but we know it's a dir.
  1112.   if( path.isEmpty() || path == "/" )
  1113.   {
  1114.     UDSEntry entry;
  1115.     //entry.insert( KIO::UDSEntry::UDS_NAME, UDSField( QString() ) );
  1116.     entry.insert( KIO::UDSEntry::UDS_NAME, QString::fromLatin1( "." ) );
  1117.     entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
  1118.     entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
  1119.     entry.insert( KIO::UDSEntry::UDS_USER, QString::fromLatin1( "root" ) );
  1120.     entry.insert( KIO::UDSEntry::UDS_GROUP, QString::fromLatin1( "root" ) );
  1121.     // no size
  1122.     statEntry( entry );
  1123.     finished();
  1124.     return;
  1125.   }
  1126.   KUrl tempurl( url );
  1127.   tempurl.setPath( path ); // take the clean one
  1128.   QString listarg; // = tempurl.directory(KUrl::ObeyTrailingSlash);
  1129.   QString parentDir;
  1130.   QString filename = tempurl.fileName();
  1131.   Q_ASSERT(!filename.isEmpty());
  1132.   QString search = filename;
  1133.   // Try cwd into it, if it works it's a dir (and then we'll list the parent directory to get more info)
  1134.   // if it doesn't work, it's a file (and then we'll use dir filename)
  1135.   bool isDir = ftpFolder(path, false);
  1136.   // if we're only interested in "file or directory", we should stop here
  1137.   QString sDetails = metaData("details");
  1138.   int details = sDetails.isEmpty() ? 2 : sDetails.toInt();
  1139.   kDebug(7102) << "Ftp::stat details=" << details;
  1140.   if ( details == 0 )
  1141.   {
  1142.      if ( !isDir && !ftpSize( path, 'I' ) ) // ok, not a dir -> is it a file ?
  1143.      {  // no -> it doesn't exist at all
  1144.         ftpStatAnswerNotFound( path, filename );
  1145.         return;
  1146.      }
  1147.      ftpShortStatAnswer( filename, isDir ); // successfully found a dir or a file -> done
  1148.      return;
  1149.   }
  1150.   if (!isDir)
  1151.   {
  1152.     // It is a file or it doesn't exist, try going to parent directory
  1153.     parentDir = tempurl.directory(KUrl::AppendTrailingSlash);
  1154.     // With files we can do "LIST <filename>" to avoid listing the whole dir
  1155.     listarg = filename;
  1156.   }
  1157.   else
  1158.   {
  1159.     // --- New implementation:
  1160.     // Don't list the parent dir. Too slow, might not show it, etc.
  1161.     // Just return that it's a dir.
  1162.     UDSEntry entry;
  1163.     entry.insert( KIO::UDSEntry::UDS_NAME, filename );
  1164.     entry.insert( KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR );
  1165.     entry.insert( KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH );
  1166.     // No clue about size, ownership, group, etc.
  1167.     statEntry(entry);
  1168.     finished();
  1169.     return;
  1170.     // --- Old implementation:
  1171. #if 0
  1172.     // It's a dir, remember that
  1173.     // Reason: it could be a symlink to a dir, in which case ftpReadDir
  1174.     // in the parent dir will have no idea about that. But we know better.
  1175.     isDir = true;
  1176.     // If the dir starts with '.', we'll need '-a' to see it in the listing.
  1177.     if ( search[0] == '.' )
  1178.        listarg = "-a";
  1179.     parentDir = "..";
  1180. #endif
  1181.   }
  1182.   // Now cwd the parent dir, to prepare for listing
  1183.   if( !ftpFolder(parentDir, true) )
  1184.     return;
  1185.   if( !ftpOpenCommand( "list", listarg, 'I', ERR_DOES_NOT_EXIST ) )
  1186.   {
  1187.     kError(7102) << "COULD NOT LIST";
  1188.     return;
  1189.   }
  1190.   kDebug(7102) << "Starting of list was ok";
  1191.   Q_ASSERT( !search.isEmpty() && search != "/" );
  1192.   bool bFound = false;
  1193.   KUrl      linkURL;
  1194.   FtpEntry  ftpEnt;
  1195.   while( ftpReadDir(ftpEnt) )
  1196.   {
  1197.     // We look for search or filename, since some servers (e.g. ftp.tuwien.ac.at)
  1198.     // return only the filename when doing "dir /full/path/to/file"
  1199.     if ( !bFound )
  1200.     {
  1201.         if ( ( search == ftpEnt.name || filename == ftpEnt.name ) ) {
  1202.             if ( !filename.isEmpty() ) {
  1203.               bFound = true;
  1204.               UDSEntry entry;
  1205.               ftpCreateUDSEntry( filename, ftpEnt, entry, isDir );
  1206.               statEntry( entry );
  1207.             }
  1208.         }
  1209. #if 0 // goes with the "old implementation" above
  1210.         else if ( isDir && ( ftpEnt.name == listarg || ftpEnt.name+'/' == listarg ) ) {
  1211.             // Damn, the dir we're trying to list is in fact a symlink
  1212.             // Follow it and try again
  1213.             if ( ftpEnt.link.isEmpty() )
  1214.                 kWarning(7102) << "Got " << listarg << " as answer, but empty link!";
  1215.             else
  1216.             {
  1217.                 linkURL = url;
  1218.                 kDebug(7102) << "ftpEnt.link=" << ftpEnt.link;
  1219.                 if ( ftpEnt.link[0] == '/' )
  1220.                     linkURL.setPath( ftpEnt.link ); // Absolute link
  1221.                 else
  1222.                 {
  1223.                     // Relative link (stat will take care of cleaning ../.. etc.)
  1224.                     linkURL.setPath( listarg ); // this is what we were listing (the link)
  1225.                     linkURL.setPath( linkURL.directory() ); // go up one dir
  1226.                     linkURL.addPath( ftpEnt.link ); // replace link by its destination
  1227.                     kDebug(7102) << "linkURL now " << linkURL.prettyUrl();
  1228.                 }
  1229.                 // Re-add the filename we're looking for
  1230.                 linkURL.addPath( filename );
  1231.             }
  1232.             bFound = true;
  1233.         }
  1234. #endif
  1235.     }
  1236.     // kDebug(7102) << ftpEnt.name;
  1237.   }
  1238.   ftpCloseCommand();        // closes the data connection only
  1239.   if ( !bFound )
  1240.   {
  1241.     ftpStatAnswerNotFound( path, filename );
  1242.     return;
  1243.   }
  1244.   if ( !linkURL.isEmpty() )
  1245.   {
  1246.       if ( linkURL == url || linkURL == tempurl )
  1247.       {
  1248.           error( ERR_CYCLIC_LINK, linkURL.prettyUrl() );
  1249.           return;
  1250.       }
  1251.       stat( linkURL );
  1252.       return;
  1253.   }
  1254.   kDebug(7102) << "stat : finished successfully";
  1255.   finished();
  1256. }
  1257. void Ftp::listDir( const KUrl &url )
  1258. {
  1259.   kDebug(7102) << "Ftp::listDir " << url.prettyUrl();
  1260.   if( !ftpOpenConnection(loginImplicit) )
  1261.         return;
  1262.   // No path specified ?
  1263.   QString path = url.path();
  1264.   if ( path.isEmpty() )
  1265.   {
  1266.     KUrl realURL;
  1267.     realURL.setProtocol( "ftps" );
  1268.     if ( m_user != FTP_LOGIN )
  1269.       realURL.setUser( m_user );
  1270.     // We set the password, so that we don't ask for it if it was given
  1271.     if ( m_pass != FTP_PASSWD )
  1272.       realURL.setPass( m_pass );
  1273.     realURL.setHost( m_host );
  1274.     if ( m_port > 0 && m_port != DEFAULT_FTP_PORT )
  1275.         realURL.setPort( m_port );
  1276.     if ( m_initialPath.isEmpty() )
  1277.         m_initialPath = "/";
  1278.     realURL.setPath( m_initialPath );
  1279.     kDebug(7102) << "REDIRECTION to " << realURL.prettyUrl();
  1280.     redirection( realURL );
  1281.     finished();
  1282.     return;
  1283.   }
  1284.   kDebug(7102) << "hunting for path '" << path << "'";
  1285.   if (!ftpOpenDir( path ) )
  1286.   {
  1287.     if ( ftpSize( path, 'I' ) ) // is it a file ?
  1288.     {
  1289.       error( ERR_IS_FILE, path );
  1290.       return;
  1291.     }
  1292.     // not sure which to emit
  1293.     //error( ERR_DOES_NOT_EXIST, path );
  1294.     error( ERR_CANNOT_ENTER_DIRECTORY, path );
  1295.     return;
  1296.   }
  1297.   UDSEntry entry;
  1298.   FtpEntry  ftpEnt;
  1299.   while( ftpReadDir(ftpEnt) )
  1300.   {
  1301.     //kDebug(7102) << ftpEnt.name;
  1302.     //Q_ASSERT( !ftpEnt.name.isEmpty() );
  1303.     if ( !ftpEnt.name.isEmpty() )
  1304.     {
  1305.       //if ( S_ISDIR( (mode_t)ftpEnt.type ) )
  1306.       //   kDebug(7102) << "is a dir";
  1307.       //if ( !ftpEnt.link.isEmpty() )
  1308.       //   kDebug(7102) << "is a link to " << ftpEnt.link;
  1309.       entry.clear();
  1310.       ftpCreateUDSEntry( ftpEnt.name, ftpEnt, entry, false );
  1311.       listEntry( entry, false );
  1312.     }
  1313.   }
  1314.   listEntry( entry, true ); // ready
  1315.   ftpCloseCommand();        // closes the data connection only
  1316.   finished();
  1317. }
  1318. void Ftp::slave_status()
  1319. {
  1320.   kDebug(7102) << "Got slave_status host = " << (!m_host.toAscii().isEmpty() ? m_host.toAscii() : "[None]") << " [" << (m_bLoggedOn ? "Connected" : "Not connected") << "]";
  1321.   slaveStatus( m_host, m_bLoggedOn );
  1322. }
  1323. bool Ftp::ftpOpenDir( const QString & path )
  1324. {
  1325.   //QString path( _url.path(KUrl::RemoveTrailingSlash) );
  1326.   // We try to change to this directory first to see whether it really is a directory.
  1327.   // (And also to follow symlinks)
  1328.   QString tmp = path.isEmpty() ? QString("/") : path;
  1329.   // We get '550', whether it's a file or doesn't exist...
  1330.   if( !ftpFolder(tmp, false) )
  1331.       return false;
  1332.   // Don't use the path in the list command:
  1333.   // We changed into this directory anyway - so it's enough just to send "list".
  1334.   // We use '-a' because the application MAY be interested in dot files.
  1335.   // The only way to really know would be to have a metadata flag for this...
  1336.   // Since some windows ftp server seems not to support the -a argument, we use a fallback here.
  1337.   // In fact we have to use -la otherwise -a removes the default -l (e.g. ftp.trolltech.com)
  1338.   if( !ftpOpenCommand( "list -la", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) )
  1339.   {
  1340.     if ( !ftpOpenCommand( "list", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY ) )
  1341.     {
  1342.       kWarning(7102) << "Can't open for listing";
  1343.       return false;
  1344.     }
  1345.   }
  1346.   kDebug(7102) << "Starting of list was ok";
  1347.   return true;
  1348. }
  1349. bool Ftp::ftpReadDir(FtpEntry& de)
  1350. {
  1351.   assert(m_data != NULL);
  1352.   // get a line from the data connecetion ...
  1353.   while( true )
  1354.   {
  1355.     while (!m_data->canReadLine() && m_data->waitForReadyRead()) {}
  1356.     QByteArray data = m_data->readLine();
  1357.     if (data.size() == 0)
  1358.       break;
  1359.     const char* buffer = data.data();
  1360.     kDebug(7102) << "dir > " << buffer;
  1361.     //Normally the listing looks like
  1362.     // -rw-r--r--   1 dfaure   dfaure        102 Nov  9 12:30 log
  1363.     // but on Netware servers like ftp://ci-1.ci.pwr.wroc.pl/ it looks like (#76442)
  1364.     // d [RWCEAFMS] Admin                     512 Oct 13  2004 PSI
  1365.     // we should always get the following 5 fields ...
  1366.     const char *p_access, *p_junk, *p_owner, *p_group, *p_size;
  1367.     if( (p_access = strtok((char*)buffer," ")) == 0) continue;
  1368.     if( (p_junk  = strtok(NULL," ")) == 0) continue;
  1369.     if( (p_owner = strtok(NULL," ")) == 0) continue;
  1370.     if( (p_group = strtok(NULL," ")) == 0) continue;
  1371.     if( (p_size  = strtok(NULL," ")) == 0) continue;
  1372.     //kDebug(7102) << "p_access=" << p_access << " p_junk=" << p_junk << " p_owner=" << p_owner << " p_group=" << p_group << " p_size=" << p_size;
  1373.     de.access = 0;
  1374.     if ( strlen( p_access ) == 1 && p_junk[0] == '[' ) { // Netware
  1375.       de.access = S_IRWXU | S_IRWXG | S_IRWXO; // unknown -> give all permissions
  1376.     }
  1377.     const char *p_date_1, *p_date_2, *p_date_3, *p_name;
  1378.     // A special hack for "/dev". A listing may look like this:
  1379.     // crw-rw-rw-   1 root     root       1,   5 Jun 29  1997 zero
  1380.     // So we just ignore the number in front of the ",". Ok, its a hack :-)
  1381.     if ( strchr( p_size, ',' ) != 0L )
  1382.     {
  1383.       //kDebug(7102) << "Size contains a ',' -> reading size again (/dev hack)";
  1384.       if ((p_size = strtok(NULL," ")) == 0)
  1385.         continue;
  1386.     }
  1387.     // Check whether the size we just read was really the size
  1388.     // or a month (this happens when the server lists no group)
  1389.     // Used to be the case on sunsite.uio.no, but not anymore
  1390.     // This is needed for the Netware case, too.
  1391.     if ( !isdigit( *p_size ) )
  1392.     {
  1393.       p_date_1 = p_size;
  1394.       p_size = p_group;
  1395.       p_group = 0;
  1396.       //kDebug(7102) << "Size didn't have a digit -> size=" << p_size << " date_1=" << p_date_1;
  1397.     }
  1398.     else
  1399.     {
  1400.       p_date_1 = strtok(NULL," ");
  1401.       //kDebug(7102) << "Size has a digit -> ok. p_date_1=" << p_date_1;
  1402.     }
  1403.     if ( p_date_1 != 0 &&
  1404.          (p_date_2 = strtok(NULL," ")) != 0 &&
  1405.          (p_date_3 = strtok(NULL," ")) != 0 &&
  1406.          (p_name = strtok(NULL,"rn")) != 0 )
  1407.     {
  1408.       {
  1409.         QByteArray tmp( p_name );
  1410.         if ( p_access[0] == 'l' )
  1411.         {
  1412.           int i = tmp.lastIndexOf( " -> " );
  1413.           if ( i != -1 ) {
  1414.             de.link = remoteEncoding()->decode(p_name + i + 4);
  1415.             tmp.truncate( i );
  1416.           }
  1417.           else
  1418.             de.link.clear();
  1419.         }
  1420.         else
  1421.           de.link.clear();
  1422.         if ( tmp[0] == '/' ) // listing on ftp://ftp.gnupg.org/ starts with '/'
  1423.           tmp.remove( 0, 1 );
  1424.         if (tmp.indexOf('/') != -1)
  1425.           continue; // Don't trick us!
  1426.         // Some sites put more than one space between the date and the name
  1427.         // e.g. ftp://ftp.uni-marburg.de/mirror/
  1428.         de.name     = remoteEncoding()->decode(tmp.trimmed());
  1429.       }
  1430.       de.type = S_IFREG;
  1431.       switch ( p_access[0] ) {
  1432.       case 'd':
  1433.         de.type = S_IFDIR;
  1434.         break;
  1435.       case 's':
  1436.         de.type = S_IFSOCK;
  1437.         break;
  1438.       case 'b':
  1439.         de.type = S_IFBLK;
  1440.         break;
  1441.       case 'c':
  1442.         de.type = S_IFCHR;
  1443.         break;
  1444.       case 'l':
  1445.         de.type = S_IFREG;
  1446.         // we don't set S_IFLNK here.  de.link says it.
  1447.         break;
  1448.       default:
  1449.         break;
  1450.       }
  1451.       if ( p_access[1] == 'r' )
  1452.         de.access |= S_IRUSR;
  1453.       if ( p_access[2] == 'w' )
  1454.         de.access |= S_IWUSR;
  1455.       if ( p_access[3] == 'x' || p_access[3] == 's' )
  1456.         de.access |= S_IXUSR;
  1457.       if ( p_access[4] == 'r' )
  1458.         de.access |= S_IRGRP;
  1459.       if ( p_access[5] == 'w' )
  1460.         de.access |= S_IWGRP;
  1461.       if ( p_access[6] == 'x' || p_access[6] == 's' )
  1462.         de.access |= S_IXGRP;
  1463.       if ( p_access[7] == 'r' )
  1464.         de.access |= S_IROTH;
  1465.       if ( p_access[8] == 'w' )
  1466.         de.access |= S_IWOTH;
  1467.       if ( p_access[9] == 'x' || p_access[9] == 't' )
  1468.         de.access |= S_IXOTH;
  1469.       if ( p_access[3] == 's' || p_access[3] == 'S' )
  1470.         de.access |= S_ISUID;
  1471.       if ( p_access[6] == 's' || p_access[6] == 'S' )
  1472.         de.access |= S_ISGID;
  1473.       if ( p_access[9] == 't' || p_access[9] == 'T' )
  1474.         de.access |= S_ISVTX;
  1475.       de.owner    = remoteEncoding()->decode(p_owner);
  1476.       de.group    = remoteEncoding()->decode(p_group);
  1477.       de.size     = charToLongLong(p_size);
  1478.       // Parsing the date is somewhat tricky
  1479.       // Examples : "Oct  6 22:49", "May 13  1999"
  1480.       // First get current time - we need the current month and year
  1481.       time_t currentTime = time( 0L );
  1482.       struct tm * tmptr = gmtime( &currentTime );
  1483.       int currentMonth = tmptr->tm_mon;
  1484.       //kDebug(7102) << "Current time :" << asctime( tmptr );
  1485.       // Reset time fields
  1486.       tmptr->tm_sec = 0;
  1487.       tmptr->tm_min = 0;
  1488.       tmptr->tm_hour = 0;
  1489.       // Get day number (always second field)
  1490.       tmptr->tm_mday = atoi( p_date_2 );
  1491.       // Get month from first field
  1492.       // NOTE : no, we don't want to use KLocale here
  1493.       // It seems all FTP servers use the English way
  1494.       //kDebug(7102) << "Looking for month " << p_date_1;
  1495.       static const char * s_months[12] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1496.                                            "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  1497.       for ( int c = 0 ; c < 12 ; c ++ )
  1498.         if ( !strcmp( p_date_1, s_months[c]) )
  1499.         {
  1500.           //kDebug(7102) << "Found month " << c << " for " << p_date_1;
  1501.           tmptr->tm_mon = c;
  1502.           break;
  1503.         }
  1504.       // Parse third field
  1505.       if ( strlen( p_date_3 ) == 4 ) // 4 digits, looks like a year
  1506.         tmptr->tm_year = atoi( p_date_3 ) - 1900;
  1507.       else
  1508.       {
  1509.         // otherwise, the year is implicit
  1510.         // according to man ls, this happens when it is between than 6 months
  1511.         // old and 1 hour in the future.
  1512.         // So the year is : current year if tm_mon <= currentMonth+1
  1513.         // otherwise current year minus one
  1514.         // (The +1 is a security for the "+1 hour" at the end of the month issue)
  1515.         if ( tmptr->tm_mon > currentMonth + 1 )
  1516.           tmptr->tm_year--;
  1517.         // and p_date_3 contains probably a time
  1518.         char * semicolon;
  1519.         if ( ( semicolon = (char*)strchr( p_date_3, ':' ) ) )
  1520.         {
  1521.           *semicolon = '';
  1522.           tmptr->tm_min = atoi( semicolon + 1 );
  1523.           tmptr->tm_hour = atoi( p_date_3 );
  1524.         }
  1525.         else
  1526.           kWarning(7102) << "Can't parse third field " << p_date_3;
  1527.       }
  1528.       //kDebug(7102) << asctime( tmptr );
  1529.       de.date = mktime( tmptr );
  1530.       return true;
  1531.     }
  1532.   } // line invalid, loop to get another line
  1533.   return false;
  1534. }
  1535. //===============================================================================
  1536. // public: get           download file from server
  1537. // helper: ftpGet        called from get() and copy()
  1538. //===============================================================================
  1539. void Ftp::get( const KUrl & url )
  1540. {
  1541.   kDebug(7102) << "Ftp::get " << url.url();
  1542.   int iError = 0;
  1543.   ftpGet(iError, -1, url, 0);               // iError gets status
  1544.   if(iError)                                // can have only server side errs
  1545.      error(iError, url.path());
  1546.   ftpCloseCommand();                        // must close command!
  1547. }
  1548. Ftp::StatusCode Ftp::ftpGet(int& iError, int iCopyFile, const KUrl& url, KIO::fileoffset_t llOffset)
  1549. {
  1550.   // Calls error() by itself!
  1551.   if( !ftpOpenConnection(loginImplicit) )
  1552.     return statusServerError;
  1553.   // Try to find the size of the file (and check that it exists at
  1554.   // the same time). If we get back a 550, "File does not exist"
  1555.   // or "not a plain file", check if it is a directory. If it is a
  1556.   // directory, return an error; otherwise simply try to retrieve
  1557.   // the request...
  1558.   if ( !ftpSize( url.path(), '?' ) && (m_iRespCode == 550) &&
  1559.        ftpFolder(url.path(), false) )
  1560.   {
  1561.     // Ok it's a dir in fact
  1562.     kDebug(7102) << "ftpGet: it is a directory in fact";
  1563.     iError = ERR_IS_DIRECTORY;
  1564.     return statusServerError;
  1565.   }
  1566.   QString resumeOffset = metaData("resume");
  1567.   if ( !resumeOffset.isEmpty() )
  1568.   {
  1569.     llOffset = resumeOffset.toLongLong();
  1570.     kDebug(7102) << "ftpGet: got offset from metadata : " << llOffset;
  1571.   }
  1572.   if( !ftpOpenCommand("retr", url.path(), '?', ERR_CANNOT_OPEN_FOR_READING, llOffset) )
  1573.   {
  1574.     kWarning(7102) << "ftpGet: Can't open for reading";
  1575.     return statusServerError;
  1576.   }
  1577.   // Read the size from the response string
  1578.   if(m_size == UnknownSize)
  1579.   {
  1580.     const char* psz = strrchr( ftpResponse(4), '(' );
  1581.     if(psz) m_size = charToLongLong(psz+1);
  1582.     if (!m_size) m_size = UnknownSize;
  1583.   }
  1584.   KIO::filesize_t bytesLeft = 0;
  1585.   if ( m_size != UnknownSize )
  1586.     bytesLeft = m_size - llOffset;
  1587.   kDebug(7102) << "ftpGet: starting with offset=" << llOffset;
  1588.   KIO::fileoffset_t processed_size = llOffset;
  1589.   QByteArray array;
  1590.   bool mimetypeEmitted = false;
  1591.   char buffer[maximumIpcSize];
  1592.   // start with small data chunks in case of a slow data source (modem)
  1593.   // - unfortunately this has a negative impact on performance for large
  1594.   // - files - so we will increase the block size after a while ...
  1595.   int iBlockSize = initialIpcSize;
  1596.   int iBufferCur = 0;
  1597.   while(m_size == UnknownSize || bytesLeft > 0)
  1598.   {  // let the buffer size grow if the file is larger 64kByte ...
  1599.     if(processed_size-llOffset > 1024 * 64)
  1600.       iBlockSize = maximumIpcSize;
  1601.     // read the data and detect EOF or error ...
  1602.     if(iBlockSize+iBufferCur > (int)sizeof(buffer))
  1603.       iBlockSize = sizeof(buffer) - iBufferCur;
  1604.     if (m_data->bytesAvailable() == 0)
  1605.       m_data->waitForReadyRead();
  1606.     int n = m_data->read( buffer+iBufferCur, iBlockSize );
  1607.     if(n <= 0)
  1608.     {   // this is how we detect EOF in case of unknown size
  1609.       if( m_size == UnknownSize && n == 0 )
  1610.         break;
  1611.       // unexpected eof. Happens when the daemon gets killed.
  1612.       iError = ERR_COULD_NOT_READ;
  1613.       return statusServerError;
  1614.     }
  1615.     processed_size += n;
  1616.     // collect very small data chunks in buffer before processing ...
  1617.     if(m_size != UnknownSize)
  1618.     {
  1619.       bytesLeft -= n;
  1620.       iBufferCur += n;
  1621.       if(iBufferCur < mimimumMimeSize && bytesLeft > 0)
  1622.       {
  1623.         processedSize( processed_size );
  1624.         continue;
  1625.       }
  1626.       n = iBufferCur;
  1627.       iBufferCur = 0;
  1628.     }
  1629.     // get the mime type and set the total size ...
  1630.     if(!mimetypeEmitted)
  1631.     {
  1632.       mimetypeEmitted = true;
  1633.       array = QByteArray::fromRawData(buffer, n);
  1634.       KMimeType::Ptr mime = KMimeType::findByNameAndContent(url.fileName(), array);
  1635.       array.clear();
  1636.       kDebug(7102) << "ftpGet: Emitting mimetype " << mime->name();
  1637.       mimeType( mime->name() );
  1638.       if( m_size != UnknownSize ) // Emit total size AFTER mimetype
  1639.         totalSize( m_size );
  1640.     }
  1641.     // write output file or pass to data pump ...
  1642.     if(iCopyFile == -1)
  1643.     {
  1644.         array = QByteArray::fromRawData(buffer, n);
  1645.         data( array );
  1646.         array.clear();
  1647.     }
  1648.     else if( (iError = WriteToFile(iCopyFile, buffer, n)) != 0)
  1649.        return statusClientError;              // client side error
  1650.     processedSize( processed_size );
  1651.   }
  1652.   kDebug(7102) << "ftpGet: done";
  1653.   if(iCopyFile == -1)          // must signal EOF to data pump ...
  1654.     data(array);               // array is empty and must be empty!
  1655.   processedSize( m_size == UnknownSize ? processed_size : m_size );
  1656.   kDebug(7102) << "ftpGet: emitting finished()";
  1657.   finished();
  1658.   return statusSuccess;
  1659. }
  1660. #if 0
  1661.   void Ftp::mimetype( const KUrl& url )
  1662.   {
  1663.     if( !ftpOpenConnection(loginImplicit) )
  1664.           return;
  1665.     if ( !ftpOpenCommand( "retr", url.path(), 'I', ERR_CANNOT_OPEN_FOR_READING, 0 ) ) {
  1666.       kWarning(7102) << "Can't open for reading";
  1667.       return;
  1668.     }
  1669.     char buffer[ 2048 ];
  1670.     QByteArray array;
  1671.     // Get one chunk of data only and send it, KIO::Job will determine the
  1672.     // mimetype from it using KMimeMagic
  1673.     int n = m_data->read( buffer, 2048 );
  1674.     array.setRawData(buffer, n);
  1675.     data( array );
  1676.     array.resetRawData(buffer, n);
  1677.     kDebug(7102) << "aborting";
  1678.     ftpAbortTransfer();
  1679.     kDebug(7102) << "finished";
  1680.     finished();
  1681.     kDebug(7102) << "after finished";
  1682.   }
  1683.   void Ftp::ftpAbortTransfer()
  1684.   {
  1685.     // RFC 959, page 34-35
  1686.     // IAC (interpret as command) = 255 ; IP (interrupt process) = 254
  1687.     // DM = 242 (data mark)
  1688.      char msg[4];
  1689.      // 1. User system inserts the Telnet "Interrupt Process" (IP) signal
  1690.      //   in the Telnet stream.
  1691.      msg[0] = (char) 255; //IAC
  1692.      msg[1] = (char) 254; //IP
  1693.      (void) send(sControl, msg, 2, 0);
  1694.      // 2. User system sends the Telnet "Sync" signal.
  1695.      msg[0] = (char) 255; //IAC
  1696.      msg[1] = (char) 242; //DM
  1697.      if (send(sControl, msg, 2, MSG_OOB) != 2)
  1698.        ; // error...
  1699.      // Send ABOR
  1700.      kDebug(7102) << "send ABOR";
  1701.      QCString buf = "ABORrn";
  1702.      if ( KSocks::self()->write( sControl, buf.data(), buf.length() ) <= 0 )  {
  1703.        error( ERR_COULD_NOT_WRITE, QString() );
  1704.        return;
  1705.      }
  1706.      //
  1707.      kDebug(7102) << "read resp";
  1708.      if ( readresp() != '2' )
  1709.      {
  1710.        error( ERR_COULD_NOT_READ, QString() );
  1711.        return;
  1712.      }
  1713.     kDebug(7102) << "close sockets";
  1714.     closeSockets();
  1715.   }
  1716. #endif
  1717. //===============================================================================
  1718. // public: put           upload file to server
  1719. // helper: ftpPut        called from put() and copy()
  1720. //===============================================================================
  1721. void Ftp::put(const KUrl& url, int permissions, KIO::JobFlags flags)
  1722. {
  1723.   kDebug(7102) << "Ftp::put " << url.url();
  1724.   int iError = 0;                           // iError gets status
  1725.   ftpPut(iError, -1, url, permissions, flags);
  1726.   if(iError)                                // can have only server side errs
  1727.      error(iError, url.path());
  1728.   ftpCloseCommand();                        // must close command!
  1729. }
  1730. Ftp::StatusCode Ftp::ftpPut(int& iError, int iCopyFile, const KUrl& dest_url,
  1731.                             int permissions, KIO::JobFlags flags)
  1732. {
  1733.   if( !ftpOpenConnection(loginImplicit) )
  1734.     return statusServerError;
  1735.   // Don't use mark partial over anonymous FTP.
  1736.   // My incoming dir allows put but not rename...
  1737.   bool bMarkPartial;
  1738.   if (m_user.isEmpty () || m_user == FTP_LOGIN)
  1739.     bMarkPartial = false;
  1740.   else
  1741.     bMarkPartial = config()->readEntry("MarkPartial", true);
  1742.   QString dest_orig = dest_url.path();
  1743.   QString dest_part( dest_orig );
  1744.   dest_part += ".part";
  1745.   if ( ftpSize( dest_orig, 'I' ) )
  1746.   {
  1747.     if ( m_size == 0 )
  1748.     { // delete files with zero size
  1749.       QByteArray cmd = "DELE ";
  1750.       cmd += remoteEncoding()->encode(dest_orig);
  1751.       if( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
  1752.       {
  1753.         iError = ERR_CANNOT_DELETE_PARTIAL;
  1754.         return statusServerError;
  1755.       }
  1756.     }
  1757.     else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
  1758.     {
  1759.        iError = ERR_FILE_ALREADY_EXIST;
  1760.        return statusServerError;
  1761.     }
  1762.     else if ( bMarkPartial )
  1763.     { // when using mark partial, append .part extension
  1764.       if ( !ftpRename( dest_orig, dest_part, KIO::Overwrite ) )
  1765.       {
  1766.         iError = ERR_CANNOT_RENAME_PARTIAL;
  1767.         return statusServerError;
  1768.       }
  1769.     }
  1770.     // Don't chmod an existing file
  1771.     permissions = -1;
  1772.   }
  1773.   else if ( bMarkPartial && ftpSize( dest_part, 'I' ) )
  1774.   { // file with extension .part exists
  1775.     if ( m_size == 0 )
  1776.     {  // delete files with zero size
  1777.       QByteArray cmd = "DELE ";
  1778.       cmd += remoteEncoding()->encode(dest_part);
  1779.       if ( !ftpSendCmd( cmd ) || (m_iRespType != 2) )
  1780.       {
  1781.         iError = ERR_CANNOT_DELETE_PARTIAL;
  1782.         return statusServerError;
  1783.       }
  1784.     }
  1785.     else if ( !(flags & KIO::Overwrite) && !(flags & KIO::Resume) )
  1786.     {
  1787.       flags |= canResume (m_size) ? KIO::Resume : KIO::DefaultFlags;
  1788.       if (!(flags & KIO::Resume))
  1789.       {
  1790.         iError = ERR_FILE_ALREADY_EXIST;
  1791.         return statusServerError;
  1792.       }
  1793.     }
  1794.   }
  1795.   else
  1796.     m_size = 0;
  1797.   QString dest;
  1798.   // if we are using marking of partial downloads -> add .part extension
  1799.   if ( bMarkPartial ) {
  1800.     kDebug(7102) << "Adding .part extension to " << dest_orig;
  1801.     dest = dest_part;
  1802.   } else
  1803.     dest = dest_orig;
  1804.   KIO::fileoffset_t offset = 0;
  1805.   // set the mode according to offset
  1806.   if( (flags & KIO::Resume) && m_size > 0 )
  1807.   {
  1808.     offset = m_size;
  1809.     if(iCopyFile != -1)
  1810.     {
  1811.       if( KDE_lseek(iCopyFile, offset, SEEK_SET) < 0 )
  1812.       {
  1813.         iError = ERR_CANNOT_RESUME;
  1814.         return statusClientError;
  1815.       }
  1816.     }
  1817.   }
  1818.   if (! ftpOpenCommand( "stor", dest, '?', ERR_COULD_NOT_WRITE, offset ) )
  1819.      return statusServerError;
  1820.   kDebug(7102) << "ftpPut: starting with offset=" << offset;
  1821.   KIO::fileoffset_t processed_size = offset;
  1822.   QByteArray buffer;
  1823.   int result;
  1824.   int iBlockSize = initialIpcSize;
  1825.   // Loop until we got 'dataEnd'
  1826.   do
  1827.   {
  1828.     if(iCopyFile == -1)
  1829.     {
  1830.       dataReq(); // Request for data
  1831.       result = readData( buffer );
  1832.     }
  1833.     else
  1834.     { // let the buffer size grow if the file is larger 64kByte ...
  1835.       if(processed_size-offset > 1024 * 64)
  1836.         iBlockSize = maximumIpcSize;
  1837.       buffer.resize(iBlockSize);
  1838.       result = ::read(iCopyFile, buffer.data(), buffer.size());
  1839.       if(result < 0)
  1840.         iError = ERR_COULD_NOT_WRITE;
  1841.       else
  1842.         buffer.resize(result);
  1843.     }
  1844.     if (result > 0)
  1845.     {
  1846.       m_data->write( buffer );
  1847.       while (m_data->bytesToWrite() && m_data->waitForBytesWritten()) {}
  1848.       processed_size += result;
  1849.       processedSize (processed_size);
  1850.     }
  1851.   }
  1852.   while ( result > 0 );
  1853.   if (result != 0) // error
  1854.   {
  1855.     ftpCloseCommand();               // don't care about errors
  1856.     kDebug(7102) << "Error during 'put'. Aborting.";
  1857.     if (bMarkPartial)
  1858.     {
  1859.       // Remove if smaller than minimum size
  1860.       if ( ftpSize( dest, 'I' ) &&
  1861.            ( processed_size < config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE) ) )
  1862.       {
  1863.         QByteArray cmd = "DELE ";
  1864.         cmd += remoteEncoding()->encode(dest);
  1865.         (void) ftpSendCmd( cmd );
  1866.       }
  1867.     }
  1868.     return statusServerError;
  1869.   }
  1870.   if ( !ftpCloseCommand() )
  1871.   {
  1872.     iError = ERR_COULD_NOT_WRITE;
  1873.     return statusServerError;
  1874.   }
  1875.   // after full download rename the file back to original name
  1876.   if ( bMarkPartial )
  1877.   {
  1878.     kDebug(7102) << "renaming dest (" << dest << ") back to dest_orig (" << dest_orig << ")";
  1879.     if ( !ftpRename( dest, dest_orig, KIO::Overwrite ) )
  1880.     {
  1881.       iError = ERR_CANNOT_RENAME_PARTIAL;
  1882.       return statusServerError;
  1883.     }
  1884.   }
  1885.   // set final permissions
  1886.   if ( permissions != -1 )
  1887.   {
  1888.     if ( m_user == FTP_LOGIN )
  1889.       kDebug(7102) << "Trying to chmod over anonymous FTP ???";
  1890.     // chmod the file we just put
  1891.     if ( ! ftpChmod( dest_orig, permissions ) )
  1892.     {
  1893.         // To be tested
  1894.         //if ( m_user != FTP_LOGIN )
  1895.         //    warning( i18n( "Could not change permissions forn%1" ).arg( dest_orig ) );
  1896.     }
  1897.   }
  1898.   // We have done our job => finish
  1899.   finished();
  1900.   return statusSuccess;
  1901. }
  1902. /** Use the SIZE command to get the file size.
  1903.     Warning : the size depends on the transfer mode, hence the second arg. */
  1904. bool Ftp::ftpSize( const QString & path, char mode )
  1905. {
  1906.   m_size = UnknownSize;
  1907.   if( !ftpDataMode(mode) )
  1908.       return false;
  1909.   QByteArray buf;
  1910.   buf = "SIZE ";
  1911.   buf += remoteEncoding()->encode(path);
  1912.   if( !ftpSendCmd( buf ) || (m_iRespType != 2) )
  1913.     return false;
  1914.   // skip leading "213 " (response code)
  1915.   const char* psz = ftpResponse(4);
  1916.   if(!psz)
  1917.     return false;
  1918.   m_size = charToLongLong(psz);
  1919.   if (!m_size) m_size = UnknownSize;
  1920.   return true;
  1921. }
  1922. // Today the differences between ASCII and BINARY are limited to
  1923. // CR or CR/LF line terminators. Many servers ignore ASCII (like
  1924. // win2003 -or- vsftp with default config). In the early days of
  1925. // computing, when even text-files had structure, this stuff was
  1926. // more important.
  1927. // Theoretically "list" could return different results in ASCII
  1928. // and BINARY mode. But again, most servers ignore ASCII here.
  1929. bool Ftp::ftpDataMode(char cMode)
  1930. {
  1931.   if(cMode == '?') cMode = m_bTextMode ? 'A' : 'I';
  1932.   else if(cMode == 'a') cMode = 'A';
  1933.   else if(cMode != 'A') cMode = 'I';
  1934.   kDebug(7102) << "ftpDataMode: want '" << cMode << "' has '" << m_cDataMode << "'";
  1935.   if(m_cDataMode == cMode)
  1936.     return true;
  1937.   QByteArray buf = "TYPE ";
  1938.   buf += cMode;
  1939.   if( !ftpSendCmd(buf) || (m_iRespType != 2) )
  1940.       return false;
  1941.   m_cDataMode = cMode;
  1942.   return true;
  1943. }
  1944. bool Ftp::ftpFolder(const QString& path, bool bReportError)
  1945. {
  1946.   QString newPath = path;
  1947.   int iLen = newPath.length();
  1948.   if(iLen > 1 && newPath[iLen-1] == '/') newPath.truncate(iLen-1);
  1949.   //kDebug(7102) << "ftpFolder: want '" << newPath << "' has '" << m_currentPath << "'";
  1950.   if(m_currentPath == newPath)
  1951.     return true;
  1952.   QByteArray tmp = "cwd ";
  1953.   tmp += remoteEncoding()->encode(newPath);
  1954.   if( !ftpSendCmd(tmp) )
  1955.     return false;                  // connection failure
  1956.   if(m_iRespType != 2)
  1957.   {
  1958.     if(bReportError)
  1959.       error(ERR_CANNOT_ENTER_DIRECTORY, path);
  1960.     return false;                  // not a folder
  1961.   }
  1962.   m_currentPath = newPath;
  1963.   return true;
  1964. }
  1965. //===============================================================================
  1966. // public: copy          don't use kio data pump if one side is a local file
  1967. // helper: ftpCopyPut    called from copy() on upload
  1968. // helper: ftpCopyGet    called from copy() on download
  1969. //===============================================================================
  1970. void Ftp::copy( const KUrl &src, const KUrl &dest, int permissions, KIO::JobFlags flags )
  1971. {
  1972.   int iError = 0;
  1973.   int iCopyFile = -1;
  1974.   StatusCode cs = statusSuccess;
  1975.   bool bSrcLocal = src.isLocalFile();
  1976.   bool bDestLocal = dest.isLocalFile();
  1977.   QString  sCopyFile;
  1978.   if(bSrcLocal && !bDestLocal)                    // File -> Ftp
  1979.   {
  1980.     sCopyFile = src.toLocalFile();
  1981.     kDebug(7102) << "Ftp::copy local file '" << sCopyFile << "' -> ftp '" << dest.path() << "'";
  1982.     cs = ftpCopyPut(iError, iCopyFile, sCopyFile, dest, permissions, flags);
  1983.     if( cs == statusServerError) sCopyFile = dest.url();
  1984.   }
  1985.   else if(!bSrcLocal && bDestLocal)               // Ftp -> File
  1986.   {
  1987.     sCopyFile = dest.toLocalFile();
  1988.     kDebug(7102) << "Ftp::copy ftp '" << src.path() << "' -> local file '" << sCopyFile << "'";
  1989.     cs = ftpCopyGet(iError, iCopyFile, sCopyFile, src, permissions, flags);
  1990.     if( cs == statusServerError ) sCopyFile = src.url();
  1991.   }
  1992.   else {
  1993.     error( ERR_UNSUPPORTED_ACTION, QString() );
  1994.     return;
  1995.   }
  1996.   // perform clean-ups and report error (if any)
  1997.   if(iCopyFile != -1)
  1998.     ::close(iCopyFile);
  1999.   if(iError)
  2000.     error(iError, sCopyFile);
  2001.   ftpCloseCommand();                        // must close command!
  2002. }
  2003. Ftp::StatusCode Ftp::ftpCopyPut(int& iError, int& iCopyFile, const QString &sCopyFile,
  2004.                                 const KUrl& url, int permissions, KIO::JobFlags flags)
  2005. {
  2006.   // check if source is ok ...
  2007.   KDE_struct_stat buff;
  2008.   QByteArray sSrc( QFile::encodeName(sCopyFile) );
  2009.   bool bSrcExists = (KDE_stat( sSrc.data(), &buff ) != -1);
  2010.   if(bSrcExists)
  2011.   { if(S_ISDIR(buff.st_mode))
  2012.     {
  2013.       iError = ERR_IS_DIRECTORY;
  2014.       return statusClientError;
  2015.     }
  2016.   }
  2017.   else
  2018.   {
  2019.     iError = ERR_DOES_NOT_EXIST;
  2020.     return statusClientError;
  2021.   }
  2022.   iCopyFile = KDE_open( sSrc.data(), O_RDONLY );
  2023.   if(iCopyFile == -1)
  2024.   {
  2025.     iError = ERR_CANNOT_OPEN_FOR_READING;
  2026.     return statusClientError;
  2027.   }
  2028.   // delegate the real work (iError gets status) ...
  2029.   totalSize(buff.st_size);
  2030. #ifdef  ENABLE_CAN_RESUME
  2031.   return ftpPut(iError, iCopyFile, url, permissions, flags & ~KIO::Resume);
  2032. #else
  2033.   return ftpPut(iError, iCopyFile, url, permissions, flags | KIO::Resume);
  2034. #endif
  2035. }
  2036. Ftp::StatusCode Ftp::ftpCopyGet(int& iError, int& iCopyFile, const QString &sCopyFile,
  2037.                                 const KUrl& url, int permissions, KIO::JobFlags flags)
  2038. {
  2039.   // check if destination is ok ...
  2040.   KDE_struct_stat buff;
  2041.   QByteArray sDest( QFile::encodeName(sCopyFile) );
  2042.   bool bDestExists = (KDE_stat( sDest.data(), &buff ) != -1);
  2043.   if(bDestExists)
  2044.   { if(S_ISDIR(buff.st_mode))
  2045.     {
  2046.       iError = ERR_IS_DIRECTORY;
  2047.       return statusClientError;
  2048.     }
  2049.     if(!(flags & KIO::Overwrite))
  2050.     {
  2051.       iError = ERR_FILE_ALREADY_EXIST;
  2052.       return statusClientError;
  2053.     }
  2054.   }
  2055.   // do we have a ".part" file?
  2056.   QByteArray sPart = QFile::encodeName(sCopyFile + ".part");
  2057.   bool bResume = false;
  2058.   bool bPartExists = (KDE_stat( sPart.data(), &buff ) != -1);
  2059.   bool bMarkPartial = config()->readEntry("MarkPartial", true);
  2060.   if(bMarkPartial && bPartExists && buff.st_size > 0)
  2061.   { // must not be a folder! please fix a similar bug in kio_file!!
  2062.     if(S_ISDIR(buff.st_mode))
  2063.     {
  2064.       iError = ERR_DIR_ALREADY_EXIST;
  2065.       return statusClientError;                            // client side error
  2066.     }
  2067.     //doesn't work for copy? -> design flaw?
  2068. #ifdef  ENABLE_CAN_RESUME
  2069.     bResume = canResume( buff.st_size );
  2070. #else
  2071.     bResume = true;
  2072. #endif
  2073.   }
  2074.   if(bPartExists && !bResume)                  // get rid of an unwanted ".part" file
  2075.     remove(sPart.data());
  2076.   // JPF: in kio_file overwrite disables ".part" operations. I do not believe
  2077.   // JPF: that this is a good behaviour!
  2078.   if(bDestExists)                             // must delete for overwrite
  2079.     remove(sDest.data());
  2080.   // WABA: Make sure that we keep writing permissions ourselves,
  2081.   // otherwise we can be in for a surprise on NFS.
  2082.   mode_t initialMode;
  2083.   if (permissions != -1)
  2084.     initialMode = permissions | S_IWUSR;
  2085.   else
  2086.     initialMode = 0666;
  2087.   // open the output file ...
  2088.   KIO::fileoffset_t hCopyOffset = 0;
  2089.   if(bResume)
  2090.   {
  2091.     iCopyFile = KDE_open( sPart.data(), O_RDWR );  // append if resuming
  2092.     hCopyOffset = KDE_lseek(iCopyFile, 0, SEEK_END);
  2093.     if(hCopyOffset < 0)
  2094.     {
  2095.       iError = ERR_CANNOT_RESUME;
  2096.       return statusClientError;                            // client side error
  2097.     }
  2098.     kDebug(7102) << "copy: resuming at " << hCopyOffset;
  2099.   }
  2100.   else
  2101.     iCopyFile = KDE_open(sPart.data(), O_CREAT | O_TRUNC | O_WRONLY, initialMode);
  2102.   if(iCopyFile == -1)
  2103.   {
  2104.     kDebug(7102) << "copy: ### COULD NOT WRITE " << sCopyFile;
  2105.     iError = (errno == EACCES) ? ERR_WRITE_ACCESS_DENIED
  2106.                                : ERR_CANNOT_OPEN_FOR_WRITING;
  2107.     return statusClientError;
  2108.   }
  2109.   // delegate the real work (iError gets status) ...
  2110.   StatusCode iRes = ftpGet(iError, iCopyFile, url, hCopyOffset);
  2111.   if( ::close(iCopyFile) && iRes == statusSuccess )
  2112.   {
  2113.     iError = ERR_COULD_NOT_WRITE;
  2114.     iRes = statusClientError;
  2115.   }
  2116.   // handle renaming or deletion of a partial file ...
  2117.   if(bMarkPartial)
  2118.   {
  2119.     if(iRes == statusSuccess)
  2120.     { // rename ".part" on success
  2121. #ifdef Q_OS_WIN
  2122.         if ( MoveFileExA( sPart.data(),
  2123.                           sDest.data(),
  2124.                           MOVEFILE_REPLACE_EXISTING|MOVEFILE_COPY_ALLOWED ) == 0 )
  2125. #else
  2126.       if ( ::rename( sPart.data(), sDest.data() ) )
  2127. #endif
  2128.       {
  2129.         kDebug(7102) << "copy: cannot rename " << sPart << " to " << sDest;
  2130.         iError = ERR_CANNOT_RENAME_PARTIAL;
  2131.         iRes = statusClientError;
  2132.       }
  2133.     }
  2134.     else if(KDE_stat( sPart.data(), &buff ) == 0)
  2135.     { // should a very small ".part" be deleted?
  2136.       int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE);
  2137.       if (buff.st_size <  size)
  2138.         remove(sPart.data());
  2139.     }
  2140.   }
  2141.   return iRes;
  2142. }
  2143. void SslServer::incomingConnection(int socketDescriptor)
  2144. {
  2145.      QSslSocket *m_socket = new QSslSocket;
  2146.      if (!m_socket->setSocketDescriptor(socketDescriptor)) delete m_socket; //{
  2147.          //connect(m_socket, SIGNAL(encrypted()), this, SLOT(ready()));
  2148.      //    //serverSocket->startServerEncryption();
  2149.      //} else {
  2150.      //    delete serverSocket;
  2151.      //}
  2152.  }