Changeset View
Standalone View
src/ioslaves/ftp/ftp.cpp
1 | /* This file is part of the KDE libraries | 1 | /* This file is part of the KDE libraries | ||
---|---|---|---|---|---|
2 | Copyright (C) 2000-2006 David Faure <faure@kde.org> | 2 | Copyright (C) 2000-2006 David Faure <faure@kde.org> | ||
3 | Copyright (C) 2019 Harald Sitter <sitter@kde.org> | ||||
3 | 4 | | |||
4 | This library is free software; you can redistribute it and/or | 5 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Library General Public | 6 | modify it under the terms of the GNU Library General Public | ||
6 | License as published by the Free Software Foundation; either | 7 | License as published by the Free Software Foundation; either | ||
7 | version 2 of the License, or (at your option) any later version. | 8 | version 2 of the License, or (at your option) any later version. | ||
8 | 9 | | |||
9 | This library is distributed in the hope that it will be useful, | 10 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
▲ Show 20 Lines • Show All 99 Lines • ▼ Show 20 Line(s) | 99 | { | |||
110 | return defaultMode; | 111 | return defaultMode; | ||
111 | } | 112 | } | ||
112 | 113 | | |||
113 | static bool supportedProxyScheme(const QString &scheme) | 114 | static bool supportedProxyScheme(const QString &scheme) | ||
114 | { | 115 | { | ||
115 | return (scheme == QLatin1String("ftp") || scheme == QLatin1String("socks")); | 116 | return (scheme == QLatin1String("ftp") || scheme == QLatin1String("socks")); | ||
116 | } | 117 | } | ||
117 | 118 | | |||
118 | static bool isSocksProxy() | | |||
119 | { | | |||
120 | return (QNetworkProxy::applicationProxy().type() == QNetworkProxy::Socks5Proxy); | | |||
121 | } | | |||
122 | | ||||
123 | // JPF: somebody should find a better solution for this or move this to KIO | 119 | // JPF: somebody should find a better solution for this or move this to KIO | ||
124 | namespace KIO | 120 | namespace KIO | ||
125 | { | 121 | { | ||
126 | enum buffersizes { | 122 | enum buffersizes { | ||
127 | /** | 123 | /** | ||
128 | * largest buffer size that should be used to transfer data between | 124 | * largest buffer size that should be used to transfer data between | ||
129 | * KIO slaves using the data() function | 125 | * KIO slaves using the data() function | ||
130 | */ | 126 | */ | ||
Show All 34 Lines | 150 | while (len > 0) { | |||
165 | case ENOSPC: return ERR_DISK_FULL; | 161 | case ENOSPC: return ERR_DISK_FULL; | ||
166 | default: return ERR_CANNOT_WRITE; | 162 | default: return ERR_CANNOT_WRITE; | ||
167 | } | 163 | } | ||
168 | } | 164 | } | ||
169 | return 0; | 165 | return 0; | ||
170 | } | 166 | } | ||
171 | } | 167 | } | ||
172 | 168 | | |||
173 | const KIO::filesize_t Ftp::UnknownSize = (KIO::filesize_t) - 1; | 169 | const KIO::filesize_t FtpInternal::UnknownSize = (KIO::filesize_t) - 1; | ||
174 | 170 | | |||
175 | using namespace KIO; | 171 | using namespace KIO; | ||
176 | 172 | | |||
177 | extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) | 173 | extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) | ||
178 | { | 174 | { | ||
179 | QCoreApplication app(argc, argv); | 175 | QCoreApplication app(argc, argv); | ||
180 | app.setApplicationName(QStringLiteral("kio_ftp")); | 176 | app.setApplicationName(QStringLiteral("kio_ftp")); | ||
181 | 177 | | |||
182 | qCDebug(KIO_FTP) << "Starting"; | 178 | qCDebug(KIO_FTP) << "Starting"; | ||
183 | 179 | | |||
184 | if (argc != 4) { | 180 | if (argc != 4) { | ||
185 | fprintf(stderr, "Usage: kio_ftp protocol domain-socket1 domain-socket2\n"); | 181 | fprintf(stderr, "Usage: kio_ftp protocol domain-socket1 domain-socket2\n"); | ||
186 | exit(-1); | 182 | exit(-1); | ||
187 | } | 183 | } | ||
188 | 184 | | |||
189 | Ftp slave(argv[2], argv[3]); | 185 | Ftp slave(argv[2], argv[3]); | ||
190 | slave.dispatchLoop(); | 186 | slave.dispatchLoop(); | ||
191 | 187 | | |||
192 | qCDebug(KIO_FTP) << "Done"; | 188 | qCDebug(KIO_FTP) << "Done"; | ||
193 | return 0; | 189 | return 0; | ||
194 | } | 190 | } | ||
195 | 191 | | |||
196 | //=============================================================================== | 192 | //=============================================================================== | ||
197 | // Ftp | 193 | // FtpInternal | ||
198 | //=============================================================================== | 194 | //=============================================================================== | ||
199 | 195 | | |||
200 | Ftp::Ftp(const QByteArray &pool, const QByteArray &app) | | |||
201 | : SlaveBase(QByteArrayLiteral("ftp"), pool, app) | | |||
202 | { | | |||
203 | // init the socket data | | |||
204 | m_data = m_control = nullptr; | | |||
205 | m_server = nullptr; | | |||
206 | ftpCloseControlConnection(); | | |||
207 | | ||||
208 | // init other members | | |||
209 | m_port = 0; | | |||
210 | m_socketProxyAuth = nullptr; | | |||
211 | } | | |||
212 | | ||||
213 | Ftp::~Ftp() | | |||
214 | { | | |||
215 | qCDebug(KIO_FTP); | | |||
216 | closeConnection(); | | |||
217 | } | | |||
218 | | ||||
219 | /** | 196 | /** | ||
220 | * This closes a data connection opened by ftpOpenDataConnection(). | 197 | * This closes a data connection opened by ftpOpenDataConnection(). | ||
221 | */ | 198 | */ | ||
222 | void Ftp::ftpCloseDataConnection() | 199 | void FtpInternal::ftpCloseDataConnection() | ||
223 | { | 200 | { | ||
224 | delete m_data; | 201 | delete m_data; | ||
225 | m_data = nullptr; | 202 | m_data = nullptr; | ||
226 | delete m_server; | 203 | delete m_server; | ||
227 | m_server = nullptr; | 204 | m_server = nullptr; | ||
228 | } | 205 | } | ||
229 | 206 | | |||
230 | /** | 207 | /** | ||
231 | * This closes a control connection opened by ftpOpenControlConnection() and reinits the | 208 | * This closes a control connection opened by ftpOpenControlConnection() and reinits the | ||
232 | * related states. This method gets called from the constructor with m_control = nullptr. | 209 | * related states. This method gets called from the constructor with m_control = nullptr. | ||
233 | */ | 210 | */ | ||
234 | void Ftp::ftpCloseControlConnection() | 211 | void FtpInternal::ftpCloseControlConnection() | ||
235 | { | 212 | { | ||
236 | m_extControl = 0; | 213 | m_extControl = 0; | ||
237 | delete m_control; | 214 | delete m_control; | ||
238 | m_control = nullptr; | 215 | m_control = nullptr; | ||
239 | m_cDataMode = 0; | 216 | m_cDataMode = 0; | ||
240 | m_bLoggedOn = false; // logon needs control connection | 217 | m_bLoggedOn = false; // logon needs control connection | ||
241 | m_bTextMode = false; | 218 | m_bTextMode = false; | ||
242 | m_bBusy = false; | 219 | m_bBusy = false; | ||
243 | } | 220 | } | ||
244 | 221 | | |||
245 | /** | 222 | /** | ||
246 | * Returns the last response from the server (iOffset >= 0) -or- reads a new response | 223 | * Returns the last response from the server (iOffset >= 0) -or- reads a new response | ||
247 | * (iOffset < 0). The result is returned (with iOffset chars skipped for iOffset > 0). | 224 | * (iOffset < 0). The result is returned (with iOffset chars skipped for iOffset > 0). | ||
248 | */ | 225 | */ | ||
249 | const char *Ftp::ftpResponse(int iOffset) | 226 | const char *FtpInternal::ftpResponse(int iOffset) | ||
250 | { | 227 | { | ||
251 | Q_ASSERT(m_control); // must have control connection socket | 228 | Q_ASSERT(m_control); // must have control connection socket | ||
252 | const char *pTxt = m_lastControlLine.data(); | 229 | const char *pTxt = m_lastControlLine.data(); | ||
253 | 230 | | |||
254 | // read the next line ... | 231 | // read the next line ... | ||
255 | if (iOffset < 0) { | 232 | if (iOffset < 0) { | ||
256 | int iMore = 0; | 233 | int iMore = 0; | ||
257 | m_iRespCode = 0; | 234 | m_iRespCode = 0; | ||
258 | 235 | | |||
259 | if (!pTxt) { | 236 | if (!pTxt) { | ||
260 | return nullptr; // avoid using a nullptr when calling atoi. | 237 | return nullptr; // avoid using a nullptr when calling atoi. | ||
261 | } | 238 | } | ||
262 | 239 | | |||
263 | // If the server sends a multiline response starting with | 240 | // If the server sends a multiline response starting with | ||
264 | // "nnn-text" we loop here until a final "nnn text" line is | 241 | // "nnn-text" we loop here until a final "nnn text" line is | ||
265 | // reached. Only data from the final line will be stored. | 242 | // reached. Only data from the final line will be stored. | ||
266 | do { | 243 | do { | ||
267 | while (!m_control->canReadLine() && m_control->waitForReadyRead((readTimeout() * 1000))) {} | 244 | while (!m_control->canReadLine() && m_control->waitForReadyRead((q->readTimeout() * 1000))) {} | ||
268 | m_lastControlLine = m_control->readLine(); | 245 | m_lastControlLine = m_control->readLine(); | ||
269 | pTxt = m_lastControlLine.data(); | 246 | pTxt = m_lastControlLine.data(); | ||
270 | int iCode = atoi(pTxt); | 247 | int iCode = atoi(pTxt); | ||
271 | if (iMore == 0) { | 248 | if (iMore == 0) { | ||
272 | // first line | 249 | // first line | ||
273 | qCDebug(KIO_FTP) << " > " << pTxt; | 250 | qCDebug(KIO_FTP) << " > " << pTxt; | ||
274 | if (iCode >= 100) { | 251 | if (iCode >= 100) { | ||
275 | m_iRespCode = iCode; | 252 | m_iRespCode = iCode; | ||
Show All 19 Lines | |||||
295 | 272 | | |||
296 | // return text with offset ... | 273 | // return text with offset ... | ||
297 | while (iOffset-- > 0 && pTxt[0]) { | 274 | while (iOffset-- > 0 && pTxt[0]) { | ||
298 | pTxt++; | 275 | pTxt++; | ||
299 | } | 276 | } | ||
300 | return pTxt; | 277 | return pTxt; | ||
301 | } | 278 | } | ||
302 | 279 | | |||
303 | void Ftp::closeConnection() | 280 | void FtpInternal::closeConnection() | ||
304 | { | 281 | { | ||
305 | if (m_control || m_data) | 282 | if (m_control || m_data) | ||
306 | qCDebug(KIO_FTP) << "m_bLoggedOn=" << m_bLoggedOn << " m_bBusy=" << m_bBusy; | 283 | qCDebug(KIO_FTP) << "m_bLoggedOn=" << m_bLoggedOn << " m_bBusy=" << m_bBusy; | ||
307 | 284 | | |||
308 | if (m_bBusy) { // ftpCloseCommand not called | 285 | if (m_bBusy) { // ftpCloseCommand not called | ||
309 | qCWarning(KIO_FTP) << "Abandoned data stream"; | 286 | qCWarning(KIO_FTP) << "Abandoned data stream"; | ||
310 | ftpCloseDataConnection(); | 287 | ftpCloseDataConnection(); | ||
311 | } | 288 | } | ||
312 | 289 | | |||
313 | if (m_bLoggedOn) { // send quit | 290 | if (m_bLoggedOn) { // send quit | ||
314 | if (!ftpSendCmd(QByteArrayLiteral("quit"), 0) || (m_iRespType != 2)) { | 291 | if (!ftpSendCmd(QByteArrayLiteral("quit"), 0) || (m_iRespType != 2)) { | ||
315 | qCWarning(KIO_FTP) << "QUIT returned error: " << m_iRespCode; | 292 | qCWarning(KIO_FTP) << "QUIT returned error: " << m_iRespCode; | ||
316 | } | 293 | } | ||
317 | } | 294 | } | ||
318 | 295 | | |||
319 | // close the data and control connections ... | 296 | // close the data and control connections ... | ||
320 | ftpCloseDataConnection(); | 297 | ftpCloseDataConnection(); | ||
321 | ftpCloseControlConnection(); | 298 | ftpCloseControlConnection(); | ||
322 | } | 299 | } | ||
323 | 300 | | |||
324 | void Ftp::setHost(const QString &_host, quint16 _port, const QString &_user, | 301 | FtpInternal::FtpInternal(Ftp *qptr) | ||
302 | : QObject() | ||||
303 | , q(qptr) | ||||
304 | { | ||||
305 | ftpCloseControlConnection(); | ||||
306 | } | ||||
307 | | ||||
308 | FtpInternal::~FtpInternal() | ||||
309 | { | ||||
310 | qCDebug(KIO_FTP); | ||||
311 | closeConnection(); | ||||
312 | } | ||||
313 | | ||||
314 | void FtpInternal::setHost(const QString &_host, quint16 _port, const QString &_user, | ||||
325 | const QString &_pass) | 315 | const QString &_pass) | ||
326 | { | 316 | { | ||
327 | qCDebug(KIO_FTP) << _host << "port=" << _port << "user=" << _user; | 317 | qCDebug(KIO_FTP) << _host << "port=" << _port << "user=" << _user; | ||
328 | 318 | | |||
329 | m_proxyURL.clear(); | 319 | m_proxyURL.clear(); | ||
330 | m_proxyUrls = config()->readEntry("ProxyUrls", QStringList()); | 320 | m_proxyUrls = q->config()->readEntry("ProxyUrls", QStringList()); | ||
331 | qCDebug(KIO_FTP) << "proxy urls:" << m_proxyUrls; | 321 | qCDebug(KIO_FTP) << "proxy urls:" << m_proxyUrls; | ||
332 | 322 | | |||
333 | if (m_host != _host || m_port != _port || | 323 | if (m_host != _host || m_port != _port || | ||
334 | m_user != _user || m_pass != _pass) { | 324 | m_user != _user || m_pass != _pass) { | ||
335 | closeConnection(); | 325 | closeConnection(); | ||
336 | } | 326 | } | ||
337 | 327 | | |||
338 | m_host = _host; | 328 | m_host = _host; | ||
339 | m_port = _port; | 329 | m_port = _port; | ||
340 | m_user = _user; | 330 | m_user = _user; | ||
341 | m_pass = _pass; | 331 | m_pass = _pass; | ||
342 | } | 332 | } | ||
343 | 333 | | |||
344 | void Ftp::openConnection() | 334 | Result FtpInternal::openConnection() | ||
345 | { | 335 | { | ||
346 | ftpOpenConnection(loginExplicit); | 336 | return ftpOpenConnection(LoginMode::Explicit); | ||
347 | } | 337 | } | ||
348 | 338 | | |||
349 | bool Ftp::ftpOpenConnection(LoginMode loginMode) | 339 | Result FtpInternal::ftpOpenConnection(LoginMode loginMode) | ||
350 | { | 340 | { | ||
351 | // check for implicit login if we are already logged on ... | 341 | // check for implicit login if we are already logged on ... | ||
352 | if (loginMode == loginImplicit && m_bLoggedOn) { | 342 | if (loginMode == LoginMode::Implicit && m_bLoggedOn) { | ||
353 | Q_ASSERT(m_control); // must have control connection socket | 343 | Q_ASSERT(m_control); // must have control connection socket | ||
354 | return true; | 344 | return Result::pass(); | ||
355 | } | 345 | } | ||
356 | 346 | | |||
357 | qCDebug(KIO_FTP) << "host=" << m_host << ", port=" << m_port << ", user=" << m_user << "password= [password hidden]"; | 347 | qCDebug(KIO_FTP) << "host=" << m_host << ", port=" << m_port << ", user=" << m_user << "password= [password hidden]"; | ||
358 | 348 | | |||
359 | infoMessage(i18n("Opening connection to host %1", m_host)); | 349 | q->infoMessage(i18n("Opening connection to host %1", m_host)); | ||
360 | 350 | | |||
361 | if (m_host.isEmpty()) { | 351 | if (m_host.isEmpty()) { | ||
362 | error(ERR_UNKNOWN_HOST, QString()); | 352 | return Result::fail(ERR_UNKNOWN_HOST); | ||
363 | return false; | | |||
364 | } | 353 | } | ||
365 | 354 | | |||
366 | Q_ASSERT(!m_bLoggedOn); | 355 | Q_ASSERT(!m_bLoggedOn); | ||
367 | 356 | | |||
368 | m_initialPath.clear(); | 357 | m_initialPath.clear(); | ||
369 | m_currentPath.clear(); | 358 | m_currentPath.clear(); | ||
370 | 359 | | |||
371 | if (!ftpOpenControlConnection()) { | 360 | const Result result = ftpOpenControlConnection(); | ||
372 | return false; // error emitted by ftpOpenControlConnection | 361 | if (!result.success) { | ||
362 | return result; | ||||
373 | } | 363 | } | ||
374 | infoMessage(i18n("Connected to host %1", m_host)); | 364 | q->infoMessage(i18n("Connected to host %1", m_host)); | ||
375 | 365 | | |||
376 | bool userNameChanged = false; | 366 | bool userNameChanged = false; | ||
377 | if (loginMode != loginDefered) { | 367 | if (loginMode != LoginMode::Defered) { | ||
378 | m_bLoggedOn = ftpLogin(&userNameChanged); | 368 | const Result result = ftpLogin(&userNameChanged); | ||
369 | m_bLoggedOn = result.success; | ||||
379 | if (!m_bLoggedOn) { | 370 | if (!m_bLoggedOn) { | ||
380 | return false; // error emitted by ftpLogin | 371 | return result; | ||
381 | } | 372 | } | ||
382 | } | 373 | } | ||
383 | 374 | | |||
384 | m_bTextMode = config()->readEntry("textmode", false); | 375 | m_bTextMode = q->config()->readEntry("textmode", false); | ||
385 | connected(); | 376 | q->connected(); | ||
386 | 377 | | |||
387 | // Redirected due to credential change... | 378 | // Redirected due to credential change... | ||
388 | if (userNameChanged && m_bLoggedOn) { | 379 | if (userNameChanged && m_bLoggedOn) { | ||
389 | QUrl realURL; | 380 | QUrl realURL; | ||
390 | realURL.setScheme(QStringLiteral("ftp")); | 381 | realURL.setScheme(QStringLiteral("ftp")); | ||
391 | if (m_user != QLatin1String(FTP_LOGIN)) { | 382 | if (m_user != QLatin1String(FTP_LOGIN)) { | ||
392 | realURL.setUserName(m_user); | 383 | realURL.setUserName(m_user); | ||
393 | } | 384 | } | ||
394 | if (m_pass != QLatin1String(FTP_PASSWD)) { | 385 | if (m_pass != QLatin1String(FTP_PASSWD)) { | ||
395 | realURL.setPassword(m_pass); | 386 | realURL.setPassword(m_pass); | ||
396 | } | 387 | } | ||
397 | realURL.setHost(m_host); | 388 | realURL.setHost(m_host); | ||
398 | if (m_port > 0 && m_port != DEFAULT_FTP_PORT) { | 389 | if (m_port > 0 && m_port != DEFAULT_FTP_PORT) { | ||
399 | realURL.setPort(m_port); | 390 | realURL.setPort(m_port); | ||
400 | } | 391 | } | ||
401 | if (m_initialPath.isEmpty()) { | 392 | if (m_initialPath.isEmpty()) { | ||
402 | m_initialPath = QStringLiteral("/"); | 393 | m_initialPath = QStringLiteral("/"); | ||
403 | } | 394 | } | ||
404 | realURL.setPath(m_initialPath); | 395 | realURL.setPath(m_initialPath); | ||
405 | qCDebug(KIO_FTP) << "User name changed! Redirecting to" << realURL; | 396 | qCDebug(KIO_FTP) << "User name changed! Redirecting to" << realURL; | ||
406 | redirection(realURL); | 397 | q->redirection(realURL); | ||
407 | finished(); | 398 | return Result::fail(); | ||
408 | return false; | | |||
409 | } | 399 | } | ||
410 | 400 | | |||
411 | return true; | 401 | return Result::pass(); | ||
412 | } | 402 | } | ||
413 | 403 | | |||
414 | /** | 404 | /** | ||
415 | * Called by @ref openConnection. It opens the control connection to the ftp server. | 405 | * Called by @ref openConnection. It opens the control connection to the ftp server. | ||
416 | * | 406 | * | ||
417 | * @return true on success. | 407 | * @return true on success. | ||
418 | */ | 408 | */ | ||
419 | bool Ftp::ftpOpenControlConnection() | 409 | Result FtpInternal::ftpOpenControlConnection() | ||
420 | { | 410 | { | ||
421 | if (m_proxyUrls.isEmpty()) { | 411 | if (m_proxyUrls.isEmpty()) { | ||
422 | return ftpOpenControlConnection(m_host, m_port); | 412 | return ftpOpenControlConnection(m_host, m_port); | ||
423 | } | 413 | } | ||
424 | 414 | | |||
425 | int errorCode = 0; | 415 | Result result = Result::fail(); | ||
426 | QString errorMessage; | | |||
427 | 416 | | |||
428 | for (const QString &proxyUrl : qAsConst(m_proxyUrls)) { | 417 | for (const QString &proxyUrl : qAsConst(m_proxyUrls)) { | ||
429 | const QUrl url(proxyUrl); | 418 | const QUrl url(proxyUrl); | ||
430 | const QString scheme(url.scheme()); | 419 | const QString scheme(url.scheme()); | ||
431 | 420 | | |||
432 | if (!supportedProxyScheme(scheme)) { | 421 | if (!supportedProxyScheme(scheme)) { | ||
433 | // TODO: Need a new error code to indicate unsupported URL scheme. | 422 | // TODO: Need a new error code to indicate unsupported URL scheme. | ||
434 | errorCode = ERR_CANNOT_CONNECT; | 423 | result = Result::fail(ERR_CANNOT_CONNECT, url.toString()); | ||
435 | errorMessage = url.toString(); | | |||
436 | continue; | 424 | continue; | ||
437 | } | 425 | } | ||
438 | 426 | | |||
439 | if (scheme == QLatin1String("socks")) { | 427 | if (!isSocksProxyScheme(scheme)) { | ||
440 | qCDebug(KIO_FTP) << "Connecting to SOCKS proxy @" << url; | 428 | const Result result = ftpOpenControlConnection(url.host(), url.port()); | ||
441 | const int proxyPort = url.port(); | 429 | if (result.success) { | ||
442 | QNetworkProxy proxy(QNetworkProxy::Socks5Proxy, url.host(), (proxyPort == -1 ? 0 : proxyPort)); | 430 | return Result::pass(); | ||
443 | QNetworkProxy::setApplicationProxy(proxy); | | |||
444 | if (ftpOpenControlConnection(m_host, m_port)) { | | |||
445 | return true; | | |||
446 | } | | |||
447 | QNetworkProxy::setApplicationProxy(QNetworkProxy::NoProxy); | | |||
448 | } else { | | |||
449 | if (ftpOpenControlConnection(url.host(), url.port())) { | | |||
450 | m_proxyURL = url; | | |||
451 | return true; | | |||
452 | } | | |||
453 | } | 431 | } | ||
432 | continue; | ||||
454 | } | 433 | } | ||
455 | 434 | | |||
456 | if (errorCode) { | 435 | qCDebug(KIO_FTP) << "Connecting to SOCKS proxy @" << url; | ||
457 | error(errorCode, errorMessage); | 436 | m_proxyURL = url; | ||
437 | result = ftpOpenControlConnection(m_host, m_port); | ||||
438 | if (result.success) { | ||||
439 | return result; | ||||
440 | } | ||||
441 | m_proxyURL.clear(); | ||||
458 | } | 442 | } | ||
459 | 443 | | |||
460 | return false; | 444 | return result; | ||
461 | } | 445 | } | ||
462 | 446 | | |||
463 | bool Ftp::ftpOpenControlConnection(const QString &host, int port) | 447 | Result FtpInternal::ftpOpenControlConnection(const QString &host, int port) | ||
464 | { | 448 | { | ||
465 | // implicitly close, then try to open a new connection ... | 449 | // implicitly close, then try to open a new connection ... | ||
466 | closeConnection(); | 450 | closeConnection(); | ||
467 | QString sErrorMsg; | 451 | QString sErrorMsg; | ||
468 | 452 | | |||
469 | // now connect to the server and read the login message ... | 453 | // now connect to the server and read the login message ... | ||
470 | if (port == 0) { | 454 | if (port == 0) { | ||
471 | port = 21; // default FTP port | 455 | port = 21; // default FTP port | ||
472 | } | 456 | } | ||
473 | m_control = synchronousConnectToHost(host, port); | 457 | const auto connectionResult = synchronousConnectToHost(host, port); | ||
474 | connect(m_control, &QAbstractSocket::proxyAuthenticationRequired, | 458 | m_control = connectionResult.socket; | ||
475 | this, &Ftp::proxyAuthentication); | 459 | | ||
476 | int iErrorCode = m_control->state() == QAbstractSocket::ConnectedState ? 0 : ERR_CANNOT_CONNECT; | 460 | int iErrorCode = m_control->state() == QAbstractSocket::ConnectedState ? 0 : ERR_CANNOT_CONNECT; | ||
461 | if (!connectionResult.result.success) { | ||||
462 | qDebug() << "overriding error code!!1" << connectionResult.result.error; | ||||
463 | iErrorCode = connectionResult.result.error; | ||||
464 | sErrorMsg = connectionResult.result.errorString; | ||||
465 | } | ||||
477 | 466 | | |||
478 | // on connect success try to read the server message... | 467 | // on connect success try to read the server message... | ||
479 | if (iErrorCode == 0) { | 468 | if (iErrorCode == 0) { | ||
480 | const char *psz = ftpResponse(-1); | 469 | const char *psz = ftpResponse(-1); | ||
481 | if (m_iRespType != 2) { | 470 | if (m_iRespType != 2) { | ||
482 | // login not successful, do we have an message text? | 471 | // login not successful, do we have an message text? | ||
483 | if (psz[0]) { | 472 | if (psz[0]) { | ||
484 | sErrorMsg = i18n("%1 (Error %2)", host, remoteEncoding()->decode(psz).trimmed()); | 473 | sErrorMsg = i18n("%1 (Error %2)", host, q->remoteEncoding()->decode(psz).trimmed()); | ||
485 | } | 474 | } | ||
486 | iErrorCode = ERR_CANNOT_CONNECT; | 475 | iErrorCode = ERR_CANNOT_CONNECT; | ||
487 | } | 476 | } | ||
488 | } else { | 477 | } else { | ||
489 | if (m_control->error() == QAbstractSocket::HostNotFoundError) { | 478 | if (m_control->error() == QAbstractSocket::HostNotFoundError) { | ||
490 | iErrorCode = ERR_UNKNOWN_HOST; | 479 | iErrorCode = ERR_UNKNOWN_HOST; | ||
491 | } | 480 | } | ||
492 | 481 | | |||
493 | sErrorMsg = QStringLiteral("%1: %2").arg(host, m_control->errorString()); | 482 | sErrorMsg = QStringLiteral("%1: %2").arg(host, m_control->errorString()); | ||
494 | } | 483 | } | ||
495 | 484 | | |||
496 | // if there was a problem - report it ... | 485 | // if there was a problem - report it ... | ||
497 | if (iErrorCode == 0) { // OK, return success | 486 | if (iErrorCode == 0) { // OK, return success | ||
498 | return true; | 487 | return Result::pass(); | ||
499 | } | 488 | } | ||
500 | closeConnection(); // clean-up on error | 489 | closeConnection(); // clean-up on error | ||
501 | error(iErrorCode, sErrorMsg); | 490 | return Result::fail(iErrorCode, sErrorMsg); | ||
502 | return false; | | |||
503 | } | 491 | } | ||
504 | 492 | | |||
505 | /** | 493 | /** | ||
506 | * Called by @ref openConnection. It logs us in. | 494 | * Called by @ref openConnection. It logs us in. | ||
507 | * @ref m_initialPath is set to the current working directory | 495 | * @ref m_initialPath is set to the current working directory | ||
508 | * if logging on was successful. | 496 | * if logging on was successful. | ||
509 | * | 497 | * | ||
510 | * @return true on success. | 498 | * @return true on success. | ||
511 | */ | 499 | */ | ||
512 | bool Ftp::ftpLogin(bool *userChanged) | 500 | Result FtpInternal::ftpLogin(bool *userChanged) | ||
513 | { | 501 | { | ||
514 | infoMessage(i18n("Sending login information")); | 502 | q->infoMessage(i18n("Sending login information")); | ||
515 | 503 | | |||
516 | Q_ASSERT(!m_bLoggedOn); | 504 | Q_ASSERT(!m_bLoggedOn); | ||
517 | 505 | | |||
518 | QString user(m_user); | 506 | QString user(m_user); | ||
519 | QString pass(m_pass); | 507 | QString pass(m_pass); | ||
520 | 508 | | |||
521 | if (config()->readEntry("EnableAutoLogin", false)) { | 509 | if (q->config()->readEntry("EnableAutoLogin", false)) { | ||
522 | QString au = config()->readEntry("autoLoginUser"); | 510 | QString au = q->config()->readEntry("autoLoginUser"); | ||
523 | if (!au.isEmpty()) { | 511 | if (!au.isEmpty()) { | ||
524 | user = au; | 512 | user = au; | ||
525 | pass = config()->readEntry("autoLoginPass"); | 513 | pass = q->config()->readEntry("autoLoginPass"); | ||
526 | } | 514 | } | ||
527 | } | 515 | } | ||
528 | 516 | | |||
529 | AuthInfo info; | 517 | AuthInfo info; | ||
530 | info.url.setScheme(QStringLiteral("ftp")); | 518 | info.url.setScheme(QStringLiteral("ftp")); | ||
531 | info.url.setHost(m_host); | 519 | info.url.setHost(m_host); | ||
532 | if (m_port > 0 && m_port != DEFAULT_FTP_PORT) { | 520 | if (m_port > 0 && m_port != DEFAULT_FTP_PORT) { | ||
533 | info.url.setPort(m_port); | 521 | info.url.setPort(m_port); | ||
534 | } | 522 | } | ||
535 | if (!user.isEmpty()) { | 523 | if (!user.isEmpty()) { | ||
536 | info.url.setUserName(user); | 524 | info.url.setUserName(user); | ||
537 | } | 525 | } | ||
538 | 526 | | |||
539 | // Check for cached authentication first and fallback to | 527 | // Check for cached authentication first and fallback to | ||
540 | // anonymous login when no stored credentials are found. | 528 | // anonymous login when no stored credentials are found. | ||
541 | if (!config()->readEntry("TryAnonymousLoginFirst", false) && | 529 | if (!q->config()->readEntry("TryAnonymousLoginFirst", false) && | ||
542 | pass.isEmpty() && checkCachedAuthentication(info)) { | 530 | pass.isEmpty() && q->checkCachedAuthentication(info)) { | ||
543 | user = info.username; | 531 | user = info.username; | ||
544 | pass = info.password; | 532 | pass = info.password; | ||
545 | } | 533 | } | ||
546 | 534 | | |||
547 | // Try anonymous login if both username/password | 535 | // Try anonymous login if both username/password | ||
548 | // information is blank. | 536 | // information is blank. | ||
549 | if (user.isEmpty() && pass.isEmpty()) { | 537 | if (user.isEmpty() && pass.isEmpty()) { | ||
550 | user = QStringLiteral(FTP_LOGIN); | 538 | user = QStringLiteral(FTP_LOGIN); | ||
Show All 29 Lines | 554 | if (failedAuth > 0 || (!user.isEmpty() && pass.isEmpty())) { | |||
580 | 568 | | |||
581 | info.prompt = i18n("You need to supply a username and a password " | 569 | info.prompt = i18n("You need to supply a username and a password " | ||
582 | "to access this site."); | 570 | "to access this site."); | ||
583 | info.commentLabel = i18n("Site:"); | 571 | info.commentLabel = i18n("Site:"); | ||
584 | info.comment = i18n("<b>%1</b>", m_host); | 572 | info.comment = i18n("<b>%1</b>", m_host); | ||
585 | info.keepPassword = true; // Prompt the user for persistence as well. | 573 | info.keepPassword = true; // Prompt the user for persistence as well. | ||
586 | info.setModified(false); // Default the modified flag since we reuse authinfo. | 574 | info.setModified(false); // Default the modified flag since we reuse authinfo. | ||
587 | 575 | | |||
588 | const bool disablePassDlg = config()->readEntry("DisablePassDlg", false); | 576 | const bool disablePassDlg = q->config()->readEntry("DisablePassDlg", false); | ||
589 | if (disablePassDlg) { | 577 | if (disablePassDlg) { | ||
590 | error(ERR_USER_CANCELED, m_host); | 578 | return Result::fail(ERR_USER_CANCELED, m_host); | ||
591 | return false; | | |||
592 | } | 579 | } | ||
593 | const int errorCode = openPasswordDialogV2(info, errorMsg); | 580 | const int errorCode = q->openPasswordDialogV2(info, errorMsg); | ||
594 | if (errorCode) { | 581 | if (errorCode) { | ||
595 | error(errorCode, QString()); | 582 | return Result::fail(errorCode); | ||
596 | return false; | | |||
597 | } else { | 583 | } else { | ||
598 | // User can decide go anonymous using checkbox | 584 | // User can decide go anonymous using checkbox | ||
599 | if (info.getExtraField(QStringLiteral("anonymous")).toBool()) { | 585 | if (info.getExtraField(QStringLiteral("anonymous")).toBool()) { | ||
600 | user = QStringLiteral(FTP_LOGIN); | 586 | user = QStringLiteral(FTP_LOGIN); | ||
601 | pass = QStringLiteral(FTP_PASSWD); | 587 | pass = QStringLiteral(FTP_PASSWD); | ||
602 | } else { | 588 | } else { | ||
603 | user = info.username; | 589 | user = info.username; | ||
604 | pass = info.password; | 590 | pass = info.password; | ||
Show All 40 Lines | 630 | if (user != QLatin1String(FTP_LOGIN) && pass != QLatin1String(FTP_PASSWD)) { | |||
645 | // Update the username in case it was changed during login. | 631 | // Update the username in case it was changed during login. | ||
646 | if (!m_user.isEmpty()) { | 632 | if (!m_user.isEmpty()) { | ||
647 | info.url.setUserName(user); | 633 | info.url.setUserName(user); | ||
648 | m_user = user; | 634 | m_user = user; | ||
649 | } | 635 | } | ||
650 | 636 | | |||
651 | // Cache the password if the user requested it. | 637 | // Cache the password if the user requested it. | ||
652 | if (info.keepPassword) { | 638 | if (info.keepPassword) { | ||
653 | cacheAuthentication(info); | 639 | q->cacheAuthentication(info); | ||
654 | } | 640 | } | ||
655 | } | 641 | } | ||
656 | failedAuth = -1; | 642 | failedAuth = -1; | ||
657 | } else { | 643 | } else { | ||
658 | // some servers don't let you login anymore | 644 | // some servers don't let you login anymore | ||
659 | // if you fail login once, so restart the connection here | 645 | // if you fail login once, so restart the connection here | ||
660 | lastServerResponse = QString::fromUtf8(ftpResponse(0)); | 646 | lastServerResponse = QString::fromUtf8(ftpResponse(0)); | ||
661 | if (!ftpOpenControlConnection()) { | 647 | const Result result = ftpOpenControlConnection(); | ||
662 | return false; | 648 | if (!result.success) { | ||
649 | return result; | ||||
663 | } | 650 | } | ||
664 | } | 651 | } | ||
665 | } while (++failedAuth); | 652 | } while (++failedAuth); | ||
666 | 653 | | |||
667 | qCDebug(KIO_FTP) << "Login OK"; | 654 | qCDebug(KIO_FTP) << "Login OK"; | ||
668 | infoMessage(i18n("Login OK")); | 655 | q->infoMessage(i18n("Login OK")); | ||
669 | 656 | | |||
670 | // Okay, we're logged in. If this is IIS 4, switch dir listing style to Unix: | 657 | // Okay, we're logged in. If this is IIS 4, switch dir listing style to Unix: | ||
671 | // Thanks to jk@soegaard.net (Jens Kristian Sgaard) for this hint | 658 | // Thanks to jk@soegaard.net (Jens Kristian Sgaard) for this hint | ||
672 | if (ftpSendCmd(QByteArrayLiteral("SYST")) && (m_iRespType == 2)) { | 659 | if (ftpSendCmd(QByteArrayLiteral("SYST")) && (m_iRespType == 2)) { | ||
673 | if (!qstrncmp(ftpResponse(0), "215 Windows_NT", 14)) { // should do for any version | 660 | if (!qstrncmp(ftpResponse(0), "215 Windows_NT", 14)) { // should do for any version | ||
674 | ftpSendCmd(QByteArrayLiteral("site dirstyle")); | 661 | (void) ftpSendCmd(QByteArrayLiteral("site dirstyle")); | ||
675 | // Check if it was already in Unix style | 662 | // Check if it was already in Unix style | ||
676 | // Patch from Keith Refson <Keith.Refson@earth.ox.ac.uk> | 663 | // Patch from Keith Refson <Keith.Refson@earth.ox.ac.uk> | ||
dfaure: I don't think we want to return on failure here.
If the server doesn't support this command, we… | |||||
677 | if (!qstrncmp(ftpResponse(0), "200 MSDOS-like directory output is on", 37)) | 664 | if (!qstrncmp(ftpResponse(0), "200 MSDOS-like directory output is on", 37)) | ||
678 | //It was in Unix style already! | 665 | //It was in Unix style already! | ||
679 | { | 666 | { | ||
680 | ftpSendCmd(QByteArrayLiteral("site dirstyle")); | 667 | (void) ftpSendCmd(QByteArrayLiteral("site dirstyle")); | ||
681 | } | 668 | } | ||
682 | // windows won't support chmod before KDE konquers their desktop... | 669 | // windows won't support chmod before KDE konquers their desktop... | ||
683 | m_extControl |= chmodUnknown; | 670 | m_extControl |= chmodUnknown; | ||
684 | } | 671 | } | ||
685 | } else { | 672 | } else { | ||
686 | qCWarning(KIO_FTP) << "SYST failed"; | 673 | qCWarning(KIO_FTP) << "SYST failed"; | ||
687 | } | 674 | } | ||
688 | 675 | | |||
689 | if (config()->readEntry("EnableAutoLoginMacro", false)) { | 676 | if (q->config()->readEntry("EnableAutoLoginMacro", false)) { | ||
690 | ftpAutoLoginMacro(); | 677 | ftpAutoLoginMacro(); | ||
691 | } | 678 | } | ||
692 | 679 | | |||
693 | // Get the current working directory | 680 | // Get the current working directory | ||
694 | qCDebug(KIO_FTP) << "Searching for pwd"; | 681 | qCDebug(KIO_FTP) << "Searching for pwd"; | ||
695 | if (!ftpSendCmd(QByteArrayLiteral("PWD")) || (m_iRespType != 2)) { | 682 | if (!ftpSendCmd(QByteArrayLiteral("PWD")) || (m_iRespType != 2)) { | ||
The reuse of the result var irks me a bit. Would it be a bad idea to make Result implicitly convertible to bool, so you can keep doing this inside the if? if (!ftpSendCmd(QByteArrayLiteral("PWD")) || (m_iRespType != 2)) { would still work fine then. This would simplify all cases where we test for failures, but ignore the actual error message on failure (because we'll provide a more high-level one, like here). In other cases, we indeed need a result var. dfaure: The reuse of the `result` var irks me a bit.
const vars rock :-)
Would it be a bad idea to… | |||||
I know what you mean, I originally started out with consts but moved away because it was getting fairly repetitive and in the end the constness offers little, the objects are fairly "ephemeral" much like errno. The result handling is indeed very "wordy", but I think there is value to be had in the expressiveness there: So, I would stay away from treating a Result like a bool. To get to the heart of the issue though: I actually think ftpSendCmd probably shouldn't even return a Result but rather a bool. Probably 99% of all callers entirely ignore the Result context and treat it as a bool, which seems like a strong indication that the return type is unnecessarily complex. Should I go ahead with making it bool? sitter: I know what you mean, I originally started out with consts but moved away because it was… | |||||
Forcing to use a local variable isn't forcing to test the error code, though. So I'm not convinced by this argument. But yes, let's go for your suggestion, ftpSendCmd should return a bool, good compromise. The only error codes it currently returns are "unsupported action" (unlikely case of programmer error) and "cannot login". So false obviously means "cannot login" anyway. Not sure all callers of ftpSendCmd realized this, though :) dfaure: Forcing to use a local variable isn't forcing to test the error code, though. So I'm not… | |||||
696 | qCDebug(KIO_FTP) << "Couldn't issue pwd command"; | 683 | qCDebug(KIO_FTP) << "Couldn't issue pwd command"; | ||
697 | error(ERR_CANNOT_LOGIN, i18n("Could not login to %1.", m_host)); // or anything better ? | 684 | return Result::fail(ERR_CANNOT_LOGIN, i18n("Could not login to %1.", m_host)); // or anything better ? | ||
698 | return false; | | |||
699 | } | 685 | } | ||
700 | 686 | | |||
701 | QString sTmp = remoteEncoding()->decode(ftpResponse(3)); | 687 | QString sTmp = q->remoteEncoding()->decode(ftpResponse(3)); | ||
702 | const int iBeg = sTmp.indexOf(QLatin1Char('"')); | 688 | const int iBeg = sTmp.indexOf(QLatin1Char('"')); | ||
703 | const int iEnd = sTmp.lastIndexOf(QLatin1Char('"')); | 689 | const int iEnd = sTmp.lastIndexOf(QLatin1Char('"')); | ||
704 | if (iBeg > 0 && iBeg < iEnd) { | 690 | if (iBeg > 0 && iBeg < iEnd) { | ||
705 | m_initialPath = sTmp.mid(iBeg + 1, iEnd - iBeg - 1); | 691 | m_initialPath = sTmp.mid(iBeg + 1, iEnd - iBeg - 1); | ||
706 | if (m_initialPath[0] != QLatin1Char('/')) { | 692 | if (m_initialPath[0] != QLatin1Char('/')) { | ||
707 | m_initialPath.prepend(QLatin1Char('/')); | 693 | m_initialPath.prepend(QLatin1Char('/')); | ||
708 | } | 694 | } | ||
709 | qCDebug(KIO_FTP) << "Initial path set to: " << m_initialPath; | 695 | qCDebug(KIO_FTP) << "Initial path set to: " << m_initialPath; | ||
710 | m_currentPath = m_initialPath; | 696 | m_currentPath = m_initialPath; | ||
711 | } | 697 | } | ||
712 | return true; | 698 | | ||
699 | return Result::pass(); | ||||
713 | } | 700 | } | ||
714 | 701 | | |||
715 | void Ftp::ftpAutoLoginMacro() | 702 | void FtpInternal::ftpAutoLoginMacro() | ||
716 | { | 703 | { | ||
717 | QString macro = metaData(QStringLiteral("autoLoginMacro")); | 704 | QString macro = q->metaData(QStringLiteral("autoLoginMacro")); | ||
718 | 705 | | |||
719 | if (macro.isEmpty()) { | 706 | if (macro.isEmpty()) { | ||
720 | return; | 707 | return; | ||
721 | } | 708 | } | ||
722 | 709 | | |||
723 | const QStringList list = macro.split(QLatin1Char('\n'), QString::SkipEmptyParts); | 710 | const QStringList list = macro.split(QLatin1Char('\n'), QString::SkipEmptyParts); | ||
724 | 711 | | |||
725 | for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it) { | 712 | for (QStringList::const_iterator it = list.begin(); it != list.end(); ++it) { | ||
726 | if ((*it).startsWith(QLatin1String("init"))) { | 713 | if ((*it).startsWith(QLatin1String("init"))) { | ||
727 | const QStringList list2 = macro.split(QLatin1Char('\\'), QString::SkipEmptyParts); | 714 | const QStringList list2 = macro.split(QLatin1Char('\\'), QString::SkipEmptyParts); | ||
728 | it = list2.begin(); | 715 | it = list2.begin(); | ||
729 | ++it; // ignore the macro name | 716 | ++it; // ignore the macro name | ||
730 | 717 | | |||
731 | for (; it != list2.end(); ++it) { | 718 | for (; it != list2.end(); ++it) { | ||
732 | // TODO: Add support for arbitrary commands | 719 | // TODO: Add support for arbitrary commands | ||
733 | // besides simply changing directory!! | 720 | // besides simply changing directory!! | ||
734 | if ((*it).startsWith(QLatin1String("cwd"))) { | 721 | if ((*it).startsWith(QLatin1String("cwd"))) { | ||
735 | (void)ftpFolder((*it).mid(4), false); | 722 | (void) ftpFolder((*it).mid(4)); | ||
736 | } | 723 | } | ||
737 | } | 724 | } | ||
738 | 725 | | |||
739 | break; | 726 | break; | ||
740 | } | 727 | } | ||
741 | } | 728 | } | ||
742 | } | 729 | } | ||
743 | 730 | | |||
744 | /** | 731 | /** | ||
745 | * ftpSendCmd - send a command (@p cmd) and read response | 732 | * ftpSendCmd - send a command (@p cmd) and read response | ||
746 | * | 733 | * | ||
747 | * @param maxretries number of time it should retry. Since it recursively | 734 | * @param maxretries number of time it should retry. Since it recursively | ||
748 | * calls itself if it can't read the answer (this happens especially after | 735 | * calls itself if it can't read the answer (this happens especially after | ||
749 | * timeouts), we need to limit the recursiveness ;-) | 736 | * timeouts), we need to limit the recursiveness ;-) | ||
750 | * | 737 | * | ||
751 | * return true if any response received, false on error | 738 | * return true if any response received, false on error | ||
752 | */ | 739 | */ | ||
753 | bool Ftp::ftpSendCmd(const QByteArray &cmd, int maxretries) | 740 | bool FtpInternal::ftpSendCmd(const QByteArray &cmd, int maxretries) | ||
754 | { | 741 | { | ||
755 | Q_ASSERT(m_control); // must have control connection socket | 742 | Q_ASSERT(m_control); // must have control connection socket | ||
756 | 743 | | |||
757 | if (cmd.indexOf('\r') != -1 || cmd.indexOf('\n') != -1) { | 744 | if (cmd.indexOf('\r') != -1 || cmd.indexOf('\n') != -1) { | ||
758 | qCWarning(KIO_FTP) << "Invalid command received (contains CR or LF):" | 745 | qCWarning(KIO_FTP) << "Invalid command received (contains CR or LF):" | ||
759 | << cmd.data(); | 746 | << cmd.data(); | ||
760 | error(ERR_UNSUPPORTED_ACTION, m_host); | | |||
761 | return false; | 747 | return false; | ||
762 | } | 748 | } | ||
763 | 749 | | |||
764 | // Don't print out the password... | 750 | // Don't print out the password... | ||
765 | bool isPassCmd = (cmd.left(4).toLower() == "pass"); | 751 | bool isPassCmd = (cmd.left(4).toLower() == "pass"); | ||
766 | #if 0 | 752 | #if 0 | ||
767 | if (!isPassCmd) { | 753 | if (!isPassCmd) { | ||
768 | qDebug() << "send> " << cmd.data(); | 754 | qDebug() << "send> " << cmd.data(); | ||
Show All 22 Lines | 776 | if ((m_iRespType <= 0) || (m_iRespCode == 421)) { | |||
791 | // We have not yet logged on... | 777 | // We have not yet logged on... | ||
792 | if (!m_bLoggedOn) { | 778 | if (!m_bLoggedOn) { | ||
793 | // The command was sent from the ftpLogin function, i.e. we are actually | 779 | // The command was sent from the ftpLogin function, i.e. we are actually | ||
794 | // attempting to login in. NOTE: If we already sent the username, we | 780 | // attempting to login in. NOTE: If we already sent the username, we | ||
795 | // return false and let the user decide whether (s)he wants to start from | 781 | // return false and let the user decide whether (s)he wants to start from | ||
796 | // the beginning... | 782 | // the beginning... | ||
797 | if (maxretries > 0 && !isPassCmd) { | 783 | if (maxretries > 0 && !isPassCmd) { | ||
798 | closeConnection(); | 784 | closeConnection(); | ||
799 | if (ftpOpenConnection(loginDefered)) { | 785 | const auto result = ftpOpenConnection(LoginMode::Defered); | ||
Oh... it's a recursive call... wow, tricky. Yeah, that means this method returns false even if the nested method managed to login. Very confusing, and probably wrong... dfaure: Oh... it's a recursive call... wow, tricky.
Yeah, that means this method returns false even if… | |||||
dfaure: How about adding a check for the ret val? | |||||
800 | ftpSendCmd(cmd, maxretries - 1); | 786 | if (result.success && ftpSendCmd(cmd, maxretries - 1)) { | ||
787 | return true; | ||||
801 | } | 788 | } | ||
802 | } | 789 | } | ||
803 | 790 | | |||
804 | return false; | 791 | return false; | ||
805 | } else { | 792 | } else { | ||
806 | if (maxretries < 1) { | 793 | if (maxretries < 1) { | ||
807 | return false; | 794 | return false; | ||
808 | } else { | 795 | } else { | ||
809 | qCDebug(KIO_FTP) << "Was not able to communicate with " << m_host | 796 | qCDebug(KIO_FTP) << "Was not able to communicate with " << m_host | ||
810 | << "Attempting to re-establish connection."; | 797 | << "Attempting to re-establish connection."; | ||
811 | 798 | | |||
812 | closeConnection(); // Close the old connection... | 799 | closeConnection(); // Close the old connection... | ||
813 | openConnection(); // Attempt to re-establish a new connection... | 800 | const Result openResult = openConnection(); // Attempt to re-establish a new connection... | ||
814 | 801 | | |||
815 | if (!m_bLoggedOn) { | 802 | if (!openResult.success) { | ||
816 | if (m_control) { // if openConnection succeeded ... | 803 | if (m_control) { // if openConnection succeeded ... | ||
817 | qCDebug(KIO_FTP) << "Login failure, aborting"; | 804 | qCDebug(KIO_FTP) << "Login failure, aborting"; | ||
818 | error(ERR_CANNOT_LOGIN, m_host); | | |||
819 | closeConnection(); | 805 | closeConnection(); | ||
820 | } | 806 | } | ||
821 | return false; | 807 | return false; | ||
I think this is all quite redundant. openConnection() sets m_bLoggedOn to true if it succeeds, and to false if it fails. I think this can all be simplified to if (!openResult.success) { if (m_control) { // ... closeConnection(); } return openResult; // or Result::fail(ERR_CANNOT_LOGIN, m_host), if the error code from openResult is no good } dfaure: I think this is all quite redundant.
openConnection() sets m_bLoggedOn to true if it succeeds… | |||||
822 | } | 808 | } | ||
823 | 809 | | |||
824 | qCDebug(KIO_FTP) << "Logged back in, re-issuing command"; | 810 | qCDebug(KIO_FTP) << "Logged back in, re-issuing command"; | ||
825 | 811 | | |||
826 | // If we were able to login, resend the command... | 812 | // If we were able to login, resend the command... | ||
827 | if (maxretries) { | 813 | if (maxretries) { | ||
828 | maxretries--; | 814 | maxretries--; | ||
829 | } | 815 | } | ||
830 | 816 | | |||
831 | return ftpSendCmd(cmd, maxretries); | 817 | return ftpSendCmd(cmd, maxretries); | ||
832 | } | 818 | } | ||
833 | } | 819 | } | ||
834 | } | 820 | } | ||
835 | 821 | | |||
836 | return true; | 822 | return true; | ||
837 | } | 823 | } | ||
838 | 824 | | |||
839 | /* | 825 | /* | ||
840 | * ftpOpenPASVDataConnection - set up data connection, using PASV mode | 826 | * ftpOpenPASVDataConnection - set up data connection, using PASV mode | ||
841 | * | 827 | * | ||
842 | * return 0 if successful, ERR_INTERNAL otherwise | 828 | * return 0 if successful, ERR_INTERNAL otherwise | ||
843 | * doesn't set error message, since non-pasv mode will always be tried if | 829 | * doesn't set error message, since non-pasv mode will always be tried if | ||
844 | * this one fails | 830 | * this one fails | ||
845 | */ | 831 | */ | ||
846 | int Ftp::ftpOpenPASVDataConnection() | 832 | int FtpInternal::ftpOpenPASVDataConnection() | ||
847 | { | 833 | { | ||
848 | Q_ASSERT(m_control); // must have control connection socket | 834 | Q_ASSERT(m_control); // must have control connection socket | ||
849 | Q_ASSERT(!m_data); // ... but no data connection | 835 | Q_ASSERT(!m_data); // ... but no data connection | ||
850 | 836 | | |||
851 | // Check that we can do PASV | 837 | // Check that we can do PASV | ||
852 | QHostAddress address = m_control->peerAddress(); | 838 | QHostAddress address = m_control->peerAddress(); | ||
853 | if (address.protocol() != QAbstractSocket::IPv4Protocol && !isSocksProxy()) { | 839 | if (address.protocol() != QAbstractSocket::IPv4Protocol && !isSocksProxy()) { | ||
854 | return ERR_INTERNAL; // no PASV for non-PF_INET connections | 840 | return ERR_INTERNAL; // no PASV for non-PF_INET connections | ||
Show All 32 Lines | |||||
887 | 873 | | |||
888 | // we ignore the host part on purpose for two reasons | 874 | // we ignore the host part on purpose for two reasons | ||
889 | // a) it might be wrong anyway | 875 | // a) it might be wrong anyway | ||
890 | // b) it would make us being susceptible to a port scanning attack | 876 | // b) it would make us being susceptible to a port scanning attack | ||
891 | 877 | | |||
892 | // now connect the data socket ... | 878 | // now connect the data socket ... | ||
893 | quint16 port = i[4] << 8 | i[5]; | 879 | quint16 port = i[4] << 8 | i[5]; | ||
894 | const QString host = (isSocksProxy() ? m_host : address.toString()); | 880 | const QString host = (isSocksProxy() ? m_host : address.toString()); | ||
895 | m_data = synchronousConnectToHost(host, port); | 881 | const auto connectionResult = synchronousConnectToHost(host, port); | ||
882 | m_data = connectionResult.socket; | ||||
883 | if (!connectionResult.result.success) { | ||||
884 | return connectionResult.result.error; | ||||
885 | } | ||||
896 | 886 | | |||
897 | return m_data->state() == QAbstractSocket::ConnectedState ? 0 : ERR_INTERNAL; | 887 | return m_data->state() == QAbstractSocket::ConnectedState ? 0 : ERR_INTERNAL; | ||
898 | } | 888 | } | ||
899 | 889 | | |||
900 | /* | 890 | /* | ||
901 | * ftpOpenEPSVDataConnection - opens a data connection via EPSV | 891 | * ftpOpenEPSVDataConnection - opens a data connection via EPSV | ||
902 | */ | 892 | */ | ||
903 | int Ftp::ftpOpenEPSVDataConnection() | 893 | int FtpInternal::ftpOpenEPSVDataConnection() | ||
904 | { | 894 | { | ||
905 | Q_ASSERT(m_control); // must have control connection socket | 895 | Q_ASSERT(m_control); // must have control connection socket | ||
906 | Q_ASSERT(!m_data); // ... but no data connection | 896 | Q_ASSERT(!m_data); // ... but no data connection | ||
907 | 897 | | |||
908 | QHostAddress address = m_control->peerAddress(); | 898 | QHostAddress address = m_control->peerAddress(); | ||
909 | int portnum; | 899 | int portnum; | ||
910 | 900 | | |||
911 | if (m_extControl & epsvUnknown) { | 901 | if (m_extControl & epsvUnknown) { | ||
Show All 9 Lines | 906 | if (!ftpSendCmd(QByteArrayLiteral("EPSV")) || (m_iRespType != 2)) { | |||
921 | } | 911 | } | ||
922 | return ERR_INTERNAL; | 912 | return ERR_INTERNAL; | ||
923 | } | 913 | } | ||
924 | 914 | | |||
925 | const char *start = strchr(ftpResponse(3), '|'); | 915 | const char *start = strchr(ftpResponse(3), '|'); | ||
926 | if (!start || sscanf(start, "|||%d|", &portnum) != 1) { | 916 | if (!start || sscanf(start, "|||%d|", &portnum) != 1) { | ||
927 | return ERR_INTERNAL; | 917 | return ERR_INTERNAL; | ||
928 | } | 918 | } | ||
919 | Q_ASSERT(portnum > 0); | ||||
929 | 920 | | |||
930 | const QString host = (isSocksProxy() ? m_host : address.toString()); | 921 | const QString host = (isSocksProxy() ? m_host : address.toString()); | ||
931 | m_data = synchronousConnectToHost(host, portnum); | 922 | const auto connectionResult = synchronousConnectToHost(host, static_cast<quint16>(portnum)); | ||
932 | return m_data->isOpen() ? 0 : ERR_INTERNAL; | 923 | m_data = connectionResult.socket; | ||
924 | if (!connectionResult.result.success) { | ||||
925 | return connectionResult.result.error; | ||||
926 | } | ||||
927 | return m_data->state() == QAbstractSocket::ConnectedState ? 0 : ERR_INTERNAL; | ||||
933 | } | 928 | } | ||
934 | 929 | | |||
935 | /* | 930 | /* | ||
936 | * ftpOpenDataConnection - set up data connection | 931 | * ftpOpenDataConnection - set up data connection | ||
937 | * | 932 | * | ||
938 | * The routine calls several ftpOpenXxxxConnection() helpers to find | 933 | * The routine calls several ftpOpenXxxxConnection() helpers to find | ||
939 | * the best connection mode. If a helper cannot connect if returns | 934 | * the best connection mode. If a helper cannot connect if returns | ||
940 | * ERR_INTERNAL - so this is not really an error! All other error | 935 | * ERR_INTERNAL - so this is not really an error! All other error | ||
941 | * codes are treated as fatal, e.g. they are passed back to the caller | 936 | * codes are treated as fatal, e.g. they are passed back to the caller | ||
942 | * who is responsible for calling error(). ftpOpenPortDataConnection | 937 | * who is responsible for calling error(). ftpOpenPortDataConnection | ||
943 | * can be called as last try and it does never return ERR_INTERNAL. | 938 | * can be called as last try and it does never return ERR_INTERNAL. | ||
944 | * | 939 | * | ||
945 | * @return 0 if successful, err code otherwise | 940 | * @return 0 if successful, err code otherwise | ||
946 | */ | 941 | */ | ||
947 | int Ftp::ftpOpenDataConnection() | 942 | int FtpInternal::ftpOpenDataConnection() | ||
948 | { | 943 | { | ||
949 | // make sure that we are logged on and have no data connection... | 944 | // make sure that we are logged on and have no data connection... | ||
950 | Q_ASSERT(m_bLoggedOn); | 945 | Q_ASSERT(m_bLoggedOn); | ||
951 | ftpCloseDataConnection(); | 946 | ftpCloseDataConnection(); | ||
952 | 947 | | |||
953 | int iErrCode = 0; | 948 | int iErrCode = 0; | ||
954 | int iErrCodePASV = 0; // Remember error code from PASV | 949 | int iErrCodePASV = 0; // Remember error code from PASV | ||
955 | 950 | | |||
956 | // First try passive (EPSV & PASV) modes | 951 | // First try passive (EPSV & PASV) modes | ||
957 | if (!config()->readEntry("DisablePassiveMode", false)) { | 952 | if (!q->config()->readEntry("DisablePassiveMode", false)) { | ||
958 | iErrCode = ftpOpenPASVDataConnection(); | 953 | iErrCode = ftpOpenPASVDataConnection(); | ||
959 | if (iErrCode == 0) { | 954 | if (iErrCode == 0) { | ||
960 | return 0; // success | 955 | return 0; // success | ||
961 | } | 956 | } | ||
962 | iErrCodePASV = iErrCode; | 957 | iErrCodePASV = iErrCode; | ||
963 | ftpCloseDataConnection(); | 958 | ftpCloseDataConnection(); | ||
964 | 959 | | |||
965 | if (!config()->readEntry("DisableEPSV", false)) { | 960 | if (!q->config()->readEntry("DisableEPSV", false)) { | ||
966 | iErrCode = ftpOpenEPSVDataConnection(); | 961 | iErrCode = ftpOpenEPSVDataConnection(); | ||
967 | if (iErrCode == 0) { | 962 | if (iErrCode == 0) { | ||
968 | return 0; // success | 963 | return 0; // success | ||
969 | } | 964 | } | ||
970 | ftpCloseDataConnection(); | 965 | ftpCloseDataConnection(); | ||
971 | } | 966 | } | ||
972 | 967 | | |||
973 | // if we sent EPSV ALL already and it was accepted, then we can't | 968 | // if we sent EPSV ALL already and it was accepted, then we can't | ||
Show All 15 Lines | |||||
989 | } | 984 | } | ||
990 | 985 | | |||
991 | /* | 986 | /* | ||
992 | * ftpOpenPortDataConnection - set up data connection | 987 | * ftpOpenPortDataConnection - set up data connection | ||
993 | * | 988 | * | ||
994 | * @return 0 if successful, err code otherwise (but never ERR_INTERNAL | 989 | * @return 0 if successful, err code otherwise (but never ERR_INTERNAL | ||
995 | * because this is the last connection mode that is tried) | 990 | * because this is the last connection mode that is tried) | ||
996 | */ | 991 | */ | ||
997 | int Ftp::ftpOpenPortDataConnection() | 992 | int FtpInternal::ftpOpenPortDataConnection() | ||
998 | { | 993 | { | ||
999 | Q_ASSERT(m_control); // must have control connection socket | 994 | Q_ASSERT(m_control); // must have control connection socket | ||
1000 | Q_ASSERT(!m_data); // ... but no data connection | 995 | Q_ASSERT(!m_data); // ... but no data connection | ||
1001 | 996 | | |||
1002 | m_bPasv = false; | 997 | m_bPasv = false; | ||
1003 | if (m_extControl & eprtUnknown) { | 998 | if (m_extControl & eprtUnknown) { | ||
1004 | return ERR_INTERNAL; | 999 | return ERR_INTERNAL; | ||
1005 | } | 1000 | } | ||
Show All 31 Lines | 1031 | if (ftpSendCmd(command.toLatin1()) && (m_iRespType == 2)) { | |||
1037 | return 0; | 1032 | return 0; | ||
1038 | } | 1033 | } | ||
1039 | 1034 | | |||
1040 | delete m_server; | 1035 | delete m_server; | ||
1041 | m_server = nullptr; | 1036 | m_server = nullptr; | ||
1042 | return ERR_INTERNAL; | 1037 | return ERR_INTERNAL; | ||
1043 | } | 1038 | } | ||
1044 | 1039 | | |||
1045 | bool Ftp::ftpOpenCommand(const char *_command, const QString &_path, char _mode, | 1040 | Result FtpInternal::ftpOpenCommand(const char *_command, const QString &_path, char _mode, | ||
1046 | int errorcode, KIO::fileoffset_t _offset) | 1041 | int errorcode, KIO::fileoffset_t _offset) | ||
1047 | { | 1042 | { | ||
1048 | int errCode = 0; | 1043 | int errCode = 0; | ||
1049 | if (!ftpDataMode(ftpModeFromPath(_path, _mode))) { | 1044 | if (!ftpDataMode(ftpModeFromPath(_path, _mode))) { | ||
1050 | errCode = ERR_CANNOT_CONNECT; | 1045 | errCode = ERR_CANNOT_CONNECT; | ||
1051 | } else { | 1046 | } else { | ||
1052 | errCode = ftpOpenDataConnection(); | 1047 | errCode = ftpOpenDataConnection(); | ||
1053 | } | 1048 | } | ||
1054 | 1049 | | |||
1055 | if (errCode != 0) { | 1050 | if (errCode != 0) { | ||
1056 | error(errCode, m_host); | 1051 | return Result::fail(errCode, m_host); | ||
1057 | return false; | | |||
1058 | } | 1052 | } | ||
1059 | 1053 | | |||
1060 | if (_offset > 0) { | 1054 | if (_offset > 0) { | ||
1061 | // send rest command if offset > 0, this applies to retr and stor commands | 1055 | // send rest command if offset > 0, this applies to retr and stor commands | ||
1062 | char buf[100]; | 1056 | char buf[100]; | ||
1063 | sprintf(buf, "rest %lld", _offset); | 1057 | sprintf(buf, "rest %lld", _offset); | ||
1064 | if (!ftpSendCmd(buf)) { | 1058 | if (!ftpSendCmd(buf)) { | ||
1065 | return false; | 1059 | return Result::fail(); | ||
1066 | } | 1060 | } | ||
1067 | if (m_iRespType != 3) { | 1061 | if (m_iRespType != 3) { | ||
1068 | error(ERR_CANNOT_RESUME, _path); // should never happen | 1062 | return Result::fail(ERR_CANNOT_RESUME, _path); // should never happen | ||
1069 | return false; | | |||
1070 | } | 1063 | } | ||
1071 | } | 1064 | } | ||
1072 | 1065 | | |||
1073 | QByteArray tmp = _command; | 1066 | QByteArray tmp = _command; | ||
1074 | QString errormessage; | 1067 | QString errormessage; | ||
1075 | 1068 | | |||
1076 | if (!_path.isEmpty()) { | 1069 | if (!_path.isEmpty()) { | ||
1077 | tmp += ' ' + remoteEncoding()->encode(ftpCleanPath(_path)); | 1070 | tmp += ' ' + q->remoteEncoding()->encode(ftpCleanPath(_path)); | ||
1078 | } | 1071 | } | ||
1079 | 1072 | | |||
1080 | if (!ftpSendCmd(tmp) || (m_iRespType != 1)) { | 1073 | if (!ftpSendCmd(tmp) || (m_iRespType != 1)) { | ||
1081 | if (_offset > 0 && qstrcmp(_command, "retr") == 0 && (m_iRespType == 4)) { | 1074 | if (_offset > 0 && qstrcmp(_command, "retr") == 0 && (m_iRespType == 4)) { | ||
1082 | errorcode = ERR_CANNOT_RESUME; | 1075 | errorcode = ERR_CANNOT_RESUME; | ||
1083 | } | 1076 | } | ||
1084 | // The error code here depends on the command | 1077 | // The error code here depends on the command | ||
1085 | errormessage = _path + i18n("\nThe server said: \"%1\"", QString::fromUtf8(ftpResponse(0)).trimmed()); | 1078 | errormessage = _path + i18n("\nThe server said: \"%1\"", QString::fromUtf8(ftpResponse(0)).trimmed()); | ||
1086 | } | 1079 | } | ||
1087 | 1080 | | |||
1088 | else { | 1081 | else { | ||
1089 | // Only now we know for sure that we can resume | 1082 | // Only now we know for sure that we can resume | ||
1090 | if (_offset > 0 && qstrcmp(_command, "retr") == 0) { | 1083 | if (_offset > 0 && qstrcmp(_command, "retr") == 0) { | ||
1091 | canResume(); | 1084 | q->canResume(); | ||
1092 | } | 1085 | } | ||
1093 | 1086 | | |||
1094 | if (m_server && !m_data) { | 1087 | if (m_server && !m_data) { | ||
1095 | qCDebug(KIO_FTP) << "waiting for connection from remote."; | 1088 | qCDebug(KIO_FTP) << "waiting for connection from remote."; | ||
1096 | m_server->waitForNewConnection(connectTimeout() * 1000); | 1089 | m_server->waitForNewConnection(q->connectTimeout() * 1000); | ||
1097 | m_data = m_server->nextPendingConnection(); | 1090 | m_data = m_server->nextPendingConnection(); | ||
1098 | } | 1091 | } | ||
1099 | 1092 | | |||
1100 | if (m_data) { | 1093 | if (m_data) { | ||
1101 | qCDebug(KIO_FTP) << "connected with remote."; | 1094 | qCDebug(KIO_FTP) << "connected with remote."; | ||
1102 | m_bBusy = true; // cleared in ftpCloseCommand | 1095 | m_bBusy = true; // cleared in ftpCloseCommand | ||
1103 | return true; | 1096 | return Result::pass(); | ||
1104 | } | 1097 | } | ||
1105 | 1098 | | |||
1106 | qCDebug(KIO_FTP) << "no connection received from remote."; | 1099 | qCDebug(KIO_FTP) << "no connection received from remote."; | ||
1107 | errorcode = ERR_CANNOT_ACCEPT; | 1100 | errorcode = ERR_CANNOT_ACCEPT; | ||
1108 | errormessage = m_host; | 1101 | errormessage = m_host; | ||
1109 | return false; | | |||
1110 | } | 1102 | } | ||
why not use errorcode and errormessage here? I believe this was a bug in the orig code, setting two local vars and then returning false is nonsense, the return false shouldn't have been there. dfaure: why not use errorcode and errormessage here?
or in fact just removing that line, so that it… | |||||
1111 | 1103 | | |||
1112 | if (errorcode != KJob::NoError) { | 1104 | if (errorcode != KJob::NoError) { | ||
1113 | error(errorcode, errormessage); | 1105 | return Result::fail(errorcode, errormessage); | ||
1114 | } | 1106 | } | ||
1115 | return false; | 1107 | return Result::fail(); | ||
1116 | } | 1108 | } | ||
1117 | 1109 | | |||
1118 | bool Ftp::ftpCloseCommand() | 1110 | bool FtpInternal::ftpCloseCommand() | ||
1119 | { | 1111 | { | ||
1120 | // first close data sockets (if opened), then read response that | 1112 | // first close data sockets (if opened), then read response that | ||
1121 | // we got for whatever was used in ftpOpenCommand ( should be 226 ) | 1113 | // we got for whatever was used in ftpOpenCommand ( should be 226 ) | ||
1122 | ftpCloseDataConnection(); | 1114 | ftpCloseDataConnection(); | ||
1123 | 1115 | | |||
1124 | if (!m_bBusy) { | 1116 | if (!m_bBusy) { | ||
1125 | return true; | 1117 | return true; | ||
1126 | } | 1118 | } | ||
1127 | 1119 | | |||
1128 | qCDebug(KIO_FTP) << "ftpCloseCommand: reading command result"; | 1120 | qCDebug(KIO_FTP) << "ftpCloseCommand: reading command result"; | ||
1129 | m_bBusy = false; | 1121 | m_bBusy = false; | ||
1130 | 1122 | | |||
1131 | if (!ftpResponse(-1) || (m_iRespType != 2)) { | 1123 | if (!ftpResponse(-1) || (m_iRespType != 2)) { | ||
1132 | qCDebug(KIO_FTP) << "ftpCloseCommand: no transfer complete message"; | 1124 | qCDebug(KIO_FTP) << "ftpCloseCommand: no transfer complete message"; | ||
1133 | return false; | 1125 | return false; | ||
1134 | } | 1126 | } | ||
1135 | return true; | 1127 | return true; | ||
1136 | } | 1128 | } | ||
1137 | 1129 | | |||
1138 | void Ftp::mkdir(const QUrl &url, int permissions) | 1130 | Result FtpInternal::mkdir(const QUrl &url, int permissions) | ||
1139 | { | 1131 | { | ||
1140 | if (!ftpOpenConnection(loginImplicit)) { | 1132 | auto result = ftpOpenConnection(LoginMode::Implicit); | ||
1141 | return; | 1133 | if (!result.success) { | ||
1134 | return result; | ||||
1142 | } | 1135 | } | ||
1143 | 1136 | | |||
1144 | const QByteArray encodedPath(remoteEncoding()->encode(url)); | 1137 | const QByteArray encodedPath(q->remoteEncoding()->encode(url)); | ||
1145 | const QString path = QString::fromLatin1(encodedPath.constData(), encodedPath.size()); | 1138 | const QString path = QString::fromLatin1(encodedPath.constData(), encodedPath.size()); | ||
1146 | 1139 | | |||
1147 | if (!ftpSendCmd((QByteArrayLiteral("mkd ") + encodedPath)) || (m_iRespType != 2)) { | 1140 | if (!ftpSendCmd((QByteArrayLiteral("mkd ") + encodedPath)) || (m_iRespType != 2)) { | ||
1148 | QString currentPath(m_currentPath); | 1141 | QString currentPath(m_currentPath); | ||
1149 | 1142 | | |||
1150 | // Check whether or not mkdir failed because | 1143 | // Check whether or not mkdir failed because | ||
1151 | // the directory already exists... | 1144 | // the directory already exists... | ||
1152 | if (ftpFolder(path, false)) { | 1145 | if (ftpFolder(path)) { | ||
1153 | error(ERR_DIR_ALREADY_EXIST, path); | 1146 | const QString failedPath = path; | ||
1154 | // Change the directory back to what it was... | 1147 | // Change the directory back to what it was... | ||
1155 | (void) ftpFolder(currentPath, false); | 1148 | (void) ftpFolder(currentPath); | ||
1156 | return; | 1149 | return Result::fail(ERR_DIR_ALREADY_EXIST, failedPath); | ||
1157 | } | 1150 | } | ||
1158 | 1151 | | |||
1159 | error(ERR_CANNOT_MKDIR, path); | 1152 | return Result::fail(ERR_CANNOT_MKDIR, path); | ||
1160 | return; | | |||
1161 | } | 1153 | } | ||
1162 | 1154 | | |||
1163 | if (permissions != -1) { | 1155 | if (permissions != -1) { | ||
1164 | // chmod the dir we just created, ignoring errors. | 1156 | // chmod the dir we just created, ignoring errors. | ||
1165 | (void) ftpChmod(path, permissions); | 1157 | (void) ftpChmod(path, permissions); | ||
1166 | } | 1158 | } | ||
1167 | 1159 | | |||
1168 | finished(); | 1160 | return Result::pass(); | ||
1169 | } | 1161 | } | ||
1170 | 1162 | | |||
1171 | void Ftp::rename(const QUrl &src, const QUrl &dst, KIO::JobFlags flags) | 1163 | Result FtpInternal::rename(const QUrl &src, const QUrl &dst, KIO::JobFlags flags) | ||
1172 | { | 1164 | { | ||
1173 | if (!ftpOpenConnection(loginImplicit)) { | 1165 | const auto result = ftpOpenConnection(LoginMode::Implicit); | ||
1174 | return; | 1166 | if (!result.success) { | ||
1167 | return result; | ||||
1175 | } | 1168 | } | ||
1176 | 1169 | | |||
1177 | // The actual functionality is in ftpRename because put needs it | 1170 | // The actual functionality is in ftpRename because put needs it | ||
1178 | if (ftpRename(src.path(), dst.path(), flags)) { | 1171 | return ftpRename(src.path(), dst.path(), flags); | ||
1179 | finished(); | | |||
1180 | } | | |||
1181 | } | 1172 | } | ||
1182 | 1173 | | |||
1183 | bool Ftp::ftpRename(const QString &src, const QString &dst, KIO::JobFlags jobFlags) | 1174 | Result FtpInternal::ftpRename(const QString &src, const QString &dst, KIO::JobFlags jobFlags) | ||
1184 | { | 1175 | { | ||
1185 | Q_ASSERT(m_bLoggedOn); | 1176 | Q_ASSERT(m_bLoggedOn); | ||
1186 | 1177 | | |||
1187 | // Must check if dst already exists, RNFR+RNTO overwrites by default (#127793). | 1178 | // Must check if dst already exists, RNFR+RNTO overwrites by default (#127793). | ||
1188 | if (!(jobFlags & KIO::Overwrite)) { | 1179 | if (!(jobFlags & KIO::Overwrite)) { | ||
1189 | if (ftpFileExists(dst)) { | 1180 | if (ftpFileExists(dst)) { | ||
1190 | error(ERR_FILE_ALREADY_EXIST, dst); | 1181 | return Result::fail(ERR_FILE_ALREADY_EXIST, dst); | ||
1191 | return false; | | |||
1192 | } | 1182 | } | ||
1193 | } | 1183 | } | ||
1194 | 1184 | | |||
1195 | if (ftpFolder(dst, false)) { | 1185 | if (ftpFolder(dst)) { | ||
1196 | error(ERR_DIR_ALREADY_EXIST, dst); | 1186 | return Result::fail(ERR_DIR_ALREADY_EXIST, dst); | ||
1197 | return false; | | |||
1198 | } | 1187 | } | ||
1199 | 1188 | | |||
1200 | // CD into parent folder | 1189 | // CD into parent folder | ||
1201 | const int pos = src.lastIndexOf(QLatin1Char('/')); | 1190 | const int pos = src.lastIndexOf(QLatin1Char('/')); | ||
1202 | if (pos >= 0) { | 1191 | if (pos >= 0) { | ||
1203 | if (!ftpFolder(src.left(pos + 1), false)) { | 1192 | if (!ftpFolder(src.left(pos + 1))) { | ||
1204 | return false; | 1193 | return Result::fail(ERR_CANNOT_ENTER_DIRECTORY, src); | ||
dfaure: `ERR_CANNOT_ENTER_DIRECTORY, src` | |||||
1205 | } | 1194 | } | ||
1206 | } | 1195 | } | ||
1207 | 1196 | | |||
1208 | const QByteArray from_cmd = "RNFR " + remoteEncoding()->encode(src.mid(pos + 1)); | 1197 | const QByteArray from_cmd = "RNFR " + q->remoteEncoding()->encode(src.mid(pos + 1)); | ||
1209 | if (!ftpSendCmd(from_cmd) || (m_iRespType != 3)) { | 1198 | if (!ftpSendCmd(from_cmd) || (m_iRespType != 3)) { | ||
1210 | error(ERR_CANNOT_RENAME, src); | 1199 | return Result::fail(ERR_CANNOT_RENAME, src); | ||
1211 | return false; | | |||
1212 | } | 1200 | } | ||
1213 | 1201 | | |||
1214 | const QByteArray to_cmd = "RNTO " + remoteEncoding()->encode(dst); | 1202 | const QByteArray to_cmd = "RNTO " + q->remoteEncoding()->encode(dst); | ||
1215 | if (!ftpSendCmd(to_cmd) || (m_iRespType != 2)) { | 1203 | if (!ftpSendCmd(to_cmd) || (m_iRespType != 2)) { | ||
1216 | error(ERR_CANNOT_RENAME, src); | 1204 | return Result::fail(ERR_CANNOT_RENAME, src); | ||
1217 | return false; | | |||
1218 | } | 1205 | } | ||
1219 | 1206 | | |||
1220 | return true; | 1207 | return Result::pass(); | ||
1221 | } | 1208 | } | ||
1222 | 1209 | | |||
1223 | void Ftp::del(const QUrl &url, bool isfile) | 1210 | Result FtpInternal::del(const QUrl &url, bool isfile) | ||
1224 | { | 1211 | { | ||
1225 | if (!ftpOpenConnection(loginImplicit)) { | 1212 | auto result = ftpOpenConnection(LoginMode::Implicit); | ||
1226 | return; | 1213 | if (!result.success) { | ||
1214 | return result; | ||||
1227 | } | 1215 | } | ||
1228 | 1216 | | |||
1229 | // When deleting a directory, we must exit from it first | 1217 | // When deleting a directory, we must exit from it first | ||
1230 | // The last command probably went into it (to stat it) | 1218 | // The last command probably went into it (to stat it) | ||
1231 | if (!isfile) { | 1219 | if (!isfile) { | ||
1232 | ftpFolder(remoteEncoding()->directory(url), false); // ignore errors | 1220 | (void) ftpFolder(q->remoteEncoding()->directory(url)); // ignore errors | ||
1233 | } | 1221 | } | ||
1234 | 1222 | | |||
1235 | const QByteArray cmd = (isfile ? "DELE " : "RMD ") + remoteEncoding()->encode(url); | 1223 | const QByteArray cmd = (isfile ? "DELE " : "RMD ") + q->remoteEncoding()->encode(url); | ||
1236 | 1224 | | |||
1237 | if (!ftpSendCmd(cmd) || (m_iRespType != 2)) { | 1225 | if (!ftpSendCmd(cmd) || (m_iRespType != 2)) { | ||
1238 | error(ERR_CANNOT_DELETE, url.path()); | 1226 | return Result::fail(ERR_CANNOT_DELETE, url.path()); | ||
1239 | } else { | | |||
1240 | finished(); | | |||
1241 | } | 1227 | } | ||
1228 | | ||||
1229 | return Result::pass(); | ||||
1242 | } | 1230 | } | ||
1243 | 1231 | | |||
1244 | bool Ftp::ftpChmod(const QString &path, int permissions) | 1232 | bool FtpInternal::ftpChmod(const QString &path, int permissions) | ||
1245 | { | 1233 | { | ||
1246 | Q_ASSERT(m_bLoggedOn); | 1234 | Q_ASSERT(m_bLoggedOn); | ||
1247 | 1235 | | |||
1248 | if (m_extControl & chmodUnknown) { // previous errors? | 1236 | if (m_extControl & chmodUnknown) { // previous errors? | ||
1249 | return false; | 1237 | return false; | ||
1250 | } | 1238 | } | ||
1251 | 1239 | | |||
1252 | // we need to do bit AND 777 to get permissions, in case | 1240 | // we need to do bit AND 777 to get permissions, in case | ||
1253 | // we were sent a full mode (unlikely) | 1241 | // we were sent a full mode (unlikely) | ||
1254 | const QByteArray cmd = "SITE CHMOD " + QByteArray::number(permissions & 0777/*octal*/, 8 /*octal*/) + ' ' + remoteEncoding()->encode(path); | 1242 | const QByteArray cmd = "SITE CHMOD " + QByteArray::number(permissions & 0777/*octal*/, 8 /*octal*/) + ' ' + q->remoteEncoding()->encode(path); | ||
1243 | | ||||
1244 | if (ftpSendCmd(cmd)) { | ||||
1245 | qCDebug(KIO_FTP) << "ftpChmod: Failed to issue chmod"; | ||||
1246 | return false; | ||||
1247 | } | ||||
1255 | 1248 | | |||
1256 | ftpSendCmd(cmd); | | |||
1257 | if (m_iRespType == 2) { | 1249 | if (m_iRespType == 2) { | ||
1258 | return true; | 1250 | return true; | ||
1259 | } | 1251 | } | ||
1260 | 1252 | | |||
1261 | if (m_iRespCode == 500) { | 1253 | if (m_iRespCode == 500) { | ||
1262 | m_extControl |= chmodUnknown; | 1254 | m_extControl |= chmodUnknown; | ||
1263 | qCDebug(KIO_FTP) << "ftpChmod: CHMOD not supported - disabling"; | 1255 | qCDebug(KIO_FTP) << "ftpChmod: CHMOD not supported - disabling"; | ||
1264 | } | 1256 | } | ||
1265 | return false; | 1257 | return false; | ||
1266 | } | 1258 | } | ||
1267 | 1259 | | |||
1268 | void Ftp::chmod(const QUrl &url, int permissions) | 1260 | Result FtpInternal::chmod(const QUrl &url, int permissions) | ||
1269 | { | 1261 | { | ||
1270 | if (!ftpOpenConnection(loginImplicit)) { | 1262 | const auto result = ftpOpenConnection(LoginMode::Implicit); | ||
1271 | return; | 1263 | if (!result.success) { | ||
1264 | return result; | ||||
1272 | } | 1265 | } | ||
1273 | 1266 | | |||
1274 | if (!ftpChmod(url.path(), permissions)) { | 1267 | if (!ftpChmod(url.path(), permissions)) { | ||
1275 | error(ERR_CANNOT_CHMOD, url.path()); | 1268 | return Result::fail(ERR_CANNOT_CHMOD, url.path()); | ||
1276 | } else { | | |||
1277 | finished(); | | |||
1278 | } | 1269 | } | ||
1270 | | ||||
1271 | return Result::pass(); | ||||
1279 | } | 1272 | } | ||
1280 | 1273 | | |||
1281 | void Ftp::ftpCreateUDSEntry(const QString &filename, const FtpEntry &ftpEnt, UDSEntry &entry, bool isDir) | 1274 | void FtpInternal::ftpCreateUDSEntry(const QString &filename, const FtpEntry &ftpEnt, UDSEntry &entry, bool isDir) | ||
1282 | { | 1275 | { | ||
1283 | Q_ASSERT(entry.count() == 0); // by contract :-) | 1276 | Q_ASSERT(entry.count() == 0); // by contract :-) | ||
1284 | 1277 | | |||
1285 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | 1278 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | ||
1286 | entry.fastInsert(KIO::UDSEntry::UDS_SIZE, ftpEnt.size); | 1279 | entry.fastInsert(KIO::UDSEntry::UDS_SIZE, ftpEnt.size); | ||
1287 | entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, ftpEnt.date.toSecsSinceEpoch()); | 1280 | entry.fastInsert(KIO::UDSEntry::UDS_MODIFICATION_TIME, ftpEnt.date.toSecsSinceEpoch()); | ||
1288 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, ftpEnt.access); | 1281 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, ftpEnt.access); | ||
1289 | entry.fastInsert(KIO::UDSEntry::UDS_USER, ftpEnt.owner); | 1282 | entry.fastInsert(KIO::UDSEntry::UDS_USER, ftpEnt.owner); | ||
Show All 17 Lines | 1287 | if (!ftpEnt.link.isEmpty()) { | |||
1307 | } | 1300 | } | ||
1308 | } | 1301 | } | ||
1309 | 1302 | | |||
1310 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : ftpEnt.type); | 1303 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : ftpEnt.type); | ||
1311 | // entry.insert KIO::UDSEntry::UDS_ACCESS_TIME,buff.st_atime); | 1304 | // entry.insert KIO::UDSEntry::UDS_ACCESS_TIME,buff.st_atime); | ||
1312 | // entry.insert KIO::UDSEntry::UDS_CREATION_TIME,buff.st_ctime); | 1305 | // entry.insert KIO::UDSEntry::UDS_CREATION_TIME,buff.st_ctime); | ||
1313 | } | 1306 | } | ||
1314 | 1307 | | |||
1315 | void Ftp::ftpShortStatAnswer(const QString &filename, bool isDir) | 1308 | void FtpInternal::ftpShortStatAnswer(const QString &filename, bool isDir) | ||
1316 | { | 1309 | { | ||
1317 | UDSEntry entry; | 1310 | UDSEntry entry; | ||
1318 | 1311 | | |||
1319 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | 1312 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | ||
1320 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : S_IFREG); | 1313 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, isDir ? S_IFDIR : S_IFREG); | ||
1321 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | 1314 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | ||
1322 | if (isDir) { | 1315 | if (isDir) { | ||
1323 | entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); | 1316 | entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); | ||
1324 | } | 1317 | } | ||
1325 | // No details about size, ownership, group, etc. | 1318 | // No details about size, ownership, group, etc. | ||
1326 | 1319 | | |||
1327 | statEntry(entry); | 1320 | q->statEntry(entry); | ||
1328 | finished(); | | |||
1329 | } | 1321 | } | ||
1330 | 1322 | | |||
1331 | void Ftp::ftpStatAnswerNotFound(const QString &path, const QString &filename) | 1323 | Result FtpInternal::ftpStatAnswerNotFound(const QString &path, const QString &filename) | ||
1332 | { | 1324 | { | ||
1333 | // Only do the 'hack' below if we want to download an existing file (i.e. when looking at the "source") | 1325 | // Only do the 'hack' below if we want to download an existing file (i.e. when looking at the "source") | ||
1334 | // When e.g. uploading a file, we still need stat() to return "not found" | 1326 | // When e.g. uploading a file, we still need stat() to return "not found" | ||
1335 | // when the file doesn't exist. | 1327 | // when the file doesn't exist. | ||
1336 | QString statSide = metaData(QStringLiteral("statSide")); | 1328 | QString statSide = q->metaData(QStringLiteral("statSide")); | ||
1337 | qCDebug(KIO_FTP) << "statSide=" << statSide; | 1329 | qCDebug(KIO_FTP) << "statSide=" << statSide; | ||
1338 | if (statSide == QLatin1String("source")) { | 1330 | if (statSide == QLatin1String("source")) { | ||
1339 | qCDebug(KIO_FTP) << "Not found, but assuming found, because some servers don't allow listing"; | 1331 | qCDebug(KIO_FTP) << "Not found, but assuming found, because some servers don't allow listing"; | ||
1340 | // MS Server is incapable of handling "list <blah>" in a case insensitive way | 1332 | // MS Server is incapable of handling "list <blah>" in a case insensitive way | ||
1341 | // But "retr <blah>" works. So lie in stat(), to get going... | 1333 | // But "retr <blah>" works. So lie in stat(), to get going... | ||
1342 | // | 1334 | // | ||
1343 | // There's also the case of ftp://ftp2.3ddownloads.com/90380/linuxgames/loki/patches/ut/ut-patch-436.run | 1335 | // There's also the case of ftp://ftp2.3ddownloads.com/90380/linuxgames/loki/patches/ut/ut-patch-436.run | ||
1344 | // where listing permissions are denied, but downloading is still possible. | 1336 | // where listing permissions are denied, but downloading is still possible. | ||
1345 | ftpShortStatAnswer(filename, false /*file, not dir*/); | 1337 | ftpShortStatAnswer(filename, false /*file, not dir*/); | ||
1346 | 1338 | | |||
1347 | return; | 1339 | return Result::pass(); | ||
1348 | } | 1340 | } | ||
1349 | 1341 | | |||
1350 | error(ERR_DOES_NOT_EXIST, path); | 1342 | return Result::fail(ERR_DOES_NOT_EXIST, path); | ||
1351 | } | 1343 | } | ||
1352 | 1344 | | |||
1353 | void Ftp::stat(const QUrl &url) | 1345 | Result FtpInternal::stat(const QUrl &url) | ||
1354 | { | 1346 | { | ||
1355 | qCDebug(KIO_FTP) << "path=" << url.path(); | 1347 | qCDebug(KIO_FTP) << "path=" << url.path(); | ||
1356 | if (!ftpOpenConnection(loginImplicit)) { | 1348 | auto result = ftpOpenConnection(LoginMode::Implicit); | ||
1357 | return; | 1349 | if (!result.success) { | ||
1350 | return result; | ||||
1358 | } | 1351 | } | ||
1359 | 1352 | | |||
1360 | const QString path = ftpCleanPath(QDir::cleanPath(url.path())); | 1353 | const QString path = ftpCleanPath(QDir::cleanPath(url.path())); | ||
1361 | qCDebug(KIO_FTP) << "cleaned path=" << path; | 1354 | qCDebug(KIO_FTP) << "cleaned path=" << path; | ||
1362 | 1355 | | |||
1363 | // We can't stat root, but we know it's a dir. | 1356 | // We can't stat root, but we know it's a dir. | ||
1364 | if (path.isEmpty() || path == QLatin1String("/")) { | 1357 | if (path.isEmpty() || path == QLatin1String("/")) { | ||
1365 | UDSEntry entry; | 1358 | UDSEntry entry; | ||
1366 | //entry.insert( KIO::UDSEntry::UDS_NAME, UDSField( QString() ) ); | 1359 | //entry.insert( KIO::UDSEntry::UDS_NAME, UDSField( QString() ) ); | ||
1367 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); | 1360 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); | ||
1368 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); | 1361 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); | ||
1369 | entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); | 1362 | entry.fastInsert(KIO::UDSEntry::UDS_MIME_TYPE, QStringLiteral("inode/directory")); | ||
1370 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | 1363 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | ||
1371 | entry.fastInsert(KIO::UDSEntry::UDS_USER, QStringLiteral("root")); | 1364 | entry.fastInsert(KIO::UDSEntry::UDS_USER, QStringLiteral("root")); | ||
1372 | entry.fastInsert(KIO::UDSEntry::UDS_GROUP, QStringLiteral("root")); | 1365 | entry.fastInsert(KIO::UDSEntry::UDS_GROUP, QStringLiteral("root")); | ||
1373 | // no size | 1366 | // no size | ||
1374 | 1367 | | |||
1375 | statEntry(entry); | 1368 | q->statEntry(entry); | ||
1376 | finished(); | 1369 | return Result::pass(); | ||
1377 | return; | | |||
1378 | } | 1370 | } | ||
1379 | 1371 | | |||
1380 | QUrl tempurl(url); | 1372 | QUrl tempurl(url); | ||
1381 | tempurl.setPath(path); // take the clean one | 1373 | tempurl.setPath(path); // take the clean one | ||
1382 | QString listarg; // = tempurl.directory(QUrl::ObeyTrailingSlash); | 1374 | QString listarg; // = tempurl.directory(QUrl::ObeyTrailingSlash); | ||
1383 | QString parentDir; | 1375 | QString parentDir; | ||
1384 | QString filename = tempurl.fileName(); | 1376 | QString filename = tempurl.fileName(); | ||
1385 | Q_ASSERT(!filename.isEmpty()); | 1377 | Q_ASSERT(!filename.isEmpty()); | ||
1386 | QString search = filename; | 1378 | QString search = filename; | ||
1387 | 1379 | | |||
1388 | // Try cwd into it, if it works it's a dir (and then we'll list the parent directory to get more info) | 1380 | // Try cwd into it, if it works it's a dir (and then we'll list the parent directory to get more info) | ||
1389 | // if it doesn't work, it's a file (and then we'll use dir filename) | 1381 | // if it doesn't work, it's a file (and then we'll use dir filename) | ||
1390 | bool isDir = ftpFolder(path, false); | 1382 | bool isDir = ftpFolder(path); | ||
1391 | 1383 | | |||
1392 | // if we're only interested in "file or directory", we should stop here | 1384 | // if we're only interested in "file or directory", we should stop here | ||
1393 | QString sDetails = metaData(QStringLiteral("details")); | 1385 | QString sDetails = q->metaData(QStringLiteral("details")); | ||
1394 | int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); | 1386 | int details = sDetails.isEmpty() ? 2 : sDetails.toInt(); | ||
1395 | qCDebug(KIO_FTP) << "details=" << details; | 1387 | qCDebug(KIO_FTP) << "details=" << details; | ||
1396 | if (details == 0) { | 1388 | if (details == 0) { | ||
1397 | if (!isDir && !ftpFileExists(path)) { // ok, not a dir -> is it a file ? | 1389 | if (!isDir && !ftpFileExists(path)) { // ok, not a dir -> is it a file ? | ||
1398 | // no -> it doesn't exist at all | 1390 | // no -> it doesn't exist at all | ||
1399 | ftpStatAnswerNotFound(path, filename); | 1391 | return ftpStatAnswerNotFound(path, filename); | ||
1400 | return; | | |||
1401 | } | 1392 | } | ||
1402 | ftpShortStatAnswer(filename, isDir); // successfully found a dir or a file -> done | 1393 | ftpShortStatAnswer(filename, isDir); | ||
1403 | return; | 1394 | return Result::pass(); // successfully found a dir or a file -> done | ||
1404 | } | 1395 | } | ||
1405 | 1396 | | |||
1406 | if (!isDir) { | 1397 | if (!isDir) { | ||
1407 | // It is a file or it doesn't exist, try going to parent directory | 1398 | // It is a file or it doesn't exist, try going to parent directory | ||
1408 | parentDir = tempurl.adjusted(QUrl::RemoveFilename).path(); | 1399 | parentDir = tempurl.adjusted(QUrl::RemoveFilename).path(); | ||
1409 | // With files we can do "LIST <filename>" to avoid listing the whole dir | 1400 | // With files we can do "LIST <filename>" to avoid listing the whole dir | ||
1410 | listarg = filename; | 1401 | listarg = filename; | ||
1411 | } else { | 1402 | } else { | ||
1412 | // --- New implementation: | 1403 | // --- New implementation: | ||
1413 | // Don't list the parent dir. Too slow, might not show it, etc. | 1404 | // Don't list the parent dir. Too slow, might not show it, etc. | ||
1414 | // Just return that it's a dir. | 1405 | // Just return that it's a dir. | ||
1415 | UDSEntry entry; | 1406 | UDSEntry entry; | ||
1416 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | 1407 | entry.fastInsert(KIO::UDSEntry::UDS_NAME, filename); | ||
1417 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); | 1408 | entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); | ||
1418 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | 1409 | entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH); | ||
1419 | // No clue about size, ownership, group, etc. | 1410 | // No clue about size, ownership, group, etc. | ||
1420 | 1411 | | |||
1421 | statEntry(entry); | 1412 | q->statEntry(entry); | ||
1422 | finished(); | 1413 | return Result::pass(); | ||
1423 | return; | | |||
1424 | } | 1414 | } | ||
1425 | 1415 | | |||
1426 | // Now cwd the parent dir, to prepare for listing | 1416 | // Now cwd the parent dir, to prepare for listing | ||
1427 | if (!ftpFolder(parentDir, true)) { | 1417 | if (!ftpFolder(parentDir)) { | ||
1428 | return; | 1418 | return Result::fail(ERR_CANNOT_ENTER_DIRECTORY, parentDir); | ||
1429 | } | 1419 | } | ||
1430 | 1420 | | |||
1431 | if (!ftpOpenCommand("list", listarg, 'I', ERR_DOES_NOT_EXIST)) { | 1421 | result = ftpOpenCommand("list", listarg, 'I', ERR_DOES_NOT_EXIST); | ||
1422 | if (!result.success) { | ||||
1432 | qCritical() << "COULD NOT LIST"; | 1423 | qCritical() << "COULD NOT LIST"; | ||
1433 | return; | 1424 | return result; | ||
1434 | } | 1425 | } | ||
1435 | qCDebug(KIO_FTP) << "Starting of list was ok"; | 1426 | qCDebug(KIO_FTP) << "Starting of list was ok"; | ||
1436 | 1427 | | |||
1437 | Q_ASSERT(!search.isEmpty() && search != QLatin1String("/")); | 1428 | Q_ASSERT(!search.isEmpty() && search != QLatin1String("/")); | ||
1438 | 1429 | | |||
1439 | bool bFound = false; | 1430 | bool bFound = false; | ||
1440 | QUrl linkURL; | 1431 | QUrl linkURL; | ||
1441 | FtpEntry ftpEnt; | 1432 | FtpEntry ftpEnt; | ||
Show All 18 Lines | 1448 | for (int i = 0, count = ftpValidateEntList.count(); i < count; ++i) { | |||
1460 | if (maybeEmitStatEntry(ftpEnt, search, filename, isDir)) { | 1451 | if (maybeEmitStatEntry(ftpEnt, search, filename, isDir)) { | ||
1461 | break; | 1452 | break; | ||
1462 | } | 1453 | } | ||
1463 | } | 1454 | } | ||
1464 | 1455 | | |||
1465 | ftpCloseCommand(); // closes the data connection only | 1456 | ftpCloseCommand(); // closes the data connection only | ||
1466 | 1457 | | |||
1467 | if (!bFound) { | 1458 | if (!bFound) { | ||
1468 | ftpStatAnswerNotFound(path, filename); | 1459 | return ftpStatAnswerNotFound(path, filename); | ||
1469 | return; | | |||
1470 | } | 1460 | } | ||
1471 | 1461 | | |||
1472 | if (!linkURL.isEmpty()) { | 1462 | if (!linkURL.isEmpty()) { | ||
1473 | if (linkURL == url || linkURL == tempurl) { | 1463 | if (linkURL == url || linkURL == tempurl) { | ||
1474 | error(ERR_CYCLIC_LINK, linkURL.toString()); | 1464 | return Result::fail(ERR_CYCLIC_LINK, linkURL.toString()); | ||
1475 | return; | | |||
1476 | } | 1465 | } | ||
1477 | Ftp::stat(linkURL); | 1466 | return FtpInternal::stat(linkURL); | ||
That's because it was a recursive call, so it left the callee in charge of emitting error/finished. Now that error codes are returned, your change is correct, you can remove the pragma. dfaure: That's because it was a recursive call, so it left the callee in charge of emitting… | |||||
1478 | return; | | |||
1479 | } | 1467 | } | ||
1480 | 1468 | | |||
1481 | qCDebug(KIO_FTP) << "stat : finished successfully"; | 1469 | qCDebug(KIO_FTP) << "stat : finished successfully";; | ||
1482 | finished(); | 1470 | return Result::pass(); | ||
1483 | } | 1471 | } | ||
1484 | 1472 | | |||
1485 | bool Ftp::maybeEmitStatEntry(FtpEntry &ftpEnt, const QString &search, const QString &filename, bool isDir) | 1473 | bool FtpInternal::maybeEmitStatEntry(FtpEntry &ftpEnt, const QString &search, const QString &filename, bool isDir) | ||
1486 | { | 1474 | { | ||
1487 | if ((search == ftpEnt.name || filename == ftpEnt.name) && !filename.isEmpty()) { | 1475 | if ((search == ftpEnt.name || filename == ftpEnt.name) && !filename.isEmpty()) { | ||
1488 | UDSEntry entry; | 1476 | UDSEntry entry; | ||
1489 | ftpCreateUDSEntry(filename, ftpEnt, entry, isDir); | 1477 | ftpCreateUDSEntry(filename, ftpEnt, entry, isDir); | ||
1490 | statEntry(entry); | 1478 | q->statEntry(entry); | ||
1491 | return true; | 1479 | return true; | ||
1492 | } | 1480 | } | ||
1493 | 1481 | | |||
1494 | return false; | 1482 | return false; | ||
1495 | } | 1483 | } | ||
1496 | 1484 | | |||
1497 | void Ftp::listDir(const QUrl &url) | 1485 | Result FtpInternal::listDir(const QUrl &url) | ||
1498 | { | 1486 | { | ||
1499 | qCDebug(KIO_FTP) << url; | 1487 | qCDebug(KIO_FTP) << url; | ||
1500 | if (!ftpOpenConnection(loginImplicit)) { | 1488 | auto result = ftpOpenConnection(LoginMode::Implicit); | ||
1501 | return; | 1489 | if (!result.success) { | ||
1490 | return result; | ||||
1502 | } | 1491 | } | ||
1503 | 1492 | | |||
1504 | // No path specified ? | 1493 | // No path specified ? | ||
1505 | QString path = url.path(); | 1494 | QString path = url.path(); | ||
1506 | if (path.isEmpty()) { | 1495 | if (path.isEmpty()) { | ||
1507 | QUrl realURL; | 1496 | QUrl realURL; | ||
1508 | realURL.setScheme(QStringLiteral("ftp")); | 1497 | realURL.setScheme(QStringLiteral("ftp")); | ||
1509 | realURL.setUserName(m_user); | 1498 | realURL.setUserName(m_user); | ||
1510 | realURL.setPassword(m_pass); | 1499 | realURL.setPassword(m_pass); | ||
1511 | realURL.setHost(m_host); | 1500 | realURL.setHost(m_host); | ||
1512 | if (m_port > 0 && m_port != DEFAULT_FTP_PORT) { | 1501 | if (m_port > 0 && m_port != DEFAULT_FTP_PORT) { | ||
1513 | realURL.setPort(m_port); | 1502 | realURL.setPort(m_port); | ||
1514 | } | 1503 | } | ||
1515 | if (m_initialPath.isEmpty()) { | 1504 | if (m_initialPath.isEmpty()) { | ||
1516 | m_initialPath = QStringLiteral("/"); | 1505 | m_initialPath = QStringLiteral("/"); | ||
1517 | } | 1506 | } | ||
1518 | realURL.setPath(m_initialPath); | 1507 | realURL.setPath(m_initialPath); | ||
1519 | qCDebug(KIO_FTP) << "REDIRECTION to " << realURL; | 1508 | qCDebug(KIO_FTP) << "REDIRECTION to " << realURL; | ||
1520 | redirection(realURL); | 1509 | q->redirection(realURL); | ||
1521 | finished(); | 1510 | return Result::pass(); | ||
1522 | return; | | |||
1523 | } | 1511 | } | ||
1524 | 1512 | | |||
1525 | qCDebug(KIO_FTP) << "hunting for path" << path; | 1513 | qCDebug(KIO_FTP) << "hunting for path" << path; | ||
1526 | 1514 | | |||
1527 | if (!ftpOpenDir(path)) { | 1515 | result = ftpOpenDir(path); | ||
1516 | if (!result.success) { | ||||
1528 | if (ftpFileExists(path)) { | 1517 | if (ftpFileExists(path)) { | ||
1529 | error(ERR_IS_FILE, path); | 1518 | return Result::fail(ERR_IS_FILE, path); | ||
1530 | } else { | 1519 | } | ||
1531 | // not sure which to emit | 1520 | // not sure which to emit | ||
1532 | //error( ERR_DOES_NOT_EXIST, path ); | 1521 | //error( ERR_DOES_NOT_EXIST, path ); | ||
1533 | error(ERR_CANNOT_ENTER_DIRECTORY, path); | 1522 | return Result::fail(ERR_CANNOT_ENTER_DIRECTORY, path); | ||
1534 | } | | |||
1535 | return; | | |||
1536 | } | 1523 | } | ||
1537 | 1524 | | |||
1538 | UDSEntry entry; | 1525 | UDSEntry entry; | ||
1539 | FtpEntry ftpEnt; | 1526 | FtpEntry ftpEnt; | ||
1540 | QList<FtpEntry> ftpValidateEntList; | 1527 | QList<FtpEntry> ftpValidateEntList; | ||
1541 | while (ftpReadDir(ftpEnt)) { | 1528 | while (ftpReadDir(ftpEnt)) { | ||
1542 | qCDebug(KIO_FTP) << ftpEnt.name; | 1529 | qCDebug(KIO_FTP) << ftpEnt.name; | ||
1543 | //Q_ASSERT( !ftpEnt.name.isEmpty() ); | 1530 | //Q_ASSERT( !ftpEnt.name.isEmpty() ); | ||
1544 | if (!ftpEnt.name.isEmpty()) { | 1531 | if (!ftpEnt.name.isEmpty()) { | ||
1545 | if (ftpEnt.name.at(0).isSpace()) { | 1532 | if (ftpEnt.name.at(0).isSpace()) { | ||
1546 | ftpValidateEntList.append(ftpEnt); | 1533 | ftpValidateEntList.append(ftpEnt); | ||
1547 | continue; | 1534 | continue; | ||
1548 | } | 1535 | } | ||
1549 | 1536 | | |||
1550 | //if ( S_ISDIR( (mode_t)ftpEnt.type ) ) | 1537 | //if ( S_ISDIR( (mode_t)ftpEnt.type ) ) | ||
1551 | // qDebug() << "is a dir"; | 1538 | // qDebug() << "is a dir"; | ||
1552 | //if ( !ftpEnt.link.isEmpty() ) | 1539 | //if ( !ftpEnt.link.isEmpty() ) | ||
1553 | // qDebug() << "is a link to " << ftpEnt.link; | 1540 | // qDebug() << "is a link to " << ftpEnt.link; | ||
1554 | ftpCreateUDSEntry(ftpEnt.name, ftpEnt, entry, false); | 1541 | ftpCreateUDSEntry(ftpEnt.name, ftpEnt, entry, false); | ||
1555 | listEntry(entry); | 1542 | q->listEntry(entry); | ||
1556 | entry.clear(); | 1543 | entry.clear(); | ||
1557 | } | 1544 | } | ||
1558 | } | 1545 | } | ||
1559 | 1546 | | |||
1560 | for (int i = 0, count = ftpValidateEntList.count(); i < count; ++i) { | 1547 | for (int i = 0, count = ftpValidateEntList.count(); i < count; ++i) { | ||
1561 | FtpEntry &ftpEnt = ftpValidateEntList[i]; | 1548 | FtpEntry &ftpEnt = ftpValidateEntList[i]; | ||
1562 | fixupEntryName(&ftpEnt); | 1549 | fixupEntryName(&ftpEnt); | ||
1563 | ftpCreateUDSEntry(ftpEnt.name, ftpEnt, entry, false); | 1550 | ftpCreateUDSEntry(ftpEnt.name, ftpEnt, entry, false); | ||
1564 | listEntry(entry); | 1551 | q->listEntry(entry); | ||
1565 | entry.clear(); | 1552 | entry.clear(); | ||
1566 | } | 1553 | } | ||
1567 | 1554 | | |||
1568 | ftpCloseCommand(); // closes the data connection only | 1555 | ftpCloseCommand(); // closes the data connection only | ||
1569 | finished(); | 1556 | return Result::pass(); | ||
1570 | } | 1557 | } | ||
1571 | 1558 | | |||
1572 | void Ftp::slave_status() | 1559 | void FtpInternal::slave_status() | ||
1573 | { | 1560 | { | ||
1574 | qCDebug(KIO_FTP) << "Got slave_status host = " << (!m_host.toLatin1().isEmpty() ? m_host.toLatin1() : "[None]") << " [" << (m_bLoggedOn ? "Connected" : "Not connected") << "]"; | 1561 | qCDebug(KIO_FTP) << "Got slave_status host = " << (!m_host.toLatin1().isEmpty() ? m_host.toLatin1() : "[None]") << " [" << (m_bLoggedOn ? "Connected" : "Not connected") << "]"; | ||
1575 | slaveStatus(m_host, m_bLoggedOn); | 1562 | q->slaveStatus(m_host, m_bLoggedOn); | ||
1576 | } | 1563 | } | ||
1577 | 1564 | | |||
1578 | bool Ftp::ftpOpenDir(const QString &path) | 1565 | Result FtpInternal::ftpOpenDir(const QString &path) | ||
1579 | { | 1566 | { | ||
1580 | //QString path( _url.path(QUrl::RemoveTrailingSlash) ); | 1567 | //QString path( _url.path(QUrl::RemoveTrailingSlash) ); | ||
1581 | 1568 | | |||
1582 | // We try to change to this directory first to see whether it really is a directory. | 1569 | // We try to change to this directory first to see whether it really is a directory. | ||
1583 | // (And also to follow symlinks) | 1570 | // (And also to follow symlinks) | ||
1584 | QString tmp = path.isEmpty() ? QStringLiteral("/") : path; | 1571 | QString tmp = path.isEmpty() ? QStringLiteral("/") : path; | ||
1585 | 1572 | | |||
1586 | // We get '550', whether it's a file or doesn't exist... | 1573 | // We get '550', whether it's a file or doesn't exist... | ||
1587 | if (!ftpFolder(tmp, false)) { | 1574 | if (!ftpFolder(tmp)) { | ||
1588 | return false; | 1575 | return Result::fail(); | ||
1589 | } | 1576 | } | ||
1590 | 1577 | | |||
1591 | // Don't use the path in the list command: | 1578 | // Don't use the path in the list command: | ||
1592 | // We changed into this directory anyway - so it's enough just to send "list". | 1579 | // We changed into this directory anyway - so it's enough just to send "list". | ||
1593 | // We use '-a' because the application MAY be interested in dot files. | 1580 | // We use '-a' because the application MAY be interested in dot files. | ||
1594 | // The only way to really know would be to have a metadata flag for this... | 1581 | // The only way to really know would be to have a metadata flag for this... | ||
1595 | // Since some windows ftp server seems not to support the -a argument, we use a fallback here. | 1582 | // Since some windows ftp server seems not to support the -a argument, we use a fallback here. | ||
1596 | // In fact we have to use -la otherwise -a removes the default -l (e.g. ftp.trolltech.com) | 1583 | // In fact we have to use -la otherwise -a removes the default -l (e.g. ftp.trolltech.com) | ||
1597 | // Pass KJob::NoError first because we don't want to emit error before we | 1584 | // Pass KJob::NoError first because we don't want to emit error before we | ||
1598 | // have tried all commands. | 1585 | // have tried all commands. | ||
1599 | if (!ftpOpenCommand("list -la", QString(), 'I', KJob::NoError)) { | 1586 | auto result = ftpOpenCommand("list -la", QString(), 'I', KJob::NoError); | ||
1600 | if (!ftpOpenCommand("list", QString(), 'I', KJob::NoError)) { | 1587 | if (!result.success) { | ||
1588 | result = ftpOpenCommand("list", QString(), 'I', KJob::NoError); | ||||
1589 | } | ||||
1590 | if (!result.success) { | ||||
1601 | // Servers running with Turkish locale having problems converting 'i' letter to upper case. | 1591 | // Servers running with Turkish locale having problems converting 'i' letter to upper case. | ||
1602 | // So we send correct upper case command as last resort. | 1592 | // So we send correct upper case command as last resort. | ||
dfaure: What happened to those two lines of comments? Can I suggest to keep them? | |||||
Oh! Collateral damage from removing the nesting I guess. Comments are back now. sitter: Oh! Collateral damage from removing the nesting I guess. Comments are back now. | |||||
1603 | if (!ftpOpenCommand("LIST -la", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY)) { | 1593 | result = ftpOpenCommand("LIST -la", QString(), 'I', ERR_CANNOT_ENTER_DIRECTORY); | ||
1604 | qCWarning(KIO_FTP) << "Can't open for listing"; | | |||
1605 | return false; | | |||
1606 | } | | |||
1607 | } | 1594 | } | ||
1595 | | ||||
1596 | if (!result.success) { | ||||
1597 | qCWarning(KIO_FTP) << "Can't open for listing"; | ||||
1598 | return result; | ||||
1608 | } | 1599 | } | ||
1600 | | ||||
1609 | qCDebug(KIO_FTP) << "Starting of list was ok"; | 1601 | qCDebug(KIO_FTP) << "Starting of list was ok"; | ||
1610 | return true; | 1602 | return Result::pass(); | ||
1611 | } | 1603 | } | ||
1612 | 1604 | | |||
1613 | bool Ftp::ftpReadDir(FtpEntry &de) | 1605 | bool FtpInternal::ftpReadDir(FtpEntry &de) | ||
1614 | { | 1606 | { | ||
1615 | Q_ASSERT(m_data); | 1607 | Q_ASSERT(m_data); | ||
1616 | 1608 | | |||
1617 | // get a line from the data connection ... | 1609 | // get a line from the data connection ... | ||
1618 | while (true) { | 1610 | while (true) { | ||
1619 | while (!m_data->canReadLine() && m_data->waitForReadyRead((readTimeout() * 1000))) {} | 1611 | while (!m_data->canReadLine() && m_data->waitForReadyRead((q->readTimeout() * 1000))) {} | ||
1620 | QByteArray data = m_data->readLine(); | 1612 | QByteArray data = m_data->readLine(); | ||
1621 | if (data.size() == 0) { | 1613 | if (data.size() == 0) { | ||
1622 | break; | 1614 | break; | ||
1623 | } | 1615 | } | ||
1624 | 1616 | | |||
1625 | const char *buffer = data.data(); | 1617 | const char *buffer = data.data(); | ||
1626 | qCDebug(KIO_FTP) << "dir > " << buffer; | 1618 | qCDebug(KIO_FTP) << "dir > " << buffer; | ||
1627 | 1619 | | |||
▲ Show 20 Lines • Show All 68 Lines • ▼ Show 20 Line(s) | 1687 | if (p_date_1 != nullptr && | |||
1696 | p_date_2 != nullptr && | 1688 | p_date_2 != nullptr && | ||
1697 | (p_date_3 = strtok(nullptr, " ")) != nullptr && | 1689 | (p_date_3 = strtok(nullptr, " ")) != nullptr && | ||
1698 | (p_name = strtok(nullptr, "\r\n")) != nullptr) { | 1690 | (p_name = strtok(nullptr, "\r\n")) != nullptr) { | ||
1699 | { | 1691 | { | ||
1700 | QByteArray tmp(p_name); | 1692 | QByteArray tmp(p_name); | ||
1701 | if (p_access[0] == 'l') { | 1693 | if (p_access[0] == 'l') { | ||
1702 | int i = tmp.lastIndexOf(" -> "); | 1694 | int i = tmp.lastIndexOf(" -> "); | ||
1703 | if (i != -1) { | 1695 | if (i != -1) { | ||
1704 | de.link = remoteEncoding()->decode(p_name + i + 4); | 1696 | de.link = q->remoteEncoding()->decode(p_name + i + 4); | ||
1705 | tmp.truncate(i); | 1697 | tmp.truncate(i); | ||
1706 | } else { | 1698 | } else { | ||
1707 | de.link.clear(); | 1699 | de.link.clear(); | ||
1708 | } | 1700 | } | ||
1709 | } else { | 1701 | } else { | ||
1710 | de.link.clear(); | 1702 | de.link.clear(); | ||
1711 | } | 1703 | } | ||
1712 | 1704 | | |||
1713 | if (tmp[0] == '/') { // listing on ftp://ftp.gnupg.org/ starts with '/' | 1705 | if (tmp[0] == '/') { // listing on ftp://ftp.gnupg.org/ starts with '/' | ||
1714 | tmp.remove(0, 1); | 1706 | tmp.remove(0, 1); | ||
1715 | } | 1707 | } | ||
1716 | 1708 | | |||
1717 | if (tmp.indexOf('/') != -1) { | 1709 | if (tmp.indexOf('/') != -1) { | ||
1718 | continue; // Don't trick us! | 1710 | continue; // Don't trick us! | ||
1719 | } | 1711 | } | ||
1720 | 1712 | | |||
1721 | de.name = remoteEncoding()->decode(tmp); | 1713 | de.name = q->remoteEncoding()->decode(tmp); | ||
1722 | } | 1714 | } | ||
1723 | 1715 | | |||
1724 | de.type = S_IFREG; | 1716 | de.type = S_IFREG; | ||
1725 | switch (p_access[0]) { | 1717 | switch (p_access[0]) { | ||
1726 | case 'd': | 1718 | case 'd': | ||
1727 | de.type = S_IFDIR; | 1719 | de.type = S_IFDIR; | ||
1728 | break; | 1720 | break; | ||
1729 | case 's': | 1721 | case 's': | ||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Line(s) | |||||
1775 | } | 1767 | } | ||
1776 | if (p_access[6] == 's' || p_access[6] == 'S') { | 1768 | if (p_access[6] == 's' || p_access[6] == 'S') { | ||
1777 | de.access |= S_ISGID; | 1769 | de.access |= S_ISGID; | ||
1778 | } | 1770 | } | ||
1779 | if (p_access[9] == 't' || p_access[9] == 'T') { | 1771 | if (p_access[9] == 't' || p_access[9] == 'T') { | ||
1780 | de.access |= S_ISVTX; | 1772 | de.access |= S_ISVTX; | ||
1781 | } | 1773 | } | ||
1782 | 1774 | | |||
1783 | de.owner = remoteEncoding()->decode(p_owner); | 1775 | de.owner = q->remoteEncoding()->decode(p_owner); | ||
1784 | de.group = remoteEncoding()->decode(p_group); | 1776 | de.group = q->remoteEncoding()->decode(p_group); | ||
1785 | de.size = charToLongLong(p_size); | 1777 | de.size = charToLongLong(p_size); | ||
1786 | 1778 | | |||
1787 | // Parsing the date is somewhat tricky | 1779 | // Parsing the date is somewhat tricky | ||
1788 | // Examples : "Oct 6 22:49", "May 13 1999" | 1780 | // Examples : "Oct 6 22:49", "May 13 1999" | ||
1789 | 1781 | | |||
1790 | // First get current date - we need the current month and year | 1782 | // First get current date - we need the current month and year | ||
1791 | QDate currentDate(QDate::currentDate()); | 1783 | QDate currentDate(QDate::currentDate()); | ||
1792 | int currentMonth = currentDate.month(); | 1784 | int currentMonth = currentDate.month(); | ||
▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Line(s) | |||||
1845 | } // line invalid, loop to get another line | 1837 | } // line invalid, loop to get another line | ||
1846 | return false; | 1838 | return false; | ||
1847 | } | 1839 | } | ||
1848 | 1840 | | |||
1849 | //=============================================================================== | 1841 | //=============================================================================== | ||
1850 | // public: get download file from server | 1842 | // public: get download file from server | ||
1851 | // helper: ftpGet called from get() and copy() | 1843 | // helper: ftpGet called from get() and copy() | ||
1852 | //=============================================================================== | 1844 | //=============================================================================== | ||
1853 | void Ftp::get(const QUrl &url) | 1845 | Result FtpInternal::get(const QUrl &url) | ||
1854 | { | 1846 | { | ||
1855 | qCDebug(KIO_FTP) << url; | 1847 | qCDebug(KIO_FTP) << url; | ||
1856 | 1848 | const Result result = ftpGet(-1, QString(), url, 0); | |||
1857 | int iError = 0; | | |||
1858 | const StatusCode cs = ftpGet(iError, -1, url, 0); | | |||
1859 | ftpCloseCommand(); // must close command! | 1849 | ftpCloseCommand(); // must close command! | ||
1860 | 1850 | return result; | |||
1861 | if (cs == statusSuccess) { | | |||
1862 | finished(); | | |||
1863 | return; | | |||
1864 | } | 1851 | } | ||
1865 | 1852 | | |||
1866 | if (iError) { // can have only server side errs | 1853 | Result FtpInternal::ftpGet(int iCopyFile, const QString &sCopyFile, const QUrl &url, KIO::fileoffset_t llOffset) | ||
1867 | error(iError, url.path()); | | |||
1868 | } | | |||
1869 | } | | |||
1870 | | ||||
1871 | Ftp::StatusCode Ftp::ftpGet(int &iError, int iCopyFile, const QUrl &url, KIO::fileoffset_t llOffset) | | |||
1872 | { | 1854 | { | ||
1873 | // Calls error() by itself! | 1855 | auto result = ftpOpenConnection(LoginMode::Implicit); | ||
1874 | if (!ftpOpenConnection(loginImplicit)) { | 1856 | if (!result.success) { | ||
1875 | return statusServerError; | 1857 | return result; | ||
Wasn't that just a way to propagate the error from the low-level method to this method? Your new mechanism replaces that, no? dfaure: Wasn't that just a way to propagate the error from the low-level method to this method? Your… | |||||
I could have sworn there was code that differentiated between server and client. I can't find it anymore... maybe it was all a dream, or maybe I saw it in the history. Oh well, down with the useless enum! sitter: I could have sworn there was code that differentiated between server and client. I can't find… | |||||
1876 | } | 1858 | } | ||
1877 | 1859 | | |||
1878 | // Try to find the size of the file (and check that it exists at | 1860 | // Try to find the size of the file (and check that it exists at | ||
1879 | // the same time). If we get back a 550, "File does not exist" | 1861 | // the same time). If we get back a 550, "File does not exist" | ||
1880 | // or "not a plain file", check if it is a directory. If it is a | 1862 | // or "not a plain file", check if it is a directory. If it is a | ||
1881 | // directory, return an error; otherwise simply try to retrieve | 1863 | // directory, return an error; otherwise simply try to retrieve | ||
1882 | // the request... | 1864 | // the request... | ||
1883 | if (!ftpSize(url.path(), '?') && (m_iRespCode == 550) && | 1865 | if (!ftpSize(url.path(), '?') && (m_iRespCode == 550) && | ||
1884 | ftpFolder(url.path(), false)) { | 1866 | ftpFolder(url.path())) { | ||
1885 | // Ok it's a dir in fact | 1867 | // Ok it's a dir in fact | ||
1886 | qCDebug(KIO_FTP) << "it is a directory in fact"; | 1868 | qCDebug(KIO_FTP) << "it is a directory in fact"; | ||
1887 | iError = ERR_IS_DIRECTORY; | 1869 | return Result::fail(ERR_IS_DIRECTORY); | ||
1888 | return statusServerError; | | |||
1889 | } | 1870 | } | ||
1890 | 1871 | | |||
1891 | QString resumeOffset = metaData(QStringLiteral("range-start")); | 1872 | QString resumeOffset = q->metaData(QStringLiteral("range-start")); | ||
1892 | if (resumeOffset.isEmpty()) { | 1873 | if (resumeOffset.isEmpty()) { | ||
1893 | resumeOffset = metaData(QStringLiteral("resume")); // old name | 1874 | resumeOffset = q->metaData(QStringLiteral("resume")); // old name | ||
1894 | } | 1875 | } | ||
1895 | if (!resumeOffset.isEmpty()) { | 1876 | if (!resumeOffset.isEmpty()) { | ||
1896 | llOffset = resumeOffset.toLongLong(); | 1877 | llOffset = resumeOffset.toLongLong(); | ||
1897 | qCDebug(KIO_FTP) << "got offset from metadata : " << llOffset; | 1878 | qCDebug(KIO_FTP) << "got offset from metadata : " << llOffset; | ||
1898 | } | 1879 | } | ||
1899 | 1880 | | |||
1900 | if (!ftpOpenCommand("retr", url.path(), '?', ERR_CANNOT_OPEN_FOR_READING, llOffset)) { | 1881 | result = ftpOpenCommand("retr", url.path(), '?', ERR_CANNOT_OPEN_FOR_READING, llOffset); | ||
1882 | if (!result.success) { | ||||
1901 | qCWarning(KIO_FTP) << "Can't open for reading"; | 1883 | qCWarning(KIO_FTP) << "Can't open for reading"; | ||
1902 | return statusServerError; | 1884 | return result; | ||
dfaure: Same as above: why do we still need this distinction? Is this enum useful? | |||||
1903 | } | 1885 | } | ||
1904 | 1886 | | |||
1905 | // Read the size from the response string | 1887 | // Read the size from the response string | ||
1906 | if (m_size == UnknownSize) { | 1888 | if (m_size == UnknownSize) { | ||
1907 | const char *psz = strrchr(ftpResponse(4), '('); | 1889 | const char *psz = strrchr(ftpResponse(4), '('); | ||
1908 | if (psz) { | 1890 | if (psz) { | ||
1909 | m_size = charToLongLong(psz + 1); | 1891 | m_size = charToLongLong(psz + 1); | ||
1910 | } | 1892 | } | ||
1911 | if (!m_size) { | 1893 | if (!m_size) { | ||
1912 | m_size = UnknownSize; | 1894 | m_size = UnknownSize; | ||
1913 | } | 1895 | } | ||
1914 | } | 1896 | } | ||
1915 | 1897 | | |||
1916 | // Send the mime-type... | 1898 | // Send the mime-type... | ||
1917 | if (iCopyFile == -1) { | 1899 | if (iCopyFile == -1) { | ||
1918 | StatusCode status = ftpSendMimeType(iError, url); | 1900 | const auto result = ftpSendMimeType(url); | ||
1919 | if (status != statusSuccess) { | 1901 | if (!result.success) { | ||
1920 | return status; | 1902 | return result; | ||
1921 | } | 1903 | } | ||
1922 | } | 1904 | } | ||
1923 | 1905 | | |||
1924 | KIO::filesize_t bytesLeft = 0; | 1906 | KIO::filesize_t bytesLeft = 0; | ||
1925 | if (m_size != UnknownSize) { | 1907 | if (m_size != UnknownSize) { | ||
1926 | bytesLeft = m_size - llOffset; | 1908 | bytesLeft = m_size - llOffset; | ||
1927 | totalSize(m_size); // emit the total size... | 1909 | q->totalSize(m_size); // emit the total size... | ||
1928 | } | 1910 | } | ||
1929 | 1911 | | |||
1930 | qCDebug(KIO_FTP) << "starting with offset=" << llOffset; | 1912 | qCDebug(KIO_FTP) << "starting with offset=" << llOffset; | ||
1931 | KIO::fileoffset_t processed_size = llOffset; | 1913 | KIO::fileoffset_t processed_size = llOffset; | ||
1932 | 1914 | | |||
1933 | QByteArray array; | 1915 | QByteArray array; | ||
1934 | char buffer[maximumIpcSize]; | 1916 | char buffer[maximumIpcSize]; | ||
1935 | // start with small data chunks in case of a slow data source (modem) | 1917 | // start with small data chunks in case of a slow data source (modem) | ||
1936 | // - unfortunately this has a negative impact on performance for large | 1918 | // - unfortunately this has a negative impact on performance for large | ||
1937 | // - files - so we will increase the block size after a while ... | 1919 | // - files - so we will increase the block size after a while ... | ||
1938 | int iBlockSize = initialIpcSize; | 1920 | int iBlockSize = initialIpcSize; | ||
1939 | int iBufferCur = 0; | 1921 | int iBufferCur = 0; | ||
1940 | 1922 | | |||
1941 | while (m_size == UnknownSize || bytesLeft > 0) { | 1923 | while (m_size == UnknownSize || bytesLeft > 0) { | ||
1942 | // let the buffer size grow if the file is larger 64kByte ... | 1924 | // let the buffer size grow if the file is larger 64kByte ... | ||
1943 | if (processed_size - llOffset > 1024 * 64) { | 1925 | if (processed_size - llOffset > 1024 * 64) { | ||
1944 | iBlockSize = maximumIpcSize; | 1926 | iBlockSize = maximumIpcSize; | ||
1945 | } | 1927 | } | ||
1946 | 1928 | | |||
1947 | // read the data and detect EOF or error ... | 1929 | // read the data and detect EOF or error ... | ||
1948 | if (iBlockSize + iBufferCur > (int)sizeof(buffer)) { | 1930 | if (iBlockSize + iBufferCur > (int)sizeof(buffer)) { | ||
1949 | iBlockSize = sizeof(buffer) - iBufferCur; | 1931 | iBlockSize = sizeof(buffer) - iBufferCur; | ||
1950 | } | 1932 | } | ||
1951 | if (m_data->bytesAvailable() == 0) { | 1933 | if (m_data->bytesAvailable() == 0) { | ||
1952 | m_data->waitForReadyRead((readTimeout() * 1000)); | 1934 | m_data->waitForReadyRead((q->readTimeout() * 1000)); | ||
1953 | } | 1935 | } | ||
1954 | int n = m_data->read(buffer + iBufferCur, iBlockSize); | 1936 | int n = m_data->read(buffer + iBufferCur, iBlockSize); | ||
1955 | if (n <= 0) { | 1937 | if (n <= 0) { | ||
1956 | // this is how we detect EOF in case of unknown size | 1938 | // this is how we detect EOF in case of unknown size | ||
1957 | if (m_size == UnknownSize && n == 0) { | 1939 | if (m_size == UnknownSize && n == 0) { | ||
1958 | break; | 1940 | break; | ||
1959 | } | 1941 | } | ||
1960 | // unexpected eof. Happens when the daemon gets killed. | 1942 | // unexpected eof. Happens when the daemon gets killed. | ||
1961 | iError = ERR_CANNOT_READ; | 1943 | return Result::fail(ERR_CANNOT_READ); | ||
1962 | return statusServerError; | | |||
1963 | } | 1944 | } | ||
1964 | processed_size += n; | 1945 | processed_size += n; | ||
1965 | 1946 | | |||
1966 | // collect very small data chunks in buffer before processing ... | 1947 | // collect very small data chunks in buffer before processing ... | ||
1967 | if (m_size != UnknownSize) { | 1948 | if (m_size != UnknownSize) { | ||
1968 | bytesLeft -= n; | 1949 | bytesLeft -= n; | ||
1969 | iBufferCur += n; | 1950 | iBufferCur += n; | ||
1970 | if (iBufferCur < minimumMimeSize && bytesLeft > 0) { | 1951 | if (iBufferCur < minimumMimeSize && bytesLeft > 0) { | ||
1971 | processedSize(processed_size); | 1952 | q->processedSize(processed_size); | ||
1972 | continue; | 1953 | continue; | ||
1973 | } | 1954 | } | ||
1974 | n = iBufferCur; | 1955 | n = iBufferCur; | ||
1975 | iBufferCur = 0; | 1956 | iBufferCur = 0; | ||
1976 | } | 1957 | } | ||
1977 | 1958 | | |||
1978 | // write output file or pass to data pump ... | 1959 | // write output file or pass to data pump ... | ||
1960 | int writeError = 0; | ||||
1979 | if (iCopyFile == -1) { | 1961 | if (iCopyFile == -1) { | ||
1980 | array = QByteArray::fromRawData(buffer, n); | 1962 | array = QByteArray::fromRawData(buffer, n); | ||
1981 | data(array); | 1963 | q->data(array); | ||
1982 | array.clear(); | 1964 | array.clear(); | ||
1983 | } else if ((iError = WriteToFile(iCopyFile, buffer, n)) != 0) { | 1965 | } else if ((writeError = WriteToFile(iCopyFile, buffer, n)) != 0) { | ||
1984 | return statusClientError; // client side error | 1966 | return Result::fail(writeError, sCopyFile); | ||
Yep. Pass sCopyFile as argument, when ftpCopyGet calls ftpGet, just like what happens with iCopyFile. dfaure: Yep. Pass sCopyFile as argument, when ftpCopyGet calls ftpGet, just like what happens with… | |||||
1985 | } | 1967 | } | ||
1986 | processedSize(processed_size); | 1968 | | ||
1969 | Q_ASSERT(processed_size >= 0); | ||||
1970 | q->processedSize(static_cast<KIO::filesize_t>(processed_size)); | ||||
1987 | } | 1971 | } | ||
1988 | 1972 | | |||
1989 | qCDebug(KIO_FTP) << "done"; | 1973 | qCDebug(KIO_FTP) << "done"; | ||
1990 | if (iCopyFile == -1) { // must signal EOF to data pump ... | 1974 | if (iCopyFile == -1) { // must signal EOF to data pump ... | ||
1991 | data(array); // array is empty and must be empty! | 1975 | q->data(array); // array is empty and must be empty! | ||
1992 | } | 1976 | } | ||
1993 | 1977 | | |||
1994 | processedSize(m_size == UnknownSize ? processed_size : m_size); | 1978 | q->processedSize(m_size == UnknownSize ? processed_size : m_size); | ||
1995 | return statusSuccess; | 1979 | return Result::pass(); | ||
1996 | } | 1980 | } | ||
1997 | 1981 | | |||
1998 | #if 0 | 1982 | #if 0 | ||
1999 | void Ftp::mimetype(const QUrl &url) | 1983 | void FtpInternal::mimetype(const QUrl &url) | ||
2000 | { | 1984 | { | ||
2001 | if (!ftpOpenConnection(loginImplicit)) { | 1985 | if (!ftpOpenConnection(loginImplicit)) { | ||
2002 | return; | 1986 | return; | ||
2003 | } | 1987 | } | ||
2004 | 1988 | | |||
2005 | if (!ftpOpenCommand("retr", url.path(), 'I', ERR_CANNOT_OPEN_FOR_READING, 0)) { | 1989 | if (!ftpOpenCommand("retr", url.path(), 'I', ERR_CANNOT_OPEN_FOR_READING, 0)) { | ||
2006 | qCWarning(KIO_FTP) << "Can't open for reading"; | 1990 | qCWarning(KIO_FTP) << "Can't open for reading"; | ||
2007 | return; | 1991 | return; | ||
2008 | } | 1992 | } | ||
2009 | char buffer[ 2048 ]; | 1993 | char buffer[ 2048 ]; | ||
2010 | QByteArray array; | 1994 | QByteArray array; | ||
2011 | // Get one chunk of data only and send it, KIO::Job will determine the | 1995 | // Get one chunk of data only and send it, KIO::Job will determine the | ||
2012 | // mimetype from it using KMimeMagic | 1996 | // mimetype from it using KMimeMagic | ||
2013 | int n = m_data->read(buffer, 2048); | 1997 | int n = m_data->read(buffer, 2048); | ||
2014 | array.setRawData(buffer, n); | 1998 | array.setRawData(buffer, n); | ||
2015 | data(array); | 1999 | data(array); | ||
2016 | array.resetRawData(buffer, n); | 2000 | array.resetRawData(buffer, n); | ||
2017 | 2001 | | |||
2018 | qCDebug(KIO_FTP) << "aborting"; | 2002 | qCDebug(KIO_FTP) << "aborting"; | ||
2019 | ftpAbortTransfer(); | 2003 | ftpAbortTransfer(); | ||
2020 | 2004 | | |||
2021 | qCDebug(KIO_FTP) << "finished"; | 2005 | qCDebug(KIO_FTP) << "finished"; | ||
2022 | finished(); | 2006 | finished(); | ||
dfaure: ? | |||||
dfaure: `finished(Q_FUNC_INFO)` looks like a local hack? | |||||
2023 | qCDebug(KIO_FTP) << "after finished"; | 2007 | qCDebug(KIO_FTP) << "after finished"; | ||
2024 | } | 2008 | } | ||
2025 | 2009 | | |||
2026 | void Ftp::ftpAbortTransfer() | 2010 | void FtpInternal::ftpAbortTransfer() | ||
2027 | { | 2011 | { | ||
2028 | // RFC 959, page 34-35 | 2012 | // RFC 959, page 34-35 | ||
2029 | // IAC (interpret as command) = 255 ; IP (interrupt process) = 254 | 2013 | // IAC (interpret as command) = 255 ; IP (interrupt process) = 254 | ||
2030 | // DM = 242 (data mark) | 2014 | // DM = 242 (data mark) | ||
2031 | char msg[4]; | 2015 | char msg[4]; | ||
2032 | // 1. User system inserts the Telnet "Interrupt Process" (IP) signal | 2016 | // 1. User system inserts the Telnet "Interrupt Process" (IP) signal | ||
2033 | // in the Telnet stream. | 2017 | // in the Telnet stream. | ||
2034 | msg[0] = (char) 255; //IAC | 2018 | msg[0] = (char) 255; //IAC | ||
Show All 24 Lines | |||||
2059 | closeSockets(); | 2043 | closeSockets(); | ||
2060 | } | 2044 | } | ||
2061 | #endif | 2045 | #endif | ||
2062 | 2046 | | |||
2063 | //=============================================================================== | 2047 | //=============================================================================== | ||
2064 | // public: put upload file to server | 2048 | // public: put upload file to server | ||
2065 | // helper: ftpPut called from put() and copy() | 2049 | // helper: ftpPut called from put() and copy() | ||
2066 | //=============================================================================== | 2050 | //=============================================================================== | ||
2067 | void Ftp::put(const QUrl &url, int permissions, KIO::JobFlags flags) | 2051 | Result FtpInternal::put(const QUrl &url, int permissions, KIO::JobFlags flags) | ||
2068 | { | 2052 | { | ||
2069 | qCDebug(KIO_FTP) << url; | 2053 | qCDebug(KIO_FTP) << url; | ||
2070 | 2054 | const auto result = ftpPut(-1, url, permissions, flags); | |||
2071 | int iError = 0; // iError gets status | | |||
2072 | const StatusCode cs = ftpPut(iError, -1, url, permissions, flags); | | |||
2073 | ftpCloseCommand(); // must close command! | 2055 | ftpCloseCommand(); // must close command! | ||
2074 | 2056 | return result; | |||
2075 | if (cs == statusSuccess) { | | |||
2076 | finished(); | | |||
2077 | return; | | |||
2078 | } | | |||
2079 | | ||||
2080 | if (iError) { // can have only server side errs | | |||
2081 | error(iError, url.path()); | | |||
2082 | } | | |||
2083 | } | 2057 | } | ||
2084 | 2058 | | |||
2085 | Ftp::StatusCode Ftp::ftpPut(int &iError, int iCopyFile, const QUrl &dest_url, | 2059 | Result FtpInternal::ftpPut(int iCopyFile, const QUrl &dest_url, | ||
2086 | int permissions, KIO::JobFlags flags) | 2060 | int permissions, KIO::JobFlags flags) | ||
dfaure: outdated warning? | |||||
2087 | { | 2061 | { | ||
2088 | if (!ftpOpenConnection(loginImplicit)) { | 2062 | const auto openResult = ftpOpenConnection(LoginMode::Implicit); | ||
2089 | return statusServerError; | 2063 | if (!openResult.success) { | ||
2064 | return openResult; | ||||
2090 | } | 2065 | } | ||
2091 | 2066 | | |||
2092 | // Don't use mark partial over anonymous FTP. | 2067 | // Don't use mark partial over anonymous FTP. | ||
2093 | // My incoming dir allows put but not rename... | 2068 | // My incoming dir allows put but not rename... | ||
2094 | bool bMarkPartial; | 2069 | bool bMarkPartial; | ||
2095 | if (m_user.isEmpty() || m_user == QLatin1String(FTP_LOGIN)) { | 2070 | if (m_user.isEmpty() || m_user == QLatin1String(FTP_LOGIN)) { | ||
2096 | bMarkPartial = false; | 2071 | bMarkPartial = false; | ||
2097 | } else { | 2072 | } else { | ||
2098 | bMarkPartial = config()->readEntry("MarkPartial", true); | 2073 | bMarkPartial = q->config()->readEntry("MarkPartial", true); | ||
2099 | } | 2074 | } | ||
2100 | 2075 | | |||
2101 | QString dest_orig = dest_url.path(); | 2076 | QString dest_orig = dest_url.path(); | ||
2102 | const QString dest_part = dest_orig + QLatin1String(".part"); | 2077 | const QString dest_part = dest_orig + QLatin1String(".part"); | ||
2103 | 2078 | | |||
2104 | if (ftpSize(dest_orig, 'I')) { | 2079 | if (ftpSize(dest_orig, 'I')) { | ||
2105 | if (m_size == 0) { | 2080 | if (m_size == 0) { | ||
2106 | // delete files with zero size | 2081 | // delete files with zero size | ||
2107 | const QByteArray cmd = "DELE " + remoteEncoding()->encode(dest_orig); | 2082 | const QByteArray cmd = "DELE " + q->remoteEncoding()->encode(dest_orig); | ||
2108 | if (!ftpSendCmd(cmd) || (m_iRespType != 2)) { | 2083 | if (!ftpSendCmd(cmd) || (m_iRespType != 2)) { | ||
2109 | iError = ERR_CANNOT_DELETE_PARTIAL; | 2084 | return Result::fail(ERR_CANNOT_DELETE_PARTIAL, QString()); | ||
2110 | return statusServerError; | | |||
2111 | } | 2085 | } | ||
2112 | } else if (!(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { | 2086 | } else if (!(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { | ||
2113 | iError = ERR_FILE_ALREADY_EXIST; | 2087 | return Result::fail(ERR_FILE_ALREADY_EXIST, QString()); | ||
2114 | return statusServerError; | | |||
2115 | } else if (bMarkPartial) { | 2088 | } else if (bMarkPartial) { | ||
2116 | // when using mark partial, append .part extension | 2089 | // when using mark partial, append .part extension | ||
2117 | if (!ftpRename(dest_orig, dest_part, KIO::Overwrite)) { | 2090 | const auto result = ftpRename(dest_orig, dest_part, KIO::Overwrite); | ||
2118 | iError = ERR_CANNOT_RENAME_PARTIAL; | 2091 | if (!result.success) { | ||
2119 | return statusServerError; | 2092 | return Result::fail(ERR_CANNOT_RENAME_PARTIAL, QString()); | ||
2120 | } | 2093 | } | ||
2121 | } | 2094 | } | ||
2122 | // Don't chmod an existing file | 2095 | // Don't chmod an existing file | ||
2123 | permissions = -1; | 2096 | permissions = -1; | ||
2124 | } else if (bMarkPartial && ftpSize(dest_part, 'I')) { | 2097 | } else if (bMarkPartial && ftpSize(dest_part, 'I')) { | ||
2125 | // file with extension .part exists | 2098 | // file with extension .part exists | ||
2126 | if (m_size == 0) { | 2099 | if (m_size == 0) { | ||
2127 | // delete files with zero size | 2100 | // delete files with zero size | ||
2128 | const QByteArray cmd = "DELE " + remoteEncoding()->encode(dest_part); | 2101 | const QByteArray cmd = "DELE " + q->remoteEncoding()->encode(dest_part); | ||
2129 | if (!ftpSendCmd(cmd) || (m_iRespType != 2)) { | 2102 | if (!ftpSendCmd(cmd) || (m_iRespType != 2)) { | ||
2130 | iError = ERR_CANNOT_DELETE_PARTIAL; | 2103 | return Result::fail(ERR_CANNOT_DELETE_PARTIAL, QString()); | ||
2131 | return statusServerError; | | |||
2132 | } | 2104 | } | ||
2133 | } else if (!(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { | 2105 | } else if (!(flags & KIO::Overwrite) && !(flags & KIO::Resume)) { | ||
2134 | flags |= canResume(m_size) ? KIO::Resume : KIO::DefaultFlags; | 2106 | flags |= q->canResume(m_size) ? KIO::Resume : KIO::DefaultFlags; | ||
2135 | if (!(flags & KIO::Resume)) { | 2107 | if (!(flags & KIO::Resume)) { | ||
2136 | iError = ERR_FILE_ALREADY_EXIST; | 2108 | return Result::fail(ERR_FILE_ALREADY_EXIST, QString()); | ||
2137 | return statusServerError; | | |||
2138 | } | 2109 | } | ||
2139 | } | 2110 | } | ||
2140 | } else { | 2111 | } else { | ||
2141 | m_size = 0; | 2112 | m_size = 0; | ||
2142 | } | 2113 | } | ||
2143 | 2114 | | |||
2144 | QString dest; | 2115 | QString dest; | ||
2145 | 2116 | | |||
2146 | // if we are using marking of partial downloads -> add .part extension | 2117 | // if we are using marking of partial downloads -> add .part extension | ||
2147 | if (bMarkPartial) { | 2118 | if (bMarkPartial) { | ||
2148 | qCDebug(KIO_FTP) << "Adding .part extension to " << dest_orig; | 2119 | qCDebug(KIO_FTP) << "Adding .part extension to " << dest_orig; | ||
2149 | dest = dest_part; | 2120 | dest = dest_part; | ||
2150 | } else { | 2121 | } else { | ||
2151 | dest = dest_orig; | 2122 | dest = dest_orig; | ||
2152 | } | 2123 | } | ||
2153 | 2124 | | |||
2154 | KIO::fileoffset_t offset = 0; | 2125 | KIO::fileoffset_t offset = 0; | ||
2155 | 2126 | | |||
2156 | // set the mode according to offset | 2127 | // set the mode according to offset | ||
2157 | if ((flags & KIO::Resume) && m_size > 0) { | 2128 | if ((flags & KIO::Resume) && m_size > 0) { | ||
2158 | offset = m_size; | 2129 | offset = m_size; | ||
2159 | if (iCopyFile != -1) { | 2130 | if (iCopyFile != -1) { | ||
2160 | if (QT_LSEEK(iCopyFile, offset, SEEK_SET) < 0) { | 2131 | if (QT_LSEEK(iCopyFile, offset, SEEK_SET) < 0) { | ||
2161 | iError = ERR_CANNOT_RESUME; | 2132 | return Result::fail(ERR_CANNOT_RESUME, QString()); | ||
2162 | return statusClientError; | | |||
2163 | } | 2133 | } | ||
2164 | } | 2134 | } | ||
2165 | } | 2135 | } | ||
2166 | 2136 | | |||
2167 | if (! ftpOpenCommand("stor", dest, '?', ERR_CANNOT_WRITE, offset)) { | 2137 | const auto storResult = ftpOpenCommand("stor", dest, '?', ERR_CANNOT_WRITE, offset); | ||
2168 | return statusServerError; | 2138 | if (!storResult.success) { | ||
dfaure: remember to remove this | |||||
2139 | return storResult; | ||||
2169 | } | 2140 | } | ||
dfaure: because of the useless enum? ;) | |||||
2170 | 2141 | | |||
2171 | qCDebug(KIO_FTP) << "ftpPut: starting with offset=" << offset; | 2142 | qCDebug(KIO_FTP) << "ftpPut: starting with offset=" << offset; | ||
2172 | KIO::fileoffset_t processed_size = offset; | 2143 | KIO::fileoffset_t processed_size = offset; | ||
2173 | 2144 | | |||
2174 | QByteArray buffer; | 2145 | QByteArray buffer; | ||
2175 | int result; | 2146 | int result; | ||
2176 | int iBlockSize = initialIpcSize; | 2147 | int iBlockSize = initialIpcSize; | ||
2148 | int writeError = 0; | ||||
2177 | // Loop until we got 'dataEnd' | 2149 | // Loop until we got 'dataEnd' | ||
2178 | do { | 2150 | do { | ||
2179 | if (iCopyFile == -1) { | 2151 | if (iCopyFile == -1) { | ||
2180 | dataReq(); // Request for data | 2152 | q->dataReq(); // Request for data | ||
2181 | result = readData(buffer); | 2153 | result = q->readData(buffer); | ||
2182 | } else { | 2154 | } else { | ||
2183 | // let the buffer size grow if the file is larger 64kByte ... | 2155 | // let the buffer size grow if the file is larger 64kByte ... | ||
2184 | if (processed_size - offset > 1024 * 64) { | 2156 | if (processed_size - offset > 1024 * 64) { | ||
2185 | iBlockSize = maximumIpcSize; | 2157 | iBlockSize = maximumIpcSize; | ||
2186 | } | 2158 | } | ||
2187 | buffer.resize(iBlockSize); | 2159 | buffer.resize(iBlockSize); | ||
2188 | result = QT_READ(iCopyFile, buffer.data(), buffer.size()); | 2160 | result = QT_READ(iCopyFile, buffer.data(), buffer.size()); | ||
2189 | if (result < 0) { | 2161 | if (result < 0) { | ||
2190 | iError = ERR_CANNOT_WRITE; | 2162 | writeError = ERR_CANNOT_READ; | ||
dfaure: yeah, feel free to change it. | |||||
2191 | } else { | 2163 | } else { | ||
2192 | buffer.resize(result); | 2164 | buffer.resize(result); | ||
2193 | } | 2165 | } | ||
2194 | } | 2166 | } | ||
2195 | 2167 | | |||
2196 | if (result > 0) { | 2168 | if (result > 0) { | ||
2197 | m_data->write(buffer); | 2169 | m_data->write(buffer); | ||
2198 | while (m_data->bytesToWrite() && m_data->waitForBytesWritten()) {} | 2170 | while (m_data->bytesToWrite() && m_data->waitForBytesWritten()) {} | ||
2199 | processed_size += result; | 2171 | processed_size += result; | ||
2200 | processedSize(processed_size); | 2172 | q->processedSize(processed_size); | ||
2201 | } | 2173 | } | ||
2202 | } while (result > 0); | 2174 | } while (result > 0); | ||
2203 | 2175 | | |||
2204 | if (result != 0) { // error | 2176 | if (result != 0) { // error | ||
2205 | ftpCloseCommand(); // don't care about errors | 2177 | ftpCloseCommand(); // don't care about errors | ||
2206 | qCDebug(KIO_FTP) << "Error during 'put'. Aborting."; | 2178 | qCDebug(KIO_FTP) << "Error during 'put'. Aborting."; | ||
2207 | if (bMarkPartial) { | 2179 | if (bMarkPartial) { | ||
2208 | // Remove if smaller than minimum size | 2180 | // Remove if smaller than minimum size | ||
2209 | if (ftpSize(dest, 'I') && | 2181 | if (ftpSize(dest, 'I') && | ||
2210 | (processed_size < config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE))) { | 2182 | (processed_size < q->config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE))) { | ||
2211 | const QByteArray cmd = "DELE " + remoteEncoding()->encode(dest); | 2183 | const QByteArray cmd = "DELE " + q->remoteEncoding()->encode(dest); | ||
2212 | (void) ftpSendCmd(cmd); | 2184 | (void) ftpSendCmd(cmd); | ||
2213 | } | 2185 | } | ||
2214 | } | 2186 | } | ||
2215 | return statusServerError; | 2187 | return Result::fail(writeError, dest_url.toString()); | ||
2216 | } | 2188 | } | ||
2217 | 2189 | | |||
2218 | if (!ftpCloseCommand()) { | 2190 | if (!ftpCloseCommand()) { | ||
2219 | iError = ERR_CANNOT_WRITE; | 2191 | return Result::fail(ERR_CANNOT_WRITE); | ||
2220 | return statusServerError; | | |||
2221 | } | 2192 | } | ||
2222 | 2193 | | |||
2223 | // after full download rename the file back to original name | 2194 | // after full download rename the file back to original name | ||
2224 | if (bMarkPartial) { | 2195 | if (bMarkPartial) { | ||
2225 | qCDebug(KIO_FTP) << "renaming dest (" << dest << ") back to dest_orig (" << dest_orig << ")"; | 2196 | qCDebug(KIO_FTP) << "renaming dest (" << dest << ") back to dest_orig (" << dest_orig << ")"; | ||
2226 | if (!ftpRename(dest, dest_orig, KIO::Overwrite)) { | 2197 | const auto result = ftpRename(dest, dest_orig, KIO::Overwrite); | ||
2227 | iError = ERR_CANNOT_RENAME_PARTIAL; | 2198 | if (!result.success) { | ||
2228 | return statusServerError; | 2199 | return Result::fail(ERR_CANNOT_RENAME_PARTIAL); | ||
2229 | } | 2200 | } | ||
2230 | } | 2201 | } | ||
2231 | 2202 | | |||
2232 | // set final permissions | 2203 | // set final permissions | ||
2233 | if (permissions != -1) { | 2204 | if (permissions != -1) { | ||
2234 | if (m_user == QLatin1String(FTP_LOGIN)) | 2205 | if (m_user == QLatin1String(FTP_LOGIN)) | ||
2235 | qCDebug(KIO_FTP) << "Trying to chmod over anonymous FTP ???"; | 2206 | qCDebug(KIO_FTP) << "Trying to chmod over anonymous FTP ???"; | ||
2236 | // chmod the file we just put | 2207 | // chmod the file we just put | ||
2237 | if (! ftpChmod(dest_orig, permissions)) { | 2208 | if (! ftpChmod(dest_orig, permissions)) { | ||
2238 | // To be tested | 2209 | // To be tested | ||
2239 | //if ( m_user != FTP_LOGIN ) | 2210 | //if ( m_user != FTP_LOGIN ) | ||
2240 | // warning( i18n( "Could not change permissions for\n%1" ).arg( dest_orig ) ); | 2211 | // warning( i18n( "Could not change permissions for\n%1" ).arg( dest_orig ) ); | ||
2241 | } | 2212 | } | ||
2242 | } | 2213 | } | ||
2243 | 2214 | | |||
2244 | return statusSuccess; | 2215 | return Result::pass(); | ||
2245 | } | 2216 | } | ||
2246 | 2217 | | |||
2247 | /** Use the SIZE command to get the file size. | 2218 | /** Use the SIZE command to get the file size. | ||
2248 | Warning : the size depends on the transfer mode, hence the second arg. */ | 2219 | Warning : the size depends on the transfer mode, hence the second arg. */ | ||
2249 | bool Ftp::ftpSize(const QString &path, char mode) | 2220 | bool FtpInternal::ftpSize(const QString &path, char mode) | ||
2250 | { | 2221 | { | ||
2251 | m_size = UnknownSize; | 2222 | m_size = UnknownSize; | ||
2252 | if (!ftpDataMode(mode)) { | 2223 | if (!ftpDataMode(mode)) { | ||
2253 | return false; | 2224 | return false; | ||
2254 | } | 2225 | } | ||
2255 | 2226 | | |||
2256 | const QByteArray buf = "SIZE " + remoteEncoding()->encode(path); | 2227 | const QByteArray buf = "SIZE " + q->remoteEncoding()->encode(path); | ||
2257 | if (!ftpSendCmd(buf) || (m_iRespType != 2)) { | 2228 | if (!ftpSendCmd(buf) || (m_iRespType != 2)) { | ||
2258 | return false; | 2229 | return false; | ||
2259 | } | 2230 | } | ||
2260 | 2231 | | |||
2261 | // skip leading "213 " (response code) | 2232 | // skip leading "213 " (response code) | ||
2262 | QByteArray psz(ftpResponse(4)); | 2233 | QByteArray psz(ftpResponse(4)); | ||
2263 | if (psz.isEmpty()) { | 2234 | if (psz.isEmpty()) { | ||
2264 | return false; | 2235 | return false; | ||
2265 | } | 2236 | } | ||
2266 | bool ok = false; | 2237 | bool ok = false; | ||
2267 | m_size = psz.trimmed().toLongLong(&ok); | 2238 | m_size = psz.trimmed().toLongLong(&ok); | ||
2268 | if (!ok) { | 2239 | if (!ok) { | ||
2269 | m_size = UnknownSize; | 2240 | m_size = UnknownSize; | ||
2270 | } | 2241 | } | ||
2271 | return true; | 2242 | return true; | ||
2272 | } | 2243 | } | ||
2273 | 2244 | | |||
2274 | bool Ftp::ftpFileExists(const QString &path) | 2245 | bool FtpInternal::ftpFileExists(const QString &path) | ||
2275 | { | 2246 | { | ||
2276 | const QByteArray buf = "SIZE " + remoteEncoding()->encode(path); | 2247 | const QByteArray buf = "SIZE " + q->remoteEncoding()->encode(path); | ||
2277 | if (!ftpSendCmd(buf) || (m_iRespType != 2)) { | 2248 | if (!ftpSendCmd(buf) || (m_iRespType != 2)) { | ||
2278 | return false; | 2249 | return false; | ||
2279 | } | 2250 | } | ||
2280 | 2251 | | |||
2281 | // skip leading "213 " (response code) | 2252 | // skip leading "213 " (response code) | ||
2282 | const char *psz = ftpResponse(4); | 2253 | const char *psz = ftpResponse(4); | ||
2283 | return psz != nullptr; | 2254 | return psz != nullptr; | ||
2284 | } | 2255 | } | ||
2285 | 2256 | | |||
2286 | // Today the differences between ASCII and BINARY are limited to | 2257 | // Today the differences between ASCII and BINARY are limited to | ||
2287 | // CR or CR/LF line terminators. Many servers ignore ASCII (like | 2258 | // CR or CR/LF line terminators. Many servers ignore ASCII (like | ||
2288 | // win2003 -or- vsftp with default config). In the early days of | 2259 | // win2003 -or- vsftp with default config). In the early days of | ||
2289 | // computing, when even text-files had structure, this stuff was | 2260 | // computing, when even text-files had structure, this stuff was | ||
2290 | // more important. | 2261 | // more important. | ||
2291 | // Theoretically "list" could return different results in ASCII | 2262 | // Theoretically "list" could return different results in ASCII | ||
2292 | // and BINARY mode. But again, most servers ignore ASCII here. | 2263 | // and BINARY mode. But again, most servers ignore ASCII here. | ||
2293 | bool Ftp::ftpDataMode(char cMode) | 2264 | bool FtpInternal::ftpDataMode(char cMode) | ||
2294 | { | 2265 | { | ||
2295 | if (cMode == '?') { | 2266 | if (cMode == '?') { | ||
2296 | cMode = m_bTextMode ? 'A' : 'I'; | 2267 | cMode = m_bTextMode ? 'A' : 'I'; | ||
2297 | } else if (cMode == 'a') { | 2268 | } else if (cMode == 'a') { | ||
2298 | cMode = 'A'; | 2269 | cMode = 'A'; | ||
2299 | } else if (cMode != 'A') { | 2270 | } else if (cMode != 'A') { | ||
2300 | cMode = 'I'; | 2271 | cMode = 'I'; | ||
2301 | } | 2272 | } | ||
2302 | 2273 | | |||
2303 | qCDebug(KIO_FTP) << "want" << cMode << "has" << m_cDataMode; | 2274 | qCDebug(KIO_FTP) << "want" << cMode << "has" << m_cDataMode; | ||
2304 | if (m_cDataMode == cMode) { | 2275 | if (m_cDataMode == cMode) { | ||
2305 | return true; | 2276 | return true; | ||
2306 | } | 2277 | } | ||
2307 | 2278 | | |||
2308 | const QByteArray buf = QByteArrayLiteral("TYPE ") + cMode; | 2279 | const QByteArray buf = QByteArrayLiteral("TYPE ") + cMode; | ||
2309 | if (!ftpSendCmd(buf) || (m_iRespType != 2)) { | 2280 | if (!ftpSendCmd(buf) || (m_iRespType != 2)) { | ||
2310 | return false; | 2281 | return false; | ||
2311 | } | 2282 | } | ||
2312 | m_cDataMode = cMode; | 2283 | m_cDataMode = cMode; | ||
2313 | return true; | 2284 | return true; | ||
2314 | } | 2285 | } | ||
2315 | 2286 | | |||
2316 | bool Ftp::ftpFolder(const QString &path, bool bReportError) | 2287 | bool FtpInternal::ftpFolder(const QString &path) | ||
2317 | { | 2288 | { | ||
2318 | QString newPath = path; | 2289 | QString newPath = path; | ||
2319 | int iLen = newPath.length(); | 2290 | int iLen = newPath.length(); | ||
2320 | if (iLen > 1 && newPath[iLen - 1] == QLatin1Char('/')) { | 2291 | if (iLen > 1 && newPath[iLen - 1] == QLatin1Char('/')) { | ||
2321 | newPath.chop(1); | 2292 | newPath.chop(1); | ||
2322 | } | 2293 | } | ||
2323 | 2294 | | |||
2324 | qCDebug(KIO_FTP) << "want" << newPath << "has" << m_currentPath; | 2295 | qCDebug(KIO_FTP) << "want" << newPath << "has" << m_currentPath; | ||
2325 | if (m_currentPath == newPath) { | 2296 | if (m_currentPath == newPath) { | ||
2326 | return true; | 2297 | return true; | ||
2327 | } | 2298 | } | ||
2328 | 2299 | | |||
2329 | const QByteArray tmp = "cwd " + remoteEncoding()->encode(newPath); | 2300 | const QByteArray tmp = "cwd " + q->remoteEncoding()->encode(newPath); | ||
2330 | if (!ftpSendCmd(tmp)) { | 2301 | if (!ftpSendCmd(tmp)) { | ||
2331 | return false; // connection failure | 2302 | return false; // connection failure | ||
2332 | } | 2303 | } | ||
2333 | if (m_iRespType != 2) { | 2304 | if (m_iRespType != 2) { | ||
2334 | if (bReportError) { | | |||
2335 | error(ERR_CANNOT_ENTER_DIRECTORY, path); | | |||
2336 | } | | |||
2337 | return false; // not a folder | 2305 | return false; // not a folder | ||
Well, that's part of your refactoring. It was used to decide if a method should emit error on failure or just return false. It was part of the big mess that you're cleaning up. Kill bReportError completely. dfaure: Well, that's part of your refactoring. It was used to decide if a method should emit error on… | |||||
2338 | } | 2306 | } | ||
2339 | m_currentPath = newPath; | 2307 | m_currentPath = newPath; | ||
2340 | return true; | 2308 | return true; | ||
2341 | } | 2309 | } | ||
2342 | 2310 | | |||
2343 | //=============================================================================== | 2311 | //=============================================================================== | ||
2344 | // public: copy don't use kio data pump if one side is a local file | 2312 | // public: copy don't use kio data pump if one side is a local file | ||
2345 | // helper: ftpCopyPut called from copy() on upload | 2313 | // helper: ftpCopyPut called from copy() on upload | ||
2346 | // helper: ftpCopyGet called from copy() on download | 2314 | // helper: ftpCopyGet called from copy() on download | ||
2347 | //=============================================================================== | 2315 | //=============================================================================== | ||
2348 | void Ftp::copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) | 2316 | Result FtpInternal::copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) | ||
2349 | { | 2317 | { | ||
2350 | int iError = 0; | | |||
2351 | int iCopyFile = -1; | 2318 | int iCopyFile = -1; | ||
2352 | StatusCode cs = statusSuccess; | | |||
2353 | bool bSrcLocal = src.isLocalFile(); | 2319 | bool bSrcLocal = src.isLocalFile(); | ||
2354 | bool bDestLocal = dest.isLocalFile(); | 2320 | bool bDestLocal = dest.isLocalFile(); | ||
2355 | QString sCopyFile; | 2321 | QString sCopyFile; | ||
2356 | 2322 | | |||
2323 | Result result = Result::pass(); | ||||
dfaure: optimistic but useless initializations, all code paths set (or ignore) result. | |||||
I do need to declare it though, and by design a Result cannot have a "null" state, so I either need to initialize a failure or pass here. Do you have a better suggestion? sitter: I do need to declare it though, and by design a Result cannot have a "null" state, so I either… | |||||
dfaure: Ah ok, never mind then. | |||||
2357 | if (bSrcLocal && !bDestLocal) { // File -> Ftp | 2324 | if (bSrcLocal && !bDestLocal) { // File -> Ftp | ||
2358 | sCopyFile = src.toLocalFile(); | 2325 | sCopyFile = src.toLocalFile(); | ||
2359 | qCDebug(KIO_FTP) << "local file" << sCopyFile << "-> ftp" << dest.path(); | 2326 | qCDebug(KIO_FTP) << "local file" << sCopyFile << "-> ftp" << dest.path(); | ||
2360 | cs = ftpCopyPut(iError, iCopyFile, sCopyFile, dest, permissions, flags); | 2327 | result = ftpCopyPut(iCopyFile, sCopyFile, dest, permissions, flags); | ||
2361 | } else if (!bSrcLocal && bDestLocal) { // Ftp -> File | 2328 | } else if (!bSrcLocal && bDestLocal) { // Ftp -> File | ||
2362 | sCopyFile = dest.toLocalFile(); | 2329 | sCopyFile = dest.toLocalFile(); | ||
2363 | qCDebug(KIO_FTP) << "ftp" << src.path() << "-> local file" << sCopyFile; | 2330 | qCDebug(KIO_FTP) << "ftp" << src.path() << "-> local file" << sCopyFile; | ||
2364 | cs = ftpCopyGet(iError, iCopyFile, sCopyFile, src, permissions, flags); | 2331 | result = ftpCopyGet(iCopyFile, sCopyFile, src, permissions, flags); | ||
2365 | } else { | 2332 | } else { | ||
2366 | error(ERR_UNSUPPORTED_ACTION, QString()); | 2333 | return Result::fail(ERR_UNSUPPORTED_ACTION, QString()); | ||
2367 | return; | | |||
2368 | } | 2334 | } | ||
2369 | 2335 | | |||
2370 | // perform clean-ups and report error (if any) | 2336 | // perform clean-ups and report error (if any) | ||
2371 | if (iCopyFile != -1) { | 2337 | if (iCopyFile != -1) { | ||
2372 | QT_CLOSE(iCopyFile); | 2338 | QT_CLOSE(iCopyFile); | ||
2373 | } | 2339 | } | ||
2374 | ftpCloseCommand(); // must close command! | 2340 | ftpCloseCommand(); // must close command! | ||
2375 | if (cs == statusServerError && iError) { | 2341 | | ||
2376 | error(iError, sCopyFile); | 2342 | return result; | ||
2377 | } else { | 2343 | } | ||
2378 | finished(); | 2344 | | ||
2345 | bool FtpInternal::isSocksProxyScheme(const QString &scheme) | ||||
2346 | { | ||||
2347 | return scheme == "socks" || scheme == "socks5"; | ||||
2379 | } | 2348 | } | ||
2349 | | ||||
2350 | bool FtpInternal::isSocksProxy() const | ||||
2351 | { | ||||
2352 | return isSocksProxyScheme(m_proxyURL.scheme()); | ||||
2380 | } | 2353 | } | ||
2381 | 2354 | | |||
2382 | Ftp::StatusCode Ftp::ftpCopyPut(int &iError, int &iCopyFile, const QString &sCopyFile, | 2355 | Result FtpInternal::ftpCopyPut(int &iCopyFile, const QString &sCopyFile, | ||
2383 | const QUrl &url, int permissions, KIO::JobFlags flags) | 2356 | const QUrl &url, int permissions, KIO::JobFlags flags) | ||
2384 | { | 2357 | { | ||
2385 | // check if source is ok ... | 2358 | // check if source is ok ... | ||
2386 | QFileInfo info(sCopyFile); | 2359 | QFileInfo info(sCopyFile); | ||
2387 | bool bSrcExists = info.exists(); | 2360 | bool bSrcExists = info.exists(); | ||
2388 | if (bSrcExists) { | 2361 | if (bSrcExists) { | ||
2389 | if (info.isDir()) { | 2362 | if (info.isDir()) { | ||
2390 | iError = ERR_IS_DIRECTORY; | 2363 | return Result::fail(ERR_IS_DIRECTORY); | ||
2391 | return statusClientError; | | |||
2392 | } | 2364 | } | ||
2393 | } else { | 2365 | } else { | ||
2394 | iError = ERR_DOES_NOT_EXIST; | 2366 | return Result::fail(ERR_DOES_NOT_EXIST); | ||
2395 | return statusClientError; | | |||
2396 | } | 2367 | } | ||
2397 | 2368 | | |||
2398 | iCopyFile = QT_OPEN(QFile::encodeName(sCopyFile).constData(), O_RDONLY); | 2369 | iCopyFile = QT_OPEN(QFile::encodeName(sCopyFile).constData(), O_RDONLY); | ||
2399 | if (iCopyFile == -1) { | 2370 | if (iCopyFile == -1) { | ||
2400 | iError = ERR_CANNOT_OPEN_FOR_READING; | 2371 | return Result::fail(ERR_CANNOT_OPEN_FOR_READING); | ||
2401 | return statusClientError; | | |||
2402 | } | 2372 | } | ||
2403 | 2373 | | |||
2404 | // delegate the real work (iError gets status) ... | 2374 | // delegate the real work (iError gets status) ... | ||
2405 | totalSize(info.size()); | 2375 | q->totalSize(info.size()); | ||
2406 | #ifdef ENABLE_CAN_RESUME | 2376 | #ifdef ENABLE_CAN_RESUME | ||
2407 | return ftpPut(iError, iCopyFile, url, permissions, flags & ~KIO::Resume); | 2377 | return ftpPut(iCopyFile, url, permissions, flags & ~KIO::Resume); | ||
2408 | #else | 2378 | #else | ||
2409 | return ftpPut(iError, iCopyFile, url, permissions, flags | KIO::Resume); | 2379 | return ftpPut(iCopyFile, url, permissions, flags | KIO::Resume); | ||
dfaure: probably needs the same treatment, i.e. removing iError?
(ifdef'ed out code) | |||||
2410 | #endif | 2380 | #endif | ||
2411 | } | 2381 | } | ||
2412 | 2382 | | |||
2413 | Ftp::StatusCode Ftp::ftpCopyGet(int &iError, int &iCopyFile, const QString &sCopyFile, | 2383 | Result FtpInternal::ftpCopyGet(int &iCopyFile, const QString &sCopyFile, | ||
2414 | const QUrl &url, int permissions, KIO::JobFlags flags) | 2384 | const QUrl &url, int permissions, KIO::JobFlags flags) | ||
2415 | { | 2385 | { | ||
2416 | // check if destination is ok ... | 2386 | // check if destination is ok ... | ||
2417 | QFileInfo info(sCopyFile); | 2387 | QFileInfo info(sCopyFile); | ||
2418 | const bool bDestExists = info.exists(); | 2388 | const bool bDestExists = info.exists(); | ||
2419 | if (bDestExists) { | 2389 | if (bDestExists) { | ||
2420 | if (info.isDir()) { | 2390 | if (info.isDir()) { | ||
2421 | iError = ERR_IS_DIRECTORY; | 2391 | return Result::fail(ERR_IS_DIRECTORY); | ||
2422 | return statusClientError; | | |||
2423 | } | 2392 | } | ||
2424 | if (!(flags & KIO::Overwrite)) { | 2393 | if (!(flags & KIO::Overwrite)) { | ||
2425 | iError = ERR_FILE_ALREADY_EXIST; | 2394 | return Result::fail(ERR_FILE_ALREADY_EXIST); | ||
2426 | return statusClientError; | | |||
2427 | } | 2395 | } | ||
2428 | } | 2396 | } | ||
2429 | 2397 | | |||
2430 | // do we have a ".part" file? | 2398 | // do we have a ".part" file? | ||
2431 | const QString sPart = sCopyFile + QLatin1String(".part"); | 2399 | const QString sPart = sCopyFile + QLatin1String(".part"); | ||
2432 | bool bResume = false; | 2400 | bool bResume = false; | ||
2433 | QFileInfo sPartInfo(sPart); | 2401 | QFileInfo sPartInfo(sPart); | ||
2434 | const bool bPartExists = sPartInfo.exists(); | 2402 | const bool bPartExists = sPartInfo.exists(); | ||
2435 | const bool bMarkPartial = config()->readEntry("MarkPartial", true); | 2403 | const bool bMarkPartial = q->config()->readEntry("MarkPartial", true); | ||
2436 | const QString dest = bMarkPartial ? sPart : sCopyFile; | 2404 | const QString dest = bMarkPartial ? sPart : sCopyFile; | ||
2437 | if (bMarkPartial && bPartExists && sPartInfo.size() > 0) { | 2405 | if (bMarkPartial && bPartExists && sPartInfo.size() > 0) { | ||
2438 | // must not be a folder! please fix a similar bug in kio_file!! | 2406 | // must not be a folder! please fix a similar bug in kio_file!! | ||
2439 | if (sPartInfo.isDir()) { | 2407 | if (sPartInfo.isDir()) { | ||
2440 | iError = ERR_DIR_ALREADY_EXIST; | 2408 | return Result::fail(ERR_DIR_ALREADY_EXIST); | ||
2441 | return statusClientError; // client side error | | |||
2442 | } | 2409 | } | ||
2443 | //doesn't work for copy? -> design flaw? | 2410 | //doesn't work for copy? -> design flaw? | ||
2444 | #ifdef ENABLE_CAN_RESUME | 2411 | #ifdef ENABLE_CAN_RESUME | ||
2445 | bResume = canResume(sPartInfo.size()); | 2412 | bResume = q->canResume(sPartInfo.size()); | ||
2446 | #else | 2413 | #else | ||
2447 | bResume = true; | 2414 | bResume = true; | ||
2448 | #endif | 2415 | #endif | ||
2449 | } | 2416 | } | ||
2450 | 2417 | | |||
2451 | if (bPartExists && !bResume) { // get rid of an unwanted ".part" file | 2418 | if (bPartExists && !bResume) { // get rid of an unwanted ".part" file | ||
2452 | QFile::remove(sPart); | 2419 | QFile::remove(sPart); | ||
2453 | } | 2420 | } | ||
2454 | 2421 | | |||
2455 | // WABA: Make sure that we keep writing permissions ourselves, | 2422 | // WABA: Make sure that we keep writing permissions ourselves, | ||
2456 | // otherwise we can be in for a surprise on NFS. | 2423 | // otherwise we can be in for a surprise on NFS. | ||
2457 | mode_t initialMode; | 2424 | mode_t initialMode; | ||
2458 | if (permissions != -1) { | 2425 | if (permissions >= 0) { | ||
2459 | initialMode = permissions | S_IWUSR; | 2426 | initialMode = static_cast<mode_t>(permissions | S_IWUSR); | ||
2460 | } else { | 2427 | } else { | ||
2461 | initialMode = 0666; | 2428 | initialMode = 0666; | ||
2462 | } | 2429 | } | ||
2463 | 2430 | | |||
2464 | // open the output file ... | 2431 | // open the output file ... | ||
2465 | KIO::fileoffset_t hCopyOffset = 0; | 2432 | KIO::fileoffset_t hCopyOffset = 0; | ||
2466 | if (bResume) { | 2433 | if (bResume) { | ||
2467 | iCopyFile = QT_OPEN(QFile::encodeName(sPart).constData(), O_RDWR); // append if resuming | 2434 | iCopyFile = QT_OPEN(QFile::encodeName(sPart).constData(), O_RDWR); // append if resuming | ||
2468 | hCopyOffset = QT_LSEEK(iCopyFile, 0, SEEK_END); | 2435 | hCopyOffset = QT_LSEEK(iCopyFile, 0, SEEK_END); | ||
2469 | if (hCopyOffset < 0) { | 2436 | if (hCopyOffset < 0) { | ||
2470 | iError = ERR_CANNOT_RESUME; | 2437 | return Result::fail(ERR_CANNOT_RESUME); | ||
2471 | return statusClientError; // client side error | | |||
2472 | } | 2438 | } | ||
2473 | qCDebug(KIO_FTP) << "resuming at " << hCopyOffset; | 2439 | qCDebug(KIO_FTP) << "resuming at " << hCopyOffset; | ||
2474 | } else { | 2440 | } else { | ||
2475 | iCopyFile = QT_OPEN(QFile::encodeName(dest).constData(), O_CREAT | O_TRUNC | O_WRONLY, initialMode); | 2441 | iCopyFile = QT_OPEN(QFile::encodeName(dest).constData(), O_CREAT | O_TRUNC | O_WRONLY, initialMode); | ||
2476 | } | 2442 | } | ||
2477 | 2443 | | |||
2478 | if (iCopyFile == -1) { | 2444 | if (iCopyFile == -1) { | ||
2479 | qCDebug(KIO_FTP) << "### COULD NOT WRITE " << sCopyFile; | 2445 | qCDebug(KIO_FTP) << "### COULD NOT WRITE " << sCopyFile; | ||
2480 | iError = (errno == EACCES) ? ERR_WRITE_ACCESS_DENIED | 2446 | const int error = (errno == EACCES) ? ERR_WRITE_ACCESS_DENIED | ||
2481 | : ERR_CANNOT_OPEN_FOR_WRITING; | 2447 | : ERR_CANNOT_OPEN_FOR_WRITING; | ||
2482 | return statusClientError; | 2448 | return Result::fail(error); | ||
2483 | } | 2449 | } | ||
2484 | 2450 | | |||
2485 | // delegate the real work (iError gets status) ... | 2451 | // delegate the real work (iError gets status) ... | ||
2486 | StatusCode iRes = ftpGet(iError, iCopyFile, url, hCopyOffset); | 2452 | auto result = ftpGet(iCopyFile, sCopyFile, url, hCopyOffset); | ||
2487 | if (QT_CLOSE(iCopyFile) && iRes == statusSuccess) { | 2453 | | ||
2488 | iError = ERR_CANNOT_WRITE; | 2454 | if (QT_CLOSE(iCopyFile) == 0 && !result.success) { | ||
2489 | iRes = statusClientError; | 2455 | // If closing the file failed but there isn't an error yet, switch | ||
dfaure: s/Closing/closing/ | |||||
2456 | // into an error! | ||||
2457 | result = Result::fail(ERR_CANNOT_WRITE); | ||||
2490 | } | 2458 | } | ||
dfaure: no, we keep going in order to delete the .part file | |||||
2491 | iCopyFile = -1; | 2459 | iCopyFile = -1; | ||
2492 | 2460 | | |||
2493 | // handle renaming or deletion of a partial file ... | 2461 | // handle renaming or deletion of a partial file ... | ||
2494 | if (bMarkPartial) { | 2462 | if (bMarkPartial) { | ||
2495 | if (iRes == statusSuccess) { | 2463 | if (result.success) { | ||
2496 | // rename ".part" on success | 2464 | // rename ".part" on success | ||
2497 | if (!QFile::rename(sPart, sCopyFile)) { | 2465 | if (!QFile::rename(sPart, sCopyFile)) { | ||
2498 | // If rename fails, try removing the destination first if it exists. | 2466 | // If rename fails, try removing the destination first if it exists. | ||
2499 | if (!bDestExists || !(QFile::remove(sCopyFile) && QFile::rename(sPart, sCopyFile))) { | 2467 | if (!bDestExists || !(QFile::remove(sCopyFile) && QFile::rename(sPart, sCopyFile))) { | ||
2500 | qCDebug(KIO_FTP) << "cannot rename " << sPart << " to " << sCopyFile; | 2468 | qCDebug(KIO_FTP) << "cannot rename " << sPart << " to " << sCopyFile; | ||
2501 | iError = ERR_CANNOT_RENAME_PARTIAL; | 2469 | result = Result::fail(ERR_CANNOT_RENAME_PARTIAL); | ||
2502 | iRes = statusClientError; | | |||
2503 | } | 2470 | } | ||
2504 | } | 2471 | } | ||
2505 | } else { | 2472 | } else { | ||
2506 | sPartInfo.refresh(); | 2473 | sPartInfo.refresh(); | ||
2507 | if (sPartInfo.exists()) { // should a very small ".part" be deleted? | 2474 | if (sPartInfo.exists()) { // should a very small ".part" be deleted? | ||
2508 | int size = config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE); | 2475 | int size = q->config()->readEntry("MinimumKeepSize", DEFAULT_MINIMUM_KEEP_SIZE); | ||
2509 | if (sPartInfo.size() < size) { | 2476 | if (sPartInfo.size() < size) { | ||
2510 | QFile::remove(sPart); | 2477 | QFile::remove(sPart); | ||
2511 | } | 2478 | } | ||
2512 | } | 2479 | } | ||
2513 | } | 2480 | } | ||
2514 | } | 2481 | } | ||
2515 | 2482 | | |||
2516 | if (iRes == statusSuccess) { | 2483 | if (result.success) { | ||
2517 | const QString mtimeStr = metaData(QStringLiteral("modified")); | 2484 | const QString mtimeStr = q->metaData(QStringLiteral("modified")); | ||
2518 | if (!mtimeStr.isEmpty()) { | 2485 | if (!mtimeStr.isEmpty()) { | ||
2519 | QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); | 2486 | QDateTime dt = QDateTime::fromString(mtimeStr, Qt::ISODate); | ||
2520 | if (dt.isValid()) { | 2487 | if (dt.isValid()) { | ||
2521 | qCDebug(KIO_FTP) << "Updating modified timestamp to" << mtimeStr; | 2488 | qCDebug(KIO_FTP) << "Updating modified timestamp to" << mtimeStr; | ||
2522 | struct utimbuf utbuf; | 2489 | struct utimbuf utbuf; | ||
2523 | info.refresh(); | 2490 | info.refresh(); | ||
2524 | utbuf.actime = info.lastRead().toSecsSinceEpoch(); // access time, unchanged | 2491 | utbuf.actime = info.lastRead().toSecsSinceEpoch(); // access time, unchanged | ||
2525 | utbuf.modtime = dt.toSecsSinceEpoch(); // modification time | 2492 | utbuf.modtime = dt.toSecsSinceEpoch(); // modification time | ||
2526 | ::utime(QFile::encodeName(sCopyFile).constData(), &utbuf); | 2493 | ::utime(QFile::encodeName(sCopyFile).constData(), &utbuf); | ||
2527 | } | 2494 | } | ||
2528 | } | 2495 | } | ||
2529 | } | 2496 | } | ||
2530 | 2497 | | |||
2531 | return iRes; | 2498 | return result; | ||
2532 | } | 2499 | } | ||
2533 | 2500 | | |||
2534 | Ftp::StatusCode Ftp::ftpSendMimeType(int &iError, const QUrl &url) | 2501 | Result FtpInternal::ftpSendMimeType(const QUrl &url) | ||
2535 | { | 2502 | { | ||
2536 | const int totalSize = ((m_size == UnknownSize || m_size > 1024) ? 1024 : m_size); | 2503 | const int totalSize = ((m_size == UnknownSize || m_size > 1024) ? 1024 : static_cast<int>(m_size)); | ||
2537 | QByteArray buffer(totalSize, '\0'); | 2504 | QByteArray buffer(totalSize, '\0'); | ||
2538 | 2505 | | |||
2539 | while (true) { | 2506 | while (true) { | ||
2540 | // Wait for content to be available... | 2507 | // Wait for content to be available... | ||
2541 | if (m_data->bytesAvailable() == 0 && !m_data->waitForReadyRead((readTimeout() * 1000))) { | 2508 | if (m_data->bytesAvailable() == 0 && !m_data->waitForReadyRead((q->readTimeout() * 1000))) { | ||
2542 | iError = ERR_CANNOT_READ; | 2509 | return Result::fail(ERR_CANNOT_READ, url.toString()); | ||
2543 | return statusServerError; | | |||
2544 | } | 2510 | } | ||
2545 | 2511 | | |||
2546 | const int bytesRead = m_data->peek(buffer.data(), totalSize); | 2512 | const qint64 bytesRead = m_data->peek(buffer.data(), totalSize); | ||
2547 | 2513 | | |||
2548 | // If we got a -1, it must be an error so return an error. | 2514 | // If we got a -1, it must be an error so return an error. | ||
2549 | if (bytesRead == -1) { | 2515 | if (bytesRead == -1) { | ||
2550 | iError = ERR_CANNOT_READ; | 2516 | return Result::fail(ERR_CANNOT_READ, url.toString()); | ||
2551 | return statusServerError; | | |||
2552 | } | 2517 | } | ||
2553 | 2518 | | |||
2554 | // If m_size is unknown, peek returns 0 (0 sized file ??), or peek returns size | 2519 | // If m_size is unknown, peek returns 0 (0 sized file ??), or peek returns size | ||
2555 | // equal to the size we want, then break. | 2520 | // equal to the size we want, then break. | ||
2556 | if (bytesRead == 0 || bytesRead == totalSize || m_size == UnknownSize) { | 2521 | if (bytesRead == 0 || bytesRead == totalSize || m_size == UnknownSize) { | ||
2557 | break; | 2522 | break; | ||
2558 | } | 2523 | } | ||
2559 | } | 2524 | } | ||
2560 | 2525 | | |||
2561 | if (!buffer.isEmpty()) { | 2526 | if (!buffer.isEmpty()) { | ||
2562 | QMimeDatabase db; | 2527 | QMimeDatabase db; | ||
2563 | QMimeType mime = db.mimeTypeForFileNameAndData(url.path(), buffer); | 2528 | QMimeType mime = db.mimeTypeForFileNameAndData(url.path(), buffer); | ||
2564 | qCDebug(KIO_FTP) << "Emitting mimetype" << mime.name(); | 2529 | qCDebug(KIO_FTP) << "Emitting mimetype" << mime.name(); | ||
2565 | mimeType(mime.name()); // emit the mime type... | 2530 | q->mimeType(mime.name()); // emit the mime type... | ||
2566 | } | | |||
2567 | | ||||
2568 | return statusSuccess; | | |||
2569 | } | | |||
2570 | | ||||
2571 | void Ftp::proxyAuthentication(const QNetworkProxy &proxy, QAuthenticator *authenticator) | | |||
2572 | { | | |||
2573 | Q_UNUSED(proxy); | | |||
2574 | qCDebug(KIO_FTP) << "Authenticator received -- realm:" << authenticator->realm() << "user:" << authenticator->user(); | | |||
2575 | | ||||
2576 | AuthInfo info; | | |||
2577 | info.url = m_proxyURL; | | |||
2578 | info.realmValue = authenticator->realm(); | | |||
2579 | info.verifyPath = true; //### whatever | | |||
2580 | info.username = authenticator->user(); | | |||
2581 | | ||||
2582 | const bool haveCachedCredentials = checkCachedAuthentication(info); | | |||
2583 | | ||||
2584 | // if m_socketProxyAuth is a valid pointer then authentication has been attempted before, | | |||
2585 | // and it was not successful. see below and saveProxyAuthenticationForSocket(). | | |||
2586 | if (!haveCachedCredentials || m_socketProxyAuth) { | | |||
2587 | // Save authentication info if the connection succeeds. We need to disconnect | | |||
2588 | // this after saving the auth data (or an error) so we won't save garbage afterwards! | | |||
2589 | connect(m_control, &QAbstractSocket::connected, this, &Ftp::saveProxyAuthentication); | | |||
2590 | //### fillPromptInfo(&info); | | |||
2591 | info.prompt = i18n("You need to supply a username and a password for " | | |||
2592 | "the proxy server listed below before you are allowed " | | |||
2593 | "to access any sites."); | | |||
2594 | info.keepPassword = true; | | |||
2595 | info.commentLabel = i18n("Proxy:"); | | |||
2596 | info.comment = i18n("<b>%1</b> at <b>%2</b>", info.realmValue, m_proxyURL.host()); | | |||
2597 | const int errorCode = openPasswordDialogV2(info, i18n("Proxy Authentication Failed.")); | | |||
2598 | if (errorCode) { | | |||
2599 | qCDebug(KIO_FTP) << "user canceled proxy authentication, or communication error."; | | |||
2600 | error(errorCode, m_proxyURL.host()); | | |||
2601 | return; | | |||
2602 | } | | |||
2603 | } | | |||
2604 | authenticator->setUser(info.username); | | |||
2605 | authenticator->setPassword(info.password); | | |||
2606 | authenticator->setOption(QStringLiteral("keepalive"), info.keepPassword); | | |||
2607 | | ||||
2608 | if (m_socketProxyAuth) { | | |||
2609 | *m_socketProxyAuth = *authenticator; | | |||
2610 | } else { | | |||
2611 | m_socketProxyAuth = new QAuthenticator(*authenticator); | | |||
2612 | } | | |||
2613 | | ||||
2614 | m_proxyURL.setUserName(info.username); | | |||
2615 | m_proxyURL.setPassword(info.password); | | |||
2616 | } | 2531 | } | ||
2617 | 2532 | | |||
2618 | void Ftp::saveProxyAuthentication() | 2533 | return Result::pass(); | ||
2619 | { | | |||
2620 | qCDebug(KIO_FTP); | | |||
2621 | disconnect(m_control, &QAbstractSocket::connected, this, &Ftp::saveProxyAuthentication); | | |||
2622 | Q_ASSERT(m_socketProxyAuth); | | |||
2623 | if (m_socketProxyAuth) { | | |||
2624 | qCDebug(KIO_FTP) << "-- realm:" << m_socketProxyAuth->realm() << "user:" << m_socketProxyAuth->user(); | | |||
2625 | KIO::AuthInfo a; | | |||
2626 | a.verifyPath = true; | | |||
2627 | a.url = m_proxyURL; | | |||
2628 | a.realmValue = m_socketProxyAuth->realm(); | | |||
2629 | a.username = m_socketProxyAuth->user(); | | |||
2630 | a.password = m_socketProxyAuth->password(); | | |||
2631 | a.keepPassword = m_socketProxyAuth->option(QStringLiteral("keepalive")).toBool(); | | |||
2632 | cacheAuthentication(a); | | |||
2633 | } | | |||
2634 | delete m_socketProxyAuth; | | |||
2635 | m_socketProxyAuth = nullptr; | | |||
2636 | } | 2534 | } | ||
2637 | 2535 | | |||
2638 | void Ftp::fixupEntryName(FtpEntry *e) | 2536 | void FtpInternal::fixupEntryName(FtpEntry *e) | ||
Yes, clearly. But the question is where to check it. Hmm, how does this method even get called, without an event loop? From m_control->waitForReadyRead, maybe? The old code was broken here, it would for sure emit error+error or error+finished, since the main code had no idea that this slot emitted error already. Hmm. Shouldn't we do this connect *before* the waitForConnected in Ftp::synchronousConnectToHost? If a proxy exists, it will surely act at connection time? dfaure: Yes, clearly. But the question is where to check it.
Hmm, how does this method even get called… | |||||
Since you mention it... where even is the eventloop for any of this? From some limited testing your assessment would be mostly correct though. When trying to waitForConnected there'd be a ProxyAuthenticationRequiredError. Which we can handle somewhat awkwardly by querying the info from the user and then setting a newly "enhanced" proxy QNetworkProxy::setApplicationProxy before creating a new socket I guess. I'm not too excited about fixing proxy auth support no one apparently needs. Originally introduced without review as well: sitter: Since you mention it... where even is the eventloop for any of this?
SlaveBase has no event… | |||||
The (restricted) event loop is within waitFor*. It's not a full event loop, but it processes socket events, which in turn leads to that proxy handling. dfaure: The (restricted) event loop is within waitFor*. It's not a full event loop, but it processes… | |||||
2639 | { | 2537 | { | ||
2640 | Q_ASSERT(e); | 2538 | Q_ASSERT(e); | ||
2641 | if (e->type == S_IFDIR) { | 2539 | if (e->type == S_IFDIR) { | ||
2642 | if (!ftpFolder(e->name, false)) { | 2540 | if (!ftpFolder(e->name)) { | ||
2643 | QString name(e->name.trimmed()); | 2541 | QString name(e->name.trimmed()); | ||
2644 | if (ftpFolder(name, false)) { | 2542 | if (ftpFolder(name)) { | ||
2645 | e->name = name; | 2543 | e->name = name; | ||
2646 | qCDebug(KIO_FTP) << "fixing up directory name from" << e->name << "to" << name; | 2544 | qCDebug(KIO_FTP) << "fixing up directory name from" << e->name << "to" << name; | ||
2647 | } else { | 2545 | } else { | ||
2648 | int index = 0; | 2546 | int index = 0; | ||
2649 | while (e->name.at(index).isSpace()) { | 2547 | while (e->name.at(index).isSpace()) { | ||
2650 | index++; | 2548 | index++; | ||
2651 | name = e->name.mid(index); | 2549 | name = e->name.mid(index); | ||
2652 | if (ftpFolder(name, false)) { | 2550 | if (ftpFolder(name)) { | ||
2653 | qCDebug(KIO_FTP) << "fixing up directory name from" << e->name << "to" << name; | 2551 | qCDebug(KIO_FTP) << "fixing up directory name from" << e->name << "to" << name; | ||
2654 | e->name = name; | 2552 | e->name = name; | ||
2655 | break; | 2553 | break; | ||
2656 | } | 2554 | } | ||
2657 | } | 2555 | } | ||
2658 | } | 2556 | } | ||
2659 | } | 2557 | } | ||
2660 | } else { | 2558 | } else { | ||
Show All 13 Lines | 2569 | if (ftpFileExists(name)) { | |||
2674 | break; | 2572 | break; | ||
2675 | } | 2573 | } | ||
2676 | } | 2574 | } | ||
2677 | } | 2575 | } | ||
2678 | } | 2576 | } | ||
2679 | } | 2577 | } | ||
2680 | } | 2578 | } | ||
2681 | 2579 | | |||
2682 | QTcpSocket *Ftp::synchronousConnectToHost(const QString &host, quint16 port) | 2580 | ConnectionResult FtpInternal::synchronousConnectToHost(const QString &host, quint16 port) | ||
2683 | { | 2581 | { | ||
2582 | const QUrl proxyUrl = m_proxyURL; | ||||
2583 | QNetworkProxy proxy; | ||||
2584 | if (!proxyUrl.isEmpty()) { | ||||
2585 | proxy = QNetworkProxy(QNetworkProxy::Socks5Proxy, | ||||
2586 | proxyUrl.host(), | ||||
2587 | static_cast<quint16>(proxyUrl.port(0)), | ||||
2588 | proxyUrl.userName(), | ||||
2589 | proxyUrl.password()); | ||||
2590 | } | ||||
2591 | | ||||
2684 | QTcpSocket *socket = new QSslSocket; | 2592 | QTcpSocket *socket = new QSslSocket; | ||
2593 | socket->setProxy(proxy); | ||||
2594 | socket->connectToHost(host, port); | ||||
2595 | socket->waitForConnected(q->connectTimeout() * 1000); | ||||
2596 | | ||||
2597 | if (socket->error() == QAbstractSocket::ProxyAuthenticationRequiredError) { | ||||
2598 | AuthInfo info; | ||||
2599 | info.url = proxyUrl; | ||||
2600 | info.verifyPath = true; //### whatever | ||||
2601 | | ||||
2602 | if (!q->checkCachedAuthentication(info)) { | ||||
2603 | info.prompt = i18n("You need to supply a username and a password for " | ||||
2604 | "the proxy server listed below before you are allowed " | ||||
2605 | "to access any sites."); | ||||
2606 | info.keepPassword = true; | ||||
2607 | info.commentLabel = i18n("Proxy:"); | ||||
2608 | info.comment = i18n("<b>%1</b>", proxy.hostName()); | ||||
2609 | | ||||
2610 | const int errorCode = q->openPasswordDialogV2(info, i18n("Proxy Authentication Failed.")); | ||||
2611 | if (errorCode != KJob::NoError) { | ||||
2612 | qCDebug(KIO_FTP) << "user canceled proxy authentication, or communication error." << errorCode; | ||||
2613 | return ConnectionResult { socket, | ||||
2614 | Result::fail(errorCode, proxyUrl.toString()) }; | ||||
2615 | } | ||||
2616 | } | ||||
2617 | | ||||
2618 | proxy.setUser(info.username); | ||||
2619 | proxy.setPassword(info.password); | ||||
2620 | | ||||
2621 | delete socket; | ||||
2622 | socket = new QSslSocket; | ||||
2623 | socket->setProxy(proxy); | ||||
2685 | socket->connectToHost(host, port); | 2624 | socket->connectToHost(host, port); | ||
2686 | socket->waitForConnected(connectTimeout() * 1000); | 2625 | socket->waitForConnected(q->connectTimeout() * 1000); | ||
2687 | return socket; | 2626 | | ||
2627 | if (socket->state() == QAbstractSocket::ConnectedState) { | ||||
2628 | // reconnect with credentials was successful -> save data | ||||
2629 | q->cacheAuthentication(info); | ||||
2630 | | ||||
2631 | m_proxyURL.setUserName(info.username); | ||||
2632 | m_proxyURL.setPassword(info.password); | ||||
2633 | } | ||||
2634 | } | ||||
2635 | | ||||
dfaure: yeah, looks like wishful thinking.... remove? | |||||
2636 | return ConnectionResult { socket, Result::pass() }; | ||||
2637 | } | ||||
2638 | | ||||
2639 | //=============================================================================== | ||||
2640 | // Ftp | ||||
2641 | //=============================================================================== | ||||
2642 | | ||||
2643 | Ftp::Ftp(const QByteArray &pool, const QByteArray &app) | ||||
2644 | : SlaveBase(QByteArrayLiteral("ftp"), pool, app) | ||||
2645 | , d(new FtpInternal(this)) | ||||
2646 | { | ||||
2647 | } | ||||
2648 | | ||||
2649 | Ftp::~Ftp() = default; | ||||
2650 | | ||||
2651 | void Ftp::setHost(const QString &host, quint16 port, const QString &user, const QString &pass) | ||||
2652 | { | ||||
2653 | d->setHost(host, port, user, pass); | ||||
2654 | } | ||||
2655 | | ||||
2656 | void Ftp::openConnection() | ||||
2657 | { | ||||
2658 | const auto result = d->openConnection(); | ||||
2659 | if (!result.success) { | ||||
2660 | error(result.error, result.errorString); | ||||
2661 | return; | ||||
2662 | } | ||||
2663 | opened(); | ||||
2664 | } | ||||
2665 | | ||||
2666 | void Ftp::closeConnection() | ||||
2667 | { | ||||
2668 | d->closeConnection(); | ||||
2669 | } | ||||
2670 | | ||||
2671 | void Ftp::stat(const QUrl &url) | ||||
2672 | { | ||||
2673 | finalize(d->stat(url)); | ||||
2674 | } | ||||
2675 | | ||||
2676 | void Ftp::listDir(const QUrl &url) | ||||
2677 | { | ||||
2678 | finalize(d->listDir(url)); | ||||
2679 | } | ||||
2680 | | ||||
2681 | void Ftp::mkdir(const QUrl &url, int permissions) | ||||
2682 | { | ||||
2683 | finalize(d->mkdir(url, permissions)); | ||||
2684 | } | ||||
2685 | | ||||
2686 | void Ftp::rename(const QUrl &src, const QUrl &dst, JobFlags flags) | ||||
2687 | { | ||||
2688 | finalize(d->rename(src, dst, flags)); | ||||
2689 | } | ||||
2690 | | ||||
2691 | void Ftp::del(const QUrl &url, bool isfile) | ||||
2692 | { | ||||
2693 | finalize(d->del(url, isfile)); | ||||
2694 | } | ||||
2695 | | ||||
2696 | void Ftp::chmod(const QUrl &url, int permissions) | ||||
2697 | { | ||||
2698 | finalize(d->chmod(url, permissions)); | ||||
2699 | } | ||||
2700 | | ||||
2701 | void Ftp::get(const QUrl &url) | ||||
2702 | { | ||||
2703 | finalize(d->get(url)); | ||||
2704 | } | ||||
2705 | | ||||
2706 | void Ftp::put(const QUrl &url, int permissions, JobFlags flags) | ||||
2707 | { | ||||
2708 | finalize(d->put(url, permissions, flags)); | ||||
2709 | } | ||||
2710 | | ||||
2711 | void Ftp::slave_status() | ||||
2712 | { | ||||
2713 | d->slave_status(); | ||||
2714 | } | ||||
2715 | | ||||
2716 | void Ftp::copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) | ||||
2717 | { | ||||
2718 | finalize(d->copy(src, dest, permissions, flags)); | ||||
2719 | } | ||||
2720 | | ||||
2721 | void Ftp::finalize(const Result &result) | ||||
2722 | { | ||||
2723 | if (!result.success) { | ||||
2724 | error(result.error, result.errorString); | ||||
2725 | return; | ||||
2726 | } | ||||
2727 | finished(); | ||||
2728 | } | ||||
2729 | | ||||
2730 | QDebug operator<<(QDebug dbg, const Result &r) | ||||
2731 | | ||||
2732 | { | ||||
2733 | QDebugStateSaver saver(dbg); | ||||
2734 | dbg.nospace() << "Result(" | ||||
2735 | << "success=" << r.success | ||||
2736 | << ", err=" << r.error | ||||
2737 | << ", str=" << r.errorString | ||||
2738 | << ')'; | ||||
2739 | return dbg; | ||||
2688 | } | 2740 | } | ||
2689 | 2741 | | |||
2690 | // needed for JSON file embedding | 2742 | // needed for JSON file embedding | ||
2691 | #include "ftp.moc" | 2743 | #include "ftp.moc" | ||
This looks wrong. openConnection() should emit connected() or error(), but never finished(). dfaure: This looks wrong. openConnection() should emit connected() or error(), but never finished(). | |||||
You may wish to take a look at D23537 ;) sitter: You may wish to take a look at D23537 ;) | |||||
dfaure: remember to clean up the qDebug()s before the final version |
I don't think we want to return on failure here.
If the server doesn't support this command, we might as well try proceeding.
That's why the old code actually ignored errors here.