File indexing completed on 2024-05-19 15:15:44

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