File indexing completed on 2024-05-05 04:59:44
0001 /* 0002 * SPDX-FileCopyrightText: 2001 Lucas Fisher <ljfisher@purdue.edu> 0003 * SPDX-FileCopyrightText: 2009 Andreas Schneider <mail@cynapses.org> 0004 * SPDX-FileCopyrightText: 2020-2022 Harald Sitter <sitter@kde.org> 0005 * 0006 * SPDX-License-Identifier: LGPL-2.0-or-later 0007 */ 0008 0009 #ifndef __kio_sftp_h__ 0010 #define __kio_sftp_h__ 0011 0012 #include <KIO/Global> 0013 #include <KIO/WorkerBase> 0014 0015 #include <libssh/callbacks.h> 0016 #include <libssh/libssh.h> 0017 #include <libssh/sftp.h> 0018 0019 #include <QQueue> 0020 #include <QUrl> 0021 0022 #include <QCoroGenerator> 0023 0024 namespace KIO 0025 { 0026 class AuthInfo; 0027 } // namespace KIO 0028 0029 using Result = KIO::WorkerResult; 0030 0031 namespace std 0032 { 0033 template<> 0034 struct default_delete<struct sftp_attributes_struct> { 0035 void operator()(struct sftp_attributes_struct *ptr) const 0036 { 0037 sftp_attributes_free(ptr); 0038 } 0039 }; 0040 } // namespace std 0041 0042 using SFTPAttributesPtr = std::unique_ptr<sftp_attributes_struct>; 0043 0044 class SFTPWorker : public KIO::WorkerBase 0045 { 0046 public: 0047 explicit SFTPWorker(const QByteArray &poolSocket, const QByteArray &appSocket); 0048 ~SFTPWorker() override; 0049 Q_DISABLE_COPY_MOVE(SFTPWorker) 0050 0051 void setHost(const QString &h, quint16 port, const QString &user, const QString &pass) override; 0052 Q_REQUIRED_RESULT Result get(const QUrl &url) override; 0053 Q_REQUIRED_RESULT Result listDir(const QUrl &url) override; 0054 Q_REQUIRED_RESULT Result mimetype(const QUrl &url) override; 0055 Q_REQUIRED_RESULT Result stat(const QUrl &url) override; 0056 Q_REQUIRED_RESULT Result copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) override; 0057 Q_REQUIRED_RESULT Result put(const QUrl &url, int permissions, KIO::JobFlags flags) override; 0058 void closeConnection() override; 0059 void worker_status() override; 0060 Q_REQUIRED_RESULT Result del(const QUrl &url, bool isfile) override; 0061 Q_REQUIRED_RESULT Result chmod(const QUrl &url, int permissions) override; 0062 Q_REQUIRED_RESULT Result symlink(const QString &target, const QUrl &dest, KIO::JobFlags flags) override; 0063 Q_REQUIRED_RESULT Result rename(const QUrl &src, const QUrl &dest, KIO::JobFlags flags) override; 0064 Q_REQUIRED_RESULT Result mkdir(const QUrl &url, int permissions) override; 0065 Q_REQUIRED_RESULT Result openConnection() override; 0066 0067 // KIO::FileJob interface 0068 Q_REQUIRED_RESULT Result open(const QUrl &url, QIODevice::OpenMode mode) override; 0069 Q_REQUIRED_RESULT Result read(KIO::filesize_t size) override; 0070 Q_REQUIRED_RESULT Result write(const QByteArray &data) override; 0071 Q_REQUIRED_RESULT Result seek(KIO::filesize_t offset) override; 0072 Q_REQUIRED_RESULT Result truncate(KIO::filesize_t length) override; 0073 Q_REQUIRED_RESULT Result close() override; 0074 Q_REQUIRED_RESULT Result special(const QByteArray &data) override; 0075 0076 // libssh authentication callback (note that this is called by the 0077 // global ::auth_callback() call. 0078 int auth_callback(const char *prompt, char *buf, size_t len, int echo, int verify, void *userdata); 0079 0080 // libssh logging callback (note that this is called by the 0081 // global ::log_callback() call. 0082 void log_callback(int priority, const char *function, const char *buffer, void *userdata); 0083 0084 // Must call after construction! 0085 // Bit rubbish, but we need to return something on init. 0086 Q_REQUIRED_RESULT Result init(); 0087 0088 Q_REQUIRED_RESULT Result fileSystemFreeSpace(const QUrl &url) override; 0089 0090 private: // Private variables 0091 /** True if worker is connected to sftp server. */ 0092 bool mConnected = false; 0093 0094 /** Host we are connected to. */ 0095 QString mHost; 0096 0097 /** Port we are connected to. */ 0098 int mPort = -1; 0099 0100 /** The ssh session for the connection */ 0101 ssh_session mSession = nullptr; 0102 0103 /** The sftp session for the connection */ 0104 sftp_session mSftp = nullptr; 0105 0106 /** Username to use when connecting */ 0107 QString mUsername; 0108 0109 /** User's password */ 0110 QString mPassword; 0111 0112 /** The open file */ 0113 sftp_file mOpenFile = nullptr; 0114 0115 /** The open URL */ 0116 QUrl mOpenUrl; 0117 0118 ssh_callbacks mCallbacks = nullptr; 0119 0120 // KIO::FileJob interface 0121 KIO::filesize_t openOffset = 0; 0122 0123 /** 0124 * Holds public key authentication info for proper retry handling. 0125 */ 0126 KIO::AuthInfo *mPublicKeyAuthInfo = nullptr; 0127 0128 /** 0129 * GetRequest encapsulates several SFTP get requests into a single object. 0130 * As SFTP messages are limited to MAX_XFER_BUF_SIZE several requests 0131 * should be sent simultaneously in order to increase transfer speeds. 0132 */ 0133 class GetRequest 0134 { 0135 public: 0136 /** 0137 * Creates a new GetRequest object. 0138 * Requests do not take ownership of the SFTP pointers! The caller is 0139 * responsible for freeing them. 0140 * @param file the sftp_file object which should be transferred. 0141 * @param size the total size of the file. 0142 * @param maxPendingRequests the maximum number of parallel requests to start with. 0143 * The more are pending the higher the potential memory 0144 * foot print, however if the connection allows it 0145 * we'll get better throughput. 0146 */ 0147 GetRequest(sftp_file file, uint64_t size, ushort maxPendingRequests = 128); 0148 /** 0149 * Removes all pending requests and closes the SFTP channel and attributes 0150 * in order to avoid memory leaks. 0151 */ 0152 ~GetRequest(); 0153 Q_DISABLE_COPY_MOVE(GetRequest) 0154 0155 /** 0156 * Starts up to maxPendingRequests file requests. Reading is performed in the 0157 * via the readChunks method. 0158 */ 0159 bool enqueueChunks(); 0160 /** 0161 * Attempts to read all pending chunks in the given QByteArray. 0162 * @param data the array into which the data should be saved (it should be empty). 0163 * @return 0 on EOF or timeout, -1 on error and the number of bytes read otherwise. 0164 */ 0165 size_t readChunks(QByteArray &data); 0166 0167 private: 0168 struct Request { 0169 /** Identifier as returned by the sftp_async_read_begin call */ 0170 int id; 0171 /** The number of bytes expected to be returned */ 0172 uint32_t expectedLength; 0173 /** The SSH start offset when this request was made */ 0174 uint64_t startOffset; 0175 }; 0176 0177 sftp_file m_file; 0178 const uint64_t m_size; // size of file (max readable) 0179 ushort m_maxPendingRequests; 0180 QQueue<Request> m_pendingRequests; 0181 }; 0182 0183 struct ReadResponse { 0184 // NOTE: older GCC versions have trouble when using structs that were aggregate initialized and crash on 0185 // double-destruction problems. If you want to get rid of the ctors make double sure that reasonable GCC 0186 // versions are fine. 0187 ReadResponse() = default; 0188 explicit ReadResponse(const QByteArray &filedata_) 0189 : filedata(filedata_) 0190 { 0191 } 0192 explicit ReadResponse(int error_) 0193 : error(error_) 0194 { 0195 } 0196 0197 QByteArray filedata; 0198 int error = KJob::NoError; 0199 }; 0200 QCoro::Generator<ReadResponse> asyncRead(sftp_file file, size_t size); 0201 0202 struct WriteResponse { 0203 size_t bytes = 0; 0204 int error = KJob::NoError; 0205 }; 0206 QCoro::Generator<WriteResponse> asyncWrite(sftp_file file, QCoro::Generator<ReadResponse> reader); 0207 0208 private: // private methods 0209 int authenticateKeyboardInteractive(KIO::AuthInfo &info); 0210 0211 Q_REQUIRED_RESULT Result reportError(const QUrl &url, int err); 0212 0213 Q_REQUIRED_RESULT Result createUDSEntry(SFTPAttributesPtr sb, KIO::UDSEntry &entry, const QByteArray &path, const QString &name, int details); 0214 0215 QString canonicalizePath(const QString &path); 0216 void requiresUserNameRedirection(); 0217 void clearPubKeyAuthInfo(); 0218 Q_REQUIRED_RESULT Result sftpLogin(); 0219 Q_REQUIRED_RESULT Result sftpOpenConnection(const KIO::AuthInfo &); 0220 0221 Q_REQUIRED_RESULT Result sftpGet(const QUrl &url, KIO::fileoffset_t offset = -1, int fd = -1); 0222 Q_REQUIRED_RESULT Result sftpPut(const QUrl &url, int permissions, KIO::JobFlags flags, int fd = -1); 0223 0224 Q_REQUIRED_RESULT Result sftpCopyGet(const QUrl &url, const QString &src, int permissions, KIO::JobFlags flags); 0225 Q_REQUIRED_RESULT Result sftpCopyPut(const QUrl &url, const QString &dest, int permissions, KIO::JobFlags flags); 0226 Q_REQUIRED_RESULT Result sftpSendMimetype(sftp_file file, const QUrl &url); 0227 Q_REQUIRED_RESULT Result openConnectionWithoutCloseOnError(); 0228 }; 0229 0230 #endif