Changeset View
Standalone View
sftp/kio_sftp.cpp
1 | /* | 1 | /* | ||
---|---|---|---|---|---|
2 | * Copyright (c) 2001 Lucas Fisher <ljfisher@purdue.edu> | 2 | * Copyright (c) 2001 Lucas Fisher <ljfisher@purdue.edu> | ||
3 | * Copyright (c) 2009 Andreas Schneider <mail@cynapses.org> | 3 | * Copyright (c) 2009 Andreas Schneider <mail@cynapses.org> | ||
4 | * Copyright (c) 2020 Harald Sitter <sitter@kde.org> | ||||
4 | * | 5 | * | ||
5 | * This library is free software; you can redistribute it and/or | 6 | * This library is free software; you can redistribute it and/or | ||
6 | * modify it under the terms of the GNU Library General Public | 7 | * modify it under the terms of the GNU Library General Public | ||
7 | * License (LGPL) as published by the Free Software Foundation; | 8 | * License (LGPL) as published by the Free Software Foundation; | ||
8 | * either version 2 of the License, or (at your option) any later | 9 | * either version 2 of the License, or (at your option) any later | ||
9 | * version. | 10 | * version. | ||
10 | * | 11 | * | ||
11 | * This library is distributed in the hope that it will be useful, | 12 | * This library is distributed in the hope that it will be useful, | ||
▲ Show 20 Lines • Show All 56 Lines • ▼ Show 20 Line(s) | 66 | { | |||
68 | 69 | | |||
69 | qCDebug(KIO_SFTP_LOG) << "*** Starting kio_sftp "; | 70 | qCDebug(KIO_SFTP_LOG) << "*** Starting kio_sftp "; | ||
70 | 71 | | |||
71 | if (argc != 4) { | 72 | if (argc != 4) { | ||
72 | qCDebug(KIO_SFTP_LOG) << "Usage: kio_sftp protocol domain-socket1 domain-socket2"; | 73 | qCDebug(KIO_SFTP_LOG) << "Usage: kio_sftp protocol domain-socket1 domain-socket2"; | ||
73 | exit(-1); | 74 | exit(-1); | ||
74 | } | 75 | } | ||
75 | 76 | | |||
76 | sftpProtocol slave(argv[2], argv[3]); | 77 | SFTPSlave slave(argv[2], argv[3]); | ||
77 | slave.dispatchLoop(); | 78 | slave.dispatchLoop(); | ||
78 | 79 | | |||
79 | qCDebug(KIO_SFTP_LOG) << "*** kio_sftp Done"; | 80 | qCDebug(KIO_SFTP_LOG) << "*** kio_sftp Done"; | ||
80 | return 0; | 81 | return 0; | ||
81 | } | 82 | } | ||
82 | } | 83 | } | ||
83 | 84 | | |||
84 | // Converts SSH error into KIO error. Only must be called for error handling | 85 | // Converts SSH error into KIO error. Only must be called for error handling | ||
85 | // as this will always return an error state and never NoError. | 86 | // as this will always return an error state and never NoError. | ||
86 | static int toKIOError (const int err) | 87 | static int toKIOError(const int err) | ||
87 | { | 88 | { | ||
88 | switch (err) { | 89 | switch (err) { | ||
89 | case SSH_FX_NO_SUCH_FILE: | 90 | case SSH_FX_NO_SUCH_FILE: | ||
90 | case SSH_FX_NO_SUCH_PATH: | 91 | case SSH_FX_NO_SUCH_PATH: | ||
91 | return KIO::ERR_DOES_NOT_EXIST; | 92 | return KIO::ERR_DOES_NOT_EXIST; | ||
92 | case SSH_FX_PERMISSION_DENIED: | 93 | case SSH_FX_PERMISSION_DENIED: | ||
93 | return KIO::ERR_ACCESS_DENIED; | 94 | return KIO::ERR_ACCESS_DENIED; | ||
94 | case SSH_FX_FILE_ALREADY_EXISTS: | 95 | case SSH_FX_FILE_ALREADY_EXISTS: | ||
Show All 11 Lines | |||||
106 | // encountered an error on the libssh side, this needs to be mapped to *any* | 107 | // encountered an error on the libssh side, this needs to be mapped to *any* | ||
107 | // KIO error. Not mapping is not an option at this point, even if the ssh err | 108 | // KIO error. Not mapping is not an option at this point, even if the ssh err | ||
108 | // is wrong or 'ok'. | 109 | // is wrong or 'ok'. | ||
109 | Q_UNREACHABLE(); | 110 | Q_UNREACHABLE(); | ||
110 | return KIO::ERR_UNKNOWN; | 111 | return KIO::ERR_UNKNOWN; | ||
111 | } | 112 | } | ||
112 | 113 | | |||
113 | // Writes 'len' bytes from 'buf' to the file handle 'fd'. | 114 | // Writes 'len' bytes from 'buf' to the file handle 'fd'. | ||
114 | static int writeToFile(int fd, const char *buf, size_t len) | 115 | static int writeToFile(int fd, const char *buf, int len) | ||
115 | { | 116 | { | ||
116 | while (len > 0) { | 117 | while (len > 0) { | ||
117 | ssize_t written = write(fd, buf, len); | 118 | size_t lenSize = static_cast<size_t>(len); // len is always >> 0 and, being an int, always << size_t | ||
119 | ssize_t written = write(fd, buf, lenSize); | ||||
118 | 120 | | |||
119 | if (written >= 0) { | 121 | if (written >= 0) { | ||
120 | buf += written; | 122 | buf += written; | ||
121 | len -= written; | 123 | len -= written; | ||
122 | continue; | 124 | continue; | ||
123 | } | 125 | } | ||
124 | 126 | | |||
125 | switch(errno) { | 127 | switch(errno) { | ||
126 | case EINTR: | 128 | case EINTR: | ||
127 | case EAGAIN: | 129 | case EAGAIN: | ||
128 | continue; | 130 | continue; | ||
129 | case EPIPE: | 131 | case EPIPE: | ||
130 | return ERR_CONNECTION_BROKEN; | 132 | return ERR_CONNECTION_BROKEN; | ||
131 | case ENOSPC: | 133 | case ENOSPC: | ||
132 | return ERR_DISK_FULL; | 134 | return ERR_DISK_FULL; | ||
133 | default: | 135 | default: | ||
134 | return ERR_CANNOT_WRITE; | 136 | return ERR_CANNOT_WRITE; | ||
135 | } | 137 | } | ||
136 | } | 138 | } | ||
137 | return 0; | 139 | return 0; | ||
138 | } | 140 | } | ||
139 | 141 | | |||
140 | static int seekPos(int fd, KIO::fileoffset_t pos, int mode) | 142 | static KIO::fileoffset_t seekPos(int fd, KIO::fileoffset_t pos, int mode) | ||
141 | { | 143 | { | ||
142 | KIO::fileoffset_t offset = -1; | 144 | KIO::fileoffset_t offset = -1; | ||
143 | while ((offset = QT_LSEEK(fd, pos, mode)) == EAGAIN); | 145 | while ((offset = QT_LSEEK(fd, pos, mode)) == EAGAIN); | ||
feverfew: Technically unrelated to your changes, but does this even work? isn't the check supposed to be… | |||||
True enough. I expect, like many things in the slaves, this never actually worked. that line was introduced as KDE_lseek and that too was just an alias for lseek :| sitter: True enough. I expect, like many things in the slaves, this never actually worked. that line… | |||||
144 | return offset; | 146 | return offset; | ||
145 | } | 147 | } | ||
146 | 148 | | |||
147 | static bool wasUsernameChanged(const QString& username, const KIO::AuthInfo& info) | 149 | static bool wasUsernameChanged(const QString &username, const KIO::AuthInfo &info) | ||
148 | { | 150 | { | ||
149 | QString loginName (username); | 151 | QString loginName (username); | ||
150 | // If username is empty, assume the current logged in username. Why ? | 152 | // If username is empty, assume the current logged in username. Why ? | ||
151 | // Because libssh's SSH_OPTIONS_USER will default to that when it is not | 153 | // Because libssh's SSH_OPTIONS_USER will default to that when it is not | ||
152 | // set and it won't be set unless the user explicitly typed a user user | 154 | // set and it won't be set unless the user explicitly typed a user user | ||
153 | // name as part of the request URL. | 155 | // name as part of the request URL. | ||
154 | if (loginName.isEmpty()) { | 156 | if (loginName.isEmpty()) { | ||
155 | KUser u; | 157 | KUser u; | ||
156 | loginName = u.loginName(); | 158 | loginName = u.loginName(); | ||
157 | } | 159 | } | ||
158 | return (loginName != info.username); | 160 | return (loginName != info.username); | ||
159 | } | 161 | } | ||
160 | 162 | | |||
161 | // The callback function for libssh | 163 | // The callback function for libssh | ||
162 | static int auth_callback(const char *prompt, char *buf, size_t len, | 164 | static int auth_callback(const char *prompt, char *buf, size_t len, | ||
163 | int echo, int verify, void *userdata) | 165 | int echo, int verify, void *userdata) | ||
164 | { | 166 | { | ||
165 | if (userdata == nullptr) { | 167 | if (userdata == nullptr) { | ||
166 | return -1; | 168 | return -1; | ||
167 | } | 169 | } | ||
168 | 170 | | |||
169 | sftpProtocol *slave = (sftpProtocol *) userdata; | 171 | SFTPInternal *slave = static_cast<SFTPInternal *>(userdata); | ||
170 | 172 | | |||
171 | if (slave->auth_callback(prompt, buf, len, echo, verify, userdata) < 0) { | 173 | if (slave->auth_callback(prompt, buf, len, echo, verify, userdata) < 0) { | ||
172 | return -1; | 174 | return -1; | ||
173 | } | 175 | } | ||
174 | 176 | | |||
175 | return 0; | 177 | return 0; | ||
176 | } | 178 | } | ||
177 | 179 | | |||
178 | static void log_callback(int priority, const char *function, const char *buffer, | 180 | static void log_callback(int priority, const char *function, const char *buffer, | ||
179 | void *userdata) | 181 | void *userdata) | ||
180 | { | 182 | { | ||
181 | if (userdata == nullptr) { | 183 | if (userdata == nullptr) { | ||
182 | return; | 184 | return; | ||
183 | } | 185 | } | ||
184 | 186 | | |||
185 | sftpProtocol *slave = (sftpProtocol *) userdata; | 187 | SFTPInternal *slave = static_cast<SFTPInternal *>(userdata); | ||
186 | 188 | | |||
187 | slave->log_callback(priority, function, buffer, userdata); | 189 | slave->log_callback(priority, function, buffer, userdata); | ||
188 | } | 190 | } | ||
189 | 191 | | |||
190 | int sftpProtocol::auth_callback(const char *prompt, char *buf, size_t len, | 192 | int SFTPInternal::auth_callback(const char *prompt, char *buf, size_t len, | ||
191 | int echo, int verify, void *userdata) { | 193 | int echo, int verify, void *userdata) | ||
192 | 194 | { | |||
193 | // unused variables | 195 | Q_UNUSED(echo) | ||
194 | (void) echo; | 196 | Q_UNUSED(verify) | ||
195 | (void) verify; | 197 | Q_UNUSED(userdata) | ||
196 | (void) userdata; | | |||
197 | 198 | | |||
198 | QString errMsg; | 199 | QString errMsg; | ||
199 | if (!mPublicKeyAuthInfo) { | 200 | if (!mPublicKeyAuthInfo) { | ||
200 | mPublicKeyAuthInfo = new KIO::AuthInfo; | 201 | mPublicKeyAuthInfo = new KIO::AuthInfo; | ||
201 | } else { | 202 | } else { | ||
202 | errMsg = i18n("Incorrect or invalid passphrase"); | 203 | errMsg = i18n("Incorrect or invalid passphrase"); | ||
203 | } | 204 | } | ||
204 | 205 | | |||
Show All 11 Lines | |||||
216 | mPublicKeyAuthInfo->prompt = QString::fromUtf8(prompt); | 217 | mPublicKeyAuthInfo->prompt = QString::fromUtf8(prompt); | ||
217 | mPublicKeyAuthInfo->keepPassword = false; // don't save passwords for public key, | 218 | mPublicKeyAuthInfo->keepPassword = false; // don't save passwords for public key, | ||
218 | // that's the task of ssh-agent. | 219 | // that's the task of ssh-agent. | ||
219 | mPublicKeyAuthInfo->setExtraField(QLatin1String("hide-username-line"), true); | 220 | mPublicKeyAuthInfo->setExtraField(QLatin1String("hide-username-line"), true); | ||
220 | mPublicKeyAuthInfo->setModified(false); | 221 | mPublicKeyAuthInfo->setModified(false); | ||
221 | 222 | | |||
222 | qCDebug(KIO_SFTP_LOG) << "Entering authentication callback, prompt=" << mPublicKeyAuthInfo->prompt; | 223 | qCDebug(KIO_SFTP_LOG) << "Entering authentication callback, prompt=" << mPublicKeyAuthInfo->prompt; | ||
223 | 224 | | |||
224 | if (openPasswordDialogV2(*mPublicKeyAuthInfo, errMsg) != 0) { | 225 | if (q->openPasswordDialogV2(*mPublicKeyAuthInfo, errMsg) != 0) { | ||
225 | qCDebug(KIO_SFTP_LOG) << "User canceled public key passpharse dialog"; | 226 | qCDebug(KIO_SFTP_LOG) << "User canceled public key passpharse dialog"; | ||
226 | return -1; | 227 | return -1; | ||
227 | } | 228 | } | ||
228 | 229 | | |||
229 | strncpy(buf, mPublicKeyAuthInfo->password.toUtf8().constData(), len - 1); | 230 | strncpy(buf, mPublicKeyAuthInfo->password.toUtf8().constData(), len - 1); | ||
230 | 231 | | |||
231 | mPublicKeyAuthInfo->password.fill('x'); | 232 | mPublicKeyAuthInfo->password.fill('x'); | ||
232 | mPublicKeyAuthInfo->password.clear(); | 233 | mPublicKeyAuthInfo->password.clear(); | ||
233 | 234 | | |||
234 | return 0; | 235 | return 0; | ||
235 | } | 236 | } | ||
236 | 237 | | |||
237 | void sftpProtocol::log_callback(int priority, const char *function, const char *buffer, | 238 | void SFTPInternal::log_callback(int priority, const char *function, const char *buffer, | ||
238 | void *userdata) | 239 | void *userdata) | ||
239 | { | 240 | { | ||
240 | (void) userdata; | 241 | Q_UNUSED(userdata) | ||
241 | | ||||
242 | qCDebug(KIO_SFTP_LOG) << "[" << function << "] (" << priority << ") " << buffer; | 242 | qCDebug(KIO_SFTP_LOG) << "[" << function << "] (" << priority << ") " << buffer; | ||
243 | } | 243 | } | ||
244 | 244 | | |||
245 | void sftpProtocol::virtual_hook(int id, void *data) | 245 | Result SFTPInternal::init() | ||
246 | { | ||||
247 | qCDebug(KIO_SFTP_LOG) << "pid = " << getpid(); | ||||
248 | qCDebug(KIO_SFTP_LOG) << "debug = " << getenv("KIO_SFTP_LOG_VERBOSITY"); | ||||
249 | | ||||
250 | // Members are 'value initialized' to zero because of non-user defined ()! | ||||
251 | mCallbacks = new struct ssh_callbacks_struct(); | ||||
252 | if (mCallbacks == nullptr) { | ||||
253 | return Result::fail(KIO::ERR_OUT_OF_MEMORY, i18n("Could not allocate callbacks")); | ||||
254 | } | ||||
255 | | ||||
256 | mCallbacks->userdata = this; | ||||
257 | mCallbacks->auth_function = ::auth_callback; | ||||
258 | | ||||
259 | ssh_callbacks_init(mCallbacks) | ||||
260 | | ||||
261 | bool ok; | ||||
262 | int level = qEnvironmentVariableIntValue("KIO_SFTP_LOG_VERBOSITY", &ok); | ||||
263 | if (ok) { | ||||
264 | int rc = ssh_set_log_level(level); | ||||
265 | if (rc != SSH_OK) { | ||||
266 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set log verbosity.")); | ||||
267 | } | ||||
268 | | ||||
269 | rc = ssh_set_log_userdata(this); | ||||
270 | if (rc != SSH_OK) { | ||||
271 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set log userdata.")); | ||||
272 | } | ||||
273 | | ||||
274 | rc = ssh_set_log_callback(::log_callback); | ||||
275 | if (rc != SSH_OK) { | ||||
276 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set log callback.")); | ||||
277 | } | ||||
278 | } | ||||
279 | | ||||
280 | return Result::pass(); | ||||
281 | } | ||||
282 | | ||||
283 | void SFTPSlave::virtual_hook(int id, void *data) | ||||
246 | { | 284 | { | ||
247 | switch(id) { | 285 | switch(id) { | ||
248 | case SlaveBase::GetFileSystemFreeSpace: { | 286 | case SlaveBase::GetFileSystemFreeSpace: { | ||
249 | QUrl *url = static_cast<QUrl *>(data); | 287 | QUrl *url = static_cast<QUrl *>(data); | ||
250 | fileSystemFreeSpace(*url); | 288 | finalize(d->fileSystemFreeSpace(*url)); | ||
251 | } break; | 289 | return; | ||
290 | } | ||||
252 | case SlaveBase::Truncate: { | 291 | case SlaveBase::Truncate: { | ||
253 | auto length = static_cast<KIO::filesize_t *>(data); | 292 | auto length = static_cast<KIO::filesize_t *>(data); | ||
254 | truncate(*length); | 293 | maybeError(d->truncate(*length)); | ||
255 | } break; | 294 | return; | ||
256 | default: | 295 | } | ||
296 | } | ||||
257 | SlaveBase::virtual_hook(id, data); | 297 | SlaveBase::virtual_hook(id, data); | ||
258 | } | 298 | } | ||
299 | | ||||
300 | void SFTPSlave::finalize(const Result &result) | ||||
301 | { | ||||
302 | if (!result.success) { | ||||
feverfew: `if (result.success) { ...} else { ... } seems clearer to me | |||||
That function is a verbatim copy from ftp so I'd rather not change it. sitter: That function is a verbatim copy from ftp so I'd rather not change it. | |||||
303 | error(result.error, result.errorString); | ||||
304 | return; | ||||
305 | } | ||||
306 | finished(); | ||||
259 | } | 307 | } | ||
260 | 308 | | |||
261 | int sftpProtocol::authenticateKeyboardInteractive(AuthInfo &info) { | 309 | void SFTPSlave::maybeError(const Result &result) | ||
310 | { | ||||
311 | if (!result.success) { | ||||
312 | error(result.error, result.errorString); | ||||
313 | } | ||||
314 | } | ||||
262 | 315 | | |||
316 | int SFTPInternal::authenticateKeyboardInteractive(AuthInfo &info) | ||||
317 | { | ||||
263 | int err = ssh_userauth_kbdint(mSession, nullptr, nullptr); | 318 | int err = ssh_userauth_kbdint(mSession, nullptr, nullptr); | ||
264 | 319 | | |||
265 | while (err == SSH_AUTH_INFO) { | 320 | while (err == SSH_AUTH_INFO) { | ||
266 | const QString name = QString::fromUtf8(ssh_userauth_kbdint_getname(mSession)); | 321 | const QString name = QString::fromUtf8(ssh_userauth_kbdint_getname(mSession)); | ||
267 | const QString instruction = QString::fromUtf8(ssh_userauth_kbdint_getinstruction(mSession)); | 322 | const QString instruction = QString::fromUtf8(ssh_userauth_kbdint_getinstruction(mSession)); | ||
268 | const int n = ssh_userauth_kbdint_getnprompts(mSession); | 323 | const int n = ssh_userauth_kbdint_getnprompts(mSession); | ||
269 | 324 | | |||
270 | qCDebug(KIO_SFTP_LOG) << "name=" << name << " instruction=" << instruction << " prompts=" << n; | 325 | qCDebug(KIO_SFTP_LOG) << "name=" << name << " instruction=" << instruction << " prompts=" << n; | ||
271 | 326 | | |||
272 | for (int i = 0; i < n; ++i) { | 327 | for (int iInt = 0; iInt < n; ++iInt) { | ||
328 | unsigned int i = static_cast<unsigned int>(iInt); // can only be >0 | ||||
feverfew: I'm a bit confused, why is this necessary? | |||||
Kinda unrelated to the diff, I only changed it because this class has way too many type coercions. Mind you, I think n<1 cannot even happen. The server challenged us, so there's always going to be at least one prompt, so the fact that it returns a signed prompt count is silly to begin with. sitter: Kinda unrelated to the diff, I only changed it because this class has way too many type… | |||||
273 | char echo; | 329 | char echo; | ||
274 | const char *answer = ""; | 330 | const char *answer = ""; | ||
275 | 331 | | |||
276 | const QString prompt = QString::fromUtf8(ssh_userauth_kbdint_getprompt(mSession, i, &echo)); | 332 | const QString prompt = QString::fromUtf8(ssh_userauth_kbdint_getprompt(mSession, i, &echo)); | ||
277 | qCDebug(KIO_SFTP_LOG) << "prompt=" << prompt << " echo=" << QString::number(echo); | 333 | qCDebug(KIO_SFTP_LOG) << "prompt=" << prompt << " echo=" << QString::number(echo); | ||
278 | if (echo) { | 334 | if (echo) { | ||
279 | // See RFC4256 Section 3.3 User Interface | 335 | // See RFC4256 Section 3.3 User Interface | ||
280 | KIO::AuthInfo infoKbdInt; | 336 | KIO::AuthInfo infoKbdInt; | ||
Show All 17 Lines | 353 | if (!instruction.isEmpty()) { | |||
298 | newPrompt = instruction + "<br /><br />"; | 354 | newPrompt = instruction + "<br /><br />"; | ||
299 | } | 355 | } | ||
300 | newPrompt.append(prompt); | 356 | newPrompt.append(prompt); | ||
301 | infoKbdInt.prompt = newPrompt; | 357 | infoKbdInt.prompt = newPrompt; | ||
302 | 358 | | |||
303 | infoKbdInt.readOnly = false; | 359 | infoKbdInt.readOnly = false; | ||
304 | infoKbdInt.keepPassword = false; | 360 | infoKbdInt.keepPassword = false; | ||
305 | 361 | | |||
306 | if (openPasswordDialogV2(infoKbdInt, i18n("Use the username input field to answer this question.")) == 0) { | 362 | if (q->openPasswordDialogV2(infoKbdInt, i18n("Use the username input field to answer this question.")) == KJob::NoError) { | ||
307 | qCDebug(KIO_SFTP_LOG) << "Got the answer from the password dialog"; | 363 | qCDebug(KIO_SFTP_LOG) << "Got the answer from the password dialog"; | ||
308 | answer = info.username.toUtf8().constData(); | 364 | answer = info.username.toUtf8().constData(); | ||
309 | } | 365 | } | ||
310 | 366 | | |||
311 | if (ssh_userauth_kbdint_setanswer(mSession, i, answer) < 0) { | 367 | if (ssh_userauth_kbdint_setanswer(mSession, i, answer) < 0) { | ||
312 | qCDebug(KIO_SFTP_LOG) << "An error occurred setting the answer: " | 368 | qCDebug(KIO_SFTP_LOG) << "An error occurred setting the answer: " | ||
313 | << ssh_get_error(mSession); | 369 | << ssh_get_error(mSession); | ||
314 | return SSH_AUTH_ERROR; | 370 | return SSH_AUTH_ERROR; | ||
315 | } | 371 | } | ||
316 | break; | 372 | break; | ||
317 | } else { | 373 | } else { | ||
318 | if (prompt.startsWith(QLatin1String("password:"), Qt::CaseInsensitive)) { | 374 | if (prompt.startsWith(QLatin1String("password:"), Qt::CaseInsensitive)) { | ||
319 | info.prompt = i18n("Please enter your password."); | 375 | info.prompt = i18n("Please enter your password."); | ||
320 | } else { | 376 | } else { | ||
321 | info.prompt = prompt; | 377 | info.prompt = prompt; | ||
322 | } | 378 | } | ||
323 | info.comment = info.url.url(); | 379 | info.comment = info.url.url(); | ||
324 | info.commentLabel = i18n("Site:"); | 380 | info.commentLabel = i18n("Site:"); | ||
325 | info.setExtraField(QLatin1String("hide-username-line"), true); | 381 | info.setExtraField(QLatin1String("hide-username-line"), true); | ||
326 | 382 | | |||
327 | if (openPasswordDialogV2(info) == 0) { | 383 | if (q->openPasswordDialogV2(info) == KJob::NoError) { | ||
328 | qCDebug(KIO_SFTP_LOG) << "Got the answer from the password dialog"; | 384 | qCDebug(KIO_SFTP_LOG) << "Got the answer from the password dialog"; | ||
329 | answer = info.password.toUtf8().constData(); | 385 | answer = info.password.toUtf8().constData(); | ||
330 | } | 386 | } | ||
331 | 387 | | |||
332 | if (ssh_userauth_kbdint_setanswer(mSession, i, answer) < 0) { | 388 | if (ssh_userauth_kbdint_setanswer(mSession, i, answer) < 0) { | ||
333 | qCDebug(KIO_SFTP_LOG) << "An error occurred setting the answer: " | 389 | qCDebug(KIO_SFTP_LOG) << "An error occurred setting the answer: " | ||
334 | << ssh_get_error(mSession); | 390 | << ssh_get_error(mSession); | ||
335 | return SSH_AUTH_ERROR; | 391 | return SSH_AUTH_ERROR; | ||
336 | } | 392 | } | ||
337 | } | 393 | } | ||
338 | } | 394 | } | ||
339 | err = ssh_userauth_kbdint(mSession, nullptr, nullptr); | 395 | err = ssh_userauth_kbdint(mSession, nullptr, nullptr); | ||
340 | } | 396 | } | ||
341 | 397 | | |||
342 | return err; | 398 | return err; | ||
343 | } | 399 | } | ||
344 | 400 | | |||
345 | void sftpProtocol::reportError(const QUrl &url, const int err) { | 401 | Result SFTPInternal::reportError(const QUrl &url, const int err) | ||
402 | { | ||||
346 | qCDebug(KIO_SFTP_LOG) << "url = " << url << " - err=" << err; | 403 | qCDebug(KIO_SFTP_LOG) << "url = " << url << " - err=" << err; | ||
347 | 404 | | |||
348 | const int kioError = toKIOError(err); | 405 | const int kioError = toKIOError(err); | ||
349 | Q_ASSERT(kioError != 0); | 406 | Q_ASSERT(kioError != KJob::NoError); | ||
350 | 407 | | |||
351 | error(kioError, url.toDisplayString()); | 408 | return Result::fail(kioError, url.toDisplayString()); | ||
352 | } | 409 | } | ||
353 | 410 | | |||
354 | bool sftpProtocol::createUDSEntry(const QString &filename, const QByteArray &path, | 411 | bool SFTPInternal::createUDSEntry(const QString &filename, const QByteArray &path, | ||
355 | UDSEntry &entry, short int details) { | 412 | UDSEntry &entry, short int details) | ||
413 | { | ||||
356 | mode_t access; | 414 | mode_t access; | ||
357 | char *link; | 415 | char *link; | ||
358 | bool isBrokenLink = false; | 416 | bool isBrokenLink = false; | ||
359 | long long fileType = QT_STAT_REG; | 417 | long long fileType = QT_STAT_REG; | ||
360 | long long size = 0LL; | 418 | uint64_t size = 0U; | ||
feverfew: Should then be changed from `0LL` to `0U`? | |||||
361 | 419 | | |||
362 | Q_ASSERT(entry.count() == 0); | 420 | Q_ASSERT(entry.count() == 0); | ||
363 | 421 | | |||
364 | sftp_attributes sb = sftp_lstat(mSftp, path.constData()); | 422 | sftp_attributes sb = sftp_lstat(mSftp, path.constData()); | ||
365 | if (sb == nullptr) { | 423 | if (sb == nullptr) { | ||
366 | return false; | 424 | return false; | ||
367 | } | 425 | } | ||
368 | 426 | | |||
▲ Show 20 Lines • Show All 69 Lines • ▼ Show 20 Line(s) | 481 | if (details > 0) { | |||
438 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, sb->createtime); | 496 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, sb->createtime); | ||
439 | } | 497 | } | ||
440 | 498 | | |||
441 | sftp_attributes_free(sb); | 499 | sftp_attributes_free(sb); | ||
442 | 500 | | |||
443 | return true; | 501 | return true; | ||
444 | } | 502 | } | ||
445 | 503 | | |||
446 | QString sftpProtocol::canonicalizePath(const QString &path) { | 504 | QString SFTPInternal::canonicalizePath(const QString &path) | ||
505 | { | ||||
447 | qCDebug(KIO_SFTP_LOG) << "Path to canonicalize: " << path; | 506 | qCDebug(KIO_SFTP_LOG) << "Path to canonicalize: " << path; | ||
448 | QString cPath; | 507 | QString cPath; | ||
449 | char *sPath = nullptr; | 508 | char *sPath = nullptr; | ||
450 | 509 | | |||
451 | if (path.isEmpty()) { | 510 | if (path.isEmpty()) { | ||
452 | return cPath; | 511 | return cPath; | ||
453 | } | 512 | } | ||
454 | 513 | | |||
455 | sPath = sftp_canonicalize_path(mSftp, path.toUtf8().constData()); | 514 | sPath = sftp_canonicalize_path(mSftp, path.toUtf8().constData()); | ||
456 | if (sPath == nullptr) { | 515 | if (sPath == nullptr) { | ||
457 | qCDebug(KIO_SFTP_LOG) << "Could not canonicalize path: " << path; | 516 | qCDebug(KIO_SFTP_LOG) << "Could not canonicalize path: " << path; | ||
458 | return cPath; | 517 | return cPath; | ||
459 | } | 518 | } | ||
460 | 519 | | |||
461 | cPath = QFile::decodeName(sPath); | 520 | cPath = QFile::decodeName(sPath); | ||
462 | ssh_string_free_char(sPath); | 521 | ssh_string_free_char(sPath); | ||
463 | 522 | | |||
464 | qCDebug(KIO_SFTP_LOG) << "Canonicalized path: " << cPath; | 523 | qCDebug(KIO_SFTP_LOG) << "Canonicalized path: " << cPath; | ||
465 | 524 | | |||
466 | return cPath; | 525 | return cPath; | ||
467 | } | 526 | } | ||
468 | 527 | | |||
469 | sftpProtocol::sftpProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) | 528 | SFTPInternal::SFTPInternal(SFTPSlave *qptr) | ||
470 | : SlaveBase("kio_sftp", pool_socket, app_socket), | 529 | : q(qptr) | ||
471 | mConnected(false), mPort(-1), mSession(nullptr), mSftp(nullptr), mPublicKeyAuthInfo(nullptr) { | 530 | { | ||
472 | #ifndef Q_OS_WIN | | |||
473 | qCDebug(KIO_SFTP_LOG) << "pid = " << getpid(); | | |||
474 | | ||||
475 | qCDebug(KIO_SFTP_LOG) << "debug = " << getenv("KIO_SFTP_LOG_VERBOSITY"); | | |||
476 | #endif | | |||
477 | | ||||
478 | // Members are 'value initialized' to zero because of non-user defined ()! | | |||
479 | mCallbacks = new struct ssh_callbacks_struct(); | | |||
480 | if (mCallbacks == nullptr) { | | |||
481 | error(KIO::ERR_OUT_OF_MEMORY, i18n("Could not allocate callbacks")); | | |||
482 | return; | | |||
483 | } | | |||
484 | | ||||
485 | mCallbacks->userdata = this; | | |||
486 | mCallbacks->auth_function = ::auth_callback; | | |||
487 | | ||||
488 | ssh_callbacks_init(mCallbacks); | | |||
489 | | ||||
490 | bool ok; | | |||
491 | int level = qEnvironmentVariableIntValue("KIO_SFTP_LOG_VERBOSITY", &ok); | | |||
492 | if (ok) { | | |||
493 | int rc = ssh_set_log_level(level); | | |||
494 | if (rc != SSH_OK) { | | |||
495 | error(KIO::ERR_INTERNAL, i18n("Could not set log verbosity.")); | | |||
496 | return; | | |||
497 | } | | |||
498 | | ||||
499 | rc = ssh_set_log_userdata(this); | | |||
500 | if (rc != SSH_OK) { | | |||
501 | error(KIO::ERR_INTERNAL, i18n("Could not set log userdata.")); | | |||
502 | return; | | |||
503 | } | | |||
504 | | ||||
505 | rc = ssh_set_log_callback(::log_callback); | | |||
506 | if (rc != SSH_OK) { | | |||
507 | error(KIO::ERR_INTERNAL, i18n("Could not set log callback.")); | | |||
508 | return; | | |||
509 | } | | |||
510 | } | | |||
511 | } | 531 | } | ||
512 | 532 | | |||
513 | sftpProtocol::~sftpProtocol() { | 533 | SFTPInternal::~SFTPInternal() { | ||
514 | #ifndef Q_OS_WIN | | |||
515 | qCDebug(KIO_SFTP_LOG) << "pid = " << getpid(); | 534 | qCDebug(KIO_SFTP_LOG) << "pid = " << getpid(); | ||
516 | #endif | | |||
517 | closeConnection(); | 535 | closeConnection(); | ||
518 | 536 | | |||
519 | delete mCallbacks; | 537 | delete mCallbacks; | ||
520 | delete mPublicKeyAuthInfo; // for precaution | 538 | delete mPublicKeyAuthInfo; // for precaution | ||
521 | 539 | | |||
522 | /* cleanup and shut down cryto stuff */ | 540 | /* cleanup and shut down cryto stuff */ | ||
523 | ssh_finalize(); | 541 | ssh_finalize(); | ||
524 | } | 542 | } | ||
525 | 543 | | |||
526 | void sftpProtocol::setHost(const QString& host, quint16 port, const QString& user, const QString& pass) { | 544 | void SFTPInternal::setHost(const QString& host, quint16 port, const QString& user, const QString& pass) { | ||
527 | qCDebug(KIO_SFTP_LOG) << user << "@" << host << ":" << port; | 545 | qCDebug(KIO_SFTP_LOG) << user << "@" << host << ":" << port; | ||
528 | 546 | | |||
529 | // Close connection if the request is to another server... | 547 | // Close connection if the request is to another server... | ||
530 | if (host != mHost || port != mPort || | 548 | if (host != mHost || port != mPort || | ||
531 | user != mUsername || pass != mPassword) { | 549 | user != mUsername || pass != mPassword) { | ||
532 | closeConnection(); | 550 | closeConnection(); | ||
533 | } | 551 | } | ||
534 | 552 | | |||
535 | mHost = host; | 553 | mHost = host; | ||
536 | mPort = port; | 554 | mPort = port; | ||
537 | mUsername = user; | 555 | mUsername = user; | ||
538 | mPassword = pass; | 556 | mPassword = pass; | ||
539 | } | 557 | } | ||
540 | 558 | | |||
541 | bool sftpProtocol::sftpOpenConnection (const AuthInfo& info) | 559 | Result SFTPInternal::sftpOpenConnection(const AuthInfo &info) | ||
542 | { | 560 | { | ||
543 | mSession = ssh_new(); | 561 | mSession = ssh_new(); | ||
544 | if (mSession == nullptr) { | 562 | if (mSession == nullptr) { | ||
545 | error(KIO::ERR_OUT_OF_MEMORY, i18n("Could not create a new SSH session.")); | 563 | return Result::fail(KIO::ERR_OUT_OF_MEMORY, i18n("Could not create a new SSH session.")); | ||
546 | return false; | | |||
547 | } | 564 | } | ||
548 | 565 | | |||
549 | long timeout_sec = 30, timeout_usec = 0; | 566 | long timeout_sec = 30, timeout_usec = 0; | ||
550 | 567 | | |||
551 | qCDebug(KIO_SFTP_LOG) << "Creating the SSH session and setting options"; | 568 | qCDebug(KIO_SFTP_LOG) << "Creating the SSH session and setting options"; | ||
552 | 569 | | |||
553 | // Set timeout | 570 | // Set timeout | ||
554 | int rc = ssh_options_set(mSession, SSH_OPTIONS_TIMEOUT, &timeout_sec); | 571 | int rc = ssh_options_set(mSession, SSH_OPTIONS_TIMEOUT, &timeout_sec); | ||
555 | if (rc < 0) { | 572 | if (rc < 0) { | ||
556 | error(KIO::ERR_INTERNAL, i18n("Could not set a timeout.")); | 573 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set a timeout.")); | ||
557 | return false; | | |||
558 | } | 574 | } | ||
559 | rc = ssh_options_set(mSession, SSH_OPTIONS_TIMEOUT_USEC, &timeout_usec); | 575 | rc = ssh_options_set(mSession, SSH_OPTIONS_TIMEOUT_USEC, &timeout_usec); | ||
560 | if (rc < 0) { | 576 | if (rc < 0) { | ||
561 | error(KIO::ERR_INTERNAL, i18n("Could not set a timeout.")); | 577 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set a timeout.")); | ||
562 | return false; | | |||
563 | } | 578 | } | ||
564 | 579 | | |||
565 | #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0) | 580 | #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 0) | ||
566 | // Disable Nagle's Algorithm (TCP_NODELAY). Usually faster for sftp. | 581 | // Disable Nagle's Algorithm (TCP_NODELAY). Usually faster for sftp. | ||
567 | bool nodelay = true; | 582 | bool nodelay = true; | ||
568 | rc = ssh_options_set(mSession, SSH_OPTIONS_NODELAY, &nodelay); | 583 | rc = ssh_options_set(mSession, SSH_OPTIONS_NODELAY, &nodelay); | ||
569 | if (rc < 0) { | 584 | if (rc < 0) { | ||
570 | error(KIO::ERR_INTERNAL, i18n("Could not disable Nagle's Algorithm.")); | 585 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not disable Nagle's Algorithm.")); | ||
571 | return false; | | |||
572 | } | 586 | } | ||
573 | #endif // 0.8.0 | 587 | #endif // 0.8.0 | ||
574 | 588 | | |||
575 | // Don't use any compression | 589 | // Don't use any compression | ||
576 | rc = ssh_options_set(mSession, SSH_OPTIONS_COMPRESSION_C_S, "none"); | 590 | rc = ssh_options_set(mSession, SSH_OPTIONS_COMPRESSION_C_S, "none"); | ||
577 | if (rc < 0) { | 591 | if (rc < 0) { | ||
578 | error(KIO::ERR_INTERNAL, i18n("Could not set compression.")); | 592 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set compression.")); | ||
579 | return false; | | |||
580 | } | 593 | } | ||
581 | 594 | | |||
582 | rc = ssh_options_set(mSession, SSH_OPTIONS_COMPRESSION_S_C, "none"); | 595 | rc = ssh_options_set(mSession, SSH_OPTIONS_COMPRESSION_S_C, "none"); | ||
583 | if (rc < 0) { | 596 | if (rc < 0) { | ||
584 | error(KIO::ERR_INTERNAL, i18n("Could not set compression.")); | 597 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set compression.")); | ||
585 | return false; | | |||
586 | } | 598 | } | ||
587 | 599 | | |||
588 | // Set host and port | 600 | // Set host and port | ||
589 | rc = ssh_options_set(mSession, SSH_OPTIONS_HOST, mHost.toUtf8().constData()); | 601 | rc = ssh_options_set(mSession, SSH_OPTIONS_HOST, mHost.toUtf8().constData()); | ||
590 | if (rc < 0) { | 602 | if (rc < 0) { | ||
591 | error(KIO::ERR_INTERNAL, i18n("Could not set host.")); | 603 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set host.")); | ||
592 | return false; | | |||
593 | } | 604 | } | ||
594 | 605 | | |||
595 | if (mPort > 0) { | 606 | if (mPort > 0) { | ||
596 | rc = ssh_options_set(mSession, SSH_OPTIONS_PORT, &mPort); | 607 | rc = ssh_options_set(mSession, SSH_OPTIONS_PORT, &mPort); | ||
597 | if (rc < 0) { | 608 | if (rc < 0) { | ||
598 | error(KIO::ERR_INTERNAL, i18n("Could not set port.")); | 609 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set port.")); | ||
599 | return false; | | |||
600 | } | 610 | } | ||
601 | } | 611 | } | ||
602 | 612 | | |||
603 | // Set the username | 613 | // Set the username | ||
604 | if (!info.username.isEmpty()) { | 614 | if (!info.username.isEmpty()) { | ||
605 | rc = ssh_options_set(mSession, SSH_OPTIONS_USER, info.username.toUtf8().constData()); | 615 | rc = ssh_options_set(mSession, SSH_OPTIONS_USER, info.username.toUtf8().constData()); | ||
606 | if (rc < 0) { | 616 | if (rc < 0) { | ||
607 | error(KIO::ERR_INTERNAL, i18n("Could not set username.")); | 617 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not set username.")); | ||
608 | return false; | | |||
609 | } | 618 | } | ||
610 | } | 619 | } | ||
611 | 620 | | |||
612 | // Read ~/.ssh/config | 621 | // Read ~/.ssh/config | ||
613 | rc = ssh_options_parse_config(mSession, nullptr); | 622 | rc = ssh_options_parse_config(mSession, nullptr); | ||
614 | if (rc < 0) { | 623 | if (rc < 0) { | ||
615 | error(KIO::ERR_INTERNAL, i18n("Could not parse the config file.")); | 624 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not parse the config file.")); | ||
616 | return false; | | |||
617 | } | 625 | } | ||
618 | 626 | | |||
619 | ssh_set_callbacks(mSession, mCallbacks); | 627 | ssh_set_callbacks(mSession, mCallbacks); | ||
620 | 628 | | |||
621 | qCDebug(KIO_SFTP_LOG) << "Trying to connect to the SSH server"; | 629 | qCDebug(KIO_SFTP_LOG) << "Trying to connect to the SSH server"; | ||
622 | 630 | | |||
623 | unsigned int effectivePort; | 631 | unsigned int effectivePort; | ||
624 | if (mPort > 0) { | 632 | if (mPort > 0) { | ||
625 | effectivePort = mPort; | 633 | effectivePort = mPort; | ||
626 | } else { | 634 | } else { | ||
627 | effectivePort = DEFAULT_SFTP_PORT; | 635 | effectivePort = DEFAULT_SFTP_PORT; | ||
628 | ssh_options_get_port(mSession, &effectivePort); | 636 | ssh_options_get_port(mSession, &effectivePort); | ||
629 | } | 637 | } | ||
630 | 638 | | |||
631 | qCDebug(KIO_SFTP_LOG) << "username=" << mUsername << ", host=" << mHost << ", port=" << effectivePort; | 639 | qCDebug(KIO_SFTP_LOG) << "username=" << mUsername << ", host=" << mHost << ", port=" << effectivePort; | ||
632 | 640 | | |||
633 | infoMessage(xi18n("Opening SFTP connection to host %1:%2", mHost, QString::number(effectivePort))); | 641 | q->infoMessage(xi18n("Opening SFTP connection to host %1:%2", mHost, QString::number(effectivePort))); | ||
634 | 642 | | |||
635 | /* try to connect */ | 643 | /* try to connect */ | ||
636 | rc = ssh_connect(mSession); | 644 | rc = ssh_connect(mSession); | ||
637 | if (rc < 0) { | 645 | if (rc < 0) { | ||
638 | error(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession))); | 646 | const QString errorString = QString::fromUtf8(ssh_get_error(mSession)); | ||
639 | closeConnection(); | 647 | closeConnection(); | ||
640 | return false; | 648 | return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString); | ||
641 | } | 649 | } | ||
642 | 650 | | |||
643 | return true; | 651 | return Result::pass(); | ||
644 | } | 652 | } | ||
645 | 653 | | |||
646 | | ||||
647 | #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 3) | 654 | #if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0, 8, 3) | ||
648 | void sftpProtocol::openConnection() | 655 | Result SFTPInternal::openConnection() | ||
649 | { | 656 | { | ||
650 | if (mConnected) { | 657 | if (mConnected) { | ||
651 | return; | 658 | return Result::pass(); | ||
652 | } | 659 | } | ||
653 | 660 | | |||
654 | if (mHost.isEmpty()) { | 661 | if (mHost.isEmpty()) { | ||
655 | qCDebug(KIO_SFTP_LOG) << "openConnection(): Need hostname..."; | 662 | qCDebug(KIO_SFTP_LOG) << "openConnection(): Need hostname..."; | ||
656 | error(KIO::ERR_UNKNOWN_HOST, QString()); | 663 | return Result::fail(KIO::ERR_UNKNOWN_HOST, QString()); | ||
657 | return; | | |||
658 | } | 664 | } | ||
659 | 665 | | |||
660 | AuthInfo info; | 666 | AuthInfo info; | ||
661 | info.url.setScheme("sftp"); | 667 | info.url.setScheme("sftp"); | ||
662 | info.url.setHost(mHost); | 668 | info.url.setHost(mHost); | ||
663 | if ( mPort > 0 && mPort != DEFAULT_SFTP_PORT ) { | 669 | if ( mPort > 0 && mPort != DEFAULT_SFTP_PORT ) { | ||
664 | info.url.setPort(mPort); | 670 | info.url.setPort(mPort); | ||
665 | } | 671 | } | ||
666 | info.url.setUserName(mUsername); | 672 | info.url.setUserName(mUsername); | ||
667 | info.username = mUsername; | 673 | info.username = mUsername; | ||
668 | 674 | | |||
669 | // Check for cached authentication info if no password is specified... | 675 | // Check for cached authentication info if no password is specified... | ||
670 | if (mPassword.isEmpty()) { | 676 | if (mPassword.isEmpty()) { | ||
671 | qCDebug(KIO_SFTP_LOG) << "checking cache: info.username =" << info.username | 677 | qCDebug(KIO_SFTP_LOG) << "checking cache: info.username =" << info.username | ||
672 | << ", info.url =" << info.url.toDisplayString(); | 678 | << ", info.url =" << info.url.toDisplayString(); | ||
673 | checkCachedAuthentication(info); | 679 | q->checkCachedAuthentication(info); | ||
674 | } else { | 680 | } else { | ||
675 | info.password = mPassword; | 681 | info.password = mPassword; | ||
676 | } | 682 | } | ||
677 | 683 | | |||
678 | // Start the ssh connection. | 684 | // Start the ssh connection. | ||
679 | QString msg; // msg for dialog box | 685 | QString msg; // msg for dialog box | ||
680 | QString caption; // dialog box caption | 686 | QString caption; // dialog box caption | ||
681 | unsigned char *hash = nullptr; // the server hash | 687 | unsigned char *hash = nullptr; // the server hash | ||
682 | size_t hlen; | 688 | size_t hlen; | ||
683 | ssh_key srv_pubkey = nullptr; | 689 | ssh_key srv_pubkey = nullptr; | ||
684 | const char *srv_pubkey_type = nullptr; | 690 | const char *srv_pubkey_type = nullptr; | ||
685 | char *fingerprint = nullptr; | 691 | char *fingerprint = nullptr; | ||
686 | enum ssh_known_hosts_e state; | 692 | enum ssh_known_hosts_e state; | ||
687 | int rc; | 693 | int rc; | ||
688 | 694 | | |||
689 | // Attempt to start a ssh session and establish a connection with the server. | 695 | // Attempt to start a ssh session and establish a connection with the server. | ||
690 | if (!sftpOpenConnection(info)) { | 696 | const Result openResult = sftpOpenConnection(info); | ||
691 | return; | 697 | if (!openResult.success) { | ||
698 | return openResult; | ||||
692 | } | 699 | } | ||
693 | 700 | | |||
694 | qCDebug(KIO_SFTP_LOG) << "Getting the SSH server hash"; | 701 | qCDebug(KIO_SFTP_LOG) << "Getting the SSH server hash"; | ||
695 | 702 | | |||
696 | /* get the hash */ | 703 | /* get the hash */ | ||
697 | rc = ssh_get_server_publickey(mSession, &srv_pubkey); | 704 | rc = ssh_get_server_publickey(mSession, &srv_pubkey); | ||
698 | if (rc < 0) { | 705 | if (rc < 0) { | ||
699 | error(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession))); | 706 | const QString errorString = QString::fromUtf8(ssh_get_error(mSession)); | ||
700 | closeConnection(); | 707 | closeConnection(); | ||
701 | return; | 708 | return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString); | ||
702 | } | 709 | } | ||
703 | 710 | | |||
704 | srv_pubkey_type = ssh_key_type_to_char(ssh_key_type(srv_pubkey)); | 711 | srv_pubkey_type = ssh_key_type_to_char(ssh_key_type(srv_pubkey)); | ||
705 | if (srv_pubkey_type == nullptr) { | 712 | if (srv_pubkey_type == nullptr) { | ||
706 | ssh_key_free(srv_pubkey); | 713 | ssh_key_free(srv_pubkey); | ||
707 | error(KIO::ERR_SLAVE_DEFINED, | | |||
708 | i18n("Could not get server public key type name")); | | |||
709 | closeConnection(); | 714 | closeConnection(); | ||
710 | return; | 715 | return Result::fail(KIO::ERR_SLAVE_DEFINED, | ||
716 | i18n("Could not get server public key type name")); | ||||
711 | } | 717 | } | ||
712 | 718 | | |||
713 | rc = ssh_get_publickey_hash(srv_pubkey, | 719 | rc = ssh_get_publickey_hash(srv_pubkey, | ||
714 | SSH_PUBLICKEY_HASH_SHA256, | 720 | SSH_PUBLICKEY_HASH_SHA256, | ||
715 | &hash, | 721 | &hash, | ||
716 | &hlen); | 722 | &hlen); | ||
717 | ssh_key_free(srv_pubkey); | 723 | ssh_key_free(srv_pubkey); | ||
718 | if (rc != SSH_OK) { | 724 | if (rc != SSH_OK) { | ||
719 | error(KIO::ERR_SLAVE_DEFINED, | | |||
720 | i18n("Could not create hash from server public key")); | | |||
721 | closeConnection(); | 725 | closeConnection(); | ||
722 | return; | 726 | return Result::fail(KIO::ERR_SLAVE_DEFINED, | ||
727 | i18n("Could not create hash from server public key")); | ||||
723 | } | 728 | } | ||
724 | 729 | | |||
725 | fingerprint = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256, | 730 | fingerprint = ssh_get_fingerprint_hash(SSH_PUBLICKEY_HASH_SHA256, | ||
726 | hash, | 731 | hash, | ||
727 | hlen); | 732 | hlen); | ||
728 | ssh_string_free_char((char *)hash); | 733 | ssh_string_free_char((char *)hash); | ||
729 | if (fingerprint == nullptr) { | 734 | if (fingerprint == nullptr) { | ||
730 | error(KIO::ERR_SLAVE_DEFINED, | | |||
731 | i18n("Could not create fingerprint for server public key")); | | |||
732 | closeConnection(); | 735 | closeConnection(); | ||
733 | return; | 736 | return Result::fail(KIO::ERR_SLAVE_DEFINED, | ||
737 | i18n("Could not create fingerprint for server public key")); | ||||
734 | } | 738 | } | ||
735 | 739 | | |||
736 | qCDebug(KIO_SFTP_LOG) << "Checking if the SSH server is known"; | 740 | qCDebug(KIO_SFTP_LOG) << "Checking if the SSH server is known"; | ||
737 | 741 | | |||
738 | /* check the server public key hash */ | 742 | /* check the server public key hash */ | ||
739 | state = ssh_session_is_known_server(mSession); | 743 | state = ssh_session_is_known_server(mSession); | ||
740 | switch (state) { | 744 | switch (state) { | ||
741 | case SSH_KNOWN_HOSTS_OTHER: | 745 | case SSH_KNOWN_HOSTS_OTHER: { | ||
742 | ssh_string_free_char(fingerprint); | 746 | ssh_string_free_char(fingerprint); | ||
743 | error(KIO::ERR_SLAVE_DEFINED, | 747 | const QString errorString = i18n("An %1 host key for this server was " | ||
744 | i18n("An %1 host key for this server was " | | |||
745 | "not found, but another type of key exists.\n" | 748 | "not found, but another type of key exists.\n" | ||
746 | "An attacker might change the default server key to confuse your " | 749 | "An attacker might change the default server key to confuse your " | ||
747 | "client into thinking the key does not exist.\n" | 750 | "client into thinking the key does not exist.\n" | ||
748 | "Please contact your system administrator.\n" | 751 | "Please contact your system administrator.\n" | ||
749 | "%2", | 752 | "%2", | ||
750 | QString::fromUtf8(srv_pubkey_type), | 753 | QString::fromUtf8(srv_pubkey_type), | ||
751 | QString::fromUtf8(ssh_get_error(mSession)))); | 754 | QString::fromUtf8(ssh_get_error(mSession))); | ||
752 | closeConnection(); | 755 | closeConnection(); | ||
753 | return; | 756 | return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString); | ||
754 | case SSH_KNOWN_HOSTS_CHANGED: | 757 | } | ||
755 | error(KIO::ERR_SLAVE_DEFINED, | 758 | case SSH_KNOWN_HOSTS_CHANGED: { | ||
756 | i18n("The host key for the server %1 has changed.\n" | 759 | const QString errorString = i18n("The host key for the server %1 has changed.\n" | ||
757 | "This could either mean that DNS SPOOFING is happening or the IP " | 760 | "This could either mean that DNS SPOOFING is happening or the IP " | ||
758 | "address for the host and its host key have changed at the same time.\n" | 761 | "address for the host and its host key have changed at the same time.\n" | ||
759 | "The fingerprint for the %2 key sent by the remote host is:\n" | 762 | "The fingerprint for the %2 key sent by the remote host is:\n" | ||
760 | " SHA256:%3\n" | 763 | " SHA256:%3\n" | ||
761 | "Please contact your system administrator.\n%4", | 764 | "Please contact your system administrator.\n%4", | ||
762 | mHost, | 765 | mHost, | ||
763 | QString::fromUtf8(srv_pubkey_type), | 766 | QString::fromUtf8(srv_pubkey_type), | ||
764 | QString::fromUtf8(fingerprint), | 767 | QString::fromUtf8(fingerprint), | ||
765 | QString::fromUtf8(ssh_get_error(mSession)))); | 768 | QString::fromUtf8(ssh_get_error(mSession))); | ||
766 | ssh_string_free_char(fingerprint); | 769 | ssh_string_free_char(fingerprint); | ||
767 | closeConnection(); | 770 | closeConnection(); | ||
768 | return; | 771 | return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString); | ||
772 | } | ||||
769 | case SSH_KNOWN_HOSTS_NOT_FOUND: | 773 | case SSH_KNOWN_HOSTS_NOT_FOUND: | ||
770 | case SSH_KNOWN_HOSTS_UNKNOWN: | 774 | case SSH_KNOWN_HOSTS_UNKNOWN: { | ||
771 | caption = i18n("Warning: Cannot verify host's identity."); | 775 | caption = i18n("Warning: Cannot verify host's identity."); | ||
772 | msg = i18n("The authenticity of host %1 cannot be established.\n" | 776 | msg = i18n("The authenticity of host %1 cannot be established.\n" | ||
773 | "The %2 key fingerprint is: %3\n" | 777 | "The %2 key fingerprint is: %3\n" | ||
774 | "Are you sure you want to continue connecting?", | 778 | "Are you sure you want to continue connecting?", | ||
775 | mHost, | 779 | mHost, | ||
776 | QString::fromUtf8(srv_pubkey_type), | 780 | QString::fromUtf8(srv_pubkey_type), | ||
777 | QString::fromUtf8(fingerprint)); | 781 | QString::fromUtf8(fingerprint)); | ||
778 | ssh_string_free_char(fingerprint); | 782 | ssh_string_free_char(fingerprint); | ||
779 | 783 | | |||
780 | if (KMessageBox::Yes != messageBox(WarningYesNo, msg, caption)) { | 784 | if (KMessageBox::Yes != q->messageBox(SlaveBase::WarningYesNo, msg, caption)) { | ||
781 | closeConnection(); | 785 | closeConnection(); | ||
782 | error(KIO::ERR_USER_CANCELED, QString()); | 786 | return Result::fail(KIO::ERR_USER_CANCELED); | ||
783 | return; | | |||
784 | } | 787 | } | ||
785 | 788 | | |||
786 | /* write the known_hosts file */ | 789 | /* write the known_hosts file */ | ||
787 | qCDebug(KIO_SFTP_LOG) << "Adding server to known_hosts file."; | 790 | qCDebug(KIO_SFTP_LOG) << "Adding server to known_hosts file."; | ||
788 | rc = ssh_session_update_known_hosts(mSession); | 791 | rc = ssh_session_update_known_hosts(mSession); | ||
789 | if (rc != SSH_OK) { | 792 | if (rc != SSH_OK) { | ||
790 | error(KIO::ERR_USER_CANCELED, | 793 | const QString errorString = QString::fromUtf8(ssh_get_error(mSession)); | ||
791 | QString::fromUtf8(ssh_get_error(mSession))); | | |||
792 | closeConnection(); | 794 | closeConnection(); | ||
793 | return; | 795 | return Result::fail(KIO::ERR_USER_CANCELED,errorString); | ||
794 | } | 796 | } | ||
795 | break; | 797 | break; | ||
798 | } | ||||
796 | case SSH_KNOWN_HOSTS_ERROR: | 799 | case SSH_KNOWN_HOSTS_ERROR: | ||
797 | ssh_string_free_char(fingerprint); | 800 | ssh_string_free_char(fingerprint); | ||
798 | error(KIO::ERR_SLAVE_DEFINED, | 801 | return Result::fail(KIO::ERR_SLAVE_DEFINED, | ||
799 | QString::fromUtf8(ssh_get_error(mSession))); | 802 | QString::fromUtf8(ssh_get_error(mSession))); | ||
800 | return; | | |||
801 | case SSH_KNOWN_HOSTS_OK: | 803 | case SSH_KNOWN_HOSTS_OK: | ||
802 | break; | 804 | break; | ||
803 | } | 805 | } | ||
804 | 806 | | |||
805 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with the server"; | 807 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with the server"; | ||
806 | 808 | | |||
807 | // Try to login without authentication | 809 | // Try to login without authentication | ||
808 | rc = ssh_userauth_none(mSession, nullptr); | 810 | rc = ssh_userauth_none(mSession, nullptr); | ||
809 | if (rc == SSH_AUTH_ERROR) { | 811 | if (rc == SSH_AUTH_ERROR) { | ||
810 | closeConnection(); | 812 | closeConnection(); | ||
811 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 813 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
812 | return; | | |||
813 | } | 814 | } | ||
814 | 815 | | |||
815 | // This NEEDS to be called after ssh_userauth_none() !!! | 816 | // This NEEDS to be called after ssh_userauth_none() !!! | ||
816 | int method = ssh_auth_list(mSession); | 817 | int method = ssh_auth_list(mSession); | ||
817 | if (rc != SSH_AUTH_SUCCESS && method == 0) { | 818 | if (rc != SSH_AUTH_SUCCESS && method == 0) { | ||
818 | closeConnection(); | 819 | closeConnection(); | ||
819 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed. The server " | 820 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed. The server " | ||
820 | "didn't send any authentication methods")); | 821 | "didn't send any authentication methods")); | ||
821 | return; | | |||
822 | } | 822 | } | ||
823 | 823 | | |||
824 | // Try to authenticate with public key first | 824 | // Try to authenticate with public key first | ||
825 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PUBLICKEY)) { | 825 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PUBLICKEY)) { | ||
826 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with public key"; | 826 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with public key"; | ||
827 | for(;;) { | 827 | for(;;) { | ||
828 | rc = ssh_userauth_publickey_auto(mSession, nullptr, nullptr); | 828 | rc = ssh_userauth_publickey_auto(mSession, nullptr, nullptr); | ||
829 | if (rc == SSH_AUTH_ERROR) { | 829 | if (rc == SSH_AUTH_ERROR) { | ||
830 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | 830 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | ||
831 | QString::fromUtf8(ssh_get_error(mSession)); | 831 | QString::fromUtf8(ssh_get_error(mSession)); | ||
832 | closeConnection(); | 832 | closeConnection(); | ||
833 | clearPubKeyAuthInfo(); | 833 | clearPubKeyAuthInfo(); | ||
834 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 834 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
835 | return; | | |||
836 | } else if (rc != SSH_AUTH_DENIED || !mPublicKeyAuthInfo || !mPublicKeyAuthInfo->isModified()) { | 835 | } else if (rc != SSH_AUTH_DENIED || !mPublicKeyAuthInfo || !mPublicKeyAuthInfo->isModified()) { | ||
837 | clearPubKeyAuthInfo(); | 836 | clearPubKeyAuthInfo(); | ||
838 | break; | 837 | break; | ||
839 | } | 838 | } | ||
840 | } | 839 | } | ||
841 | } | 840 | } | ||
842 | 841 | | |||
843 | // Try to authenticate with GSSAPI | 842 | // Try to authenticate with GSSAPI | ||
844 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_GSSAPI_MIC)) { | 843 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_GSSAPI_MIC)) { | ||
845 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with GSSAPI"; | 844 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with GSSAPI"; | ||
846 | rc = ssh_userauth_gssapi(mSession); | 845 | rc = ssh_userauth_gssapi(mSession); | ||
847 | if (rc == SSH_AUTH_ERROR) { | 846 | if (rc == SSH_AUTH_ERROR) { | ||
848 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | 847 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | ||
849 | QString::fromUtf8(ssh_get_error(mSession)); | 848 | QString::fromUtf8(ssh_get_error(mSession)); | ||
850 | closeConnection(); | 849 | closeConnection(); | ||
851 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 850 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
852 | return; | | |||
853 | } | 851 | } | ||
854 | } | 852 | } | ||
855 | 853 | | |||
856 | // Try to authenticate with keyboard interactive | 854 | // Try to authenticate with keyboard interactive | ||
857 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_INTERACTIVE)) { | 855 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_INTERACTIVE)) { | ||
858 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with keyboard interactive"; | 856 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with keyboard interactive"; | ||
859 | AuthInfo info2 (info); | 857 | AuthInfo info2 (info); | ||
860 | rc = authenticateKeyboardInteractive(info2); | 858 | rc = authenticateKeyboardInteractive(info2); | ||
861 | if (rc == SSH_AUTH_SUCCESS) { | 859 | if (rc == SSH_AUTH_SUCCESS) { | ||
862 | info = info2; | 860 | info = info2; | ||
863 | } else if (rc == SSH_AUTH_ERROR) { | 861 | } else if (rc == SSH_AUTH_ERROR) { | ||
864 | qCDebug(KIO_SFTP_LOG) << "Keyboard interactive authentication failed:" | 862 | qCDebug(KIO_SFTP_LOG) << "Keyboard interactive authentication failed:" | ||
865 | << QString::fromUtf8(ssh_get_error(mSession)); | 863 | << QString::fromUtf8(ssh_get_error(mSession)); | ||
866 | closeConnection(); | 864 | closeConnection(); | ||
867 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 865 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
868 | return; | | |||
869 | } | 866 | } | ||
870 | } | 867 | } | ||
871 | 868 | | |||
872 | // Try to authenticate with password | 869 | // Try to authenticate with password | ||
873 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PASSWORD)) { | 870 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PASSWORD)) { | ||
874 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with password"; | 871 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with password"; | ||
875 | 872 | | |||
876 | info.caption = i18n("SFTP Login"); | 873 | info.caption = i18n("SFTP Login"); | ||
Show All 10 Lines | 880 | if (!isFirstLoginAttempt || info.password.isEmpty()) { | |||
887 | QString username (info.username); | 884 | QString username (info.username); | ||
888 | const QString errMsg(isFirstLoginAttempt ? QString() : i18n("Incorrect username or password")); | 885 | const QString errMsg(isFirstLoginAttempt ? QString() : i18n("Incorrect username or password")); | ||
889 | 886 | | |||
890 | qCDebug(KIO_SFTP_LOG) << "Username:" << username << "first attempt?" | 887 | qCDebug(KIO_SFTP_LOG) << "Username:" << username << "first attempt?" | ||
891 | << isFirstLoginAttempt << "error:" << errMsg; | 888 | << isFirstLoginAttempt << "error:" << errMsg; | ||
892 | 889 | | |||
893 | // Handle user canceled or dialog failed to open... | 890 | // Handle user canceled or dialog failed to open... | ||
894 | 891 | | |||
895 | int errCode = openPasswordDialogV2(info, errMsg); | 892 | int errCode = q->openPasswordDialogV2(info, errMsg); | ||
896 | if (errCode != 0) { | 893 | if (errCode != 0) { | ||
897 | qCDebug(KIO_SFTP_LOG) << "User canceled password/retry dialog"; | 894 | qCDebug(KIO_SFTP_LOG) << "User canceled password/retry dialog"; | ||
898 | closeConnection(); | 895 | closeConnection(); | ||
899 | error(errCode, QString()); | 896 | return Result::fail(errCode, QString()); | ||
900 | return; | | |||
901 | } | 897 | } | ||
902 | 898 | | |||
903 | // If the user name changes, we have to re-establish connection again | 899 | // If the user name changes, we have to re-establish connection again | ||
904 | // since the user name must always be set before calling ssh_connect. | 900 | // since the user name must always be set before calling ssh_connect. | ||
905 | if (wasUsernameChanged(username, info)) { | 901 | if (wasUsernameChanged(username, info)) { | ||
906 | qCDebug(KIO_SFTP_LOG) << "Username changed to" << info.username; | 902 | qCDebug(KIO_SFTP_LOG) << "Username changed to" << info.username; | ||
907 | if (!info.url.userName().isEmpty()) { | 903 | if (!info.url.userName().isEmpty()) { | ||
908 | info.url.setUserName(info.username); | 904 | info.url.setUserName(info.username); | ||
909 | } | 905 | } | ||
910 | closeConnection(); | 906 | closeConnection(); | ||
911 | if (!sftpOpenConnection(info)) { | 907 | const auto result = sftpOpenConnection(info); | ||
912 | return; | 908 | if (!result.success) { | ||
909 | return result; | ||||
913 | } | 910 | } | ||
914 | } | 911 | } | ||
915 | } | 912 | } | ||
916 | 913 | | |||
917 | rc = ssh_userauth_password(mSession, info.username.toUtf8().constData(), info.password.toUtf8().constData()); | 914 | rc = ssh_userauth_password(mSession, info.username.toUtf8().constData(), info.password.toUtf8().constData()); | ||
918 | if (rc == SSH_AUTH_SUCCESS) { | 915 | if (rc == SSH_AUTH_SUCCESS) { | ||
919 | break; | 916 | break; | ||
920 | } else if (rc == SSH_AUTH_ERROR) { | 917 | } else if (rc == SSH_AUTH_ERROR) { | ||
921 | qCDebug(KIO_SFTP_LOG) << "Password authentication failed:" | 918 | qCDebug(KIO_SFTP_LOG) << "Password authentication failed:" | ||
922 | << QString::fromUtf8(ssh_get_error(mSession)); | 919 | << QString::fromUtf8(ssh_get_error(mSession)); | ||
923 | closeConnection(); | 920 | closeConnection(); | ||
924 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 921 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
925 | return; | | |||
926 | } | 922 | } | ||
927 | 923 | | |||
928 | isFirstLoginAttempt = false; // failed attempt to login. | 924 | isFirstLoginAttempt = false; // failed attempt to login. | ||
929 | info.password.clear(); // clear the password after failed attempts. | 925 | info.password.clear(); // clear the password after failed attempts. | ||
930 | } | 926 | } | ||
931 | } | 927 | } | ||
932 | 928 | | |||
933 | // If we're still not authenticated then we need to leave. | 929 | // If we're still not authenticated then we need to leave. | ||
934 | if (rc != SSH_AUTH_SUCCESS) { | 930 | if (rc != SSH_AUTH_SUCCESS) { | ||
935 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 931 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
936 | return; | | |||
937 | } | 932 | } | ||
938 | 933 | | |||
939 | // start sftp session | 934 | // start sftp session | ||
940 | qCDebug(KIO_SFTP_LOG) << "Trying to request the sftp session"; | 935 | qCDebug(KIO_SFTP_LOG) << "Trying to request the sftp session"; | ||
941 | mSftp = sftp_new(mSession); | 936 | mSftp = sftp_new(mSession); | ||
942 | if (mSftp == nullptr) { | 937 | if (mSftp == nullptr) { | ||
943 | closeConnection(); | 938 | closeConnection(); | ||
944 | error(KIO::ERR_CANNOT_LOGIN, i18n("Unable to request the SFTP subsystem. " | 939 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Unable to request the SFTP subsystem. " | ||
945 | "Make sure SFTP is enabled on the server.")); | 940 | "Make sure SFTP is enabled on the server.")); | ||
946 | return; | | |||
947 | } | 941 | } | ||
948 | 942 | | |||
949 | qCDebug(KIO_SFTP_LOG) << "Trying to initialize the sftp session"; | 943 | qCDebug(KIO_SFTP_LOG) << "Trying to initialize the sftp session"; | ||
950 | if (sftp_init(mSftp) < 0) { | 944 | if (sftp_init(mSftp) < 0) { | ||
951 | closeConnection(); | 945 | closeConnection(); | ||
952 | error(KIO::ERR_CANNOT_LOGIN, i18n("Could not initialize the SFTP session.")); | 946 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Could not initialize the SFTP session.")); | ||
953 | return; | | |||
954 | } | 947 | } | ||
955 | 948 | | |||
956 | // Login succeeded! | 949 | // Login succeeded! | ||
957 | infoMessage(i18n("Successfully connected to %1", mHost)); | 950 | q->infoMessage(i18n("Successfully connected to %1", mHost)); | ||
958 | if (info.keepPassword) { | 951 | if (info.keepPassword) { | ||
959 | qCDebug(KIO_SFTP_LOG) << "Caching info.username = " << info.username | 952 | qCDebug(KIO_SFTP_LOG) << "Caching info.username = " << info.username | ||
960 | << ", info.url = " << info.url.toDisplayString(); | 953 | << ", info.url = " << info.url.toDisplayString(); | ||
961 | cacheAuthentication(info); | 954 | q->cacheAuthentication(info); | ||
962 | } | 955 | } | ||
963 | 956 | | |||
964 | // Update the original username in case it was changed! | 957 | // Update the original username in case it was changed! | ||
965 | if (!mUsername.isEmpty()) { | 958 | if (!mUsername.isEmpty()) { | ||
966 | mUsername = info.username; | 959 | mUsername = info.username; | ||
967 | } | 960 | } | ||
968 | 961 | | |||
969 | setTimeoutSpecialCommand(KIO_SFTP_SPECIAL_TIMEOUT); | 962 | q->setTimeoutSpecialCommand(KIO_SFTP_SPECIAL_TIMEOUT); | ||
970 | 963 | | |||
971 | mConnected = true; | 964 | mConnected = true; | ||
972 | connected(); | 965 | q->connected(); | ||
973 | 966 | | |||
974 | info.password.fill('x'); | 967 | info.password.fill('x'); | ||
975 | info.password.clear(); | 968 | info.password.clear(); | ||
969 | | ||||
970 | return Result::pass(); | ||||
976 | } | 971 | } | ||
977 | #else // < 0.8.0 | 972 | #else // < 0.8.0 | ||
978 | void sftpProtocol::openConnection() | 973 | Result SFTPInternal::openConnection() | ||
979 | { | 974 | { | ||
980 | if (mConnected) { | 975 | if (mConnected) { | ||
981 | return; | 976 | return Result::pass(); | ||
982 | } | 977 | } | ||
983 | 978 | | |||
984 | if (mHost.isEmpty()) { | 979 | if (mHost.isEmpty()) { | ||
985 | qCDebug(KIO_SFTP_LOG) << "openConnection(): Need hostname..."; | 980 | qCDebug(KIO_SFTP_LOG) << "openConnection(): Need hostname..."; | ||
986 | error(KIO::ERR_UNKNOWN_HOST, QString()); | 981 | return Result::fail(KIO::ERR_UNKNOWN_HOST); | ||
987 | return; | | |||
988 | } | 982 | } | ||
989 | 983 | | |||
990 | AuthInfo info; | 984 | AuthInfo info; | ||
991 | info.url.setScheme("sftp"); | 985 | info.url.setScheme("sftp"); | ||
992 | info.url.setHost(mHost); | 986 | info.url.setHost(mHost); | ||
993 | if ( mPort > 0 && mPort != DEFAULT_SFTP_PORT ) { | 987 | if ( mPort > 0 && mPort != DEFAULT_SFTP_PORT ) { | ||
994 | info.url.setPort(mPort); | 988 | info.url.setPort(mPort); | ||
995 | } | 989 | } | ||
996 | info.url.setUserName(mUsername); | 990 | info.url.setUserName(mUsername); | ||
997 | info.username = mUsername; | 991 | info.username = mUsername; | ||
998 | 992 | | |||
999 | // Check for cached authentication info if no password is specified... | 993 | // Check for cached authentication info if no password is specified... | ||
1000 | if (mPassword.isEmpty()) { | 994 | if (mPassword.isEmpty()) { | ||
1001 | qCDebug(KIO_SFTP_LOG) << "checking cache: info.username =" << info.username | 995 | qCDebug(KIO_SFTP_LOG) << "checking cache: info.username =" << info.username | ||
1002 | << ", info.url =" << info.url.toDisplayString(); | 996 | << ", info.url =" << info.url.toDisplayString(); | ||
1003 | checkCachedAuthentication(info); | 997 | q->checkCachedAuthentication(info); | ||
1004 | } else { | 998 | } else { | ||
1005 | info.password = mPassword; | 999 | info.password = mPassword; | ||
1006 | } | 1000 | } | ||
1007 | 1001 | | |||
1008 | // Start the ssh connection. | 1002 | // Start the ssh connection. | ||
1009 | QString msg; // msg for dialog box | 1003 | QString msg; // msg for dialog box | ||
1010 | QString caption; // dialog box caption | 1004 | QString caption; // dialog box caption | ||
1011 | unsigned char *hash = nullptr; // the server hash | 1005 | unsigned char *hash = nullptr; // the server hash | ||
1012 | ssh_key srv_pubkey; | 1006 | ssh_key srv_pubkey; | ||
1013 | char *hexa; | 1007 | char *hexa; | ||
1014 | size_t hlen; | 1008 | size_t hlen; | ||
1015 | int rc, state; | 1009 | int rc, state; | ||
1016 | 1010 | | |||
1017 | // Attempt to start a ssh session and establish a connection with the server. | 1011 | // Attempt to start a ssh session and establish a connection with the server. | ||
1018 | if (!sftpOpenConnection(info)) { | 1012 | const auto openResult = sftpOpenConnection(info); | ||
1019 | return; | 1013 | if (!openResult.success) { | ||
1014 | return openResult; | ||||
1020 | } | 1015 | } | ||
1021 | 1016 | | |||
1022 | qCDebug(KIO_SFTP_LOG) << "Getting the SSH server hash"; | 1017 | qCDebug(KIO_SFTP_LOG) << "Getting the SSH server hash"; | ||
1023 | 1018 | | |||
1024 | /* get the hash */ | 1019 | /* get the hash */ | ||
1025 | rc = ssh_get_publickey(mSession, &srv_pubkey); | 1020 | rc = ssh_get_publickey(mSession, &srv_pubkey); | ||
1026 | if (rc < 0) { | 1021 | if (rc < 0) { | ||
1027 | error(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession))); | 1022 | const auto result = Result::fail(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession))); | ||
1028 | closeConnection(); | 1023 | closeConnection(); | ||
1029 | return; | 1024 | return result; | ||
1030 | } | 1025 | } | ||
1031 | 1026 | | |||
1032 | rc = ssh_get_publickey_hash(srv_pubkey, | 1027 | rc = ssh_get_publickey_hash(srv_pubkey, | ||
1033 | SSH_PUBLICKEY_HASH_SHA1, | 1028 | SSH_PUBLICKEY_HASH_SHA1, | ||
1034 | &hash, | 1029 | &hash, | ||
1035 | &hlen); | 1030 | &hlen); | ||
1036 | ssh_key_free(srv_pubkey); | 1031 | ssh_key_free(srv_pubkey); | ||
1037 | if (rc < 0) { | 1032 | if (rc < 0) { | ||
1038 | error(KIO::ERR_SLAVE_DEFINED, | | |||
1039 | i18n("Could not create hash from server public key")); | | |||
1040 | closeConnection(); | 1033 | closeConnection(); | ||
1041 | return; | 1034 | return Result::fail(KIO::ERR_SLAVE_DEFINED, | ||
1035 | i18n("Could not create hash from server public key")); | ||||
1042 | } | 1036 | } | ||
1043 | 1037 | | |||
1044 | qCDebug(KIO_SFTP_LOG) << "Checking if the SSH server is known"; | 1038 | qCDebug(KIO_SFTP_LOG) << "Checking if the SSH server is known"; | ||
1045 | 1039 | | |||
1046 | /* check the server public key hash */ | 1040 | /* check the server public key hash */ | ||
1047 | state = ssh_is_server_known(mSession); | 1041 | state = ssh_is_server_known(mSession); | ||
1048 | switch (state) { | 1042 | switch (state) { | ||
1049 | case SSH_SERVER_KNOWN_OK: | 1043 | case SSH_SERVER_KNOWN_OK: | ||
1050 | break; | 1044 | break; | ||
1051 | case SSH_SERVER_FOUND_OTHER: | 1045 | case SSH_SERVER_FOUND_OTHER: { | ||
1052 | ssh_string_free_char((char *)hash); | 1046 | ssh_string_free_char((char *)hash); | ||
1053 | error(KIO::ERR_SLAVE_DEFINED, i18n("The host key for this server was " | 1047 | const QString errorString = i18n("The host key for this server was " | ||
1054 | "not found, but another type of key exists.\n" | 1048 | "not found, but another type of key exists.\n" | ||
1055 | "An attacker might change the default server key to confuse your " | 1049 | "An attacker might change the default server key to confuse your " | ||
1056 | "client into thinking the key does not exist.\n" | 1050 | "client into thinking the key does not exist.\n" | ||
1057 | "Please contact your system administrator.\n%1", QString::fromUtf8(ssh_get_error(mSession)))); | 1051 | "Please contact your system administrator.\n%1", | ||
1052 | QString::fromUtf8(ssh_get_error(mSession))); | ||||
1058 | closeConnection(); | 1053 | closeConnection(); | ||
1059 | return; | 1054 | return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString); | ||
1060 | case SSH_SERVER_KNOWN_CHANGED: | 1055 | } | ||
1056 | case SSH_SERVER_KNOWN_CHANGED: { | ||||
1061 | hexa = ssh_get_hexa(hash, hlen); | 1057 | hexa = ssh_get_hexa(hash, hlen); | ||
1062 | ssh_string_free_char((char *)hash); | 1058 | ssh_string_free_char((char *)hash); | ||
1063 | /* TODO print known_hosts file, port? */ | 1059 | /* TODO print known_hosts file, port? */ | ||
1064 | error(KIO::ERR_SLAVE_DEFINED, i18n("The host key for the server %1 has changed.\n" | 1060 | const QString errorString = i18n("The host key for the server %1 has changed.\n" | ||
1065 | "This could either mean that DNS SPOOFING is happening or the IP " | 1061 | "This could either mean that DNS SPOOFING is happening or the IP " | ||
1066 | "address for the host and its host key have changed at the same time.\n" | 1062 | "address for the host and its host key have changed at the same time.\n" | ||
1067 | "The fingerprint for the key sent by the remote host is:\n %2\n" | 1063 | "The fingerprint for the key sent by the remote host is:\n %2\n" | ||
1068 | "Please contact your system administrator.\n%3", | 1064 | "Please contact your system administrator.\n%3", | ||
1069 | mHost, QString::fromUtf8(hexa), QString::fromUtf8(ssh_get_error(mSession)))); | 1065 | mHost, | ||
1066 | QString::fromUtf8(hexa), | ||||
1067 | QString::fromUtf8(ssh_get_error(mSession))); | ||||
1070 | ssh_string_free_char(hexa); | 1068 | ssh_string_free_char(hexa); | ||
1071 | closeConnection(); | 1069 | closeConnection(); | ||
1072 | return; | 1070 | return Result::fail(KIO::ERR_SLAVE_DEFINED, errorString); | ||
1071 | } | ||||
1073 | case SSH_SERVER_FILE_NOT_FOUND: | 1072 | case SSH_SERVER_FILE_NOT_FOUND: | ||
1074 | case SSH_SERVER_NOT_KNOWN: | 1073 | case SSH_SERVER_NOT_KNOWN: { | ||
1075 | hexa = ssh_get_hexa(hash, hlen); | 1074 | hexa = ssh_get_hexa(hash, hlen); | ||
1076 | ssh_string_free_char((char *)hash); | 1075 | ssh_string_free_char((char *)hash); | ||
1077 | caption = i18n("Warning: Cannot verify host's identity."); | 1076 | caption = i18n("Warning: Cannot verify host's identity."); | ||
1078 | msg = i18n("The authenticity of host %1 cannot be established.\n" | 1077 | msg = i18n("The authenticity of host %1 cannot be established.\n" | ||
1079 | "The key fingerprint is: %2\n" | 1078 | "The key fingerprint is: %2\n" | ||
1080 | "Are you sure you want to continue connecting?", mHost, hexa); | 1079 | "Are you sure you want to continue connecting?", mHost, hexa); | ||
1081 | ssh_string_free_char(hexa); | 1080 | ssh_string_free_char(hexa); | ||
1082 | 1081 | | |||
1083 | if (KMessageBox::Yes != messageBox(WarningYesNo, msg, caption)) { | 1082 | if (KMessageBox::Yes != q->messageBox(SlaveBase::WarningYesNo, msg, caption)) { | ||
1084 | closeConnection(); | 1083 | closeConnection(); | ||
1085 | error(KIO::ERR_USER_CANCELED, QString()); | 1084 | return Result::fail(KIO::ERR_USER_CANCELED); | ||
1086 | return; | | |||
1087 | } | 1085 | } | ||
1088 | 1086 | | |||
1089 | /* write the known_hosts file */ | 1087 | /* write the known_hosts file */ | ||
1090 | qCDebug(KIO_SFTP_LOG) << "Adding server to known_hosts file."; | 1088 | qCDebug(KIO_SFTP_LOG) << "Adding server to known_hosts file."; | ||
1091 | if (ssh_write_knownhost(mSession) < 0) { | 1089 | if (ssh_write_knownhost(mSession) < 0) { | ||
1092 | error(KIO::ERR_USER_CANCELED, QString::fromUtf8(ssh_get_error(mSession))); | 1090 | const QString errorString = QString::fromUtf8(ssh_get_error(mSession)); | ||
1093 | closeConnection(); | 1091 | closeConnection(); | ||
1094 | return; | 1092 | return Result::fail(KIO::ERR_USER_CANCELED, errorString); | ||
1095 | } | 1093 | } | ||
1096 | break; | 1094 | break; | ||
1095 | } | ||||
1097 | case SSH_SERVER_ERROR: | 1096 | case SSH_SERVER_ERROR: | ||
1098 | ssh_string_free_char((char *)hash); | 1097 | ssh_string_free_char((char *)hash); | ||
1099 | error(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession))); | 1098 | return Result::fail(KIO::ERR_SLAVE_DEFINED, QString::fromUtf8(ssh_get_error(mSession))); | ||
1100 | return; | | |||
1101 | } | 1099 | } | ||
1102 | 1100 | | |||
1103 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with the server"; | 1101 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with the server"; | ||
1104 | 1102 | | |||
1105 | // Try to login without authentication | 1103 | // Try to login without authentication | ||
1106 | rc = ssh_userauth_none(mSession, nullptr); | 1104 | rc = ssh_userauth_none(mSession, nullptr); | ||
1107 | if (rc == SSH_AUTH_ERROR) { | 1105 | if (rc == SSH_AUTH_ERROR) { | ||
1108 | closeConnection(); | 1106 | closeConnection(); | ||
1109 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 1107 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
1110 | return; | | |||
1111 | } | 1108 | } | ||
1112 | 1109 | | |||
1113 | // This NEEDS to be called after ssh_userauth_none() !!! | 1110 | // This NEEDS to be called after ssh_userauth_none() !!! | ||
1114 | int method = ssh_auth_list(mSession); | 1111 | int method = ssh_auth_list(mSession); | ||
1115 | if (rc != SSH_AUTH_SUCCESS && method == 0) { | 1112 | if (rc != SSH_AUTH_SUCCESS && method == 0) { | ||
1116 | closeConnection(); | 1113 | closeConnection(); | ||
1117 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed. The server " | 1114 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed. The server " | ||
1118 | "didn't send any authentication methods")); | 1115 | "didn't send any authentication methods")); | ||
1119 | return; | | |||
1120 | } | 1116 | } | ||
1121 | 1117 | | |||
1122 | // Try to authenticate with public key first | 1118 | // Try to authenticate with public key first | ||
1123 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PUBLICKEY)) { | 1119 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PUBLICKEY)) { | ||
1124 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with public key"; | 1120 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with public key"; | ||
1125 | for(;;) { | 1121 | for(;;) { | ||
1126 | rc = ssh_userauth_publickey_auto(mSession, nullptr, nullptr); | 1122 | rc = ssh_userauth_publickey_auto(mSession, nullptr, nullptr); | ||
1127 | if (rc == SSH_AUTH_ERROR) { | 1123 | if (rc == SSH_AUTH_ERROR) { | ||
1128 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | 1124 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | ||
1129 | QString::fromUtf8(ssh_get_error(mSession)); | 1125 | QString::fromUtf8(ssh_get_error(mSession)); | ||
1130 | closeConnection(); | 1126 | closeConnection(); | ||
1131 | clearPubKeyAuthInfo(); | 1127 | clearPubKeyAuthInfo(); | ||
1132 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 1128 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
1133 | return; | | |||
1134 | } else if (rc != SSH_AUTH_DENIED || !mPublicKeyAuthInfo || !mPublicKeyAuthInfo->isModified()) { | 1129 | } else if (rc != SSH_AUTH_DENIED || !mPublicKeyAuthInfo || !mPublicKeyAuthInfo->isModified()) { | ||
1135 | clearPubKeyAuthInfo(); | 1130 | clearPubKeyAuthInfo(); | ||
1136 | break; | 1131 | break; | ||
1137 | } | 1132 | } | ||
1138 | } | 1133 | } | ||
1139 | } | 1134 | } | ||
1140 | 1135 | | |||
1141 | // Try to authenticate with GSSAPI | 1136 | // Try to authenticate with GSSAPI | ||
1142 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_GSSAPI_MIC)) { | 1137 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_GSSAPI_MIC)) { | ||
1143 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with GSSAPI"; | 1138 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with GSSAPI"; | ||
1144 | rc = ssh_userauth_gssapi(mSession); | 1139 | rc = ssh_userauth_gssapi(mSession); | ||
1145 | if (rc == SSH_AUTH_ERROR) { | 1140 | if (rc == SSH_AUTH_ERROR) { | ||
1146 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | 1141 | qCDebug(KIO_SFTP_LOG) << "Public key authentication failed:" << | ||
1147 | QString::fromUtf8(ssh_get_error(mSession)); | 1142 | QString::fromUtf8(ssh_get_error(mSession)); | ||
1148 | closeConnection(); | 1143 | closeConnection(); | ||
1149 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 1144 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
1150 | return; | | |||
1151 | } | 1145 | } | ||
1152 | } | 1146 | } | ||
1153 | 1147 | | |||
1154 | // Try to authenticate with keyboard interactive | 1148 | // Try to authenticate with keyboard interactive | ||
1155 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_INTERACTIVE)) { | 1149 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_INTERACTIVE)) { | ||
1156 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with keyboard interactive"; | 1150 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with keyboard interactive"; | ||
1157 | AuthInfo info2 (info); | 1151 | AuthInfo info2 (info); | ||
1158 | rc = authenticateKeyboardInteractive(info2); | 1152 | rc = authenticateKeyboardInteractive(info2); | ||
1159 | if (rc == SSH_AUTH_SUCCESS) { | 1153 | if (rc == SSH_AUTH_SUCCESS) { | ||
1160 | info = info2; | 1154 | info = info2; | ||
1161 | } else if (rc == SSH_AUTH_ERROR) { | 1155 | } else if (rc == SSH_AUTH_ERROR) { | ||
1162 | qCDebug(KIO_SFTP_LOG) << "Keyboard interactive authentication failed:" | 1156 | qCDebug(KIO_SFTP_LOG) << "Keyboard interactive authentication failed:" | ||
1163 | << QString::fromUtf8(ssh_get_error(mSession)); | 1157 | << QString::fromUtf8(ssh_get_error(mSession)); | ||
1164 | closeConnection(); | 1158 | closeConnection(); | ||
1165 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 1159 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
1166 | return; | | |||
1167 | } | 1160 | } | ||
1168 | } | 1161 | } | ||
1169 | 1162 | | |||
1170 | // Try to authenticate with password | 1163 | // Try to authenticate with password | ||
1171 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PASSWORD)) { | 1164 | if (rc != SSH_AUTH_SUCCESS && (method & SSH_AUTH_METHOD_PASSWORD)) { | ||
1172 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with password"; | 1165 | qCDebug(KIO_SFTP_LOG) << "Trying to authenticate with password"; | ||
1173 | 1166 | | |||
1174 | info.caption = i18n("SFTP Login"); | 1167 | info.caption = i18n("SFTP Login"); | ||
Show All 10 Lines | 1174 | if (!isFirstLoginAttempt || info.password.isEmpty()) { | |||
1185 | QString username (info.username); | 1178 | QString username (info.username); | ||
1186 | const QString errMsg(isFirstLoginAttempt ? QString() : i18n("Incorrect username or password")); | 1179 | const QString errMsg(isFirstLoginAttempt ? QString() : i18n("Incorrect username or password")); | ||
1187 | 1180 | | |||
1188 | qCDebug(KIO_SFTP_LOG) << "Username:" << username << "first attempt?" | 1181 | qCDebug(KIO_SFTP_LOG) << "Username:" << username << "first attempt?" | ||
1189 | << isFirstLoginAttempt << "error:" << errMsg; | 1182 | << isFirstLoginAttempt << "error:" << errMsg; | ||
1190 | 1183 | | |||
1191 | // Handle user canceled or dialog failed to open... | 1184 | // Handle user canceled or dialog failed to open... | ||
1192 | 1185 | | |||
1193 | int errCode = openPasswordDialogV2(info, errMsg); | 1186 | int errCode = q->openPasswordDialogV2(info, errMsg); | ||
1194 | if (errCode != 0) { | 1187 | if (errCode != KJob::NoError) { | ||
1195 | qCDebug(KIO_SFTP_LOG) << "User canceled password/retry dialog"; | 1188 | qCDebug(KIO_SFTP_LOG) << "User canceled password/retry dialog"; | ||
1196 | closeConnection(); | 1189 | closeConnection(); | ||
1197 | error(errCode, QString()); | 1190 | return Result::fail(errCode); | ||
1198 | return; | | |||
1199 | } | 1191 | } | ||
1200 | 1192 | | |||
1201 | // If the user name changes, we have to restablish connection again | 1193 | // If the user name changes, we have to restablish connection again | ||
1202 | // since the user name must always be set before calling ssh_connect. | 1194 | // since the user name must always be set before calling ssh_connect. | ||
1203 | if (wasUsernameChanged(username, info)) { | 1195 | if (wasUsernameChanged(username, info)) { | ||
1204 | qCDebug(KIO_SFTP_LOG) << "Username changed to" << info.username; | 1196 | qCDebug(KIO_SFTP_LOG) << "Username changed to" << info.username; | ||
1205 | if (!info.url.userName().isEmpty()) { | 1197 | if (!info.url.userName().isEmpty()) { | ||
1206 | info.url.setUserName(info.username); | 1198 | info.url.setUserName(info.username); | ||
1207 | } | 1199 | } | ||
1208 | closeConnection(); | 1200 | closeConnection(); | ||
1209 | if (!sftpOpenConnection(info)) { | 1201 | const auto result = sftpOpenConnection(info); | ||
1210 | return; | 1202 | if (!result.success) { | ||
1203 | return result; | ||||
1211 | } | 1204 | } | ||
1212 | } | 1205 | } | ||
1213 | } | 1206 | } | ||
1214 | 1207 | | |||
1215 | rc = ssh_userauth_password(mSession, info.username.toUtf8().constData(), info.password.toUtf8().constData()); | 1208 | rc = ssh_userauth_password(mSession, info.username.toUtf8().constData(), info.password.toUtf8().constData()); | ||
1216 | if (rc == SSH_AUTH_SUCCESS) { | 1209 | if (rc == SSH_AUTH_SUCCESS) { | ||
1217 | break; | 1210 | break; | ||
1218 | } else if (rc == SSH_AUTH_ERROR) { | 1211 | } else if (rc == SSH_AUTH_ERROR) { | ||
1219 | qCDebug(KIO_SFTP_LOG) << "Password authentication failed:" | 1212 | qCDebug(KIO_SFTP_LOG) << "Password authentication failed:" | ||
1220 | << QString::fromUtf8(ssh_get_error(mSession)); | 1213 | << QString::fromUtf8(ssh_get_error(mSession)); | ||
1221 | closeConnection(); | 1214 | closeConnection(); | ||
1222 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 1215 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
1223 | return; | | |||
1224 | } | 1216 | } | ||
1225 | 1217 | | |||
1226 | isFirstLoginAttempt = false; // failed attempt to login. | 1218 | isFirstLoginAttempt = false; // failed attempt to login. | ||
1227 | info.password.clear(); // clear the password after failed attempts. | 1219 | info.password.clear(); // clear the password after failed attempts. | ||
1228 | } | 1220 | } | ||
1229 | } | 1221 | } | ||
1230 | 1222 | | |||
1231 | // If we're still not authenticated then we need to leave. | 1223 | // If we're still not authenticated then we need to leave. | ||
1232 | if (rc != SSH_AUTH_SUCCESS) { | 1224 | if (rc != SSH_AUTH_SUCCESS) { | ||
1233 | error(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | 1225 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Authentication failed.")); | ||
1234 | return; | | |||
1235 | } | 1226 | } | ||
1236 | 1227 | | |||
1237 | // start sftp session | 1228 | // start sftp session | ||
1238 | qCDebug(KIO_SFTP_LOG) << "Trying to request the sftp session"; | 1229 | qCDebug(KIO_SFTP_LOG) << "Trying to request the sftp session"; | ||
1239 | mSftp = sftp_new(mSession); | 1230 | mSftp = sftp_new(mSession); | ||
1240 | if (mSftp == nullptr) { | 1231 | if (mSftp == nullptr) { | ||
1241 | closeConnection(); | 1232 | closeConnection(); | ||
1242 | error(KIO::ERR_CANNOT_LOGIN, i18n("Unable to request the SFTP subsystem. " | 1233 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Unable to request the SFTP subsystem. " | ||
1243 | "Make sure SFTP is enabled on the server.")); | 1234 | "Make sure SFTP is enabled on the server.")); | ||
1244 | return; | | |||
1245 | } | 1235 | } | ||
1246 | 1236 | | |||
1247 | qCDebug(KIO_SFTP_LOG) << "Trying to initialize the sftp session"; | 1237 | qCDebug(KIO_SFTP_LOG) << "Trying to initialize the sftp session"; | ||
1248 | if (sftp_init(mSftp) < 0) { | 1238 | if (sftp_init(mSftp) < 0) { | ||
1249 | closeConnection(); | 1239 | closeConnection(); | ||
1250 | error(KIO::ERR_CANNOT_LOGIN, i18n("Could not initialize the SFTP session.")); | 1240 | return Result::fail(KIO::ERR_CANNOT_LOGIN, i18n("Could not initialize the SFTP session.")); | ||
1251 | return; | | |||
1252 | } | 1241 | } | ||
1253 | 1242 | | |||
1254 | // Login succeeded! | 1243 | // Login succeeded! | ||
1255 | infoMessage(i18n("Successfully connected to %1", mHost)); | 1244 | q->infoMessage(i18n("Successfully connected to %1", mHost)); | ||
1256 | if (info.keepPassword) { | 1245 | if (info.keepPassword) { | ||
1257 | qCDebug(KIO_SFTP_LOG) << "Caching info.username = " << info.username | 1246 | qCDebug(KIO_SFTP_LOG) << "Caching info.username = " << info.username | ||
1258 | << ", info.url = " << info.url.toDisplayString(); | 1247 | << ", info.url = " << info.url.toDisplayString(); | ||
1259 | cacheAuthentication(info); | 1248 | q->cacheAuthentication(info); | ||
1260 | } | 1249 | } | ||
1261 | 1250 | | |||
1262 | // Update the original username in case it was changed! | 1251 | // Update the original username in case it was changed! | ||
1263 | if (!mUsername.isEmpty()) { | 1252 | if (!mUsername.isEmpty()) { | ||
1264 | mUsername = info.username; | 1253 | mUsername = info.username; | ||
1265 | } | 1254 | } | ||
1266 | 1255 | | |||
1267 | setTimeoutSpecialCommand(KIO_SFTP_SPECIAL_TIMEOUT); | 1256 | q->setTimeoutSpecialCommand(KIO_SFTP_SPECIAL_TIMEOUT); | ||
1268 | 1257 | | |||
1269 | mConnected = true; | 1258 | mConnected = true; | ||
1270 | connected(); | 1259 | q->connected(); | ||
1271 | 1260 | | |||
1272 | info.password.fill('x'); | 1261 | info.password.fill('x'); | ||
1273 | info.password.clear(); | 1262 | info.password.clear(); | ||
1274 | } | 1263 | } | ||
1275 | #endif // 0.8.0 | 1264 | #endif // 0.8.0 | ||
1276 | 1265 | | |||
1277 | void sftpProtocol::closeConnection() { | 1266 | void SFTPInternal::closeConnection() { | ||
1278 | qCDebug(KIO_SFTP_LOG); | 1267 | qCDebug(KIO_SFTP_LOG); | ||
1279 | 1268 | | |||
1280 | if (mSftp) { | 1269 | if (mSftp) { | ||
1281 | sftp_free(mSftp); | 1270 | sftp_free(mSftp); | ||
1282 | mSftp = nullptr; | 1271 | mSftp = nullptr; | ||
1283 | } | 1272 | } | ||
1284 | 1273 | | |||
1285 | if (mSession) { | 1274 | if (mSession) { | ||
1286 | ssh_disconnect(mSession); | 1275 | ssh_disconnect(mSession); | ||
1287 | ssh_free(mSession); | 1276 | ssh_free(mSession); | ||
1288 | mSession = nullptr; | 1277 | mSession = nullptr; | ||
1289 | } | 1278 | } | ||
1290 | 1279 | | |||
1291 | mConnected = false; | 1280 | mConnected = false; | ||
1292 | } | 1281 | } | ||
1293 | 1282 | | |||
1294 | void sftpProtocol::special(const QByteArray &) { | 1283 | Result SFTPInternal::special(const QByteArray &) { | ||
1295 | int rc; | | |||
1296 | qCDebug(KIO_SFTP_LOG) << "special(): polling"; | 1284 | qCDebug(KIO_SFTP_LOG) << "special(): polling"; | ||
1297 | 1285 | | |||
1298 | if (!mSftp) { | 1286 | if (!mSftp) { | ||
1299 | error(KIO::ERR_INTERNAL, i18n("Invalid sftp context")); | 1287 | return Result::fail(KIO::ERR_INTERNAL, i18n("Invalid sftp context")); | ||
1300 | return; | | |||
1301 | } | 1288 | } | ||
1302 | 1289 | | |||
1303 | /* | 1290 | /* | ||
1304 | * ssh_channel_poll() returns the number of bytes that may be read on the | 1291 | * ssh_channel_poll() returns the number of bytes that may be read on the | ||
1305 | * channel. It does so by checking the input buffer and eventually the | 1292 | * channel. It does so by checking the input buffer and eventually the | ||
1306 | * network socket for data to read. If the input buffer is not empty, it | 1293 | * network socket for data to read. If the input buffer is not empty, it | ||
1307 | * will not probe the network (and such not read packets nor reply to | 1294 | * will not probe the network (and such not read packets nor reply to | ||
1308 | * keepalives). | 1295 | * keepalives). | ||
1309 | * | 1296 | * | ||
1310 | * As ssh_channel_poll can act on two specific buffers (a channel has two | 1297 | * As ssh_channel_poll can act on two specific buffers (a channel has two | ||
1311 | * different stream: stdio and stderr), polling for data on the stderr | 1298 | * different stream: stdio and stderr), polling for data on the stderr | ||
1312 | * stream has more chance of not being in the problematic case (data left | 1299 | * stream has more chance of not being in the problematic case (data left | ||
1313 | * in the buffer). Checking the return value (for >0) would be a good idea | 1300 | * in the buffer). Checking the return value (for >0) would be a good idea | ||
1314 | * to debug the problem. | 1301 | * to debug the problem. | ||
1315 | */ | 1302 | */ | ||
1316 | rc = ssh_channel_poll(mSftp->channel, 0); | 1303 | int rc = ssh_channel_poll(mSftp->channel, 0); | ||
1317 | if (rc > 0) { | 1304 | if (rc > 0) { | ||
1318 | rc = ssh_channel_poll(mSftp->channel, 1); | 1305 | rc = ssh_channel_poll(mSftp->channel, 1); | ||
1319 | } | 1306 | } | ||
1320 | 1307 | | |||
1321 | if (rc < 0) { | 1308 | if (rc < 0) { // first or second poll failed | ||
Warning, you changed the logic. This "else" wasn't there, in order to catch the case where the second call to ssh_channel_poll failed too. I think you need to revert this "else". dfaure: Warning, you changed the logic. This "else" wasn't there, in order to catch the case where the… | |||||
1322 | qCDebug(KIO_SFTP_LOG) << "ssh_channel_poll failed: " << ssh_get_error(mSession); | 1309 | qCDebug(KIO_SFTP_LOG) << "ssh_channel_poll failed: " << ssh_get_error(mSession); | ||
1323 | } | 1310 | } | ||
1324 | 1311 | | |||
1325 | setTimeoutSpecialCommand(KIO_SFTP_SPECIAL_TIMEOUT); | 1312 | q->setTimeoutSpecialCommand(KIO_SFTP_SPECIAL_TIMEOUT); | ||
1326 | 1313 | | |||
1327 | finished(); | 1314 | return Result::pass(); | ||
1328 | } | 1315 | } | ||
1329 | 1316 | | |||
1330 | void sftpProtocol::open(const QUrl &url, QIODevice::OpenMode mode) { | 1317 | Result SFTPInternal::open(const QUrl &url, QIODevice::OpenMode mode) { | ||
1331 | qCDebug(KIO_SFTP_LOG) << "open: " << url; | 1318 | qCDebug(KIO_SFTP_LOG) << "open: " << url; | ||
1332 | 1319 | | |||
1333 | if (!sftpLogin()) { | 1320 | const auto loginResult = sftpLogin(); | ||
1334 | // sftpLogin finished() | 1321 | if (!loginResult.success) { | ||
1335 | return; | 1322 | return loginResult; | ||
1336 | } | 1323 | } | ||
1337 | 1324 | | |||
1338 | const QString path = url.path(); | 1325 | const QString path = url.path(); | ||
1339 | const QByteArray path_c = path.toUtf8(); | 1326 | const QByteArray path_c = path.toUtf8(); | ||
1340 | 1327 | | |||
1341 | sftp_attributes sb = sftp_lstat(mSftp, path_c.constData()); | 1328 | sftp_attributes sb = sftp_lstat(mSftp, path_c.constData()); | ||
1342 | if (sb == nullptr) { | 1329 | if (sb == nullptr) { | ||
1343 | reportError(url, sftp_get_error(mSftp)); | 1330 | return reportError(url, sftp_get_error(mSftp)); | ||
1344 | return; | | |||
1345 | } | 1331 | } | ||
1346 | 1332 | | |||
1347 | switch (sb->type) { | 1333 | switch (sb->type) { | ||
1348 | case SSH_FILEXFER_TYPE_DIRECTORY: | 1334 | case SSH_FILEXFER_TYPE_DIRECTORY: | ||
1349 | error(KIO::ERR_IS_DIRECTORY, url.toDisplayString()); | | |||
1350 | sftp_attributes_free(sb); | 1335 | sftp_attributes_free(sb); | ||
1351 | return; | 1336 | return Result::fail(KIO::ERR_IS_DIRECTORY, url.toDisplayString()); | ||
1352 | case SSH_FILEXFER_TYPE_SPECIAL: | 1337 | case SSH_FILEXFER_TYPE_SPECIAL: | ||
1353 | case SSH_FILEXFER_TYPE_UNKNOWN: | 1338 | case SSH_FILEXFER_TYPE_UNKNOWN: | ||
1354 | error(KIO::ERR_CANNOT_OPEN_FOR_READING, url.toDisplayString()); | | |||
1355 | sftp_attributes_free(sb); | 1339 | sftp_attributes_free(sb); | ||
1356 | return; | 1340 | return Result::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, url.toDisplayString()); | ||
1357 | case SSH_FILEXFER_TYPE_SYMLINK: | 1341 | case SSH_FILEXFER_TYPE_SYMLINK: | ||
1358 | case SSH_FILEXFER_TYPE_REGULAR: | 1342 | case SSH_FILEXFER_TYPE_REGULAR: | ||
1359 | break; | 1343 | break; | ||
1360 | } | 1344 | } | ||
1361 | 1345 | | |||
1362 | KIO::filesize_t fileSize = sb->size; | 1346 | KIO::filesize_t fileSize = sb->size; | ||
1363 | sftp_attributes_free(sb); | 1347 | sftp_attributes_free(sb); | ||
1364 | 1348 | | |||
Show All 17 Lines | |||||
1382 | 1366 | | |||
1383 | if (flags & O_CREAT) { | 1367 | if (flags & O_CREAT) { | ||
1384 | mOpenFile = sftp_open(mSftp, path_c.constData(), flags, 0644); | 1368 | mOpenFile = sftp_open(mSftp, path_c.constData(), flags, 0644); | ||
1385 | } else { | 1369 | } else { | ||
1386 | mOpenFile = sftp_open(mSftp, path_c.constData(), flags, 0); | 1370 | mOpenFile = sftp_open(mSftp, path_c.constData(), flags, 0); | ||
1387 | } | 1371 | } | ||
1388 | 1372 | | |||
1389 | if (mOpenFile == nullptr) { | 1373 | if (mOpenFile == nullptr) { | ||
1390 | error(KIO::ERR_CANNOT_OPEN_FOR_READING, path); | 1374 | return Result::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, path); | ||
1391 | return; | | |||
1392 | } | 1375 | } | ||
1393 | 1376 | | |||
1394 | // Determine the mimetype of the file to be retrieved, and emit it. | 1377 | // Determine the mimetype of the file to be retrieved, and emit it. | ||
1395 | // This is mandatory in all slaves (for KRun/BrowserRun to work). | 1378 | // This is mandatory in all slaves (for KRun/BrowserRun to work). | ||
1396 | // If we're not opening the file ReadOnly or ReadWrite, don't attempt to | 1379 | // If we're not opening the file ReadOnly or ReadWrite, don't attempt to | ||
1397 | // read the file and send the mimetype. | 1380 | // read the file and send the mimetype. | ||
1398 | if (mode & QIODevice::ReadOnly) { | 1381 | if (mode & QIODevice::ReadOnly) { | ||
1399 | size_t bytesRequested = 1024; | 1382 | size_t bytesRequested = 1024; | ||
1400 | ssize_t bytesRead = 0; | 1383 | ssize_t bytesRead = 0; | ||
1401 | QVarLengthArray<char> buffer(bytesRequested); | 1384 | QVarLengthArray<char> buffer(bytesRequested); | ||
1402 | 1385 | | |||
1403 | bytesRead = sftp_read(mOpenFile, buffer.data(), bytesRequested); | 1386 | bytesRead = sftp_read(mOpenFile, buffer.data(), bytesRequested); | ||
1404 | if (bytesRead < 0) { | 1387 | if (bytesRead < 0) { | ||
1405 | error(KIO::ERR_CANNOT_READ, mOpenUrl.toDisplayString()); | 1388 | close(); | ||
1406 | closeWithoutFinish(); | 1389 | return Result::fail(KIO::ERR_CANNOT_READ, mOpenUrl.toDisplayString()); | ||
1407 | return; | | |||
1408 | } else { | 1390 | } else { | ||
1409 | QByteArray fileData = QByteArray::fromRawData(buffer.data(), bytesRead); | 1391 | QByteArray fileData = QByteArray::fromRawData(buffer.data(), bytesRead); | ||
1410 | QMimeDatabase db; | 1392 | QMimeDatabase db; | ||
1411 | QMimeType mime = db.mimeTypeForFileNameAndData(mOpenUrl.fileName(), fileData); | 1393 | QMimeType mime = db.mimeTypeForFileNameAndData(mOpenUrl.fileName(), fileData); | ||
1412 | mimeType(mime.name()); | 1394 | q->mimeType(mime.name()); | ||
1413 | 1395 | | |||
1414 | // Go back to the beginning of the file. | 1396 | // Go back to the beginning of the file. | ||
1415 | sftp_rewind(mOpenFile); | 1397 | sftp_rewind(mOpenFile); | ||
1416 | } | 1398 | } | ||
1417 | } | 1399 | } | ||
1418 | 1400 | | |||
1419 | mOpenUrl = url; | 1401 | mOpenUrl = url; | ||
1420 | 1402 | | |||
1421 | openOffset = 0; | 1403 | openOffset = 0; | ||
1422 | totalSize(fileSize); | 1404 | q->totalSize(fileSize); | ||
1423 | position(0); | 1405 | q->position(0); | ||
1424 | opened(); | 1406 | | ||
1407 | return Result::pass(); | ||||
1425 | } | 1408 | } | ||
1426 | 1409 | | |||
1427 | void sftpProtocol::read(KIO::filesize_t bytes) { | 1410 | Result SFTPInternal::read(KIO::filesize_t bytes) | ||
1411 | { | ||||
1428 | qCDebug(KIO_SFTP_LOG) << "read, offset = " << openOffset << ", bytes = " << bytes; | 1412 | qCDebug(KIO_SFTP_LOG) << "read, offset = " << openOffset << ", bytes = " << bytes; | ||
1429 | 1413 | | |||
1430 | Q_ASSERT(mOpenFile != nullptr); | 1414 | Q_ASSERT(mOpenFile != nullptr); | ||
1431 | 1415 | | |||
1432 | QVarLengthArray<char> buffer(bytes); | 1416 | QVarLengthArray<char> buffer(bytes); | ||
1433 | 1417 | | |||
1434 | ssize_t bytesRead = sftp_read(mOpenFile, buffer.data(), bytes); | 1418 | ssize_t bytesRead = sftp_read(mOpenFile, buffer.data(), bytes); | ||
1435 | Q_ASSERT(bytesRead <= static_cast<ssize_t>(bytes)); | 1419 | Q_ASSERT(bytesRead <= static_cast<ssize_t>(bytes)); | ||
1436 | 1420 | | |||
1437 | if (bytesRead < 0) { | 1421 | if (bytesRead < 0) { | ||
1438 | qCDebug(KIO_SFTP_LOG) << "Could not read " << mOpenUrl; | 1422 | qCDebug(KIO_SFTP_LOG) << "Could not read " << mOpenUrl; | ||
1439 | error(KIO::ERR_CANNOT_READ, mOpenUrl.toDisplayString()); | 1423 | close(); | ||
1440 | closeWithoutFinish(); | 1424 | return Result::fail(KIO::ERR_CANNOT_READ, mOpenUrl.toDisplayString()); | ||
1441 | return; | | |||
1442 | } | 1425 | } | ||
1443 | 1426 | | |||
1444 | const QByteArray fileData = QByteArray::fromRawData(buffer.data(), bytesRead); | 1427 | const QByteArray fileData = QByteArray::fromRawData(buffer.data(), bytesRead); | ||
1445 | data(fileData); | 1428 | q->data(fileData); | ||
1429 | | ||||
1430 | return Result::pass(); | ||||
1446 | } | 1431 | } | ||
1447 | 1432 | | |||
1448 | void sftpProtocol::write(const QByteArray &data) { | 1433 | Result SFTPInternal::write(const QByteArray &data) | ||
1434 | { | ||||
1449 | qCDebug(KIO_SFTP_LOG) << "write, offset = " << openOffset << ", bytes = " << data.size(); | 1435 | qCDebug(KIO_SFTP_LOG) << "write, offset = " << openOffset << ", bytes = " << data.size(); | ||
1450 | 1436 | | |||
1451 | Q_ASSERT(mOpenFile != nullptr); | 1437 | Q_ASSERT(mOpenFile != nullptr); | ||
1452 | 1438 | | |||
1453 | ssize_t bytesWritten = sftp_write(mOpenFile, data.data(), data.size()); | 1439 | ssize_t bytesWritten = sftp_write(mOpenFile, data.data(), data.size()); | ||
1454 | if (bytesWritten < 0) { | 1440 | if (bytesWritten < 0) { | ||
1455 | qCDebug(KIO_SFTP_LOG) << "Could not write to " << mOpenUrl; | 1441 | qCDebug(KIO_SFTP_LOG) << "Could not write to " << mOpenUrl; | ||
1456 | error(KIO::ERR_CANNOT_WRITE, mOpenUrl.toDisplayString()); | 1442 | close(); | ||
1457 | closeWithoutFinish(); | 1443 | return Result::fail(KIO::ERR_CANNOT_WRITE, mOpenUrl.toDisplayString()); | ||
1458 | return; | | |||
1459 | } | 1444 | } | ||
1460 | 1445 | | |||
1461 | written(bytesWritten); | 1446 | q->written(bytesWritten); | ||
1447 | | ||||
1448 | return Result::pass(); | ||||
1462 | } | 1449 | } | ||
1463 | 1450 | | |||
1464 | void sftpProtocol::seek(KIO::filesize_t offset) { | 1451 | Result SFTPInternal::seek(KIO::filesize_t offset) | ||
1452 | { | ||||
1465 | qCDebug(KIO_SFTP_LOG) << "seek, offset = " << offset; | 1453 | qCDebug(KIO_SFTP_LOG) << "seek, offset = " << offset; | ||
1466 | 1454 | | |||
1467 | Q_ASSERT(mOpenFile != nullptr); | 1455 | Q_ASSERT(mOpenFile != nullptr); | ||
1468 | 1456 | | |||
1469 | if (sftp_seek64(mOpenFile, static_cast<uint64_t>(offset)) < 0) { | 1457 | if (sftp_seek64(mOpenFile, static_cast<uint64_t>(offset)) < 0) { | ||
1470 | error(KIO::ERR_CANNOT_SEEK, mOpenUrl.path()); | 1458 | close(); | ||
1471 | closeWithoutFinish(); | 1459 | return Result::fail(KIO::ERR_CANNOT_SEEK, mOpenUrl.path()); | ||
1472 | return; | | |||
1473 | } | 1460 | } | ||
1474 | 1461 | | |||
1475 | position(sftp_tell64(mOpenFile)); | 1462 | q->position(sftp_tell64(mOpenFile)); | ||
1463 | | ||||
1464 | return Result::pass(); | ||||
1476 | } | 1465 | } | ||
1477 | void sftpProtocol::truncate(KIO::filesize_t length) { | 1466 | | ||
1467 | Result SFTPInternal::truncate(KIO::filesize_t length) | ||||
1468 | { | ||||
1478 | qCDebug(KIO_SFTP_LOG) << "truncate, length =" << length; | 1469 | qCDebug(KIO_SFTP_LOG) << "truncate, length =" << length; | ||
1479 | 1470 | | |||
1480 | Q_ASSERT(mOpenFile); | 1471 | Q_ASSERT(mOpenFile); | ||
1481 | 1472 | | |||
1482 | int errorCode = 0; | 1473 | int errorCode = KJob::NoError; | ||
1483 | sftp_attributes attr = sftp_fstat(mOpenFile); | 1474 | sftp_attributes attr = sftp_fstat(mOpenFile); | ||
1484 | if (attr) { | 1475 | if (attr) { | ||
1485 | attr->size = length; | 1476 | attr->size = length; | ||
1486 | if (sftp_setstat(mSftp, mOpenUrl.path().toUtf8().constData(), attr) == 0) { | 1477 | if (sftp_setstat(mSftp, mOpenUrl.path().toUtf8().constData(), attr) == 0) { | ||
1487 | truncated(length); | 1478 | q->truncated(length); | ||
1488 | } else { | 1479 | } else { | ||
1489 | errorCode = toKIOError(sftp_get_error(mSftp)); | 1480 | errorCode = toKIOError(sftp_get_error(mSftp)); | ||
1490 | } | 1481 | } | ||
1491 | sftp_attributes_free(attr); | 1482 | sftp_attributes_free(attr); | ||
1492 | } else { | 1483 | } else { | ||
1493 | errorCode = toKIOError(sftp_get_error(mSftp)); | 1484 | errorCode = toKIOError(sftp_get_error(mSftp)); | ||
1494 | } | 1485 | } | ||
1495 | 1486 | | |||
1496 | if (errorCode) { | 1487 | if (errorCode) { | ||
1497 | error(errorCode == KIO::ERR_INTERNAL ? KIO::ERR_CANNOT_TRUNCATE : errorCode, mOpenUrl.path()); | 1488 | close(); | ||
1498 | closeWithoutFinish(); | 1489 | return Result::fail(errorCode == KIO::ERR_INTERNAL ? KIO::ERR_CANNOT_TRUNCATE : errorCode, mOpenUrl.path()); | ||
1499 | } | 1490 | } | ||
1491 | | ||||
1492 | return Result::pass(); | ||||
1500 | } | 1493 | } | ||
1501 | 1494 | | |||
1502 | void sftpProtocol::close() { | 1495 | void SFTPInternal::close() | ||
1503 | closeWithoutFinish(); | 1496 | { | ||
1504 | finished(); | 1497 | sftp_close(mOpenFile); | ||
1498 | mOpenFile = nullptr; | ||||
We should be calling finished() (or Result::pass) on close() IIRC? I'm not seeing it called. feverfew: We should be calling `finished()` (or `Result::pass`) on `close()` IIRC? I'm not seeing it… | |||||
1505 | } | 1499 | } | ||
1506 | 1500 | | |||
1507 | void sftpProtocol::get(const QUrl& url) { | 1501 | Result SFTPInternal::get(const QUrl& url) | ||
1502 | { | ||||
1508 | qCDebug(KIO_SFTP_LOG) << url; | 1503 | qCDebug(KIO_SFTP_LOG) << url; | ||
1509 | 1504 | | |||
1510 | int errorCode = 0; | 1505 | const auto result = sftpGet(url); | ||
1511 | const sftpProtocol::StatusCode cs = sftpGet(url, errorCode); | 1506 | if (!result.success) { | ||
1512 | 1507 | return Result::fail(result.error, url.toDisplayString()); | |||
1513 | // The call to sftpGet should only return server side errors since the file | | |||
1514 | // descriptor parameter is set to -1. | | |||
1515 | if (cs == sftpProtocol::ServerError && errorCode) { | | |||
1516 | error(errorCode, url.toDisplayString()); | | |||
1517 | return; | | |||
1518 | } | 1508 | } | ||
1519 | 1509 | | |||
1520 | finished(); | 1510 | return Result::pass(); | ||
1521 | } | 1511 | } | ||
1522 | 1512 | | |||
1523 | sftpProtocol::StatusCode sftpProtocol::sftpGet(const QUrl& url, int& errorCode, KIO::fileoffset_t offset, int fd) { | 1513 | Result SFTPInternal::sftpGet(const QUrl &url, KIO::fileoffset_t offset, int fd) | ||
1524 | 1514 | { | |||
1525 | qCDebug(KIO_SFTP_LOG) << url; | 1515 | qCDebug(KIO_SFTP_LOG) << url; | ||
1526 | 1516 | | |||
1527 | if (!sftpLogin()) { | 1517 | const auto loginResult = sftpLogin(); | ||
1528 | return sftpProtocol::ServerError; | 1518 | if (!loginResult.success) { | ||
1519 | return loginResult; | ||||
1529 | } | 1520 | } | ||
1530 | 1521 | | |||
1531 | QByteArray path = url.path().toUtf8(); | 1522 | QByteArray path = url.path().toUtf8(); | ||
1532 | 1523 | | |||
1533 | sftp_file file = nullptr; | 1524 | sftp_file file = nullptr; | ||
1534 | KIO::filesize_t totalbytesread = 0; | 1525 | KIO::filesize_t totalbytesread = 0; | ||
1535 | QByteArray filedata; | 1526 | QByteArray filedata; | ||
1536 | 1527 | | |||
1537 | sftp_attributes sb = sftp_lstat(mSftp, path.constData()); | 1528 | sftp_attributes sb = sftp_lstat(mSftp, path.constData()); | ||
1538 | if (sb == nullptr) { | 1529 | if (sb == nullptr) { | ||
1539 | errorCode = toKIOError(sftp_get_error(mSftp)); | 1530 | return Result::fail(toKIOError(sftp_get_error(mSftp)), url.toString()); | ||
1540 | return sftpProtocol::ServerError; | | |||
1541 | } | 1531 | } | ||
1542 | 1532 | | |||
1543 | switch (sb->type) { | 1533 | switch (sb->type) { | ||
1544 | case SSH_FILEXFER_TYPE_DIRECTORY: | 1534 | case SSH_FILEXFER_TYPE_DIRECTORY: | ||
1545 | errorCode = KIO::ERR_IS_DIRECTORY; | | |||
1546 | sftp_attributes_free(sb); | 1535 | sftp_attributes_free(sb); | ||
1547 | return sftpProtocol::ServerError; | 1536 | return Result::fail(KIO::ERR_IS_DIRECTORY, url.toString()); | ||
1548 | case SSH_FILEXFER_TYPE_SPECIAL: | 1537 | case SSH_FILEXFER_TYPE_SPECIAL: | ||
1549 | case SSH_FILEXFER_TYPE_UNKNOWN: | 1538 | case SSH_FILEXFER_TYPE_UNKNOWN: | ||
1550 | errorCode = KIO::ERR_CANNOT_OPEN_FOR_READING; | 1539 | return Result::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, url.toString()); | ||
1551 | sftp_attributes_free(sb); | | |||
1552 | return sftpProtocol::ServerError; | | |||
1553 | case SSH_FILEXFER_TYPE_SYMLINK: | 1540 | case SSH_FILEXFER_TYPE_SYMLINK: | ||
1554 | case SSH_FILEXFER_TYPE_REGULAR: | 1541 | case SSH_FILEXFER_TYPE_REGULAR: | ||
1555 | break; | 1542 | break; | ||
1556 | } | 1543 | } | ||
1557 | 1544 | | |||
1558 | // Open file | 1545 | // Open file | ||
1559 | file = sftp_open(mSftp, path.constData(), O_RDONLY, 0); | 1546 | file = sftp_open(mSftp, path.constData(), O_RDONLY, 0); | ||
1560 | if (file == nullptr) { | 1547 | if (file == nullptr) { | ||
1561 | errorCode = KIO::ERR_CANNOT_OPEN_FOR_READING; | | |||
1562 | sftp_attributes_free(sb); | 1548 | sftp_attributes_free(sb); | ||
1563 | return sftpProtocol::ServerError; | 1549 | return Result::fail(KIO::ERR_CANNOT_OPEN_FOR_READING, url.toString()); | ||
1564 | } | 1550 | } | ||
1565 | 1551 | | |||
1566 | char mimeTypeBuf[1024]; | 1552 | char mimeTypeBuf[1024]; | ||
1567 | ssize_t bytesread = sftp_read(file, mimeTypeBuf, sizeof(mimeTypeBuf)); | 1553 | ssize_t bytesread = sftp_read(file, mimeTypeBuf, sizeof(mimeTypeBuf)); | ||
1568 | 1554 | | |||
1569 | if (bytesread < 0) { | 1555 | if (bytesread < 0) { | ||
1570 | errorCode = KIO::ERR_CANNOT_READ; | 1556 | return Result::fail(KIO::ERR_CANNOT_READ, url.toString()); | ||
1571 | return sftpProtocol::ServerError; | | |||
1572 | } else { | 1557 | } else { | ||
1573 | QMimeDatabase db; | 1558 | QMimeDatabase db; | ||
1574 | QMimeType mime = db.mimeTypeForFileNameAndData(url.fileName(), QByteArray(mimeTypeBuf, bytesread)); | 1559 | QMimeType mime = db.mimeTypeForFileNameAndData(url.fileName(), QByteArray(mimeTypeBuf, bytesread)); | ||
1575 | if (!mime.isDefault()) { | 1560 | if (!mime.isDefault()) { | ||
1576 | mimeType(mime.name()); | 1561 | q->mimeType(mime.name()); | ||
1577 | } else { | 1562 | } else { | ||
1578 | mime = db.mimeTypeForUrl(url); | 1563 | mime = db.mimeTypeForUrl(url); | ||
1579 | mimeType(mime.name()); | 1564 | q->mimeType(mime.name()); | ||
1580 | } | 1565 | } | ||
1581 | sftp_rewind(file); | 1566 | sftp_rewind(file); | ||
1582 | } | 1567 | } | ||
1583 | 1568 | | |||
1584 | // Set the total size | 1569 | // Set the total size | ||
1585 | totalSize(sb->size); | 1570 | q->totalSize(sb->size); | ||
1586 | 1571 | | |||
1587 | // If offset is not specified, check the "resume" meta-data. | 1572 | // If offset is not specified, check the "resume" meta-data. | ||
1588 | if (offset < 0) { | 1573 | if (offset < 0) { | ||
1589 | const QString resumeOffsetStr = metaData(QLatin1String("resume")); | 1574 | const QString resumeOffsetStr = q->metaData(QLatin1String("resume")); | ||
1590 | if (!resumeOffsetStr.isEmpty()) { | 1575 | if (!resumeOffsetStr.isEmpty()) { | ||
1591 | bool ok; | 1576 | bool ok; | ||
1592 | qlonglong resumeOffset = resumeOffsetStr.toLongLong(&ok); | 1577 | qlonglong resumeOffset = resumeOffsetStr.toLongLong(&ok); | ||
1593 | if (ok) { | 1578 | if (ok) { | ||
1594 | offset = resumeOffset; | 1579 | offset = resumeOffset; | ||
1595 | } | 1580 | } | ||
1596 | } | 1581 | } | ||
1597 | } | 1582 | } | ||
1598 | 1583 | | |||
1599 | // If we can resume, offset the buffer properly. | 1584 | // If we can resume, offset the buffer properly. | ||
1600 | if (offset > 0 && ((unsigned long long) offset < sb->size)) | 1585 | if (offset > 0 && ((unsigned long long) offset < sb->size)) | ||
1601 | { | 1586 | { | ||
1602 | if (sftp_seek64(file, offset) == 0) { | 1587 | if (sftp_seek64(file, offset) == 0) { | ||
1603 | canResume(); | 1588 | q->canResume(); | ||
1604 | totalbytesread = offset; | 1589 | totalbytesread = offset; | ||
1605 | qCDebug(KIO_SFTP_LOG) << "Resume offset: " << QString::number(offset); | 1590 | qCDebug(KIO_SFTP_LOG) << "Resume offset: " << QString::number(offset); | ||
1606 | } | 1591 | } | ||
1607 | } | 1592 | } | ||
1608 | 1593 | | |||
1609 | bytesread = 0; | 1594 | bytesread = 0; | ||
1610 | sftpProtocol::GetRequest request(file, sb); | 1595 | SFTPInternal::GetRequest request(file, sb); | ||
1611 | 1596 | | |||
1612 | for (;;) { | 1597 | for (;;) { | ||
1613 | // Enqueue get requests | 1598 | // Enqueue get requests | ||
1614 | if (!request.enqueueChunks()) { | 1599 | if (!request.enqueueChunks()) { | ||
1615 | errorCode = KIO::ERR_CANNOT_READ; | 1600 | return Result::fail(KIO::ERR_CANNOT_READ, url.toString()); | ||
1616 | return sftpProtocol::ServerError; | | |||
1617 | } | 1601 | } | ||
1618 | 1602 | | |||
1619 | filedata.clear(); | 1603 | filedata.clear(); | ||
1620 | bytesread = request.readChunks(filedata); | 1604 | bytesread = request.readChunks(filedata); | ||
1621 | // Read pending get requests | 1605 | // Read pending get requests | ||
1622 | if (bytesread == -1) { | 1606 | if (bytesread == -1) { | ||
1623 | errorCode = KIO::ERR_CANNOT_READ; | 1607 | return Result::fail(KIO::ERR_CANNOT_READ, url.toString()); | ||
1624 | return sftpProtocol::ServerError; | | |||
1625 | } else if (bytesread == 0) { | 1608 | } else if (bytesread == 0) { | ||
1626 | if (file->eof) | 1609 | if (file->eof) | ||
1627 | break; | 1610 | break; | ||
1628 | else | 1611 | else | ||
1629 | continue; | 1612 | continue; | ||
1630 | } | 1613 | } | ||
1631 | 1614 | | |||
1615 | int error = KJob::NoError; | ||||
1632 | if (fd == -1) { | 1616 | if (fd == -1) { | ||
1633 | data(filedata); | 1617 | q->data(filedata); | ||
1634 | } else if ((errorCode = writeToFile(fd, filedata.constData(), filedata.size())) != 0) { | 1618 | } else if ((error = writeToFile(fd, filedata.constData(), filedata.size())) != KJob::NoError) { | ||
1635 | return sftpProtocol::ClientError; | 1619 | return Result::fail(error, url.toString()); | ||
1636 | } | 1620 | } | ||
1637 | // increment total bytes read | 1621 | // increment total bytes read | ||
1638 | totalbytesread += filedata.length(); | 1622 | totalbytesread += filedata.length(); | ||
1639 | 1623 | | |||
1640 | processedSize(totalbytesread); | 1624 | q->processedSize(totalbytesread); | ||
1641 | } | 1625 | } | ||
1642 | 1626 | | |||
1643 | if (fd == -1) | 1627 | if (fd == -1) { | ||
1644 | data(QByteArray()); | 1628 | q->data(QByteArray()); | ||
1629 | } | ||||
1645 | 1630 | | |||
1646 | processedSize(static_cast<KIO::filesize_t>(sb->size)); | 1631 | q->processedSize(static_cast<KIO::filesize_t>(sb->size)); | ||
1647 | return sftpProtocol::Success; | 1632 | return Result::pass(); | ||
1648 | } | 1633 | } | ||
1649 | 1634 | | |||
1650 | void sftpProtocol::put(const QUrl& url, int permissions, KIO::JobFlags flags) { | 1635 | Result SFTPInternal::put(const QUrl &url, int permissions, KIO::JobFlags flags) | ||
1636 | { | ||||
1651 | qCDebug(KIO_SFTP_LOG) << url << ", permissions =" << permissions | 1637 | qCDebug(KIO_SFTP_LOG) << url << ", permissions =" << permissions | ||
1652 | << ", overwrite =" << (flags & KIO::Overwrite) | 1638 | << ", overwrite =" << (flags & KIO::Overwrite) | ||
1653 | << ", resume =" << (flags & KIO::Resume); | 1639 | << ", resume =" << (flags & KIO::Resume); | ||
1654 | 1640 | | |||
1655 | qCDebug(KIO_SFTP_LOG) << url; | 1641 | qCDebug(KIO_SFTP_LOG) << url; | ||
1656 | 1642 | | |||
1657 | int errorCode = 0; | 1643 | return sftpPut(url, permissions, flags); | ||
1658 | const sftpProtocol::StatusCode cs = sftpPut(url, permissions, flags, errorCode); | | |||
1659 | | ||||
1660 | // The call to sftpPut should only return server side errors since the file | | |||
1661 | // descriptor parameter is set to -1. | | |||
1662 | if (cs == sftpProtocol::ServerError && errorCode) { | | |||
1663 | error(errorCode, url.toDisplayString()); | | |||
1664 | return; | | |||
1665 | } | 1644 | } | ||
1666 | 1645 | | |||
1667 | finished(); | 1646 | Result SFTPInternal::sftpPut(const QUrl &url, int permissions, JobFlags flags, int fd) | ||
1668 | } | 1647 | { | ||
1669 | | ||||
1670 | sftpProtocol::StatusCode sftpProtocol::sftpPut(const QUrl& url, int permissions, JobFlags flags, int& errorCode, int fd) { | | |||
1671 | qCDebug(KIO_SFTP_LOG) << url << ", permissions =" << permissions | 1648 | qCDebug(KIO_SFTP_LOG) << url << ", permissions =" << permissions | ||
1672 | << ", overwrite =" << (flags & KIO::Overwrite) | 1649 | << ", overwrite =" << (flags & KIO::Overwrite) | ||
1673 | << ", resume =" << (flags & KIO::Resume); | 1650 | << ", resume =" << (flags & KIO::Resume); | ||
1674 | 1651 | | |||
1675 | if (!sftpLogin()) { | 1652 | const auto loginResult = sftpLogin(); | ||
1676 | return sftpProtocol::ServerError; | 1653 | if (!loginResult.success) { | ||
1654 | return loginResult; | ||||
1677 | } | 1655 | } | ||
1678 | 1656 | | |||
1679 | const QString dest_orig = url.path(); | 1657 | const QString dest_orig = url.path(); | ||
1680 | const QByteArray dest_orig_c = dest_orig.toUtf8(); | 1658 | const QByteArray dest_orig_c = dest_orig.toUtf8(); | ||
1681 | const QString dest_part = dest_orig + ".part"; | 1659 | const QString dest_part = dest_orig + ".part"; | ||
1682 | const QByteArray dest_part_c = dest_part.toUtf8(); | 1660 | const QByteArray dest_part_c = dest_part.toUtf8(); | ||
1683 | uid_t owner = 0; | 1661 | uid_t owner = 0; | ||
1684 | gid_t group = 0; | 1662 | gid_t group = 0; | ||
1685 | 1663 | | |||
1686 | sftp_attributes sb = sftp_lstat(mSftp, dest_orig_c.constData()); | 1664 | sftp_attributes sb = sftp_lstat(mSftp, dest_orig_c.constData()); | ||
1687 | const bool bOrigExists = (sb != nullptr); | 1665 | const bool bOrigExists = (sb != nullptr); | ||
1688 | bool bPartExists = false; | 1666 | bool bPartExists = false; | ||
1689 | const bool bMarkPartial = configValue(QStringLiteral("MarkPartial"), true); | 1667 | const bool bMarkPartial = q->configValue(QStringLiteral("MarkPartial"), true); | ||
1690 | 1668 | | |||
1691 | // Don't change permissions of the original file | 1669 | // Don't change permissions of the original file | ||
1692 | if (bOrigExists) { | 1670 | if (bOrigExists) { | ||
1693 | permissions = sb->permissions; | 1671 | permissions = sb->permissions; | ||
1694 | owner = sb->uid; | 1672 | owner = sb->uid; | ||
1695 | group = sb->gid; | 1673 | group = sb->gid; | ||
1696 | } | 1674 | } | ||
1697 | 1675 | | |||
1698 | if (bMarkPartial) { | 1676 | if (bMarkPartial) { | ||
1699 | sftp_attributes sbPart = sftp_lstat(mSftp, dest_part_c.constData()); | 1677 | sftp_attributes sbPart = sftp_lstat(mSftp, dest_part_c.constData()); | ||
1700 | bPartExists = (sbPart != nullptr); | 1678 | bPartExists = (sbPart != nullptr); | ||
1701 | 1679 | | |||
1702 | if (bPartExists && !(flags & KIO::Resume) && !(flags & KIO::Overwrite) && | 1680 | if (bPartExists && !(flags & KIO::Resume) && !(flags & KIO::Overwrite) && | ||
1703 | sbPart->size > 0 && sbPart->type == SSH_FILEXFER_TYPE_REGULAR) { | 1681 | sbPart->size > 0 && sbPart->type == SSH_FILEXFER_TYPE_REGULAR) { | ||
1704 | 1682 | | |||
1705 | if (fd == -1) { | 1683 | if (fd == -1) { | ||
1706 | // Maybe we can use this partial file for resuming | 1684 | // Maybe we can use this partial file for resuming | ||
1707 | // Tell about the size we have, and the app will tell us | 1685 | // Tell about the size we have, and the app will tell us | ||
1708 | // if it's ok to resume or not. | 1686 | // if it's ok to resume or not. | ||
1709 | qCDebug(KIO_SFTP_LOG) << "calling canResume with " << sbPart->size; | 1687 | qCDebug(KIO_SFTP_LOG) << "calling canResume with " << sbPart->size; | ||
1710 | flags |= canResume(sbPart->size) ? KIO::Resume : KIO::DefaultFlags; | 1688 | flags |= q->canResume(sbPart->size) ? KIO::Resume : KIO::DefaultFlags; | ||
1711 | qCDebug(KIO_SFTP_LOG) << "put got answer " << (flags & KIO::Resume); | 1689 | qCDebug(KIO_SFTP_LOG) << "put got answer " << (flags & KIO::Resume); | ||
1712 | 1690 | | |||
1713 | } else { | 1691 | } else { | ||
1714 | KIO::filesize_t pos = seekPos(fd, sbPart->size, SEEK_SET); | 1692 | KIO::filesize_t pos = seekPos(fd, sbPart->size, SEEK_SET); | ||
1715 | if (pos != sbPart->size) { | 1693 | if (pos != sbPart->size) { | ||
1716 | qCDebug(KIO_SFTP_LOG) << "Failed to seek to" << sbPart->size << "bytes in source file. Reason given" << strerror(errno); | 1694 | qCDebug(KIO_SFTP_LOG) << "Failed to seek to" << sbPart->size << "bytes in source file. Reason given" << strerror(errno); | ||
1717 | sftp_attributes_free(sb); | 1695 | sftp_attributes_free(sb); | ||
1718 | sftp_attributes_free(sbPart); | 1696 | sftp_attributes_free(sbPart); | ||
1719 | errorCode = ERR_CANNOT_SEEK; | 1697 | return Result::fail(ERR_CANNOT_SEEK, url.toString()); | ||
1720 | return sftpProtocol::ClientError; | | |||
1721 | } | 1698 | } | ||
1722 | flags |= KIO::Resume; | 1699 | flags |= KIO::Resume; | ||
1723 | } | 1700 | } | ||
1724 | qCDebug(KIO_SFTP_LOG) << "Resuming at" << sbPart->size; | 1701 | qCDebug(KIO_SFTP_LOG) << "Resuming at" << sbPart->size; | ||
1725 | sftp_attributes_free(sbPart); | 1702 | sftp_attributes_free(sbPart); | ||
1726 | } | 1703 | } | ||
1727 | } | 1704 | } | ||
1728 | 1705 | | |||
1729 | if (bOrigExists && !(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { | 1706 | if (bOrigExists && !(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { | ||
1730 | errorCode = KSFTP_ISDIR(sb) ? KIO::ERR_DIR_ALREADY_EXIST : KIO::ERR_FILE_ALREADY_EXIST; | 1707 | const int error = KSFTP_ISDIR(sb) ? KIO::ERR_DIR_ALREADY_EXIST : KIO::ERR_FILE_ALREADY_EXIST; | ||
1731 | sftp_attributes_free(sb); | 1708 | sftp_attributes_free(sb); | ||
1732 | return sftpProtocol::ServerError; | 1709 | return Result::fail(error, url.toString()); | ||
1733 | } | 1710 | } | ||
1734 | 1711 | | |||
1735 | QByteArray dest; | 1712 | QByteArray dest; | ||
1736 | int result = -1; | 1713 | int result = -1; | ||
1737 | sftp_file file = nullptr; | 1714 | sftp_file file = nullptr; | ||
1738 | StatusCode cs = sftpProtocol::Success; | | |||
1739 | KIO::fileoffset_t totalBytesSent = 0; | 1715 | KIO::fileoffset_t totalBytesSent = 0; | ||
1740 | 1716 | | |||
1717 | int errorCode = KJob::NoError; | ||||
1741 | // Loop until we got 0 (end of data) | 1718 | // Loop until we got 0 (end of data) | ||
1742 | do { | 1719 | do { | ||
1743 | QByteArray buffer; | 1720 | QByteArray buffer; | ||
1744 | 1721 | | |||
1745 | if (fd == -1) { | 1722 | if (fd == -1) { | ||
1746 | dataReq(); // Request for data | 1723 | q->dataReq(); // Request for data | ||
1747 | result = readData(buffer); | 1724 | result = q->readData(buffer); | ||
1748 | } else { | 1725 | } else { | ||
1749 | char buf[MAX_XFER_BUF_SIZE]; // | 1726 | char buf[MAX_XFER_BUF_SIZE]; // | ||
1750 | result = ::read(fd, buf, sizeof(buf)); | 1727 | result = ::read(fd, buf, sizeof(buf)); | ||
1751 | if(result < 0) { | 1728 | if(result < 0) { | ||
1752 | errorCode = ERR_CANNOT_READ; | 1729 | errorCode = ERR_CANNOT_READ; | ||
1753 | cs = sftpProtocol::ClientError; | | |||
1754 | break; | 1730 | break; | ||
1755 | } | 1731 | } | ||
1756 | buffer = QByteArray(buf, result); | 1732 | buffer = QByteArray(buf, result); | ||
1757 | } | 1733 | } | ||
1758 | 1734 | | |||
1759 | if (result >= 0) { | 1735 | if (result >= 0) { | ||
1760 | if (dest.isEmpty()) { | 1736 | if (dest.isEmpty()) { | ||
1761 | if (bMarkPartial) { | 1737 | if (bMarkPartial) { | ||
▲ Show 20 Lines • Show All 42 Lines • ▼ Show 20 Line(s) | 1779 | if (file == nullptr) { | |||
1804 | qCDebug(KIO_SFTP_LOG) << "COULD NOT WRITE " << QString(dest) | 1780 | qCDebug(KIO_SFTP_LOG) << "COULD NOT WRITE " << QString(dest) | ||
1805 | << ", permissions=" << permissions | 1781 | << ", permissions=" << permissions | ||
1806 | << ", error=" << ssh_get_error(mSession); | 1782 | << ", error=" << ssh_get_error(mSession); | ||
1807 | if (sftp_get_error(mSftp) == SSH_FX_PERMISSION_DENIED) { | 1783 | if (sftp_get_error(mSftp) == SSH_FX_PERMISSION_DENIED) { | ||
1808 | errorCode = KIO::ERR_WRITE_ACCESS_DENIED; | 1784 | errorCode = KIO::ERR_WRITE_ACCESS_DENIED; | ||
1809 | } else { | 1785 | } else { | ||
1810 | errorCode = KIO::ERR_CANNOT_OPEN_FOR_WRITING; | 1786 | errorCode = KIO::ERR_CANNOT_OPEN_FOR_WRITING; | ||
1811 | } | 1787 | } | ||
1812 | cs = sftpProtocol::ServerError; | | |||
1813 | result = -1; | 1788 | result = -1; | ||
1814 | continue; | 1789 | continue; | ||
1815 | } // file | 1790 | } // file | ||
1816 | } // dest.isEmpty | 1791 | } // dest.isEmpty | ||
1817 | 1792 | | |||
1818 | ssize_t bytesWritten = sftp_write(file, buffer.data(), buffer.size()); | 1793 | ssize_t bytesWritten = sftp_write(file, buffer.data(), buffer.size()); | ||
1819 | if (bytesWritten < 0) { | 1794 | if (bytesWritten < 0) { | ||
1820 | errorCode = KIO::ERR_CANNOT_WRITE; | 1795 | errorCode = KIO::ERR_CANNOT_WRITE; | ||
1821 | result = -1; | 1796 | result = -1; | ||
1822 | } else { | 1797 | } else { | ||
1823 | totalBytesSent += bytesWritten; | 1798 | totalBytesSent += bytesWritten; | ||
1824 | processedSize(totalBytesSent); | 1799 | q->processedSize(totalBytesSent); | ||
1825 | } | 1800 | } | ||
1826 | } // result | 1801 | } // result | ||
1827 | } while (result > 0); | 1802 | } while (result > 0); | ||
1828 | sftp_attributes_free(sb); | 1803 | sftp_attributes_free(sb); | ||
1829 | 1804 | | |||
1830 | // An error occurred deal with it. | 1805 | // An error occurred deal with it. | ||
1831 | if (result < 0) { | 1806 | if (result < 0) { | ||
1832 | qCDebug(KIO_SFTP_LOG) << "Error during 'put'. Aborting."; | 1807 | qCDebug(KIO_SFTP_LOG) << "Error during 'put'. Aborting."; | ||
1833 | 1808 | | |||
1834 | if (file != nullptr) { | 1809 | if (file != nullptr) { | ||
1835 | sftp_close(file); | 1810 | sftp_close(file); | ||
1836 | 1811 | | |||
1837 | sftp_attributes attr = sftp_stat(mSftp, dest.constData()); | 1812 | sftp_attributes attr = sftp_stat(mSftp, dest.constData()); | ||
1838 | if (bMarkPartial && attr != nullptr) { | 1813 | if (bMarkPartial && attr != nullptr) { | ||
1839 | size_t size = configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); | 1814 | size_t size = q->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); | ||
1840 | if (attr->size < size) { | 1815 | if (attr->size < size) { | ||
1841 | sftp_unlink(mSftp, dest.constData()); | 1816 | sftp_unlink(mSftp, dest.constData()); | ||
1842 | } | 1817 | } | ||
1843 | } | 1818 | } | ||
1844 | sftp_attributes_free(attr); | 1819 | sftp_attributes_free(attr); | ||
1845 | } | 1820 | } | ||
1846 | 1821 | | |||
1847 | //::exit(255); | 1822 | return errorCode == KJob::NoError ? Result::pass() : Result::fail(errorCode, url.toString()); | ||
1848 | return cs; | | |||
1849 | } | 1823 | } | ||
1850 | 1824 | | |||
1851 | if (file == nullptr) { // we got nothing to write out, so we never opened the file | 1825 | if (file == nullptr) { // we got nothing to write out, so we never opened the file | ||
1852 | return sftpProtocol::Success; | 1826 | return Result::pass(); | ||
1853 | } | 1827 | } | ||
1854 | 1828 | | |||
1855 | if (sftp_close(file) < 0) { | 1829 | if (sftp_close(file) < 0) { | ||
1856 | qCWarning(KIO_SFTP_LOG) << "Error when closing file descriptor"; | 1830 | qCWarning(KIO_SFTP_LOG) << "Error when closing file descriptor"; | ||
1857 | error(KIO::ERR_CANNOT_WRITE, dest_orig); | 1831 | return Result::fail(KIO::ERR_CANNOT_WRITE, url.toString()); | ||
1858 | return sftpProtocol::ServerError; | | |||
1859 | } | 1832 | } | ||
1860 | 1833 | | |||
1861 | // after full download rename the file back to original name | 1834 | // after full download rename the file back to original name | ||
1862 | if (bMarkPartial) { | 1835 | if (bMarkPartial) { | ||
1863 | // If the original URL is a symlink and we were asked to overwrite it, | 1836 | // If the original URL is a symlink and we were asked to overwrite it, | ||
1864 | // remove the symlink first. This ensures that we do not overwrite the | 1837 | // remove the symlink first. This ensures that we do not overwrite the | ||
1865 | // current source if the symlink points to it. | 1838 | // current source if the symlink points to it. | ||
1866 | if ((flags & KIO::Overwrite)) { | 1839 | if ((flags & KIO::Overwrite)) { | ||
1867 | sftp_unlink(mSftp, dest_orig_c.constData()); | 1840 | sftp_unlink(mSftp, dest_orig_c.constData()); | ||
1868 | } | 1841 | } | ||
1869 | 1842 | | |||
1870 | if (sftp_rename(mSftp, dest.constData(), dest_orig_c.constData()) < 0) { | 1843 | if (sftp_rename(mSftp, dest.constData(), dest_orig_c.constData()) < 0) { | ||
1871 | qCWarning(KIO_SFTP_LOG) << " Couldn't rename " << dest << " to " << dest_orig; | 1844 | qCWarning(KIO_SFTP_LOG) << " Couldn't rename " << dest << " to " << dest_orig; | ||
1872 | errorCode = KIO::ERR_CANNOT_RENAME_PARTIAL; | 1845 | return Result::fail(ERR_CANNOT_RENAME_PARTIAL, url.toString()); | ||
1873 | return sftpProtocol::ServerError; | | |||
1874 | } | 1846 | } | ||
1875 | } | 1847 | } | ||
1876 | 1848 | | |||
1877 | // set final permissions | 1849 | // set final permissions | ||
1878 | if (permissions != -1 && !(flags & KIO::Resume)) { | 1850 | if (permissions != -1 && !(flags & KIO::Resume)) { | ||
1879 | qCDebug(KIO_SFTP_LOG) << "Trying to set final permissions of " << dest_orig << " to " << QString::number(permissions); | 1851 | qCDebug(KIO_SFTP_LOG) << "Trying to set final permissions of " << dest_orig << " to " << QString::number(permissions); | ||
1880 | if (sftp_chmod(mSftp, dest_orig_c.constData(), permissions) < 0) { | 1852 | if (sftp_chmod(mSftp, dest_orig_c.constData(), permissions) < 0) { | ||
1881 | errorCode = -1; // force copy to call sftpSendWarning... | 1853 | q->warning(i18n("Could not change permissions for\n%1", url.toString())); | ||
1882 | return sftpProtocol::ServerError; | 1854 | return Result::pass(); | ||
1883 | } | 1855 | } | ||
1884 | } | 1856 | } | ||
1885 | 1857 | | |||
1886 | // set original owner and group | 1858 | // set original owner and group | ||
1887 | if (bOrigExists) { | 1859 | if (bOrigExists) { | ||
1888 | qCDebug(KIO_SFTP_LOG) << "Trying to restore original owner and group of " << dest_orig; | 1860 | qCDebug(KIO_SFTP_LOG) << "Trying to restore original owner and group of " << dest_orig; | ||
1889 | if (sftp_chown(mSftp, dest_orig_c.constData(), owner, group) < 0) { | 1861 | if (sftp_chown(mSftp, dest_orig_c.constData(), owner, group) < 0) { | ||
1890 | qCWarning(KIO_SFTP_LOG) << "Could not change owner and group for" << dest_orig; | 1862 | qCWarning(KIO_SFTP_LOG) << "Could not change owner and group for" << dest_orig; | ||
1891 | // warning(i18n( "Could not change owner and group for\n%1", dest_orig)); | 1863 | // warning(i18n( "Could not change owner and group for\n%1", dest_orig)); | ||
1892 | } | 1864 | } | ||
1893 | } | 1865 | } | ||
1894 | 1866 | | |||
1895 | // set modification time | 1867 | // set modification time | ||
1896 | const QString mtimeStr = metaData("modified"); | 1868 | const QString mtimeStr = q->metaData("modified"); | ||
1897 | if (!mtimeStr.isEmpty()) { | 1869 | if (!mtimeStr.isEmpty()) { | ||
1898 | QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); | 1870 | QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); | ||
1899 | if (dt.isValid()) { | 1871 | if (dt.isValid()) { | ||
1900 | struct timeval times[2]; | 1872 | struct timeval times[2]; | ||
1901 | 1873 | | |||
1902 | sftp_attributes attr = sftp_lstat(mSftp, dest_orig_c.constData()); | 1874 | sftp_attributes attr = sftp_lstat(mSftp, dest_orig_c.constData()); | ||
1903 | if (attr != nullptr) { | 1875 | if (attr != nullptr) { | ||
1904 | times[0].tv_sec = attr->atime; //// access time, unchanged | 1876 | times[0].tv_sec = attr->atime; //// access time, unchanged | ||
1905 | times[1].tv_sec = dt.toSecsSinceEpoch(); // modification time | 1877 | times[1].tv_sec = dt.toSecsSinceEpoch(); // modification time | ||
1906 | times[0].tv_usec = times[1].tv_usec = 0; | 1878 | times[0].tv_usec = times[1].tv_usec = 0; | ||
1907 | 1879 | | |||
1908 | qCDebug(KIO_SFTP_LOG) << "Trying to restore mtime for " << dest_orig << " to: " << mtimeStr; | 1880 | qCDebug(KIO_SFTP_LOG) << "Trying to restore mtime for " << dest_orig << " to: " << mtimeStr; | ||
1909 | result = sftp_utimes(mSftp, dest_orig_c.constData(), times); | 1881 | result = sftp_utimes(mSftp, dest_orig_c.constData(), times); | ||
1910 | if (result < 0) { | 1882 | if (result < 0) { | ||
1911 | qCWarning(KIO_SFTP_LOG) << "Failed to set mtime for" << dest_orig; | 1883 | qCWarning(KIO_SFTP_LOG) << "Failed to set mtime for" << dest_orig; | ||
1912 | } | 1884 | } | ||
1913 | sftp_attributes_free(attr); | 1885 | sftp_attributes_free(attr); | ||
1914 | } | 1886 | } | ||
1915 | } | 1887 | } | ||
1916 | } | 1888 | } | ||
1917 | 1889 | | |||
1918 | return sftpProtocol::Success; | 1890 | return Result::pass(); | ||
1919 | } | 1891 | } | ||
1920 | 1892 | | |||
1921 | void sftpProtocol::copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) | 1893 | Result SFTPInternal::copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) | ||
1922 | { | 1894 | { | ||
1923 | qCDebug(KIO_SFTP_LOG) << src << " -> " << dest << " , permissions = " << QString::number(permissions) | 1895 | qCDebug(KIO_SFTP_LOG) << src << " -> " << dest << " , permissions = " << QString::number(permissions) | ||
1924 | << ", overwrite = " << (flags & KIO::Overwrite) | 1896 | << ", overwrite = " << (flags & KIO::Overwrite) | ||
1925 | << ", resume = " << (flags & KIO::Resume); | 1897 | << ", resume = " << (flags & KIO::Resume); | ||
1926 | 1898 | | |||
1927 | QString sCopyFile; | | |||
1928 | int errorCode = 0; | | |||
1929 | StatusCode cs = sftpProtocol::ClientError; | | |||
1930 | const bool isSourceLocal = src.isLocalFile(); | 1899 | const bool isSourceLocal = src.isLocalFile(); | ||
1931 | const bool isDestinationLocal = dest.isLocalFile(); | 1900 | const bool isDestinationLocal = dest.isLocalFile(); | ||
1932 | 1901 | | |||
1933 | if (!isSourceLocal && isDestinationLocal) { // sftp -> file | 1902 | if (!isSourceLocal && isDestinationLocal) { // sftp -> file | ||
1934 | sCopyFile = dest.toLocalFile(); | 1903 | return sftpCopyGet(src, dest.toLocalFile(), permissions, flags); | ||
1935 | cs = sftpCopyGet(src, sCopyFile, permissions, flags, errorCode); | | |||
1936 | if (cs == sftpProtocol::ServerError) | | |||
1937 | sCopyFile = src.url(); | | |||
1938 | } else if (isSourceLocal && !isDestinationLocal) { // file -> sftp | 1904 | } else if (isSourceLocal && !isDestinationLocal) { // file -> sftp | ||
1939 | sCopyFile = src.toLocalFile(); | 1905 | return sftpCopyPut(dest, src.toLocalFile(), permissions, flags); | ||
1940 | cs = sftpCopyPut(dest, sCopyFile, permissions, flags, errorCode); | | |||
1941 | if (cs == sftpProtocol::ServerError) | | |||
1942 | sCopyFile = dest.url(); | | |||
1943 | } else { | | |||
1944 | errorCode = KIO::ERR_UNSUPPORTED_ACTION; | | |||
1945 | sCopyFile.clear(); | | |||
1946 | } | | |||
1947 | | ||||
1948 | if (cs != sftpProtocol::Success && errorCode > 0) { | | |||
1949 | error(errorCode, sCopyFile); | | |||
1950 | return; | | |||
1951 | } | | |||
1952 | | ||||
1953 | if (errorCode < 0) { | | |||
1954 | sftpSendWarning(errorCode, sCopyFile); | | |||
1955 | } | 1906 | } | ||
1956 | 1907 | | |||
1957 | finished(); | 1908 | return Result::fail(ERR_UNSUPPORTED_ACTION); | ||
1958 | } | 1909 | } | ||
1959 | 1910 | | |||
1960 | sftpProtocol::StatusCode sftpProtocol::sftpCopyGet(const QUrl& url, const QString& sCopyFile, int permissions, KIO::JobFlags flags, int& errorCode) | 1911 | Result SFTPInternal::sftpCopyGet(const QUrl &url, const QString &sCopyFile, int permissions, KIO::JobFlags flags) | ||
1961 | { | 1912 | { | ||
1962 | qCDebug(KIO_SFTP_LOG) << url << "->" << sCopyFile << ", permissions=" << permissions; | 1913 | qCDebug(KIO_SFTP_LOG) << url << "->" << sCopyFile << ", permissions=" << permissions; | ||
1963 | 1914 | | |||
1964 | // check if destination is ok ... | 1915 | // check if destination is ok ... | ||
1965 | QFileInfo copyFile(sCopyFile); | 1916 | QFileInfo copyFile(sCopyFile); | ||
1966 | const bool bDestExists = copyFile.exists(); | 1917 | const bool bDestExists = copyFile.exists(); | ||
1967 | if(bDestExists) { | 1918 | if (bDestExists) { | ||
1968 | if(copyFile.isDir()) { | 1919 | if (copyFile.isDir()) { | ||
1969 | errorCode = ERR_IS_DIRECTORY; | 1920 | return Result::fail(ERR_IS_DIRECTORY, sCopyFile); | ||
1970 | return sftpProtocol::ClientError; | | |||
1971 | } | 1921 | } | ||
1972 | 1922 | | |||
1973 | if(!(flags & KIO::Overwrite)) { | 1923 | if (!(flags & KIO::Overwrite)) { | ||
1974 | errorCode = ERR_FILE_ALREADY_EXIST; | 1924 | return Result::fail(ERR_FILE_ALREADY_EXIST, sCopyFile); | ||
1975 | return sftpProtocol::ClientError; | | |||
1976 | } | 1925 | } | ||
1977 | } | 1926 | } | ||
1978 | 1927 | | |||
1979 | bool bResume = false; | 1928 | bool bResume = false; | ||
1980 | const QString sPart = sCopyFile + QLatin1String(".part"); // do we have a ".part" file? | 1929 | const QString sPart = sCopyFile + QLatin1String(".part"); // do we have a ".part" file? | ||
1981 | QFileInfo partFile(sPart); | 1930 | QFileInfo partFile(sPart); | ||
1982 | const bool bPartExists = partFile.exists(); | 1931 | const bool bPartExists = partFile.exists(); | ||
1983 | const bool bMarkPartial = configValue(QStringLiteral("MarkPartial"), true); | 1932 | const bool bMarkPartial = q->configValue(QStringLiteral("MarkPartial"), true); | ||
1984 | const QString dest = (bMarkPartial ? sPart : sCopyFile); | 1933 | const QString dest = (bMarkPartial ? sPart : sCopyFile); | ||
1985 | 1934 | | |||
1986 | if (bMarkPartial && bPartExists && copyFile.size() > 0) { | 1935 | if (bMarkPartial && bPartExists && copyFile.size() > 0) { | ||
1987 | if(partFile.isDir()) { | 1936 | if (partFile.isDir()) { | ||
1988 | errorCode = ERR_DIR_ALREADY_EXIST; | 1937 | return Result::fail(ERR_FILE_ALREADY_EXIST, sCopyFile); | ||
1989 | return sftpProtocol::ClientError; // client side error | | |||
1990 | } | 1938 | } | ||
1991 | bResume = canResume( copyFile.size() ); | 1939 | bResume = q->canResume(copyFile.size()); | ||
1992 | } | 1940 | } | ||
1993 | 1941 | | |||
1994 | if (bPartExists && !bResume) // get rid of an unwanted ".part" file | 1942 | if (bPartExists && !bResume) // get rid of an unwanted ".part" file | ||
1995 | QFile::remove(sPart); | 1943 | QFile::remove(sPart); | ||
1996 | 1944 | | |||
1997 | // WABA: Make sure that we keep writing permissions ourselves, | 1945 | // WABA: Make sure that we keep writing permissions ourselves, | ||
1998 | // otherwise we can be in for a surprise on NFS. | 1946 | // otherwise we can be in for a surprise on NFS. | ||
1999 | mode_t initialMode; | 1947 | mode_t initialMode; | ||
2000 | if (permissions != -1) | 1948 | if (permissions != -1) | ||
2001 | #ifdef Q_OS_WIN | 1949 | #ifdef Q_OS_WIN | ||
2002 | initialMode = permissions | static_cast<mode_t>(perms::owner_write); | 1950 | initialMode = permissions | static_cast<mode_t>(perms::owner_write); | ||
2003 | #else | 1951 | #else | ||
2004 | initialMode = permissions | S_IWUSR; | 1952 | initialMode = permissions | S_IWUSR; | ||
2005 | #endif | 1953 | #endif | ||
2006 | else | 1954 | else | ||
2007 | initialMode = 0666; | 1955 | initialMode = 0666; | ||
2008 | 1956 | | |||
2009 | // open the output file ... | 1957 | // open the output file ... | ||
2010 | int fd = -1; | 1958 | int fd = -1; | ||
2011 | KIO::fileoffset_t offset = 0; | 1959 | KIO::fileoffset_t offset = 0; | ||
2012 | if (bResume) { | 1960 | if (bResume) { | ||
2013 | fd = QT_OPEN( QFile::encodeName(sPart), O_RDWR ); // append if resuming | 1961 | fd = QT_OPEN( QFile::encodeName(sPart), O_RDWR ); // append if resuming | ||
2014 | offset = seekPos(fd, 0, SEEK_END); | 1962 | offset = seekPos(fd, 0, SEEK_END); | ||
2015 | if(offset < 0) { | 1963 | if(offset < 0) { | ||
2016 | errorCode = ERR_CANNOT_RESUME; | | |||
2017 | ::close(fd); | 1964 | ::close(fd); | ||
2018 | return sftpProtocol::ClientError; // client side error | 1965 | return Result::fail(ERR_CANNOT_RESUME, sCopyFile); | ||
2019 | } | 1966 | } | ||
2020 | qCDebug(KIO_SFTP_LOG) << "resuming at" << offset; | 1967 | qCDebug(KIO_SFTP_LOG) << "resuming at" << offset; | ||
2021 | } | 1968 | } | ||
2022 | else { | 1969 | else { | ||
2023 | fd = QT_OPEN(QFile::encodeName(dest), O_CREAT | O_TRUNC | O_WRONLY, initialMode); | 1970 | fd = QT_OPEN(QFile::encodeName(dest), O_CREAT | O_TRUNC | O_WRONLY, initialMode); | ||
2024 | } | 1971 | } | ||
2025 | 1972 | | |||
2026 | if (fd == -1) { | 1973 | if (fd == -1) { | ||
2027 | qCDebug(KIO_SFTP_LOG) << "could not write to" << sCopyFile; | 1974 | qCDebug(KIO_SFTP_LOG) << "could not write to" << sCopyFile; | ||
2028 | errorCode = (errno == EACCES) ? ERR_WRITE_ACCESS_DENIED : ERR_CANNOT_OPEN_FOR_WRITING; | 1975 | return Result::fail((errno == EACCES) ? ERR_WRITE_ACCESS_DENIED : ERR_CANNOT_OPEN_FOR_WRITING, sCopyFile); | ||
2029 | return sftpProtocol::ClientError; | | |||
2030 | } | 1976 | } | ||
2031 | 1977 | | |||
2032 | StatusCode result = sftpGet(url, errorCode, offset, fd); | 1978 | const auto getResult = sftpGet(url, offset, fd); | ||
1979 | | ||||
1980 | // The following is a bit awkward, we'll override the internal error | ||||
1981 | // in cleanup conditions, so these are subject to change until we return. | ||||
1982 | int errorCode = getResult.error; | ||||
1983 | QString errorString = getResult.errorString; | ||||
2033 | 1984 | | |||
2034 | if( ::close(fd) && result == sftpProtocol::Success ) { | 1985 | if (::close(fd) && getResult.success) { | ||
2035 | errorCode = ERR_CANNOT_WRITE; | 1986 | errorCode = ERR_CANNOT_WRITE; | ||
2036 | result = sftpProtocol::ClientError; | 1987 | errorString = sCopyFile; | ||
2037 | } | 1988 | } | ||
2038 | 1989 | | |||
2039 | // handle renaming or deletion of a partial file ... | 1990 | // handle renaming or deletion of a partial file ... | ||
2040 | if (bMarkPartial) { | 1991 | if (bMarkPartial) { | ||
2041 | if (result == sftpProtocol::Success) { // rename ".part" on success | 1992 | if (getResult.success) { // rename ".part" on success | ||
2042 | if (!QFile::rename(sPart, sCopyFile)) { | 1993 | if (!QFile::rename(sPart, sCopyFile)) { | ||
2043 | // If rename fails, try removing the destination first if it exists. | 1994 | // If rename fails, try removing the destination first if it exists. | ||
2044 | if (!bDestExists || !QFile::remove(sCopyFile) || !QFile::rename(sPart, sCopyFile)) { | 1995 | if (!bDestExists || !QFile::remove(sCopyFile) || !QFile::rename(sPart, sCopyFile)) { | ||
2045 | qCDebug(KIO_SFTP_LOG) << "cannot rename " << sPart << " to " << sCopyFile; | 1996 | qCDebug(KIO_SFTP_LOG) << "cannot rename " << sPart << " to " << sCopyFile; | ||
2046 | errorCode = ERR_CANNOT_RENAME_PARTIAL; | 1997 | errorCode = ERR_CANNOT_RENAME_PARTIAL; | ||
2047 | result = sftpProtocol::ClientError; | 1998 | errorString = sCopyFile; | ||
2048 | } | 1999 | } | ||
2049 | } | 2000 | } | ||
2050 | } | 2001 | } else{ | ||
2051 | else{ | | |||
2052 | partFile.refresh(); | 2002 | partFile.refresh(); | ||
2053 | const int size = configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); | 2003 | const int size = q->configValue(QStringLiteral("MinimumKeepSize"), DEFAULT_MINIMUM_KEEP_SIZE); | ||
2054 | if (partFile.exists() && partFile.size() < size) { // should a very small ".part" be deleted? | 2004 | if (partFile.exists() && partFile.size() < size) { // should a very small ".part" be deleted? | ||
2055 | QFile::remove(sPart); | 2005 | QFile::remove(sPart); | ||
2056 | } | 2006 | } | ||
2057 | } | 2007 | } | ||
2058 | } | 2008 | } | ||
2059 | 2009 | | |||
2060 | const QString mtimeStr = metaData("modified"); | 2010 | const QString mtimeStr = q->metaData("modified"); | ||
2061 | if (!mtimeStr.isEmpty()) { | 2011 | if (!mtimeStr.isEmpty()) { | ||
2062 | QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); | 2012 | QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); | ||
2063 | if (dt.isValid()) { | 2013 | if (dt.isValid()) { | ||
2064 | QFile receivedFile(sCopyFile); | 2014 | QFile receivedFile(sCopyFile); | ||
2065 | if (receivedFile.exists()) { | 2015 | if (receivedFile.exists()) { | ||
2066 | if (!receivedFile.open(QIODevice::ReadWrite | QIODevice::Text)) { | 2016 | if (!receivedFile.open(QIODevice::ReadWrite | QIODevice::Text)) { | ||
2067 | QString error_msg = receivedFile.errorString(); | 2017 | QString error_msg = receivedFile.errorString(); | ||
2068 | qCDebug(KIO_SFTP_LOG) << "Couldn't update modified time : " << error_msg; | 2018 | qCDebug(KIO_SFTP_LOG) << "Couldn't update modified time : " << error_msg; | ||
2069 | } else { | 2019 | } else { | ||
2070 | receivedFile.setFileTime(dt, QFileDevice::FileModificationTime); | 2020 | receivedFile.setFileTime(dt, QFileDevice::FileModificationTime); | ||
2071 | } | 2021 | } | ||
2072 | } | 2022 | } | ||
2073 | } | 2023 | } | ||
2074 | } | 2024 | } | ||
2075 | 2025 | | |||
2076 | return result; | 2026 | return errorCode == KJob::NoError ? Result::pass() : Result::fail(errorCode, errorString); | ||
2077 | } | 2027 | } | ||
2078 | 2028 | | |||
2079 | sftpProtocol::StatusCode sftpProtocol::sftpCopyPut(const QUrl& url, const QString& sCopyFile, int permissions, JobFlags flags, int& errorCode) | 2029 | Result SFTPInternal::sftpCopyPut(const QUrl &url, const QString &sCopyFile, int permissions, JobFlags flags) | ||
2080 | { | 2030 | { | ||
2081 | qCDebug(KIO_SFTP_LOG) << sCopyFile << "->" << url << ", permissions=" << permissions << ", flags" << flags; | 2031 | qCDebug(KIO_SFTP_LOG) << sCopyFile << "->" << url << ", permissions=" << permissions << ", flags" << flags; | ||
2082 | 2032 | | |||
2083 | // check if source is ok ... | 2033 | // check if source is ok ... | ||
2084 | QFileInfo copyFile(sCopyFile); | 2034 | QFileInfo copyFile(sCopyFile); | ||
2085 | bool bSrcExists = copyFile.exists(); | 2035 | bool bSrcExists = copyFile.exists(); | ||
2086 | if (bSrcExists) { | 2036 | if (bSrcExists) { | ||
2087 | if(copyFile.isDir()) { | 2037 | if(copyFile.isDir()) { | ||
2088 | errorCode = ERR_IS_DIRECTORY; | 2038 | return Result::fail(ERR_IS_DIRECTORY, sCopyFile); | ||
2089 | return sftpProtocol::ClientError; | | |||
2090 | } | 2039 | } | ||
2091 | } else { | 2040 | } else { | ||
2092 | errorCode = ERR_DOES_NOT_EXIST; | 2041 | return Result::fail(ERR_DOES_NOT_EXIST, sCopyFile); | ||
2093 | return sftpProtocol::ClientError; | | |||
2094 | } | 2042 | } | ||
2095 | 2043 | | |||
2096 | const int fd = QT_OPEN(QFile::encodeName(sCopyFile), O_RDONLY); | 2044 | const int fd = QT_OPEN(QFile::encodeName(sCopyFile), O_RDONLY); | ||
2097 | if(fd == -1) | 2045 | if (fd == -1) { | ||
2098 | { | 2046 | return Result::fail(ERR_CANNOT_OPEN_FOR_READING, sCopyFile); | ||
2099 | errorCode = ERR_CANNOT_OPEN_FOR_READING; | | |||
2100 | return sftpProtocol::ClientError; | | |||
2101 | } | 2047 | } | ||
2102 | 2048 | | |||
2103 | totalSize(copyFile.size()); | 2049 | q->totalSize(copyFile.size()); | ||
2104 | 2050 | | |||
2105 | // delegate the real work (errorCode gets status) ... | 2051 | // delegate the real work (errorCode gets status) ... | ||
2106 | StatusCode ret = sftpPut(url, permissions, flags, errorCode, fd); | 2052 | const auto result = sftpPut(url, permissions, flags, fd); | ||
2107 | ::close(fd); | 2053 | ::close(fd); | ||
2108 | return ret; | 2054 | return result; | ||
2109 | } | 2055 | } | ||
2110 | 2056 | | |||
2111 | 2057 | Result SFTPInternal::stat(const QUrl &url) | |||
2112 | void sftpProtocol::stat(const QUrl& url) { | 2058 | { | ||
2113 | qCDebug(KIO_SFTP_LOG) << url; | 2059 | qCDebug(KIO_SFTP_LOG) << url; | ||
2114 | 2060 | | |||
2115 | if (!sftpLogin()) { | 2061 | const auto loginResult = sftpLogin(); | ||
2116 | // sftpLogin finished() | 2062 | if (!loginResult.success) { | ||
2117 | return; | 2063 | return loginResult; | ||
2118 | } | 2064 | } | ||
2119 | 2065 | | |||
2120 | if (url.path().isEmpty() || QDir::isRelativePath(url.path()) || | 2066 | if (url.path().isEmpty() || QDir::isRelativePath(url.path()) || | ||
2121 | url.path().contains("/./") || url.path().contains("/../")) { | 2067 | url.path().contains("/./") || url.path().contains("/../")) { | ||
2122 | QString cPath; | 2068 | QString cPath; | ||
2123 | 2069 | | |||
2124 | if (!url.path().isEmpty()) { | 2070 | if (!url.path().isEmpty()) { | ||
2125 | cPath = canonicalizePath(url.path()); | 2071 | cPath = canonicalizePath(url.path()); | ||
2126 | } else { | 2072 | } else { | ||
2127 | cPath = canonicalizePath(QLatin1String(".")); | 2073 | cPath = canonicalizePath(QLatin1String(".")); | ||
2128 | } | 2074 | } | ||
2129 | 2075 | | |||
2130 | if (cPath.isEmpty()) { | 2076 | if (cPath.isEmpty()) { | ||
2131 | error(KIO::ERR_MALFORMED_URL, url.toDisplayString()); | 2077 | return Result::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); | ||
dfaure: missing "return" in front | |||||
2132 | return; | | |||
2133 | } | 2078 | } | ||
2134 | QUrl redir(url); | 2079 | QUrl redir(url); | ||
2135 | redir.setPath(cPath); | 2080 | redir.setPath(cPath); | ||
2136 | redirection(redir); | 2081 | q->redirection(redir); | ||
2137 | 2082 | | |||
2138 | qCDebug(KIO_SFTP_LOG) << "redirecting to " << redir.url(); | 2083 | qCDebug(KIO_SFTP_LOG) << "redirecting to " << redir.url(); | ||
2139 | 2084 | | |||
2140 | finished(); | 2085 | return Result::pass(); | ||
2141 | return; | | |||
2142 | } | 2086 | } | ||
2143 | 2087 | | |||
2144 | QByteArray path = url.path().toUtf8(); | 2088 | QByteArray path = url.path().toUtf8(); | ||
2145 | 2089 | | |||
2146 | const QString sDetails = metaData(QLatin1String("details")); | 2090 | const QString sDetails = q->metaData(QLatin1String("details")); | ||
2147 | const int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); | 2091 | const int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); | ||
2148 | 2092 | | |||
2149 | UDSEntry entry; | 2093 | UDSEntry entry; | ||
2150 | entry.clear(); | 2094 | entry.clear(); | ||
2151 | if (!createUDSEntry(url.fileName(), path, entry, details)) { | 2095 | if (!createUDSEntry(url.fileName(), path, entry, details)) { | ||
2152 | error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); | 2096 | return Result::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); | ||
2153 | return; | | |||
2154 | } | 2097 | } | ||
2155 | 2098 | | |||
2156 | statEntry(entry); | 2099 | q->statEntry(entry); | ||
2157 | 2100 | | |||
2158 | finished(); | 2101 | return Result::pass(); | ||
2159 | } | 2102 | } | ||
2160 | 2103 | | |||
2161 | void sftpProtocol::mimetype(const QUrl& url){ | 2104 | Result SFTPInternal::mimetype(const QUrl &url) | ||
2105 | { | ||||
2162 | qCDebug(KIO_SFTP_LOG) << url; | 2106 | qCDebug(KIO_SFTP_LOG) << url; | ||
2163 | 2107 | | |||
2164 | if (!sftpLogin()) { | 2108 | const auto loginResult = sftpLogin(); | ||
2165 | // sftpLogin finished() | 2109 | if (!loginResult.success) { | ||
2166 | return; | 2110 | return loginResult; | ||
2167 | } | 2111 | } | ||
2168 | 2112 | | |||
2169 | // open() feeds the mimetype | 2113 | // open() feeds the mimetype | ||
2170 | open(url, QIODevice::ReadOnly); | 2114 | const auto result = open(url, QIODevice::ReadOnly); | ||
2171 | // open() finished(), don't finish in close again. | 2115 | close(); | ||
2172 | closeWithoutFinish(); | 2116 | | ||
2117 | return result; | ||||
2173 | } | 2118 | } | ||
2174 | 2119 | | |||
2175 | void sftpProtocol::listDir(const QUrl& url) { | 2120 | Result SFTPInternal::listDir(const QUrl &url) | ||
2121 | { | ||||
2176 | qCDebug(KIO_SFTP_LOG) << "list directory: " << url; | 2122 | qCDebug(KIO_SFTP_LOG) << "list directory: " << url; | ||
2177 | 2123 | | |||
2178 | if (!sftpLogin()) { | 2124 | const auto loginResult = sftpLogin(); | ||
2179 | // sftpLogin finished() | 2125 | if (!loginResult.success) { | ||
2180 | return; | 2126 | return loginResult; | ||
2181 | } | 2127 | } | ||
2182 | 2128 | | |||
2183 | if (url.path().isEmpty() || QDir::isRelativePath(url.path()) || | 2129 | if (url.path().isEmpty() || QDir::isRelativePath(url.path()) || | ||
2184 | url.path().contains("/./") || url.path().contains("/../")) { | 2130 | url.path().contains("/./") || url.path().contains("/../")) { | ||
2185 | QString cPath; | 2131 | QString cPath; | ||
2186 | 2132 | | |||
2187 | if (!url.path().isEmpty() ) { | 2133 | if (!url.path().isEmpty() ) { | ||
2188 | cPath = canonicalizePath(url.path()); | 2134 | cPath = canonicalizePath(url.path()); | ||
2189 | } else { | 2135 | } else { | ||
2190 | cPath = canonicalizePath(QStringLiteral(".")); | 2136 | cPath = canonicalizePath(QStringLiteral(".")); | ||
2191 | } | 2137 | } | ||
2192 | 2138 | | |||
2193 | if (cPath.isEmpty()) { | 2139 | if (cPath.isEmpty()) { | ||
2194 | error(KIO::ERR_MALFORMED_URL, url.toDisplayString()); | 2140 | return Result::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); | ||
2195 | return; | | |||
2196 | } | 2141 | } | ||
2197 | QUrl redir(url); | 2142 | QUrl redir(url); | ||
2198 | redir.setPath(cPath); | 2143 | redir.setPath(cPath); | ||
2199 | redirection(redir); | 2144 | q->redirection(redir); | ||
2200 | 2145 | | |||
2201 | qCDebug(KIO_SFTP_LOG) << "redirecting to " << redir.url(); | 2146 | qCDebug(KIO_SFTP_LOG) << "redirecting to " << redir.url(); | ||
2202 | 2147 | | |||
2203 | finished(); | 2148 | return Result::pass(); | ||
2204 | return; | | |||
2205 | } | 2149 | } | ||
2206 | 2150 | | |||
2207 | QByteArray path = url.path().toUtf8(); | 2151 | QByteArray path = url.path().toUtf8(); | ||
2208 | 2152 | | |||
2209 | sftp_dir dp = sftp_opendir(mSftp, path.constData()); | 2153 | sftp_dir dp = sftp_opendir(mSftp, path.constData()); | ||
2210 | if (dp == nullptr) { | 2154 | if (dp == nullptr) { | ||
2211 | reportError(url, sftp_get_error(mSftp)); | 2155 | return reportError(url, sftp_get_error(mSftp)); | ||
2212 | return; | | |||
2213 | } | 2156 | } | ||
2214 | 2157 | | |||
2215 | sftp_attributes dirent = nullptr; | 2158 | sftp_attributes dirent = nullptr; | ||
2216 | const QString sDetails = metaData(QLatin1String("details")); | 2159 | const QString sDetails = q->metaData(QLatin1String("details")); | ||
2217 | const int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); | 2160 | const int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); | ||
2218 | UDSEntry entry; | 2161 | UDSEntry entry; | ||
2219 | 2162 | | |||
2220 | qCDebug(KIO_SFTP_LOG) << "readdir: " << path << ", details: " << QString::number(details); | 2163 | qCDebug(KIO_SFTP_LOG) << "readdir: " << path << ", details: " << QString::number(details); | ||
2221 | 2164 | | |||
2222 | for (;;) { | 2165 | for (;;) { | ||
2223 | mode_t access; | 2166 | mode_t access; | ||
2224 | char *link; | 2167 | char *link; | ||
Show All 10 Lines | |||||
2235 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, QFile::decodeName(dirent->name)); | 2178 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, QFile::decodeName(dirent->name)); | ||
2236 | 2179 | | |||
2237 | if (dirent->type == SSH_FILEXFER_TYPE_SYMLINK) { | 2180 | if (dirent->type == SSH_FILEXFER_TYPE_SYMLINK) { | ||
2238 | QByteArray file = path + '/' + QFile::decodeName(dirent->name).toUtf8(); | 2181 | QByteArray file = path + '/' + QFile::decodeName(dirent->name).toUtf8(); | ||
2239 | 2182 | | |||
2240 | link = sftp_readlink(mSftp, file.constData()); | 2183 | link = sftp_readlink(mSftp, file.constData()); | ||
2241 | if (link == nullptr) { | 2184 | if (link == nullptr) { | ||
2242 | sftp_attributes_free(dirent); | 2185 | sftp_attributes_free(dirent); | ||
2243 | error(KIO::ERR_INTERNAL, i18n("Could not read link: %1", QString::fromUtf8(file))); | 2186 | return Result::fail(KIO::ERR_INTERNAL, i18n("Could not read link: %1", QString::fromUtf8(file))); | ||
2244 | return; | | |||
2245 | } | 2187 | } | ||
2246 | entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName(link)); | 2188 | entry.fastInsert(KIO::UDSEntry::UDS_LINK_DEST, QFile::decodeName(link)); | ||
2247 | free(link); | 2189 | free(link); | ||
2248 | // A symlink -> follow it only if details > 1 | 2190 | // A symlink -> follow it only if details > 1 | ||
2249 | if (details > 1) { | 2191 | if (details > 1) { | ||
2250 | sftp_attributes sb = sftp_stat(mSftp, file.constData()); | 2192 | sftp_attributes sb = sftp_stat(mSftp, file.constData()); | ||
2251 | if (sb == nullptr) { | 2193 | if (sb == nullptr) { | ||
2252 | isBrokenLink = true; | 2194 | isBrokenLink = true; | ||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Line(s) | 2234 | if (details > 0) { | |||
2303 | } | 2245 | } | ||
2304 | 2246 | | |||
2305 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, dirent->atime); | 2247 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS_TIME, dirent->atime); | ||
2306 | entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, dirent->mtime); | 2248 | entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, dirent->mtime); | ||
2307 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, dirent->createtime); | 2249 | entry.fastInsert(KIO::UDSEntry::UDS_CREATION_TIME, dirent->createtime); | ||
2308 | } | 2250 | } | ||
2309 | 2251 | | |||
2310 | sftp_attributes_free(dirent); | 2252 | sftp_attributes_free(dirent); | ||
2311 | listEntry(entry); | 2253 | q->listEntry(entry); | ||
2312 | } // for ever | 2254 | } // for ever | ||
2313 | sftp_closedir(dp); | 2255 | sftp_closedir(dp); | ||
2314 | finished(); | 2256 | return Result::pass(); | ||
2315 | } | 2257 | } | ||
2316 | 2258 | | |||
2317 | void sftpProtocol::mkdir(const QUrl &url, int permissions) { | 2259 | Result SFTPInternal::mkdir(const QUrl &url, int permissions) | ||
2260 | { | ||||
2318 | qCDebug(KIO_SFTP_LOG) << "create directory: " << url; | 2261 | qCDebug(KIO_SFTP_LOG) << "create directory: " << url; | ||
2319 | 2262 | | |||
2320 | if (!sftpLogin()) { | 2263 | const auto loginResult = sftpLogin(); | ||
2321 | // sftpLogin finished() | 2264 | if (!loginResult.success) { | ||
2322 | return; | 2265 | return loginResult; | ||
2323 | } | 2266 | } | ||
2324 | 2267 | | |||
2325 | if (url.path().isEmpty()) { | 2268 | if (url.path().isEmpty()) { | ||
2326 | error(KIO::ERR_MALFORMED_URL, url.toDisplayString()); | 2269 | return Result::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString()); | ||
2327 | return; | | |||
2328 | } | 2270 | } | ||
2329 | const QString path = url.path(); | 2271 | const QString path = url.path(); | ||
2330 | const QByteArray path_c = path.toUtf8(); | 2272 | const QByteArray path_c = path.toUtf8(); | ||
2331 | 2273 | | |||
2332 | // Remove existing file or symlink, if requested. | 2274 | // Remove existing file or symlink, if requested. | ||
2333 | if (metaData(QLatin1String("overwrite")) == QLatin1String("true")) { | 2275 | if (q->metaData(QLatin1String("overwrite")) == QLatin1String("true")) { | ||
2334 | qCDebug(KIO_SFTP_LOG) << "overwrite set, remove existing file or symlink: " << url; | 2276 | qCDebug(KIO_SFTP_LOG) << "overwrite set, remove existing file or symlink: " << url; | ||
2335 | sftp_unlink(mSftp, path_c.constData()); | 2277 | sftp_unlink(mSftp, path_c.constData()); | ||
2336 | } | 2278 | } | ||
2337 | 2279 | | |||
2338 | qCDebug(KIO_SFTP_LOG) << "Trying to create directory: " << path; | 2280 | qCDebug(KIO_SFTP_LOG) << "Trying to create directory: " << path; | ||
2339 | sftp_attributes sb = sftp_lstat(mSftp, path_c.constData()); | 2281 | sftp_attributes sb = sftp_lstat(mSftp, path_c.constData()); | ||
2340 | if (sb == nullptr) { | 2282 | if (sb == nullptr) { | ||
2341 | if (sftp_mkdir(mSftp, path_c.constData(), 0777) < 0) { | 2283 | if (sftp_mkdir(mSftp, path_c.constData(), 0777) < 0) { | ||
2342 | reportError(url, sftp_get_error(mSftp)); | | |||
2343 | sftp_attributes_free(sb); | 2284 | sftp_attributes_free(sb); | ||
2344 | return; | 2285 | return reportError(url, sftp_get_error(mSftp)); | ||
2345 | } | 2286 | } | ||
2346 | 2287 | | |||
2347 | qCDebug(KIO_SFTP_LOG) << "Successfully created directory: " << url; | 2288 | qCDebug(KIO_SFTP_LOG) << "Successfully created directory: " << url; | ||
2348 | if (permissions != -1) { | 2289 | if (permissions != -1) { | ||
2349 | // This will report an error or finished. | 2290 | const auto result = chmod(url, permissions); | ||
2350 | chmod(url, permissions); | 2291 | if (!result.success) { | ||
2351 | } else { | 2292 | return result; | ||
2352 | finished(); | 2293 | } | ||
2353 | } | 2294 | } | ||
2354 | 2295 | | |||
2355 | sftp_attributes_free(sb); | 2296 | sftp_attributes_free(sb); | ||
2356 | return; | 2297 | return Result::pass(); | ||
2357 | } | 2298 | } | ||
2358 | 2299 | | |||
2359 | auto err = KSFTP_ISDIR(sb) ? KIO::ERR_DIR_ALREADY_EXIST : KIO::ERR_FILE_ALREADY_EXIST; | 2300 | auto err = KSFTP_ISDIR(sb) ? KIO::ERR_DIR_ALREADY_EXIST : KIO::ERR_FILE_ALREADY_EXIST; | ||
2360 | sftp_attributes_free(sb); | 2301 | sftp_attributes_free(sb); | ||
2361 | error(err, path); | 2302 | return Result::fail(err, path); | ||
2362 | } | 2303 | } | ||
2363 | 2304 | | |||
2364 | void sftpProtocol::rename(const QUrl& src, const QUrl& dest, KIO::JobFlags flags) { | 2305 | Result SFTPInternal::rename(const QUrl &src, const QUrl &dest, KIO::JobFlags flags) | ||
2306 | { | ||||
2365 | qCDebug(KIO_SFTP_LOG) << "rename " << src << " to " << dest << flags; | 2307 | qCDebug(KIO_SFTP_LOG) << "rename " << src << " to " << dest << flags; | ||
2366 | 2308 | | |||
2367 | if (!sftpLogin()) { | 2309 | const auto loginResult = sftpLogin(); | ||
2368 | // sftpLogin finished() | 2310 | if (!loginResult.success) { | ||
2369 | return; | 2311 | return loginResult; | ||
2370 | } | 2312 | } | ||
2371 | 2313 | | |||
2372 | QByteArray qsrc = src.path().toUtf8(); | 2314 | QByteArray qsrc = src.path().toUtf8(); | ||
2373 | QByteArray qdest = dest.path().toUtf8(); | 2315 | QByteArray qdest = dest.path().toUtf8(); | ||
2374 | 2316 | | |||
2375 | sftp_attributes sb = sftp_lstat(mSftp, qdest.constData()); | 2317 | sftp_attributes sb = sftp_lstat(mSftp, qdest.constData()); | ||
2376 | if (sb != nullptr) { | 2318 | if (sb != nullptr) { | ||
2377 | const bool isDir = KSFTP_ISDIR(sb); | 2319 | const bool isDir = KSFTP_ISDIR(sb); | ||
2378 | if (!(flags & KIO::Overwrite)) { | 2320 | if (!(flags & KIO::Overwrite)) { | ||
2379 | error(isDir ? KIO::ERR_DIR_ALREADY_EXIST : KIO::ERR_FILE_ALREADY_EXIST, dest.url()); | | |||
2380 | sftp_attributes_free(sb); | 2321 | sftp_attributes_free(sb); | ||
2381 | return; | 2322 | return Result::fail(isDir ? KIO::ERR_DIR_ALREADY_EXIST : KIO::ERR_FILE_ALREADY_EXIST, dest.url()); | ||
2382 | } | 2323 | } | ||
2383 | 2324 | | |||
2384 | // Delete the existing destination file/dir... | 2325 | // Delete the existing destination file/dir... | ||
2385 | if (isDir) { | 2326 | if (isDir) { | ||
2386 | if (sftp_rmdir(mSftp, qdest.constData()) < 0) { | 2327 | if (sftp_rmdir(mSftp, qdest.constData()) < 0) { | ||
2387 | reportError(dest, sftp_get_error(mSftp)); | 2328 | return reportError(dest, sftp_get_error(mSftp)); | ||
2388 | return; | | |||
2389 | } | 2329 | } | ||
2390 | } else { | 2330 | } else { | ||
2391 | if (sftp_unlink(mSftp, qdest.constData()) < 0) { | 2331 | if (sftp_unlink(mSftp, qdest.constData()) < 0) { | ||
2392 | reportError(dest, sftp_get_error(mSftp)); | 2332 | return reportError(dest, sftp_get_error(mSftp)); | ||
2393 | return; | | |||
2394 | } | 2333 | } | ||
2395 | } | 2334 | } | ||
2396 | } | 2335 | } | ||
2397 | sftp_attributes_free(sb); | 2336 | sftp_attributes_free(sb); | ||
2398 | 2337 | | |||
2399 | if (sftp_rename(mSftp, qsrc.constData(), qdest.constData()) < 0) { | 2338 | if (sftp_rename(mSftp, qsrc.constData(), qdest.constData()) < 0) { | ||
2400 | reportError(dest, sftp_get_error(mSftp)); | 2339 | return reportError(dest, sftp_get_error(mSftp)); | ||
2401 | return; | | |||
2402 | } | 2340 | } | ||
2403 | 2341 | | |||
2404 | finished(); | 2342 | return Result::pass(); | ||
2405 | } | 2343 | } | ||
2406 | 2344 | | |||
2407 | void sftpProtocol::symlink(const QString &target, const QUrl &dest, KIO::JobFlags flags) { | 2345 | Result SFTPInternal::symlink(const QString &target, const QUrl &dest, KIO::JobFlags flags) | ||
2346 | { | ||||
2408 | qCDebug(KIO_SFTP_LOG) << "link " << target << "->" << dest | 2347 | qCDebug(KIO_SFTP_LOG) << "link " << target << "->" << dest | ||
2409 | << ", overwrite = " << (flags & KIO::Overwrite) | 2348 | << ", overwrite = " << (flags & KIO::Overwrite) | ||
2410 | << ", resume = " << (flags & KIO::Resume); | 2349 | << ", resume = " << (flags & KIO::Resume); | ||
2411 | 2350 | | |||
2412 | if (!sftpLogin()) { | 2351 | const auto loginResult = sftpLogin(); | ||
2413 | // sftpLogin finished() | 2352 | if (!loginResult.success) { | ||
2414 | return; | 2353 | return loginResult; | ||
2415 | } | 2354 | } | ||
2416 | 2355 | | |||
2417 | QByteArray t = target.toUtf8(); | 2356 | QByteArray t = target.toUtf8(); | ||
2418 | QByteArray d = dest.path().toUtf8(); | 2357 | QByteArray d = dest.path().toUtf8(); | ||
2419 | 2358 | | |||
2420 | bool failed = false; | 2359 | bool failed = false; | ||
2421 | if (sftp_symlink(mSftp, t.constData(), d.constData()) < 0) { | 2360 | if (sftp_symlink(mSftp, t.constData(), d.constData()) < 0) { | ||
2422 | if (flags == KIO::Overwrite) { | 2361 | if (flags == KIO::Overwrite) { | ||
Show All 9 Lines | 2368 | } else { | |||
2432 | } | 2371 | } | ||
2433 | } | 2372 | } | ||
2434 | } | 2373 | } | ||
2435 | sftp_attributes_free(sb); | 2374 | sftp_attributes_free(sb); | ||
2436 | } | 2375 | } | ||
2437 | } | 2376 | } | ||
2438 | 2377 | | |||
2439 | if (failed) { | 2378 | if (failed) { | ||
2440 | reportError(dest, sftp_get_error(mSftp)); | 2379 | return reportError(dest, sftp_get_error(mSftp)); | ||
2441 | return; | | |||
2442 | } | 2380 | } | ||
2443 | 2381 | | |||
2444 | finished(); | 2382 | return Result::pass(); | ||
2445 | } | 2383 | } | ||
2446 | 2384 | | |||
2447 | void sftpProtocol::chmod(const QUrl& url, int permissions) { | 2385 | Result SFTPInternal::chmod(const QUrl& url, int permissions) | ||
2386 | { | ||||
2448 | qCDebug(KIO_SFTP_LOG) << "change permission of " << url << " to " << QString::number(permissions); | 2387 | qCDebug(KIO_SFTP_LOG) << "change permission of " << url << " to " << QString::number(permissions); | ||
2449 | 2388 | | |||
2450 | if (!sftpLogin()) { | 2389 | const auto loginResult = sftpLogin(); | ||
2451 | // sftpLogin finished() | 2390 | if (!loginResult.success) { | ||
2452 | return; | 2391 | return loginResult; | ||
2453 | } | 2392 | } | ||
2454 | 2393 | | |||
2455 | QByteArray path = url.path().toUtf8(); | 2394 | QByteArray path = url.path().toUtf8(); | ||
2456 | 2395 | | |||
2457 | if (sftp_chmod(mSftp, path.constData(), permissions) < 0) { | 2396 | if (sftp_chmod(mSftp, path.constData(), permissions) < 0) { | ||
2458 | reportError(url, sftp_get_error(mSftp)); | 2397 | return reportError(url, sftp_get_error(mSftp)); | ||
2459 | return; | | |||
2460 | } | 2398 | } | ||
2461 | 2399 | | |||
2462 | finished(); | 2400 | return Result::pass(); | ||
2463 | } | 2401 | } | ||
2464 | 2402 | | |||
2465 | void sftpProtocol::del(const QUrl &url, bool isfile){ | 2403 | Result SFTPInternal::del(const QUrl &url, bool isfile) | ||
2404 | { | ||||
2466 | qCDebug(KIO_SFTP_LOG) << "deleting " << (isfile ? "file: " : "directory: ") << url; | 2405 | qCDebug(KIO_SFTP_LOG) << "deleting " << (isfile ? "file: " : "directory: ") << url; | ||
2467 | 2406 | | |||
2468 | if (!sftpLogin()) { | 2407 | const auto loginResult = sftpLogin(); | ||
2469 | // sftpLogin finished() | 2408 | if (!loginResult.success) { | ||
2470 | return; | 2409 | return loginResult; | ||
2471 | } | 2410 | } | ||
2472 | 2411 | | |||
2473 | QByteArray path = url.path().toUtf8(); | 2412 | QByteArray path = url.path().toUtf8(); | ||
2474 | 2413 | | |||
2475 | if (isfile) { | 2414 | if (isfile) { | ||
2476 | if (sftp_unlink(mSftp, path.constData()) < 0) { | 2415 | if (sftp_unlink(mSftp, path.constData()) < 0) { | ||
2477 | reportError(url, sftp_get_error(mSftp)); | 2416 | return reportError(url, sftp_get_error(mSftp)); | ||
2478 | return; | | |||
2479 | } | 2417 | } | ||
2480 | } else { | 2418 | } else { | ||
2481 | if (sftp_rmdir(mSftp, path.constData()) < 0) { | 2419 | if (sftp_rmdir(mSftp, path.constData()) < 0) { | ||
2482 | reportError(url, sftp_get_error(mSftp)); | 2420 | return reportError(url, sftp_get_error(mSftp)); | ||
2483 | return; | | |||
2484 | } | 2421 | } | ||
2485 | } | 2422 | } | ||
2486 | 2423 | | |||
2487 | finished(); | 2424 | return Result::pass(); | ||
2488 | } | 2425 | } | ||
2489 | 2426 | | |||
2490 | void sftpProtocol::slave_status() { | 2427 | void SFTPInternal::slave_status() { | ||
2491 | qCDebug(KIO_SFTP_LOG) << "connected to " << mHost << "?: " << mConnected; | 2428 | qCDebug(KIO_SFTP_LOG) << "connected to " << mHost << "?: " << mConnected; | ||
2492 | slaveStatus((mConnected ? mHost : QString()), mConnected); | 2429 | q->slaveStatus((mConnected ? mHost : QString()), mConnected); | ||
2493 | } | 2430 | } | ||
2494 | 2431 | | |||
2495 | sftpProtocol::GetRequest::GetRequest(sftp_file file, sftp_attributes sb, ushort maxPendingRequests) | 2432 | SFTPInternal::GetRequest::GetRequest(sftp_file file, sftp_attributes sb, ushort maxPendingRequests) | ||
2496 | :mFile(file), mSb(sb), mMaxPendingRequests(maxPendingRequests) { | 2433 | :mFile(file), mSb(sb), mMaxPendingRequests(maxPendingRequests) | ||
2497 | 2434 | { | |||
2498 | } | 2435 | } | ||
2499 | 2436 | | |||
2500 | bool sftpProtocol::GetRequest::enqueueChunks() | 2437 | bool SFTPInternal::GetRequest::enqueueChunks() | ||
2501 | { | 2438 | { | ||
2502 | sftpProtocol::GetRequest::Request request; | 2439 | SFTPInternal::GetRequest::Request request; | ||
2503 | 2440 | | |||
2504 | qCDebug(KIO_SFTP_TRACE_LOG) << "enqueueChunks"; | 2441 | qCDebug(KIO_SFTP_TRACE_LOG) << "enqueueChunks"; | ||
2505 | 2442 | | |||
2506 | while (pendingRequests.count() < mMaxPendingRequests) { | 2443 | while (pendingRequests.count() < mMaxPendingRequests) { | ||
2507 | request.expectedLength = MAX_XFER_BUF_SIZE; | 2444 | request.expectedLength = MAX_XFER_BUF_SIZE; | ||
2508 | request.startOffset = mFile->offset; | 2445 | request.startOffset = mFile->offset; | ||
2509 | request.id = sftp_async_read_begin(mFile, request.expectedLength); | 2446 | request.id = sftp_async_read_begin(mFile, request.expectedLength); | ||
2510 | if (request.id < 0) { | 2447 | if (request.id < 0) { | ||
Show All 14 Lines | |||||
2525 | } | 2462 | } | ||
2526 | } | 2463 | } | ||
2527 | 2464 | | |||
2528 | qCDebug(KIO_SFTP_TRACE_LOG) << "enqueueChunks done" << QString::number(pendingRequests.size()); | 2465 | qCDebug(KIO_SFTP_TRACE_LOG) << "enqueueChunks done" << QString::number(pendingRequests.size()); | ||
2529 | 2466 | | |||
2530 | return true; | 2467 | return true; | ||
2531 | } | 2468 | } | ||
2532 | 2469 | | |||
2533 | int sftpProtocol::GetRequest::readChunks(QByteArray &data) { | 2470 | int SFTPInternal::GetRequest::readChunks(QByteArray &data) | ||
2471 | { | ||||
2534 | 2472 | | |||
2535 | int totalRead = 0; | 2473 | int totalRead = 0; | ||
2536 | ssize_t bytesread = 0; | 2474 | ssize_t bytesread = 0; | ||
2537 | 2475 | | |||
2538 | while (!pendingRequests.isEmpty()) { | 2476 | while (!pendingRequests.isEmpty()) { | ||
2539 | sftpProtocol::GetRequest::Request &request = pendingRequests.head(); | 2477 | SFTPInternal::GetRequest::Request &request = pendingRequests.head(); | ||
2540 | int dataSize = data.size() + request.expectedLength; | 2478 | int dataSize = data.size() + request.expectedLength; | ||
2541 | 2479 | | |||
2542 | data.resize(dataSize); | 2480 | data.resize(dataSize); | ||
2543 | if (data.size() < dataSize) { | 2481 | if (data.size() < dataSize) { | ||
2544 | // Could not allocate enough memory - skip current chunk | 2482 | // Could not allocate enough memory - skip current chunk | ||
2545 | data.resize(dataSize - request.expectedLength); | 2483 | data.resize(dataSize - request.expectedLength); | ||
2546 | break; | 2484 | break; | ||
2547 | } | 2485 | } | ||
▲ Show 20 Lines • Show All 44 Lines • ▼ Show 20 Line(s) | |||||
2592 | } | 2530 | } | ||
2593 | 2531 | | |||
2594 | pendingRequests.dequeue(); | 2532 | pendingRequests.dequeue(); | ||
2595 | } | 2533 | } | ||
2596 | 2534 | | |||
2597 | return totalRead; | 2535 | return totalRead; | ||
2598 | } | 2536 | } | ||
2599 | 2537 | | |||
2600 | sftpProtocol::GetRequest::~GetRequest() { | 2538 | SFTPInternal::GetRequest::~GetRequest() | ||
2601 | sftpProtocol::GetRequest::Request request; | 2539 | { | ||
2540 | SFTPInternal::GetRequest::Request request; | ||||
2602 | char buf[MAX_XFER_BUF_SIZE]; | 2541 | char buf[MAX_XFER_BUF_SIZE]; | ||
2603 | 2542 | | |||
2604 | // Remove pending reads to avoid memory leaks | 2543 | // Remove pending reads to avoid memory leaks | ||
2605 | while (!pendingRequests.isEmpty()) { | 2544 | while (!pendingRequests.isEmpty()) { | ||
2606 | request = pendingRequests.dequeue(); | 2545 | request = pendingRequests.dequeue(); | ||
2607 | sftp_async_read(mFile, buf, request.expectedLength, request.id); | 2546 | sftp_async_read(mFile, buf, request.expectedLength, request.id); | ||
2608 | } | 2547 | } | ||
2609 | 2548 | | |||
2610 | // Close channel & free attributes | 2549 | // Close channel & free attributes | ||
2611 | sftp_close(mFile); | 2550 | sftp_close(mFile); | ||
2612 | sftp_attributes_free(mSb); | 2551 | sftp_attributes_free(mSb); | ||
2613 | } | 2552 | } | ||
2614 | 2553 | | |||
2615 | void sftpProtocol::requiresUserNameRedirection() | 2554 | void SFTPInternal::requiresUserNameRedirection() | ||
2616 | { | 2555 | { | ||
2617 | QUrl redirectUrl; | 2556 | QUrl redirectUrl; | ||
2618 | redirectUrl.setScheme( QLatin1String("sftp") ); | 2557 | redirectUrl.setScheme( QLatin1String("sftp") ); | ||
2619 | redirectUrl.setUserName( mUsername ); | 2558 | redirectUrl.setUserName( mUsername ); | ||
2620 | redirectUrl.setPassword( mPassword ); | 2559 | redirectUrl.setPassword( mPassword ); | ||
2621 | redirectUrl.setHost( mHost ); | 2560 | redirectUrl.setHost( mHost ); | ||
2622 | if (mPort > 0 && mPort != DEFAULT_SFTP_PORT) { | 2561 | if (mPort > 0 && mPort != DEFAULT_SFTP_PORT) { | ||
2623 | redirectUrl.setPort( mPort ); | 2562 | redirectUrl.setPort( mPort ); | ||
2624 | } | 2563 | } | ||
2625 | qCDebug(KIO_SFTP_LOG) << "redirecting to" << redirectUrl; | 2564 | qCDebug(KIO_SFTP_LOG) << "redirecting to" << redirectUrl; | ||
2626 | redirection( redirectUrl ); | 2565 | q->redirection(redirectUrl); | ||
2627 | } | 2566 | } | ||
2628 | 2567 | | |||
2629 | bool sftpProtocol::sftpLogin() | 2568 | Result SFTPInternal::sftpLogin() | ||
2630 | { | 2569 | { | ||
2631 | const QString origUsername = mUsername; | 2570 | const QString origUsername = mUsername; | ||
2632 | openConnection(); | 2571 | const auto openResult = openConnection(); | ||
2572 | if (!openResult.success) { | ||||
2573 | return openResult; | ||||
2574 | } | ||||
2633 | qCDebug(KIO_SFTP_LOG) << "connected ?" << mConnected << "username: old=" << origUsername << "new=" << mUsername; | 2575 | qCDebug(KIO_SFTP_LOG) << "connected ?" << mConnected << "username: old=" << origUsername << "new=" << mUsername; | ||
2634 | if (!origUsername.isEmpty() && origUsername != mUsername) { | 2576 | if (!origUsername.isEmpty() && origUsername != mUsername) { | ||
2635 | requiresUserNameRedirection(); | 2577 | requiresUserNameRedirection(); | ||
2636 | finished(); | 2578 | return Result::fail(); | ||
2637 | return false; | | |||
2638 | } | | |||
2639 | return mConnected; | | |||
2640 | } | | |||
2641 | | ||||
2642 | void sftpProtocol::sftpSendWarning(int errorCode, const QString& url) | | |||
2643 | { | | |||
2644 | switch (errorCode) { | | |||
2645 | case -1: | | |||
2646 | warning(i18n( "Could not change permissions for\n%1", url)); | | |||
2647 | break; | | |||
2648 | default: | | |||
2649 | break; | | |||
2650 | } | 2579 | } | ||
2580 | return mConnected ? Result::pass() : Result::fail(); | ||||
2651 | } | 2581 | } | ||
2652 | 2582 | | |||
2653 | void sftpProtocol::closeWithoutFinish() | 2583 | void SFTPInternal::clearPubKeyAuthInfo() | ||
2654 | { | | |||
2655 | sftp_close(mOpenFile); | | |||
2656 | mOpenFile = nullptr; | | |||
2657 | } | | |||
2658 | | ||||
2659 | void sftpProtocol::clearPubKeyAuthInfo() | | |||
2660 | { | 2584 | { | ||
2661 | if (mPublicKeyAuthInfo) { | 2585 | if (mPublicKeyAuthInfo) { | ||
2662 | delete mPublicKeyAuthInfo; | 2586 | delete mPublicKeyAuthInfo; | ||
2663 | mPublicKeyAuthInfo = nullptr; | 2587 | mPublicKeyAuthInfo = nullptr; | ||
2664 | } | 2588 | } | ||
2665 | } | 2589 | } | ||
2666 | 2590 | | |||
2667 | void sftpProtocol::fileSystemFreeSpace(const QUrl& url) | 2591 | Result SFTPInternal::fileSystemFreeSpace(const QUrl& url) | ||
2668 | { | 2592 | { | ||
2669 | qCDebug(KIO_SFTP_LOG) << "file system free space of" << url; | 2593 | qCDebug(KIO_SFTP_LOG) << "file system free space of" << url; | ||
2670 | 2594 | | |||
2671 | if (!sftpLogin()) { | 2595 | const auto loginResult = sftpLogin(); | ||
2672 | // sftpLogin finished() | 2596 | if (!loginResult.success) { | ||
2673 | return; | 2597 | return loginResult; | ||
2674 | } | 2598 | } | ||
2675 | 2599 | | |||
2676 | if (sftp_extension_supported(mSftp, "statvfs@openssh.com", "2") == 0) { | 2600 | if (sftp_extension_supported(mSftp, "statvfs@openssh.com", "2") == 0) { | ||
2677 | error(ERR_UNSUPPORTED_ACTION, QString()); | 2601 | return Result::fail(ERR_UNSUPPORTED_ACTION, QString()); | ||
2678 | return; | | |||
2679 | } | 2602 | } | ||
2680 | 2603 | | |||
2681 | const QByteArray path = url.path().toUtf8(); | 2604 | const QByteArray path = url.path().toUtf8(); | ||
2682 | 2605 | | |||
2683 | sftp_statvfs_t statvfs = sftp_statvfs(mSftp, path.constData()); | 2606 | sftp_statvfs_t statvfs = sftp_statvfs(mSftp, path.constData()); | ||
2684 | if (statvfs == nullptr) { | 2607 | if (statvfs == nullptr) { | ||
2685 | reportError(url, sftp_get_error(mSftp)); | 2608 | return reportError(url, sftp_get_error(mSftp)); | ||
2686 | return; | | |||
2687 | } | 2609 | } | ||
2688 | 2610 | | |||
2689 | setMetaData(QString::fromLatin1("total"), QString::number(statvfs->f_frsize * statvfs->f_blocks)); | 2611 | q->setMetaData(QString::fromLatin1("total"), QString::number(statvfs->f_frsize * statvfs->f_blocks)); | ||
2690 | setMetaData(QString::fromLatin1("available"), QString::number(statvfs->f_frsize * statvfs->f_bavail)); | 2612 | q->setMetaData(QString::fromLatin1("available"), QString::number(statvfs->f_frsize * statvfs->f_bavail)); | ||
2691 | 2613 | | |||
2692 | sftp_statvfs_free(statvfs); | 2614 | sftp_statvfs_free(statvfs); | ||
2693 | 2615 | | |||
2616 | return Result::pass(); | ||||
2617 | } | ||||
2618 | | ||||
2619 | SFTPSlave::SFTPSlave(const QByteArray &pool_socket, const QByteArray &app_socket) | ||||
2620 | : SlaveBase("kio_sftp", pool_socket, app_socket) | ||||
2621 | , d(new SFTPInternal(this)) | ||||
2622 | { | ||||
2623 | const auto result = d->init(); | ||||
2624 | if (!result.success) { | ||||
2625 | error(result.error, result.errorString); | ||||
2626 | } | ||||
2627 | } | ||||
2628 | | ||||
2629 | void SFTPSlave::setHost(const QString &host, quint16 port, const QString &user, const QString &pass) | ||||
2630 | { | ||||
2631 | d->setHost(host, port, user, pass); | ||||
2632 | } | ||||
2633 | | ||||
2634 | void SFTPSlave::get(const QUrl &url) | ||||
2635 | { | ||||
2636 | finalize(d->get(url)); | ||||
2637 | } | ||||
2638 | | ||||
2639 | void SFTPSlave::listDir(const QUrl &url) | ||||
2640 | { | ||||
2641 | finalize(d->listDir(url)); | ||||
2642 | } | ||||
2643 | | ||||
2644 | void SFTPSlave::mimetype(const QUrl &url) | ||||
2645 | { | ||||
2646 | finalize(d->mimetype(url)); | ||||
2647 | } | ||||
2648 | | ||||
2649 | void SFTPSlave::stat(const QUrl &url) | ||||
2650 | { | ||||
2651 | finalize(d->stat(url)); | ||||
2652 | } | ||||
2653 | | ||||
2654 | void SFTPSlave::copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) | ||||
2655 | { | ||||
2656 | finalize(d->copy(src, dest, permissions, flags)); | ||||
2657 | } | ||||
2658 | | ||||
2659 | void SFTPSlave::put(const QUrl &url, int permissions, JobFlags flags) | ||||
2660 | { | ||||
2661 | finalize(d->put(url, permissions, flags)); | ||||
2662 | } | ||||
2663 | | ||||
2664 | void SFTPSlave::slave_status() | ||||
2665 | { | ||||
2666 | d->slave_status(); | ||||
2667 | } | ||||
2668 | | ||||
2669 | void SFTPSlave::del(const QUrl &url, bool isfile) | ||||
2670 | { | ||||
2671 | finalize(d->del(url, isfile)); | ||||
2672 | } | ||||
2673 | | ||||
2674 | void SFTPSlave::chmod(const QUrl &url, int permissions) | ||||
2675 | { | ||||
2676 | finalize(d->chmod(url, permissions)); | ||||
2677 | } | ||||
2678 | | ||||
2679 | void SFTPSlave::symlink(const QString &target, const QUrl &dest, JobFlags flags) | ||||
2680 | { | ||||
2681 | finalize(d->symlink(target, dest, flags)); | ||||
2682 | } | ||||
2683 | | ||||
2684 | void SFTPSlave::rename(const QUrl &src, const QUrl &dest, JobFlags flags) | ||||
2685 | { | ||||
2686 | finalize(d->rename(src, dest, flags)); | ||||
2687 | } | ||||
2688 | | ||||
2689 | void SFTPSlave::mkdir(const QUrl &url, int permissions) | ||||
2690 | { | ||||
2691 | finalize(d->mkdir(url, permissions)); | ||||
2692 | } | ||||
2693 | | ||||
2694 | void SFTPSlave::openConnection() | ||||
2695 | { | ||||
2696 | const auto result = d->openConnection(); | ||||
2697 | if (!result.success) { | ||||
2698 | error(result.error, result.errorString); | ||||
2699 | return; | ||||
2700 | } | ||||
2701 | opened(); | ||||
2702 | } | ||||
2703 | | ||||
2704 | void SFTPSlave::closeConnection() | ||||
2705 | { | ||||
2706 | d->closeConnection(); | ||||
2707 | } | ||||
2708 | | ||||
2709 | void SFTPSlave::open(const QUrl &url, QIODevice::OpenMode mode) | ||||
2710 | { | ||||
2711 | const auto result = d->open(url, mode); | ||||
2712 | if (!result.success) { | ||||
2713 | error(result.error, result.errorString); | ||||
2714 | return; | ||||
2715 | } | ||||
2716 | opened(); | ||||
2717 | } | ||||
2718 | | ||||
2719 | void SFTPSlave::read(filesize_t size) | ||||
2720 | { | ||||
2721 | maybeError(d->read(size)); | ||||
2722 | } | ||||
2723 | | ||||
2724 | void SFTPSlave::write(const QByteArray &data) | ||||
2725 | { | ||||
2726 | maybeError(d->write(data)); | ||||
2727 | } | ||||
2728 | | ||||
2729 | void SFTPSlave::seek(filesize_t offset) | ||||
2730 | { | ||||
2731 | maybeError(d->seek(offset)); | ||||
2732 | } | ||||
2733 | | ||||
2734 | void SFTPSlave::close() | ||||
2735 | { | ||||
2736 | d->close(); // cannot error | ||||
2694 | finished(); | 2737 | finished(); | ||
2695 | } | 2738 | } | ||
2739 | | ||||
2740 | void SFTPSlave::special(const QByteArray &data) | ||||
2741 | { | ||||
2742 | maybeError(d->special(data)); | ||||
2743 | } |
Technically unrelated to your changes, but does this even work? isn't the check supposed to be against errno in this case, not offset?