File indexing completed on 2024-05-12 03:56:45

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
0004     SPDX-FileCopyrightText: 2019-2021 Harald Sitter <sitter@kde.org>
0005 
0006     SPDX-License-Identifier: LGPL-2.0-or-later
0007 */
0008 
0009 #ifndef KDELIBS_FTP_H
0010 #define KDELIBS_FTP_H
0011 
0012 #include <qplatformdefs.h>
0013 
0014 #include <QDateTime>
0015 #include <QUrl>
0016 
0017 #include <workerbase.h>
0018 
0019 class QTcpServer;
0020 class QTcpSocket;
0021 class QNetworkProxy;
0022 class QAuthenticator;
0023 
0024 struct FtpEntry {
0025     QString name;
0026     QString owner;
0027     QString group;
0028     QString link;
0029 
0030     KIO::filesize_t size;
0031     mode_t type;
0032     mode_t access;
0033     QDateTime date;
0034 };
0035 
0036 class FtpInternal;
0037 
0038 /**
0039  * Login Mode for ftpOpenConnection
0040  */
0041 enum class LoginMode {
0042     Deferred,
0043     Explicit,
0044     Implicit,
0045 };
0046 
0047 using Result = KIO::WorkerResult;
0048 
0049 /**
0050  * Special Result composite for errors during connection.
0051  */
0052 struct ConnectionResult {
0053     QTcpSocket *socket;
0054     Result result;
0055 };
0056 
0057 QDebug operator<<(QDebug dbg, const Result &r);
0058 
0059 //===============================================================================
0060 // Ftp
0061 // The API class. This class should not contain *any* FTP logic. It acts
0062 // as a container for FtpInternal to prevent the latter from directly doing
0063 // state manipulation via error/finished/opened etc.
0064 //===============================================================================
0065 class Ftp : public KIO::WorkerBase
0066 {
0067 public:
0068     Ftp(const QByteArray &pool, const QByteArray &app);
0069     ~Ftp() override;
0070 
0071     void setHost(const QString &host, quint16 port, const QString &user, const QString &pass) override;
0072 
0073     /**
0074      * Connects to a ftp server and logs us in
0075      * m_bLoggedOn is set to true if logging on was successful.
0076      * It is set to false if the connection becomes closed.
0077      *
0078      */
0079     KIO::WorkerResult openConnection() override;
0080 
0081     /**
0082      * Closes the connection
0083      */
0084     void closeConnection() override;
0085 
0086     KIO::WorkerResult stat(const QUrl &url) override;
0087 
0088     KIO::WorkerResult listDir(const QUrl &url) override;
0089     KIO::WorkerResult mkdir(const QUrl &url, int permissions) override;
0090     KIO::WorkerResult rename(const QUrl &src, const QUrl &dst, KIO::JobFlags flags) override;
0091     KIO::WorkerResult del(const QUrl &url, bool isfile) override;
0092     KIO::WorkerResult chmod(const QUrl &url, int permissions) override;
0093 
0094     KIO::WorkerResult get(const QUrl &url) override;
0095     KIO::WorkerResult put(const QUrl &url, int permissions, KIO::JobFlags flags) override;
0096 
0097     void worker_status() override;
0098 
0099     /**
0100      * Handles the case that one side of the job is a local file
0101      */
0102     KIO::WorkerResult copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) override;
0103 
0104     std::unique_ptr<FtpInternal> d;
0105 };
0106 
0107 /**
0108  * Internal logic class.
0109  *
0110  * This class implements strict separation between the API (Ftp) and
0111  * the logic behind the API (FtpInternal). This class' functions
0112  * are meant to return Result objects up the call stack to Ftp where
0113  * they will be turned into command results (e.g. error(),
0114  * finished(), etc.). This class cannot and must not call these signals
0115  * directly as it leads to unclear states.
0116  */
0117 class FtpInternal : public QObject
0118 {
0119     Q_OBJECT
0120 public:
0121     explicit FtpInternal(Ftp *qptr);
0122     ~FtpInternal() override;
0123 
0124     // ---------------------------------------- API
0125 
0126     void setHost(const QString &host, quint16 port, const QString &user, const QString &pass);
0127 
0128     /**
0129      * Connects to a ftp server and logs us in
0130      * m_bLoggedOn is set to true if logging on was successful.
0131      * It is set to false if the connection becomes closed.
0132      *
0133      */
0134     Q_REQUIRED_RESULT Result openConnection();
0135 
0136     /**
0137      * Closes the connection
0138      */
0139     void closeConnection();
0140 
0141     Q_REQUIRED_RESULT Result stat(const QUrl &url);
0142 
0143     Result listDir(const QUrl &url);
0144     Q_REQUIRED_RESULT Result mkdir(const QUrl &url, int permissions);
0145     Q_REQUIRED_RESULT Result rename(const QUrl &src, const QUrl &dst, KIO::JobFlags flags);
0146     Q_REQUIRED_RESULT Result del(const QUrl &url, bool isfile);
0147     Q_REQUIRED_RESULT Result chmod(const QUrl &url, int permissions);
0148 
0149     Q_REQUIRED_RESULT Result get(const QUrl &url);
0150     Q_REQUIRED_RESULT Result put(const QUrl &url, int permissions, KIO::JobFlags flags);
0151     // virtual void mimetype( const QUrl& url );
0152 
0153     void worker_status();
0154 
0155     /**
0156      * Handles the case that one side of the job is a local file
0157      */
0158     Q_REQUIRED_RESULT Result copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags);
0159 
0160     // ---------------------------------------- END API
0161 
0162     static bool isSocksProxyScheme(const QString &scheme);
0163     bool isSocksProxy() const;
0164 
0165     /**
0166      * Connect and login to the FTP server.
0167      *
0168      * @param loginMode controls if login info should be sent<br>
0169      *  loginDeferred  - must not be logged on, no login info is sent<br>
0170      *  loginExplicit - must not be logged on, login info is sent<br>
0171      *  loginImplicit - login info is sent if not logged on
0172      *
0173      * @return true on success (a login failure would return false).
0174      */
0175     Q_REQUIRED_RESULT Result ftpOpenConnection(LoginMode loginMode);
0176 
0177     /**
0178      * Called by openConnection. It logs us in.
0179      * m_initialPath is set to the current working directory
0180      * if logging on was successful.
0181      *
0182      * @param userChanged if not nullptr, will be set to true if the user name
0183      *                    was changed during login.
0184      * @return true on success.
0185      */
0186     Q_REQUIRED_RESULT Result ftpLogin(bool *userChanged = nullptr);
0187 
0188     /**
0189      * ftpSendCmd - send a command (@p cmd) and read response
0190      *
0191      * @param maxretries number of time it should retry. Since it recursively
0192      * calls itself if it can't read the answer (this happens especially after
0193      * timeouts), we need to limit the recursiveness ;-)
0194      *
0195      * return true if any response received, false on error
0196      */
0197     Q_REQUIRED_RESULT bool ftpSendCmd(const QByteArray &cmd, int maxretries = 1);
0198 
0199     /**
0200      * Use the SIZE command to get the file size.
0201      * @param mode the size depends on the transfer mode, hence this arg.
0202      * @return true on success
0203      * Gets the size into m_size.
0204      */
0205     bool ftpSize(const QString &path, char mode);
0206 
0207     /**
0208      * Returns true if the file exists.
0209      * Implemented using the SIZE command.
0210      */
0211     bool ftpFileExists(const QString &path);
0212 
0213     /**
0214      * Set the current working directory, but only if not yet current
0215      */
0216     Q_REQUIRED_RESULT bool ftpFolder(const QString &path);
0217 
0218     /**
0219      * Runs a command on the ftp server like "list" or "retr". In contrast to
0220      * ftpSendCmd a data connection is opened. The corresponding socket
0221      * sData is available for reading/writing on success.
0222      * The connection must be closed afterwards with ftpCloseCommand.
0223      *
0224      * @param mode is 'A' or 'I'. 'A' means ASCII transfer, 'I' means binary transfer.
0225      * @param errorcode the command-dependent error code to emit on error
0226      *
0227      * @return true if the command was accepted by the server.
0228      */
0229     Q_REQUIRED_RESULT Result ftpOpenCommand(const char *command, const QString &path, char mode, int errorcode, KIO::fileoffset_t offset = 0);
0230 
0231     /**
0232      * The counterpart to openCommand.
0233      * Closes data sockets and then reads line sent by server at
0234      * end of command.
0235      * @return false on error (line doesn't start with '2')
0236      */
0237     bool ftpCloseCommand();
0238 
0239     /**
0240      * Send "TYPE I" or "TYPE A" only if required, see m_cDataMode.
0241      *
0242      * Use 'A' to select ASCII and 'I' to select BINARY mode.  If
0243      * cMode is '?' the m_bTextMode flag is used to choose a mode.
0244      */
0245     bool ftpDataMode(char cMode);
0246 
0247     // void ftpAbortTransfer();
0248 
0249     /**
0250      * Used by ftpOpenCommand, return 0 on success or an error code
0251      */
0252     int ftpOpenDataConnection();
0253 
0254     /**
0255      * closes a data connection, see ftpOpenDataConnection()
0256      */
0257     void ftpCloseDataConnection();
0258 
0259     /**
0260      * Helper for ftpOpenDataConnection
0261      */
0262     int ftpOpenPASVDataConnection();
0263     /**
0264      * Helper for ftpOpenDataConnection
0265      */
0266     int ftpOpenEPSVDataConnection();
0267     /**
0268      * Helper for ftpOpenDataConnection
0269      */
0270     int ftpOpenPortDataConnection();
0271 
0272     bool ftpChmod(const QString &path, int permissions);
0273 
0274     // used by listDir
0275     Q_REQUIRED_RESULT Result ftpOpenDir(const QString &path);
0276     /**
0277      * Called to parse directory listings, call this until it returns false
0278      */
0279     bool ftpReadDir(FtpEntry &ftpEnt);
0280 
0281     /**
0282      * Helper to fill an UDSEntry
0283      */
0284     void ftpCreateUDSEntry(const QString &filename, const FtpEntry &ftpEnt, KIO::UDSEntry &entry, bool isDir);
0285 
0286     void ftpShortStatAnswer(const QString &filename, bool isDir);
0287 
0288     Q_REQUIRED_RESULT Result ftpStatAnswerNotFound(const QString &path, const QString &filename);
0289 
0290     /**
0291      * This is the internal implementation of rename() - set put().
0292      *
0293      * @return true on success.
0294      */
0295     Q_REQUIRED_RESULT Result ftpRename(const QString &src, const QString &dst, KIO::JobFlags flags);
0296 
0297     /**
0298      * Called by openConnection. It opens the control connection to the ftp server.
0299      *
0300      * @return true on success.
0301      */
0302     Q_REQUIRED_RESULT Result ftpOpenControlConnection();
0303     Q_REQUIRED_RESULT Result ftpOpenControlConnection(const QString &host, int port);
0304 
0305     /**
0306      * closes the socket holding the control connection (see ftpOpenControlConnection)
0307      */
0308     void ftpCloseControlConnection();
0309 
0310     /**
0311      * read a response from the server (a trailing CR gets stripped)
0312      * @param iOffset -1 to read a new line from the server<br>
0313      *                 0 to return the whole response string
0314      *                >0 to return the response with iOffset chars skipped
0315      * @return the response message with iOffset chars skipped (or "" if iOffset points
0316      *         behind the available data)
0317      */
0318     const char *ftpResponse(int iOffset);
0319 
0320     /**
0321      * This is the internal implementation of get() - see copy().
0322      *
0323      * IMPORTANT: the caller should call ftpCloseCommand() on return.
0324      * The function does not call error(), the caller should do this.
0325      *
0326      * @param iError      set to an ERR_xxxx code on error
0327      * @param iCopyFile   -1 -or- handle of a local destination file
0328      * @param hCopyOffset local file only: non-zero for resume
0329      * @return 0 for success, -1 for server error, -2 for client error
0330      */
0331     Q_REQUIRED_RESULT Result ftpGet(int iCopyFile, const QString &sCopyFile, const QUrl &url, KIO::fileoffset_t hCopyOffset);
0332 
0333     /**
0334      * This is the internal implementation of put() - see copy().
0335      *
0336      * IMPORTANT: the caller should call ftpCloseCommand() on return.
0337      * The function does not call error(), the caller should do this.
0338      *
0339      * @param iError      set to an ERR_xxxx code on error
0340      * @param iCopyFile   -1 -or- handle of a local source file
0341      * @return 0 for success, -1 for server error, -2 for client error
0342      */
0343     Q_REQUIRED_RESULT Result ftpPut(int iCopyFile, const QUrl &url, int permissions, KIO::JobFlags flags);
0344 
0345     /**
0346      * helper called from copy() to implement FILE -> FTP transfers
0347      *
0348      * @param iError      set to an ERR_xxxx code on error
0349      * @param iCopyFile   [out] handle of a local source file
0350      * @param sCopyFile   path of the local source file
0351      * @return 0 for success, -1 for server error, -2 for client error
0352      */
0353     Q_REQUIRED_RESULT Result ftpCopyPut(int &iCopyFile, const QString &sCopyFile, const QUrl &url, int permissions, KIO::JobFlags flags);
0354 
0355     /**
0356      * helper called from copy() to implement FTP -> FILE transfers
0357      *
0358      * @param iError      set to an ERR_xxxx code on error
0359      * @param iCopyFile   [out] handle of a local source file
0360      * @param sCopyFile   path of the local destination file
0361      * @return 0 for success, -1 for server error, -2 for client error
0362      */
0363     Q_REQUIRED_RESULT Result ftpCopyGet(int &iCopyFile, const QString &sCopyFile, const QUrl &url, int permissions, KIO::JobFlags flags);
0364 
0365     /**
0366      * Sends the MIME type of the content to retrieved.
0367      *
0368      * @param iError      set to an ERR_xxxx code on error
0369      * @return 0 for success, -1 for server error, -2 for client error
0370      */
0371     Q_REQUIRED_RESULT Result ftpSendMimeType(const QUrl &url);
0372 
0373     /**
0374      * Fixes up an entry name so that extraneous whitespaces do not cause
0375      * problems. See bug# 88575 and bug# 300988.
0376      */
0377     void fixupEntryName(FtpEntry *ftpEnt);
0378 
0379     /**
0380      * Calls @ref statEntry.
0381      */
0382     bool maybeEmitStatEntry(FtpEntry &ftpEnt, const QString &filename, bool isDir);
0383 
0384     /**
0385      * Setup the connection to the server.
0386      */
0387     Q_REQUIRED_RESULT ConnectionResult synchronousConnectToHost(const QString &host, quint16 port);
0388 
0389 private: // data members
0390     Ftp *const q;
0391 
0392     QString m_host;
0393     int m_port = 0;
0394     QString m_user;
0395     QString m_pass;
0396     /**
0397      * Where we end up after connecting
0398      */
0399     QString m_initialPath;
0400     QUrl m_proxyURL;
0401     QStringList m_proxyUrls;
0402 
0403     /**
0404      * the current working directory - see ftpFolder
0405      */
0406     QString m_currentPath;
0407 
0408     /**
0409      * the status returned by the FTP protocol, set in ftpResponse()
0410      */
0411     int m_iRespCode = 0;
0412 
0413     /**
0414      * the status/100 returned by the FTP protocol, set in ftpResponse()
0415      */
0416     int m_iRespType = 0;
0417 
0418     /**
0419      * This flag is maintained by ftpDataMode() and contains I or A after
0420      * ftpDataMode() has successfully set the mode.
0421      */
0422     char m_cDataMode;
0423 
0424     /**
0425      * true if logged on (m_control should also be non-nullptr)
0426      */
0427     bool m_bLoggedOn;
0428 
0429     /**
0430      * true if a "textmode" metadata key was found by ftpLogin(). This
0431      * switches the ftp data transfer mode from binary to ASCII.
0432      */
0433     bool m_bTextMode;
0434 
0435     /**
0436      * true if a data stream is open, used in closeConnection().
0437      *
0438      * When the user cancels a get or put command the Ftp dtor will be called,
0439      * which in turn calls closeConnection(). The later would try to send QUIT
0440      * which won't work until timeout. ftpOpenCommand sets the m_bBusy flag so
0441      * that the sockets will be closed immediately - the server should be
0442      * capable of handling this and return an error code on thru the control
0443      * connection. The m_bBusy gets cleared by the ftpCloseCommand() routine.
0444      */
0445     bool m_bBusy;
0446 
0447     bool m_bPasv;
0448 
0449     KIO::filesize_t m_size;
0450     static const KIO::filesize_t UnknownSize;
0451 
0452     enum {
0453         epsvUnknown = 0x01,
0454         epsvAllUnknown = 0x02,
0455         eprtUnknown = 0x04,
0456         epsvAllSent = 0x10,
0457         pasvUnknown = 0x20,
0458         chmodUnknown = 0x100,
0459     };
0460     int m_extControl;
0461 
0462     /**
0463      * control connection socket, only set if openControl() succeeded
0464      */
0465     QTcpSocket *m_control = nullptr;
0466     QByteArray m_lastControlLine;
0467 
0468     /**
0469      * data connection socket
0470      */
0471     QTcpSocket *m_data = nullptr;
0472 
0473     /**
0474      * active mode server socket
0475      */
0476     QTcpServer *m_server = nullptr;
0477 };
0478 
0479 #endif // KDELIBS_FTP_H