diff --git a/autotests/udsentrytest.cpp b/autotests/udsentrytest.cpp index e8f60992..3d19870a 100644 --- a/autotests/udsentrytest.cpp +++ b/autotests/udsentrytest.cpp @@ -1,264 +1,264 @@ /* This file is part of the KDE project Copyright (C) 2013 Frank Reininghaus This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "udsentrytest.h" #include #include #include #include #include #include #include #include "kiotesthelper.h" struct UDSTestField { UDSTestField() {} UDSTestField(uint uds, const QString &value) : m_uds(uds), m_string(value) { Q_ASSERT(uds & KIO::UDSEntry::UDS_STRING); } UDSTestField(uint uds, long long value) : m_uds(uds), m_long(value) { Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER); } uint m_uds; QString m_string; long long m_long; }; /** * Test that storing UDSEntries to a stream and then re-loading them works. */ void UDSEntryTest::testSaveLoad() { QVector > testCases { // Data for 1st UDSEntry. { UDSTestField(KIO::UDSEntry::UDS_SIZE, 1), UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user1")), UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1")), UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename1")), UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 123456), UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 12345), UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 2), UDSTestField(KIO::UDSEntry::UDS_INODE, 56) }, // 2nd entry: change some of the data. { UDSTestField(KIO::UDSEntry::UDS_SIZE, 2), UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user2")), UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1")), UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename2")), UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12345), UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1234), UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 87), UDSTestField(KIO::UDSEntry::UDS_INODE, 42) }, // 3rd entry: keep the data, but change the order of the entries. { UDSTestField(KIO::UDSEntry::UDS_SIZE, 2), UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group1")), UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user2")), UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename2")), UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12345), UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 87), UDSTestField(KIO::UDSEntry::UDS_INODE, 42), UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1234), }, // 4th entry: change some of the data and the order of the entries. { UDSTestField(KIO::UDSEntry::UDS_SIZE, 2), UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user4")), UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group4")), UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12346), UDSTestField(KIO::UDSEntry::UDS_DEVICE_ID, 87), UDSTestField(KIO::UDSEntry::UDS_INODE, 42), UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1235), UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename4")) }, // 5th entry: remove one field. { UDSTestField(KIO::UDSEntry::UDS_SIZE, 2), UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user4")), UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group4")), UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12346), UDSTestField(KIO::UDSEntry::UDS_INODE, 42), UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1235), UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename4")) }, // 6th entry: add a new field, and change some others. { UDSTestField(KIO::UDSEntry::UDS_SIZE, 89), UDSTestField(KIO::UDSEntry::UDS_ICON_NAME, QStringLiteral("icon6")), UDSTestField(KIO::UDSEntry::UDS_USER, QStringLiteral("user6")), UDSTestField(KIO::UDSEntry::UDS_GROUP, QStringLiteral("group4")), UDSTestField(KIO::UDSEntry::UDS_MODIFICATION_TIME, 12346), UDSTestField(KIO::UDSEntry::UDS_INODE, 32), UDSTestField(KIO::UDSEntry::UDS_CREATION_TIME, 1235), UDSTestField(KIO::UDSEntry::UDS_NAME, QStringLiteral("filename6")) } }; // Store the entries in a QByteArray. QByteArray data; { QDataStream stream(&data, QIODevice::WriteOnly); foreach (const QVector &testCase, testCases) { KIO::UDSEntry entry; foreach (const UDSTestField &field, testCase) { uint uds = field.m_uds; if (uds & KIO::UDSEntry::UDS_STRING) { entry.fastInsert(uds, field.m_string); } else { Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER); entry.fastInsert(uds, field.m_long); } } QCOMPARE(entry.count(), testCase.count()); stream << entry; } } // Re-load the entries and compare with the data in testCases. { QDataStream stream(data); foreach (const QVector &testCase, testCases) { KIO::UDSEntry entry; stream >> entry; QCOMPARE(entry.count(), testCase.count()); foreach (const UDSTestField &field, testCase) { uint uds = field.m_uds; QVERIFY(entry.contains(uds)); if (uds & KIO::UDSEntry::UDS_STRING) { QCOMPARE(entry.stringValue(uds), field.m_string); } else { Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER); QCOMPARE(entry.numberValue(uds), field.m_long); } } } } // Now: Store the fields manually in the order in which they appear in // testCases, and re-load them. This ensures that loading the entries // works no matter in which order the fields appear in the QByteArray. data.clear(); { QDataStream stream(&data, QIODevice::WriteOnly); foreach (const QVector &testCase, testCases) { stream << testCase.count(); foreach (const UDSTestField &field, testCase) { uint uds = field.m_uds; stream << uds; if (uds & KIO::UDSEntry::UDS_STRING) { stream << field.m_string; } else { Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER); stream << field.m_long; } } } } { QDataStream stream(data); foreach (const QVector &testCase, testCases) { KIO::UDSEntry entry; stream >> entry; QCOMPARE(entry.count(), testCase.count()); foreach (const UDSTestField &field, testCase) { uint uds = field.m_uds; QVERIFY(entry.contains(uds)); if (uds & KIO::UDSEntry::UDS_STRING) { QCOMPARE(entry.stringValue(uds), field.m_string); } else { Q_ASSERT(uds & KIO::UDSEntry::UDS_NUMBER); QCOMPARE(entry.numberValue(uds), field.m_long); } } } } } /** * Test to verify that move semantics work. This is only useful when ran through callgrind. */ void UDSEntryTest::testMove() { // Create a temporary file. Just to make a UDSEntry further down. QTemporaryFile file; QVERIFY(file.open()); const QByteArray filePath = file.fileName().toLocal8Bit(); const QString fileName = QUrl(file.fileName()).fileName(); // QTemporaryFile::fileName returns the full path. QVERIFY(!fileName.isEmpty()); // We have a file now. Get the stat data from it to make the UDSEntry. QT_STATBUF statBuf; QVERIFY(QT_LSTAT(filePath.constData(), &statBuf) == 0); KIO::UDSEntry entry(statBuf, fileName); // Verify that the name in the UDSEntry is the same as we've got from the fileName var. QCOMPARE(fileName, entry.stringValue(KIO::UDSEntry::UDS_NAME)); // That was the boilerplate code. Now for move semantics. // First: move assignment. { // First a copy as we need to keep the entry for the next test. KIO::UDSEntry entryCopy = entry; // Now move-assignment (two lines to prevent compiler optimization) KIO::UDSEntry movedEntry; movedEntry = std::move(entryCopy); - // And veryfy that this works. + // And verify that this works. QCOMPARE(fileName, movedEntry.stringValue(KIO::UDSEntry::UDS_NAME)); } // Move constructor { // First a copy again KIO::UDSEntry entryCopy = entry; // Now move-assignment KIO::UDSEntry movedEntry(std::move(entryCopy)); // And verify that this works. QCOMPARE(fileName, movedEntry.stringValue(KIO::UDSEntry::UDS_NAME)); } } QTEST_MAIN(UDSEntryTest) diff --git a/autotests/urlutiltest.cpp b/autotests/urlutiltest.cpp index a4c1b7b6..2d757dbc 100644 --- a/autotests/urlutiltest.cpp +++ b/autotests/urlutiltest.cpp @@ -1,61 +1,61 @@ /*************************************************************************** * Copyright (C) 2016 by Gregor Mi * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * ***************************************************************************/ #include #include "../src/filewidgets/urlutil_p.h" class UrlUtilTest : public QObject { Q_OBJECT private Q_SLOTS: void testFirstChildUrl(); }; static inline QUrl lUrl(const QString &path) { return QUrl::fromLocalFile(path); } void UrlUtilTest::testFirstChildUrl() { QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/test/data/documents/muh/"), lUrl("/home/test/")), lUrl("/home/test/data")); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/test/data/documents/muh"), lUrl("/home/test")), lUrl("/home/test/data")); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/test/data/documents/muh/"), lUrl("/home/test")), lUrl("/home/test/data")); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/test/data/documents/muh"), lUrl("/home/test/")), lUrl("/home/test/data")); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/a/"), lUrl("/home")), lUrl("/home/a")); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/te"), lUrl("/")), lUrl("/te")); // One letter under root is also a valid child QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/d"), lUrl("/")), lUrl("/d")); - // Same urls sould return QUrl() + // Same urls should return QUrl() QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/test/data"), lUrl("/home/test/data/")), QUrl()); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/test/data/"), lUrl("/home/test/data")), QUrl()); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/test/"), lUrl("/home/test/")), QUrl()); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/"), lUrl("/")), QUrl()); // Not related urls should return QUrl() QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/aaa/"), lUrl("/home/bbb/")), QUrl()); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/aaa/"), lUrl("/home/bbb/ccc")), QUrl()); QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home"), lUrl("/test")), QUrl()); // Child urls in reverse order should return QUrl() QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home"), lUrl("/home/test")), QUrl()); // # is %23 in an URL, so this test could reveal path/URL confusion in the code: QCOMPARE(KIO::UrlUtil::firstChildUrl(lUrl("/home/a#/b#"), lUrl("/home/a#")), lUrl("/home/a#/b#")); } QTEST_MAIN(UrlUtilTest) #include "urlutiltest.moc" diff --git a/src/core/job_error.cpp b/src/core/job_error.cpp index 88474eee..7bee28b0 100644 --- a/src/core/job_error.cpp +++ b/src/core/job_error.cpp @@ -1,1131 +1,1131 @@ /* This file is part of the KDE libraries Copyright (C) 2000 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "job.h" #include "kioglobal_p.h" #include #include #include #include #include #include // S_IRUSR etc QString KIO::Job::errorString() const { return KIO::buildErrorString(error(), errorText()); } KIOCORE_EXPORT QString KIO::buildErrorString(int errorCode, const QString &errorText) { QString result; switch (errorCode) { case KIO::ERR_CANNOT_OPEN_FOR_READING: result = i18n("Could not read %1.", errorText); break; case KIO::ERR_CANNOT_OPEN_FOR_WRITING: result = i18n("Could not write to %1.", errorText); break; case KIO::ERR_CANNOT_LAUNCH_PROCESS: result = i18n("Could not start process %1.", errorText); break; case KIO::ERR_INTERNAL: result = i18n("Internal Error\nPlease send a full bug report at http://bugs.kde.org\n%1", errorText); break; case KIO::ERR_MALFORMED_URL: result = i18n("Malformed URL %1.", errorText); break; case KIO::ERR_UNSUPPORTED_PROTOCOL: result = i18n("The protocol %1 is not supported.", errorText); break; case KIO::ERR_NO_SOURCE_PROTOCOL: result = i18n("The protocol %1 is only a filter protocol.", errorText); break; case KIO::ERR_UNSUPPORTED_ACTION: result = errorText; // result = i18n( "Unsupported action %1" ).arg( errorText ); break; case KIO::ERR_IS_DIRECTORY: result = i18n("%1 is a folder, but a file was expected.", errorText); break; case KIO::ERR_IS_FILE: result = i18n("%1 is a file, but a folder was expected.", errorText); break; case KIO::ERR_DOES_NOT_EXIST: result = i18n("The file or folder %1 does not exist.", errorText); break; case KIO::ERR_FILE_ALREADY_EXIST: result = i18n("A file named %1 already exists.", errorText); break; case KIO::ERR_DIR_ALREADY_EXIST: result = i18n("A folder named %1 already exists.", errorText); break; case KIO::ERR_UNKNOWN_HOST: result = errorText.isEmpty() ? i18n("No hostname specified.") : i18n("Unknown host %1", errorText); break; case KIO::ERR_ACCESS_DENIED: result = i18n("Access denied to %1.", errorText); break; case KIO::ERR_WRITE_ACCESS_DENIED: result = i18n("Access denied.\nCould not write to %1.", errorText); break; case KIO::ERR_CANNOT_ENTER_DIRECTORY: result = i18n("Could not enter folder %1.", errorText); break; case KIO::ERR_PROTOCOL_IS_NOT_A_FILESYSTEM: result = i18n("The protocol %1 does not implement a folder service.", errorText); break; case KIO::ERR_CYCLIC_LINK: result = i18n("Found a cyclic link in %1.", errorText); break; case KIO::ERR_USER_CANCELED: // Do nothing in this case. The user doesn't need to be told what he just did. break; case KIO::ERR_CYCLIC_COPY: result = i18n("Found a cyclic link while copying %1.", errorText); break; case KIO::ERR_CANNOT_CREATE_SOCKET: result = i18n("Could not create socket for accessing %1.", errorText); break; case KIO::ERR_CANNOT_CONNECT: result = i18n("Could not connect to host %1.", errorText.isEmpty() ? QStringLiteral("localhost") : errorText); break; case KIO::ERR_CONNECTION_BROKEN: result = i18n("Connection to host %1 is broken.", errorText); break; case KIO::ERR_NOT_FILTER_PROTOCOL: result = i18n("The protocol %1 is not a filter protocol.", errorText); break; case KIO::ERR_CANNOT_MOUNT: result = i18n("Could not mount device.\nThe reported error was:\n%1", errorText); break; case KIO::ERR_CANNOT_UNMOUNT: result = i18n("Could not unmount device.\nThe reported error was:\n%1", errorText); break; case KIO::ERR_CANNOT_READ: result = i18n("Could not read file %1.", errorText); break; case KIO::ERR_CANNOT_WRITE: result = i18n("Could not write to file %1.", errorText); break; case KIO::ERR_CANNOT_BIND: result = i18n("Could not bind %1.", errorText); break; case KIO::ERR_CANNOT_LISTEN: result = i18n("Could not listen %1.", errorText); break; case KIO::ERR_CANNOT_ACCEPT: result = i18n("Could not accept %1.", errorText); break; case KIO::ERR_CANNOT_LOGIN: result = errorText; break; case KIO::ERR_CANNOT_STAT: result = i18n("Could not access %1.", errorText); break; case KIO::ERR_CANNOT_CLOSEDIR: result = i18n("Could not terminate listing %1.", errorText); break; case KIO::ERR_CANNOT_MKDIR: result = i18n("Could not make folder %1.", errorText); break; case KIO::ERR_CANNOT_RMDIR: result = i18n("Could not remove folder %1.", errorText); break; case KIO::ERR_CANNOT_RESUME: result = i18n("Could not resume file %1.", errorText); break; case KIO::ERR_CANNOT_RENAME: result = i18n("Could not rename file %1.", errorText); break; case KIO::ERR_CANNOT_CHMOD: result = i18n("Could not change permissions for %1.", errorText); break; case KIO::ERR_CANNOT_CHOWN: result = i18n("Could not change ownership for %1.", errorText); break; case KIO::ERR_CANNOT_DELETE: result = i18n("Could not delete file %1.", errorText); break; case KIO::ERR_SLAVE_DIED: result = i18n("The process for the %1 protocol died unexpectedly.", errorText); break; case KIO::ERR_OUT_OF_MEMORY: result = i18n("Error. Out of memory.\n%1", errorText); break; case KIO::ERR_UNKNOWN_PROXY_HOST: result = i18n("Unknown proxy host\n%1", errorText); break; case KIO::ERR_CANNOT_AUTHENTICATE: result = i18n("Authorization failed, %1 authentication not supported", errorText); break; case KIO::ERR_ABORTED: result = i18n("User canceled action\n%1", errorText); break; case KIO::ERR_INTERNAL_SERVER: result = i18n("Internal error in server\n%1", errorText); break; case KIO::ERR_SERVER_TIMEOUT: result = i18n("Timeout on server\n%1", errorText); break; case KIO::ERR_UNKNOWN: result = i18n("Unknown error\n%1", errorText); break; case KIO::ERR_UNKNOWN_INTERRUPT: result = i18n("Unknown interrupt\n%1", errorText); break; /* case KIO::ERR_CHECKSUM_MISMATCH: if (errorText) result = i18n( "Warning: MD5 Checksum for %1 does not match checksum returned from server" ).arg(errorText); else result = i18n( "Warning: MD5 Checksum for %1 does not match checksum returned from server" ).arg("document"); break; */ case KIO::ERR_CANNOT_DELETE_ORIGINAL: result = i18n("Could not delete original file %1.\nPlease check permissions.", errorText); break; case KIO::ERR_CANNOT_DELETE_PARTIAL: result = i18n("Could not delete partial file %1.\nPlease check permissions.", errorText); break; case KIO::ERR_CANNOT_RENAME_ORIGINAL: result = i18n("Could not rename original file %1.\nPlease check permissions.", errorText); break; case KIO::ERR_CANNOT_RENAME_PARTIAL: result = i18n("Could not rename partial file %1.\nPlease check permissions.", errorText); break; case KIO::ERR_CANNOT_SYMLINK: result = i18n("Could not create symlink %1.\nPlease check permissions.", errorText); break; case KIO::ERR_NO_CONTENT: result = errorText; break; case KIO::ERR_DISK_FULL: result = i18n("There is not enough space on the disk to write %1.", errorText); break; case KIO::ERR_IDENTICAL_FILES: result = i18n("The source and destination are the same file.\n%1", errorText); break; case KIO::ERR_SLAVE_DEFINED: result = errorText; break; case KIO::ERR_UPGRADE_REQUIRED: result = i18n("%1 is required by the server, but is not available.", errorText); break; case KIO::ERR_POST_DENIED: result = i18n("Access to restricted port in POST denied."); break; case KIO::ERR_POST_NO_SIZE: result = i18n("The required content size information was not provided for a POST operation."); break; case KIO::ERR_DROP_ON_ITSELF: result = i18n("A file or folder cannot be dropped onto itself"); break; case KIO::ERR_CANNOT_MOVE_INTO_ITSELF: result = i18n("A folder cannot be moved into itself"); break; case KIO::ERR_PASSWD_SERVER: result = i18n("Communication with the local password server failed"); break; case KIO::ERR_CANNOT_CREATE_SLAVE: result = i18n("Unable to create io-slave. %1", errorText); break; case KIO::ERR_FILE_TOO_LARGE_FOR_FAT32: result = xi18n("Cannot transfer %1 because it is too large. The destination filesystem only supports files up to 4GB", errorText); break; default: result = i18n("Unknown error code %1\n%2\nPlease send a full bug report at https://bugs.kde.org.", errorCode, errorText); break; } return result; } QStringList KIO::Job::detailedErrorStrings(const QUrl *reqUrl /*= 0*/, int method /*= -1*/) const { QString errorName, techName, description, ret2; QStringList causes, solutions, ret; QByteArray raw = rawErrorDetail(error(), errorText(), reqUrl, method); QDataStream stream(raw); stream >> errorName >> techName >> description >> causes >> solutions; QString url, protocol, datetime; if (reqUrl) { QString prettyUrl; prettyUrl = reqUrl->toDisplayString(); url = prettyUrl.toHtmlEscaped(); protocol = reqUrl->scheme(); } else { url = i18nc("@info url", "(unknown)"); } datetime = QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate); ret << errorName; ret << i18nc("@info %1 error name, %2 description", "

%1

%2

", errorName, description); ret2 = QStringLiteral(""); if (!techName.isEmpty()) ret2 += QLatin1String("

") + i18n("Technical reason: ") + techName + QLatin1String("

"); ret2 += QLatin1String("

") + i18n("Details of the request:") + QLatin1String("

    ") + i18n("
  • URL: %1
  • ", url); if (!protocol.isEmpty()) { ret2 += i18n("
  • Protocol: %1
  • ", protocol); } ret2 += i18n("
  • Date and time: %1
  • ", datetime) + i18n("
  • Additional information: %1
  • ", errorText()) + QLatin1String("
"); if (!causes.isEmpty()) { ret2 += QLatin1String("

") + i18n("Possible causes:") + QLatin1String("

  • ") + causes.join(QStringLiteral("
  • ")) + QLatin1String("
"); } if (!solutions.isEmpty()) { ret2 += QLatin1String("

") + i18n("Possible solutions:") + QLatin1String("

  • ") + solutions.join(QStringLiteral("
  • ")) + QLatin1String("
"); } ret2 += QLatin1String("
"); ret << ret2; return ret; } KIOCORE_EXPORT QByteArray KIO::rawErrorDetail(int errorCode, const QString &errorText, const QUrl *reqUrl /*= 0*/, int /*method = -1*/) { QString url, host, protocol, datetime, domain, path, filename; bool isSlaveNetwork = false; if (reqUrl) { url = reqUrl->toDisplayString(); host = reqUrl->host(); protocol = reqUrl->scheme(); if (host.startsWith(QLatin1String("www."))) { domain = host.mid(4); } else { domain = host; } filename = reqUrl->fileName(); path = reqUrl->path(); // detect if protocol is a network protocol... isSlaveNetwork = KProtocolInfo::protocolClass(protocol) == QLatin1String(":internet"); } else { // assume that the errorText has the location we are interested in url = host = domain = path = filename = errorText; protocol = i18nc("@info protocol", "(unknown)"); } datetime = QDateTime::currentDateTime().toString(Qt::DefaultLocaleLongDate); QString errorName, techName, description; QStringList causes, solutions; // c == cause, s == solution QString sSysadmin = i18n("Contact your appropriate computer support system, " "whether the system administrator, or technical support group for further " "assistance."); QString sServeradmin = i18n("Contact the administrator of the server " "for further assistance."); // FIXME active link to permissions dialog QString sAccess = i18n("Check your access permissions on this resource."); QString cAccess = i18n("Your access permissions may be inadequate to " "perform the requested operation on this resource."); QString cLocked = i18n("The file may be in use (and thus locked) by " "another user or application."); QString sQuerylock = i18n("Check to make sure that no other " "application or user is using the file or has locked the file."); QString cHardware = i18n("Although unlikely, a hardware error may have " "occurred."); QString cBug = i18n("You may have encountered a bug in the program."); QString cBuglikely = i18n("This is most likely to be caused by a bug in the " "program. Please consider submitting a full bug report as detailed below."); QString sUpdate = i18n("Update your software to the latest version. " "Your distribution should provide tools to update your software."); QString sBugreport = i18n("When all else fails, please consider helping the " "KDE team or the third party maintainer of this software by submitting a " "high quality bug report. If the software is provided by a third party, " "please contact them directly. Otherwise, first look to see if " "the same bug has been submitted by someone else by searching at the " "KDE bug reporting website. If not, take " "note of the details given above, and include them in your bug report, along " "with as many other details as you think might help."); QString cNetwork = i18n("There may have been a problem with your network " "connection."); // FIXME netconf kcontrol link QString cNetconf = i18n("There may have been a problem with your network " "configuration. If you have been accessing the Internet with no problems " "recently, this is unlikely."); QString cNetpath = i18n("There may have been a problem at some point along " "the network path between the server and this computer."); QString sTryagain = i18n("Try again, either now or at a later time."); QString cProtocol = i18n("A protocol error or incompatibility may have occurred."); QString sExists = i18n("Ensure that the resource exists, and try again."); QString cExists = i18n("The specified resource may not exist."); QString sTypo = i18n("Double-check that you have entered the correct location " "and try again."); QString sNetwork = i18n("Check your network connection status."); switch (errorCode) { case KIO::ERR_CANNOT_OPEN_FOR_READING: errorName = i18n("Cannot Open Resource For Reading"); description = i18n("This means that the contents of the requested file " "or folder %1 could not be retrieved, as read " "access could not be obtained.", path); causes << i18n("You may not have permissions to read the file or open " "the folder.") << cLocked << cHardware; solutions << sAccess << sQuerylock << sSysadmin; break; case KIO::ERR_CANNOT_OPEN_FOR_WRITING: errorName = i18n("Cannot Open Resource For Writing"); description = i18n("This means that the file, %1, could " "not be written to as requested, because access with permission to " "write could not be obtained.", filename); causes << cAccess << cLocked << cHardware; solutions << sAccess << sQuerylock << sSysadmin; break; case KIO::ERR_CANNOT_LAUNCH_PROCESS: errorName = i18n("Cannot Launch Process required by the %1 Protocol", protocol); techName = i18n("Unable to Launch Process"); description = i18n("The program on your computer which provides access " "to the %1 protocol could not be found or started. This is " "usually due to technical reasons.", protocol); causes << i18n("The program which provides compatibility with this " "protocol may not have been updated with your last update of KDE. " "This can cause the program to be incompatible with the current version " "and thus not start.") << cBug; solutions << sUpdate << sSysadmin; break; case KIO::ERR_INTERNAL: errorName = i18n("Internal Error"); description = i18n("The program on your computer which provides access " "to the %1 protocol has reported an internal error.", protocol); causes << cBuglikely; solutions << sUpdate << sBugreport; break; case KIO::ERR_MALFORMED_URL: errorName = i18n("Improperly Formatted URL"); description = i18n("The Uniform Resource " "Locator (URL) that you entered was not properly " "formatted. The format of a URL is generally as follows:" "
protocol://user:password@www.example.org:port/folder/" "filename.extension?query=value
"); solutions << sTypo; break; case KIO::ERR_UNSUPPORTED_PROTOCOL: errorName = i18n("Unsupported Protocol %1", protocol); description = i18n("The protocol %1 is not supported " "by the KDE programs currently installed on this computer.", protocol); causes << i18n("The requested protocol may not be supported.") << i18n("The versions of the %1 protocol supported by this computer and " "the server may be incompatible.", protocol); solutions << i18n("You may perform a search on the Internet for a KDE " "program (called a kioslave or ioslave) which supports this protocol. " "Places to search include " "http://kde-apps.org/ and " "http://freshmeat.net/.") << sUpdate << sSysadmin; break; case KIO::ERR_NO_SOURCE_PROTOCOL: errorName = i18n("URL Does Not Refer to a Resource."); techName = i18n("Protocol is a Filter Protocol"); description = i18n("The Uniform Resource " "Locator (URL) that you entered did not refer to a " "specific resource."); causes << i18n("KDE is able to communicate through a protocol within a " "protocol; the protocol specified is only for use in such situations, " "however this is not one of these situations. This is a rare event, and " "is likely to indicate a programming error."); solutions << sTypo; break; case KIO::ERR_UNSUPPORTED_ACTION: errorName = i18n("Unsupported Action: %1", errorText); description = i18n("The requested action is not supported by the KDE " "program which is implementing the %1 protocol.", protocol); causes << i18n("This error is very much dependent on the KDE program. The " "additional information should give you more information than is available " "to the KDE input/output architecture."); solutions << i18n("Attempt to find another way to accomplish the same " "outcome."); break; case KIO::ERR_IS_DIRECTORY: errorName = i18n("File Expected"); description = i18n("The request expected a file, however the " "folder %1 was found instead.", path); causes << i18n("This may be an error on the server side.") << cBug; solutions << sUpdate << sSysadmin; break; case KIO::ERR_IS_FILE: errorName = i18n("Folder Expected"); description = i18n("The request expected a folder, however " "the file %1 was found instead.", filename); causes << cBug; solutions << sUpdate << sSysadmin; break; case KIO::ERR_DOES_NOT_EXIST: errorName = i18n("File or Folder Does Not Exist"); description = i18n("The specified file or folder %1 " "does not exist.", path); causes << cExists; solutions << sExists; break; case KIO::ERR_FILE_ALREADY_EXIST: errorName = i18n("File Already Exists"); description = i18n("The requested file could not be created because a " "file with the same name already exists."); solutions << i18n("Try moving the current file out of the way first, " "and then try again.") << i18n("Delete the current file and try again.") << i18n("Choose an alternate filename for the new file."); break; case KIO::ERR_DIR_ALREADY_EXIST: errorName = i18n("Folder Already Exists"); description = i18n("The requested folder could not be created because " "a folder with the same name already exists."); solutions << i18n("Try moving the current folder out of the way first, " "and then try again.") << i18n("Delete the current folder and try again.") << i18n("Choose an alternate name for the new folder."); break; case KIO::ERR_UNKNOWN_HOST: errorName = i18n("Unknown Host"); description = i18n("An unknown host error indicates that the server with " "the requested name, %1, could not be " "located on the Internet.", host); causes << i18n("The name that you typed, %1, may not exist: it may be " "incorrectly typed.", host) << cNetwork << cNetconf; solutions << sNetwork << sSysadmin; break; case KIO::ERR_ACCESS_DENIED: errorName = i18n("Access Denied"); description = i18n("Access was denied to the specified resource, " "%1.", url); causes << i18n("You may have supplied incorrect authentication details or " "none at all.") << i18n("Your account may not have permission to access the " "specified resource."); solutions << i18n("Retry the request and ensure your authentication details " "are entered correctly.") << sSysadmin; if (!isSlaveNetwork) { solutions << sServeradmin; } break; case KIO::ERR_WRITE_ACCESS_DENIED: errorName = i18n("Write Access Denied"); description = i18n("This means that an attempt to write to the file " "%1 was rejected.", filename); causes << cAccess << cLocked << cHardware; solutions << sAccess << sQuerylock << sSysadmin; break; case KIO::ERR_CANNOT_ENTER_DIRECTORY: errorName = i18n("Unable to Enter Folder"); description = i18n("This means that an attempt to enter (in other words, " "to open) the requested folder %1 was rejected.", path); causes << cAccess << cLocked; solutions << sAccess << sQuerylock << sSysadmin; break; case KIO::ERR_PROTOCOL_IS_NOT_A_FILESYSTEM: errorName = i18n("Folder Listing Unavailable"); techName = i18n("Protocol %1 is not a Filesystem", protocol); description = i18n("This means that a request was made which requires " "determining the contents of the folder, and the KDE program supporting " "this protocol is unable to do so."); causes << cBug; solutions << sUpdate << sBugreport; break; case KIO::ERR_CYCLIC_LINK: errorName = i18n("Cyclic Link Detected"); description = i18n("UNIX environments are commonly able to link a file or " "folder to a separate name and/or location. KDE detected a link or " "series of links that results in an infinite loop - i.e. the file was " "(perhaps in a roundabout way) linked to itself."); solutions << i18n("Delete one part of the loop in order that it does not " "cause an infinite loop, and try again.") << sSysadmin; break; case KIO::ERR_USER_CANCELED: // Do nothing in this case. The user doesn't need to be told what he just did. // rodda: However, if we have been called, an application is about to display // this information anyway. If we don't return sensible information, the // user sees a blank dialog (I have seen this myself) errorName = i18n("Request Aborted By User"); description = i18n("The request was not completed because it was " "aborted."); solutions << i18n("Retry the request."); break; case KIO::ERR_CYCLIC_COPY: errorName = i18n("Cyclic Link Detected During Copy"); description = i18n("UNIX environments are commonly able to link a file or " "folder to a separate name and/or location. During the requested copy " "operation, KDE detected a link or series of links that results in an " "infinite loop - i.e. the file was (perhaps in a roundabout way) linked " "to itself."); solutions << i18n("Delete one part of the loop in order that it does not " "cause an infinite loop, and try again.") << sSysadmin; break; case KIO::ERR_CANNOT_CREATE_SOCKET: errorName = i18n("Could Not Create Network Connection"); techName = i18n("Could Not Create Socket"); description = i18n("This is a fairly technical error in which a required " "device for network communications (a socket) could not be created."); causes << i18n("The network connection may be incorrectly configured, or " "the network interface may not be enabled."); solutions << sNetwork << sSysadmin; break; case KIO::ERR_CANNOT_CONNECT: errorName = i18n("Connection to Server Refused"); description = i18n("The server %1 refused to allow this " "computer to make a connection.", host); causes << i18n("The server, while currently connected to the Internet, " "may not be configured to allow requests.") << i18n("The server, while currently connected to the Internet, " "may not be running the requested service (%1).", protocol) << i18n("A network firewall (a device which restricts Internet " "requests), either protecting your network or the network of the server, " "may have intervened, preventing this request."); solutions << sTryagain << sServeradmin << sSysadmin; break; case KIO::ERR_CONNECTION_BROKEN: errorName = i18n("Connection to Server Closed Unexpectedly"); description = i18n("Although a connection was established to " "%1, the connection was closed at an unexpected point " "in the communication.", host); causes << cNetwork << cNetpath << i18n("A protocol error may have occurred, " "causing the server to close the connection as a response to the error."); solutions << sTryagain << sServeradmin << sSysadmin; break; case KIO::ERR_NOT_FILTER_PROTOCOL: errorName = i18n("URL Resource Invalid"); techName = i18n("Protocol %1 is not a Filter Protocol", protocol); description = i18n("The Uniform Resource " "Locator (URL) that you entered did not refer to " "a valid mechanism of accessing the specific resource, " "%1%2.", !host.isNull() ? host + QLatin1Char('/') : QString(), path); causes << i18n("KDE is able to communicate through a protocol within a " "protocol. This request specified a protocol be used as such, however " "this protocol is not capable of such an action. This is a rare event, " "and is likely to indicate a programming error."); solutions << sTypo << sSysadmin; break; case KIO::ERR_CANNOT_MOUNT: errorName = i18n("Unable to Initialize Input/Output Device"); techName = i18n("Could Not Mount Device"); description = i18n("The requested device could not be initialized " "(\"mounted\"). The reported error was: %1", errorText); causes << i18n("The device may not be ready, for example there may be " "no media in a removable media device (i.e. no CD-ROM in a CD drive), " "or in the case of a peripheral/portable device, the device may not " "be correctly connected.") << i18n("You may not have permissions to initialize (\"mount\") the " "device. On UNIX systems, often system administrator privileges are " "required to initialize a device.") << cHardware; solutions << i18n("Check that the device is ready; removable drives " "must contain media, and portable devices must be connected and powered " "on.; and try again.") << sAccess << sSysadmin; break; case KIO::ERR_CANNOT_UNMOUNT: errorName = i18n("Unable to Uninitialize Input/Output Device"); techName = i18n("Could Not Unmount Device"); description = i18n("The requested device could not be uninitialized " "(\"unmounted\"). The reported error was: %1", errorText); causes << i18n("The device may be busy, that is, still in use by " "another application or user. Even such things as having an open " "browser window on a location on this device may cause the device to " "remain in use.") << i18n("You may not have permissions to uninitialize (\"unmount\") " "the device. On UNIX systems, system administrator privileges are " "often required to uninitialize a device.") << cHardware; solutions << i18n("Check that no applications are accessing the device, " "and try again.") << sAccess << sSysadmin; break; case KIO::ERR_CANNOT_READ: errorName = i18n("Cannot Read From Resource"); description = i18n("This means that although the resource, " "%1, was able to be opened, an error occurred while " "reading the contents of the resource.", url); causes << i18n("You may not have permissions to read from the resource."); if (!isSlaveNetwork) { causes << cNetwork; } causes << cHardware; solutions << sAccess; if (!isSlaveNetwork) { solutions << sNetwork; } solutions << sSysadmin; break; case KIO::ERR_CANNOT_WRITE: errorName = i18n("Cannot Write to Resource"); description = i18n("This means that although the resource, %1" ", was able to be opened, an error occurred while writing to the resource.", url); causes << i18n("You may not have permissions to write to the resource."); if (!isSlaveNetwork) { causes << cNetwork; } causes << cHardware; solutions << sAccess; if (!isSlaveNetwork) { solutions << sNetwork; } solutions << sSysadmin; break; case KIO::ERR_CANNOT_BIND: errorName = i18n("Could Not Listen for Network Connections"); techName = i18n("Could Not Bind"); description = i18n("This is a fairly technical error in which a required " "device for network communications (a socket) could not be established " "to listen for incoming network connections."); causes << i18n("The network connection may be incorrectly configured, or " "the network interface may not be enabled."); solutions << sNetwork << sSysadmin; break; case KIO::ERR_CANNOT_LISTEN: errorName = i18n("Could Not Listen for Network Connections"); techName = i18n("Could Not Listen"); description = i18n("This is a fairly technical error in which a required " "device for network communications (a socket) could not be established " "to listen for incoming network connections."); causes << i18n("The network connection may be incorrectly configured, or " "the network interface may not be enabled."); solutions << sNetwork << sSysadmin; break; case KIO::ERR_CANNOT_ACCEPT: errorName = i18n("Could Not Accept Network Connection"); description = i18n("This is a fairly technical error in which an error " "occurred while attempting to accept an incoming network connection."); causes << i18n("The network connection may be incorrectly configured, or " "the network interface may not be enabled.") << i18n("You may not have permissions to accept the connection."); solutions << sNetwork << sSysadmin; break; case KIO::ERR_CANNOT_LOGIN: errorName = i18n("Could Not Login: %1", errorText); description = i18n("An attempt to login to perform the requested " "operation was unsuccessful."); causes << i18n("You may have supplied incorrect authentication details or " "none at all.") << i18n("Your account may not have permission to access the " "specified resource.") << cProtocol; solutions << i18n("Retry the request and ensure your authentication details " "are entered correctly.") << sServeradmin << sSysadmin; break; case KIO::ERR_CANNOT_STAT: errorName = i18n("Could Not Determine Resource Status"); techName = i18n("Could Not Stat Resource"); description = i18n("An attempt to determine information about the status " "of the resource %1, such as the resource name, type, " "size, etc., was unsuccessful.", url); causes << i18n("The specified resource may not have existed or may " "not be accessible.") << cProtocol << cHardware; solutions << i18n("Retry the request and ensure your authentication details " "are entered correctly.") << sSysadmin; break; case KIO::ERR_CANNOT_CLOSEDIR: //result = i18n( "Could not terminate listing %1" ).arg( errorText ); errorName = i18n("Could Not Cancel Listing"); techName = i18n("FIXME: Document this"); break; case KIO::ERR_CANNOT_MKDIR: errorName = i18n("Could Not Create Folder"); description = i18n("An attempt to create the requested folder failed."); causes << cAccess << i18n("The location where the folder was to be created " "may not exist."); if (!isSlaveNetwork) { causes << cProtocol; } solutions << i18n("Retry the request.") << sAccess; break; case KIO::ERR_CANNOT_RMDIR: errorName = i18n("Could Not Remove Folder"); description = i18n("An attempt to remove the specified folder, " "%1, failed.", path); causes << i18n("The specified folder may not exist.") << i18n("The specified folder may not be empty.") << cAccess; if (!isSlaveNetwork) { causes << cProtocol; } solutions << i18n("Ensure that the folder exists and is empty, and try " "again.") << sAccess; break; case KIO::ERR_CANNOT_RESUME: errorName = i18n("Could Not Resume File Transfer"); description = i18n("The specified request asked that the transfer of " "file %1 be resumed at a certain point of the " "transfer. This was not possible.", filename); causes << i18n("The protocol, or the server, may not support file " "resuming."); solutions << i18n("Retry the request without attempting to resume " "transfer."); break; case KIO::ERR_CANNOT_RENAME: errorName = i18n("Could Not Rename Resource"); description = i18n("An attempt to rename the specified resource " "%1 failed.", url); causes << cAccess << cExists; if (!isSlaveNetwork) { causes << cProtocol; } solutions << sAccess << sExists; break; case KIO::ERR_CANNOT_CHMOD: errorName = i18n("Could Not Alter Permissions of Resource"); description = i18n("An attempt to alter the permissions on the specified " "resource %1 failed.", url); causes << cAccess << cExists; solutions << sAccess << sExists; break; case KIO::ERR_CANNOT_CHOWN: errorName = i18n("Could Not Change Ownership of Resource"); description = i18n("An attempt to change the ownership of the specified " "resource %1 failed.", url); causes << cAccess << cExists; solutions << sAccess << sExists; break; case KIO::ERR_CANNOT_DELETE: errorName = i18n("Could Not Delete Resource"); description = i18n("An attempt to delete the specified resource " "%1 failed.", url); causes << cAccess << cExists; solutions << sAccess << sExists; break; case KIO::ERR_SLAVE_DIED: errorName = i18n("Unexpected Program Termination"); description = i18n("The program on your computer which provides access " "to the %1 protocol has unexpectedly terminated.", url); causes << cBuglikely; solutions << sUpdate << sBugreport; break; case KIO::ERR_OUT_OF_MEMORY: errorName = i18n("Out of Memory"); description = i18n("The program on your computer which provides access " "to the %1 protocol could not obtain the memory " "required to continue.", protocol); causes << cBuglikely; solutions << sUpdate << sBugreport; break; case KIO::ERR_UNKNOWN_PROXY_HOST: errorName = i18n("Unknown Proxy Host"); description = i18n("While retrieving information about the specified " "proxy host, %1, an Unknown Host error was encountered. " "An unknown host error indicates that the requested name could not be " "located on the Internet.", errorText); causes << i18n("There may have been a problem with your network " "configuration, specifically your proxy's hostname. If you have been " "accessing the Internet with no problems recently, this is unlikely.") << cNetwork; solutions << i18n("Double-check your proxy settings and try again.") << sSysadmin; break; case KIO::ERR_CANNOT_AUTHENTICATE: errorName = i18n("Authentication Failed: Method %1 Not Supported", errorText); description = i18n("Although you may have supplied the correct " "authentication details, the authentication failed because the " "method that the server is using is not supported by the KDE " "program implementing the protocol %1.", protocol); solutions << i18n("Please file a bug at " "http://bugs.kde.org/ to inform the KDE team of the unsupported " "authentication method.") << sSysadmin; break; case KIO::ERR_ABORTED: errorName = i18n("Request Aborted"); description = i18n("The request was not completed because it was " "aborted."); solutions << i18n("Retry the request."); break; case KIO::ERR_INTERNAL_SERVER: errorName = i18n("Internal Error in Server"); description = i18n("The program on the server which provides access " "to the %1 protocol has reported an internal error: " "%2.", protocol, errorText); causes << i18n("This is most likely to be caused by a bug in the " "server program. Please consider submitting a full bug report as " "detailed below."); solutions << i18n("Contact the administrator of the server " "to advise them of the problem.") << i18n("If you know who the authors of the server software are, " "submit the bug report directly to them."); break; case KIO::ERR_SERVER_TIMEOUT: errorName = i18n("Timeout Error"); description = i18n("Although contact was made with the server, a " "response was not received within the amount of time allocated for " "the request as follows:
    " "
  • Timeout for establishing a connection: %1 seconds
  • " "
  • Timeout for receiving a response: %2 seconds
  • " "
  • Timeout for accessing proxy servers: %3 seconds
" "Please note that you can alter these timeout settings in the KDE " "System Settings, by selecting Network Settings -> Connection Preferences.", KProtocolManager::connectTimeout(), KProtocolManager::responseTimeout(), KProtocolManager::proxyConnectTimeout()); causes << cNetpath << i18n("The server was too busy responding to other " "requests to respond."); solutions << sTryagain << sServeradmin; break; case KIO::ERR_UNKNOWN: errorName = i18n("Unknown Error"); description = i18n("The program on your computer which provides access " "to the %1 protocol has reported an unknown error: " "%2.", protocol, errorText); causes << cBug; solutions << sUpdate << sBugreport; break; case KIO::ERR_UNKNOWN_INTERRUPT: errorName = i18n("Unknown Interruption"); description = i18n("The program on your computer which provides access " "to the %1 protocol has reported an interruption of " "an unknown type: %2.", protocol, errorText); causes << cBug; solutions << sUpdate << sBugreport; break; case KIO::ERR_CANNOT_DELETE_ORIGINAL: errorName = i18n("Could Not Delete Original File"); description = i18n("The requested operation required the deleting of " "the original file, most likely at the end of a file move operation. " "The original file %1 could not be deleted.", errorText); causes << cAccess; solutions << sAccess; break; case KIO::ERR_CANNOT_DELETE_PARTIAL: errorName = i18n("Could Not Delete Temporary File"); description = i18n("The requested operation required the creation of " "a temporary file in which to save the new file while being " "downloaded. This temporary file %1 could not be " "deleted.", errorText); causes << cAccess; solutions << sAccess; break; case KIO::ERR_CANNOT_RENAME_ORIGINAL: errorName = i18n("Could Not Rename Original File"); description = i18n("The requested operation required the renaming of " "the original file %1, however it could not be " "renamed.", errorText); causes << cAccess; solutions << sAccess; break; case KIO::ERR_CANNOT_RENAME_PARTIAL: errorName = i18n("Could Not Rename Temporary File"); description = i18n("The requested operation required the creation of " "a temporary file %1, however it could not be " "created.", errorText); causes << cAccess; solutions << sAccess; break; case KIO::ERR_CANNOT_SYMLINK: errorName = i18n("Could Not Create Link"); techName = i18n("Could Not Create Symbolic Link"); description = i18n("The requested symbolic link %1 could not be created.", errorText); causes << cAccess; solutions << sAccess; break; case KIO::ERR_NO_CONTENT: errorName = i18n("No Content"); description = errorText; break; case KIO::ERR_DISK_FULL: errorName = i18n("Disk Full"); description = i18n("The requested file %1 could not be " "written to as there is inadequate disk space.", errorText); solutions << i18n("Free up enough disk space by 1) deleting unwanted and " "temporary files; 2) archiving files to removable media storage such as " "CD-Recordable discs; or 3) obtain more storage capacity.") << sSysadmin; break; case KIO::ERR_IDENTICAL_FILES: errorName = i18n("Source and Destination Files Identical"); description = i18n("The operation could not be completed because the " "source and destination files are the same file."); solutions << i18n("Choose a different filename for the destination file."); break; case KIO::ERR_DROP_ON_ITSELF: errorName = i18n("File or Folder dropped onto itself"); description = i18n("The operation could not be completed because the " "source and destination file or folder are the same."); solutions << i18n("Drop the item into a different file or folder."); break; // We assume that the slave has all the details case KIO::ERR_SLAVE_DEFINED: errorName.clear(); description = errorText; break; case KIO::ERR_CANNOT_MOVE_INTO_ITSELF: errorName = i18n("Folder moved into itself"); description = i18n("The operation could not be completed because the " "source can not be moved into itself."); solutions << i18n("Move the item into a different folder."); break; case KIO::ERR_PASSWD_SERVER: errorName = i18n("Could not communicate with password server"); description = i18n("The operation could not be completed because the " "service for requesting passwords (kpasswdserver) couldn't be contacted"); solutions << i18n("Try restarting your session, or look in the logs for errors from kiod."); break; case KIO::ERR_CANNOT_CREATE_SLAVE: errorName = i18n("Cannot Initiate the %1 Protocol", protocol); techName = i18n("Unable to Create io-slave"); description = i18n("The io-slave which provides access " "to the %1 protocol could not be started. This is " "usually due to technical reasons.", protocol); causes << i18n("klauncher could not find or start the plugin which provides the protocol." "This means you may have an outdated version of the plugin."); solutions << sUpdate << sSysadmin; break; case KIO::ERR_FILE_TOO_LARGE_FOR_FAT32: errorName = xi18n("Cannot transfer %1", errorText); - description = xi18n("The file %1 cannot be transeferred," + description = xi18n("The file %1 cannot be transferred," " because the destination filesystem does not support files that large", errorText); solutions << i18n("Reformat the destination drive to use a filesystem that supports files that large."); break; default: // fall back to the plain error... errorName = i18n("Undocumented Error"); description = buildErrorString(errorCode, errorText); } QByteArray ret; QDataStream stream(&ret, QIODevice::WriteOnly); stream << errorName << techName << description << causes << solutions; return ret; } QFile::Permissions KIO::convertPermissions(int permissions) { QFile::Permissions qPermissions; if (permissions > 0) { if (permissions & S_IRUSR) { qPermissions |= QFile::ReadOwner; } if (permissions & S_IWUSR) { qPermissions |= QFile::WriteOwner; } if (permissions & S_IXUSR) { qPermissions |= QFile::ExeOwner; } if (permissions & S_IRGRP) { qPermissions |= QFile::ReadGroup; } if (permissions & S_IWGRP) { qPermissions |= QFile::WriteGroup; } if (permissions & S_IXGRP) { qPermissions |= QFile::ExeGroup; } if (permissions & S_IROTH) { qPermissions |= QFile::ReadOther; } if (permissions & S_IWOTH) { qPermissions |= QFile::WriteOther; } if (permissions & S_IXOTH) { qPermissions |= QFile::ExeOther; } } return qPermissions; } diff --git a/src/core/slavebase.cpp b/src/core/slavebase.cpp index b0bc0ea3..243232bb 100644 --- a/src/core/slavebase.cpp +++ b/src/core/slavebase.cpp @@ -1,1483 +1,1483 @@ /* * This file is part of the KDE libraries * Copyright (c) 2000 Waldo Bastian * Copyright (c) 2000 David Faure * Copyright (c) 2000 Stephan Kulow * Copyright (c) 2007 Thiago Macieira * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * **/ // TODO: remove me #undef QT_NO_CAST_FROM_ASCII #include "slavebase.h" #include #include #include #include #ifdef Q_OS_WIN #include #endif #include #include #include #include #include #include #include #include #include #include #include "kremoteencoding.h" #include "kioglobal_p.h" #include "connection_p.h" #include "commands_p.h" #include "ioslave_defaults.h" #include "slaveinterface.h" #include "kpasswdserverclient.h" #include "kiocoredebug.h" #ifdef Q_OS_UNIX #include #endif extern "C" { static void sigpipe_handler(int sig); } using namespace KIO; typedef QList AuthKeysList; typedef QMap AuthKeysMap; #define KIO_DATA QByteArray data; QDataStream stream( &data, QIODevice::WriteOnly ); stream #define KIO_FILESIZE_T(x) quint64(x) static const int KIO_MAX_ENTRIES_PER_BATCH = 200; static const int KIO_MAX_SEND_BATCH_TIME = 300; namespace KIO { class SlaveBasePrivate { public: SlaveBase * const q; explicit SlaveBasePrivate(SlaveBase *owner) : q(owner) , nextTimeoutMsecs(0) , m_passwdServerClient(nullptr) , m_confirmationAsked(false) , m_privilegeOperationStatus(OperationNotAllowed) { if (!qEnvironmentVariableIsEmpty("KIOSLAVE_ENABLE_TESTMODE")) { QStandardPaths::setTestModeEnabled(true); } pendingListEntries.reserve(KIO_MAX_ENTRIES_PER_BATCH); appConnection.setReadMode(Connection::ReadMode::Polled); } ~SlaveBasePrivate() { delete m_passwdServerClient; } UDSEntryList pendingListEntries; QElapsedTimer m_timeSinceLastBatch; Connection appConnection; QString poolSocket; bool isConnectedToApp; QString slaveid; bool resume: 1; bool needSendCanResume: 1; bool onHold: 1; bool wasKilled: 1; bool inOpenLoop: 1; bool exit_loop: 1; MetaData configData; KConfig *config; KConfigGroup *configGroup; QUrl onHoldUrl; QElapsedTimer lastTimeout; QElapsedTimer nextTimeout; qint64 nextTimeoutMsecs; KIO::filesize_t totalSize; KRemoteEncoding *remotefile; enum { Idle, InsideMethod, FinishedCalled, ErrorCalled } m_state; QByteArray timeoutData; KPasswdServerClient *m_passwdServerClient; bool m_rootEntryListed = false; bool m_confirmationAsked; QSet m_tempAuths; QString m_warningCaption; QString m_warningMessage; int m_privilegeOperationStatus; PrivilegeOperationStatus askConfirmation() { int status = q->messageBox(SlaveBase::WarningContinueCancel, m_warningMessage, m_warningCaption, QStringLiteral("Continue"), QStringLiteral("Cancel")); switch (status) { case SlaveBase::Continue: return OperationAllowed; case SlaveBase::Cancel: return OperationCanceled; default: return OperationNotAllowed; } } void updateTempAuthStatus() { #ifdef Q_OS_UNIX QSet::iterator it = m_tempAuths.begin(); while (it != m_tempAuths.end()) { KAuth::Action action(*it); if (action.status() != KAuth::Action::AuthorizedStatus) { it = m_tempAuths.erase(it); } else { ++it; } } #endif } bool hasTempAuth() const { return !m_tempAuths.isEmpty(); } // Reconstructs configGroup from configData and mIncomingMetaData void rebuildConfig() { configGroup->deleteGroup(KConfigGroup::WriteConfigFlags()); // mIncomingMetaData cascades over config, so we write config first, // to let it be overwritten MetaData::ConstIterator end = configData.constEnd(); for (MetaData::ConstIterator it = configData.constBegin(); it != end; ++it) { configGroup->writeEntry(it.key(), it->toUtf8(), KConfigGroup::WriteConfigFlags()); } end = q->mIncomingMetaData.constEnd(); for (MetaData::ConstIterator it = q->mIncomingMetaData.constBegin(); it != end; ++it) { configGroup->writeEntry(it.key(), it->toUtf8(), KConfigGroup::WriteConfigFlags()); } } void verifyState(const char *cmdName) { if ((m_state != FinishedCalled) && (m_state != ErrorCalled)) { qCWarning(KIO_CORE) << cmdName << "did not call finished() or error()! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; } } void verifyErrorFinishedNotCalled(const char *cmdName) { if (m_state == FinishedCalled || m_state == ErrorCalled) { qCWarning(KIO_CORE) << cmdName << "called finished() or error(), but it's not supposed to! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; } } KPasswdServerClient *passwdServerClient() { if (!m_passwdServerClient) { m_passwdServerClient = new KPasswdServerClient; } return m_passwdServerClient; } }; } static SlaveBase *globalSlave; static volatile bool slaveWriteError = false; static const char *s_protocol; #ifdef Q_OS_UNIX extern "C" { static void genericsig_handler(int sigNumber) { ::signal(sigNumber, SIG_IGN); //WABA: Don't do anything that requires malloc, we can deadlock on it since //a SIGTERM signal can come in while we are in malloc/free. //qDebug()<<"kioslave : exiting due to signal "<setKillFlag(); } ::signal(SIGALRM, SIG_DFL); alarm(5); //generate an alarm signal in 5 seconds, in this time the slave has to exit } } #endif ////////////// SlaveBase::SlaveBase(const QByteArray &protocol, const QByteArray &pool_socket, const QByteArray &app_socket) : mProtocol(protocol), d(new SlaveBasePrivate(this)) { Q_ASSERT(!app_socket.isEmpty()); d->poolSocket = QFile::decodeName(pool_socket); s_protocol = protocol.data(); KCrash::initialize(); #ifdef Q_OS_UNIX struct sigaction act; act.sa_handler = sigpipe_handler; sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGPIPE, &act, nullptr); ::signal(SIGINT, &genericsig_handler); ::signal(SIGQUIT, &genericsig_handler); ::signal(SIGTERM, &genericsig_handler); #endif globalSlave = this; d->isConnectedToApp = true; // by kahl for netmgr (need a way to identify slaves) d->slaveid = QString::fromUtf8(protocol) + QString::number(getpid()); d->resume = false; d->needSendCanResume = false; d->config = new KConfig(QString(), KConfig::SimpleConfig); // The KConfigGroup needs the KConfig to exist during its whole lifetime. d->configGroup = new KConfigGroup(d->config, QString()); d->onHold = false; d->wasKilled = false; // d->processed_size = 0; d->totalSize = 0; connectSlave(QFile::decodeName(app_socket)); d->remotefile = nullptr; d->inOpenLoop = false; d->exit_loop = false; } SlaveBase::~SlaveBase() { delete d->configGroup; delete d->config; delete d->remotefile; delete d; s_protocol = ""; } void SlaveBase::dispatchLoop() { while (!d->exit_loop) { if (d->nextTimeout.isValid() && (d->nextTimeout.hasExpired(d->nextTimeoutMsecs))) { QByteArray data = d->timeoutData; d->nextTimeout.invalidate(); d->timeoutData = QByteArray(); special(data); } Q_ASSERT(d->appConnection.inited()); int ms = -1; if (d->nextTimeout.isValid()) { ms = qMax(d->nextTimeout.elapsed() - d->nextTimeoutMsecs, 1); } int ret = -1; if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(ms)) { // dispatch application messages int cmd; QByteArray data; ret = d->appConnection.read(&cmd, data); if (ret != -1) { if (d->inOpenLoop) { dispatchOpenCommand(cmd, data); } else { dispatch(cmd, data); } } } else { ret = d->appConnection.isConnected() ? 0 : -1; } if (ret == -1) { // some error occurred, perhaps no more application // When the app exits, should the slave be put back in the pool ? if (!d->exit_loop && d->isConnectedToApp && !d->poolSocket.isEmpty()) { disconnectSlave(); d->isConnectedToApp = false; closeConnection(); d->updateTempAuthStatus(); connectSlave(d->poolSocket); } else { break; } } //I think we get here when we were killed in dispatch() and not in select() if (wasKilled()) { //qDebug() << "slave was killed, returning"; break; } // execute deferred deletes QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } // execute deferred deletes QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); } void SlaveBase::connectSlave(const QString &address) { d->appConnection.connectToRemote(QUrl(address)); if (!d->appConnection.inited()) { /*qDebug() << "failed to connect to" << address << endl << "Reason:" << d->appConnection.errorString();*/ exit(); } d->inOpenLoop = false; } void SlaveBase::disconnectSlave() { d->appConnection.close(); } void SlaveBase::setMetaData(const QString &key, const QString &value) { mOutgoingMetaData.insert(key, value); // replaces existing key if already there } QString SlaveBase::metaData(const QString &key) const { auto it = mIncomingMetaData.find(key); if (it != mIncomingMetaData.end()) { return *it; } return d->configData.value(key); } MetaData SlaveBase::allMetaData() const { return mIncomingMetaData; } bool SlaveBase::hasMetaData(const QString &key) const { if (mIncomingMetaData.contains(key)) { return true; } if (d->configData.contains(key)) { return true; } return false; } KConfigGroup *SlaveBase::config() { return d->configGroup; } void SlaveBase::sendMetaData() { sendAndKeepMetaData(); mOutgoingMetaData.clear(); } void SlaveBase::sendAndKeepMetaData() { if (!mOutgoingMetaData.isEmpty()) { KIO_DATA << mOutgoingMetaData; send(INF_META_DATA, data); } } KRemoteEncoding *SlaveBase::remoteEncoding() { if (d->remotefile) { return d->remotefile; } const QByteArray charset(metaData(QStringLiteral("Charset")).toLatin1()); return (d->remotefile = new KRemoteEncoding(charset.constData())); } void SlaveBase::data(const QByteArray &data) { sendMetaData(); send(MSG_DATA, data); } void SlaveBase::dataReq() { //sendMetaData(); if (d->needSendCanResume) { canResume(0); } send(MSG_DATA_REQ); } void SlaveBase::opened() { sendMetaData(); send(MSG_OPENED); d->inOpenLoop = true; } void SlaveBase::error(int _errid, const QString &_text) { if (d->m_state == d->ErrorCalled) { qCWarning(KIO_CORE) << "error() called twice! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } else if (d->m_state == d->FinishedCalled) { qCWarning(KIO_CORE) << "error() called after finished()! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } d->m_state = d->ErrorCalled; mIncomingMetaData.clear(); // Clear meta data d->rebuildConfig(); mOutgoingMetaData.clear(); KIO_DATA << static_cast(_errid) << _text; send(MSG_ERROR, data); //reset d->totalSize = 0; d->inOpenLoop = false; d->m_confirmationAsked = false; d->m_privilegeOperationStatus = OperationNotAllowed; } void SlaveBase::connected() { send(MSG_CONNECTED); } void SlaveBase::finished() { if (!d->pendingListEntries.isEmpty()) { if (!d->m_rootEntryListed) { qCWarning(KIO_CORE) << "UDSEntry for '.' not found, creating a default one. Please fix the" << QCoreApplication::applicationName() << "KIO slave"; KIO::UDSEntry entry; entry.fastInsert(KIO::UDSEntry::UDS_NAME, QStringLiteral(".")); entry.fastInsert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.fastInsert(KIO::UDSEntry::UDS_SIZE, 0); entry.fastInsert(KIO::UDSEntry::UDS_ACCESS, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IWGRP | S_IXGRP | S_IROTH | S_IXOTH); d->pendingListEntries.append(entry); } listEntries(d->pendingListEntries); d->pendingListEntries.clear(); } if (d->m_state == d->FinishedCalled) { qCWarning(KIO_CORE) << "finished() called twice! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } else if (d->m_state == d->ErrorCalled) { qCWarning(KIO_CORE) << "finished() called after error()! Please fix the" << QCoreApplication::applicationName() << "KIO slave"; return; } d->m_state = d->FinishedCalled; mIncomingMetaData.clear(); // Clear meta data d->rebuildConfig(); sendMetaData(); send(MSG_FINISHED); // reset d->totalSize = 0; d->inOpenLoop = false; d->m_rootEntryListed = false; d->m_confirmationAsked = false; d->m_privilegeOperationStatus = OperationNotAllowed; } void SlaveBase::needSubUrlData() { send(MSG_NEED_SUBURL_DATA); } void SlaveBase::slaveStatus(const QString &host, bool connected) { qint64 pid = getpid(); qint8 b = connected ? 1 : 0; KIO_DATA << pid << mProtocol << host << b << d->onHold << d->onHoldUrl << d->hasTempAuth(); send(MSG_SLAVE_STATUS_V2, data); } void SlaveBase::canResume() { send(MSG_CANRESUME); } void SlaveBase::totalSize(KIO::filesize_t _bytes) { KIO_DATA << KIO_FILESIZE_T(_bytes); send(INF_TOTAL_SIZE, data); //this one is usually called before the first item is listed in listDir() d->totalSize = _bytes; } void SlaveBase::processedSize(KIO::filesize_t _bytes) { bool emitSignal = false; if (_bytes == d->totalSize) { emitSignal = true; } else { if (d->lastTimeout.isValid()) { emitSignal = d->lastTimeout.hasExpired(100); // emit size 10 times a second } else { emitSignal = true; } } if (emitSignal) { KIO_DATA << KIO_FILESIZE_T(_bytes); send(INF_PROCESSED_SIZE, data); d->lastTimeout.start(); } // d->processed_size = _bytes; } void SlaveBase::written(KIO::filesize_t _bytes) { KIO_DATA << KIO_FILESIZE_T(_bytes); send(MSG_WRITTEN, data); } void SlaveBase::position(KIO::filesize_t _pos) { KIO_DATA << KIO_FILESIZE_T(_pos); send(INF_POSITION, data); } void SlaveBase::processedPercent(float /* percent */) { //qDebug() << "STUB"; } void SlaveBase::speed(unsigned long _bytes_per_second) { KIO_DATA << static_cast(_bytes_per_second); send(INF_SPEED, data); } void SlaveBase::redirection(const QUrl &_url) { KIO_DATA << _url; send(INF_REDIRECTION, data); } void SlaveBase::errorPage() { send(INF_ERROR_PAGE); } static bool isSubCommand(int cmd) { return ((cmd == CMD_REPARSECONFIGURATION) || (cmd == CMD_META_DATA) || (cmd == CMD_CONFIG) || (cmd == CMD_SUBURL) || (cmd == CMD_SLAVE_STATUS) || (cmd == CMD_SLAVE_CONNECT) || (cmd == CMD_SLAVE_HOLD) || (cmd == CMD_MULTI_GET)); } void SlaveBase::mimeType(const QString &_type) { //qDebug() << _type; int cmd; do { // Send the meta-data each time we send the mime-type. if (!mOutgoingMetaData.isEmpty()) { //qDebug() << "emitting meta data"; KIO_DATA << mOutgoingMetaData; send(INF_META_DATA, data); } KIO_DATA << _type; send(INF_MIME_TYPE, data); while (true) { cmd = 0; int ret = -1; if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) { ret = d->appConnection.read(&cmd, data); } if (ret == -1) { //qDebug() << "read error"; exit(); } //qDebug() << "got" << cmd; if (cmd == CMD_HOST) { // Ignore. continue; } if (!isSubCommand(cmd)) { break; } dispatch(cmd, data); } } while (cmd != CMD_NONE); mOutgoingMetaData.clear(); } void SlaveBase::exit() { d->exit_loop = true; // Using ::exit() here is too much (crashes in qdbus's qglobalstatic object), // so let's cleanly exit dispatchLoop() instead. // Update: we do need to call exit(), otherwise a long download (get()) would // keep going until it ends, even though the application exited. ::exit(255); } void SlaveBase::warning(const QString &_msg) { KIO_DATA << _msg; send(INF_WARNING, data); } void SlaveBase::infoMessage(const QString &_msg) { KIO_DATA << _msg; send(INF_INFOMESSAGE, data); } #ifndef KIOCORE_NO_DEPRECATED bool SlaveBase::requestNetwork(const QString &host) { KIO_DATA << host << d->slaveid; send(MSG_NET_REQUEST, data); if (waitForAnswer(INF_NETWORK_STATUS, 0, data) != -1) { bool status; QDataStream stream(data); stream >> status; return status; } else { return false; } } void SlaveBase::dropNetwork(const QString &host) { KIO_DATA << host << d->slaveid; send(MSG_NET_DROP, data); } #endif void SlaveBase::statEntry(const UDSEntry &entry) { KIO_DATA << entry; send(MSG_STAT_ENTRY, data); } #ifndef KIOCORE_NO_DEPRECATED void SlaveBase::listEntry(const UDSEntry &entry, bool _ready) { if (_ready) { // #366795: many slaves don't create an entry for ".", so we keep track if they do // and we provide a fallback in finished() otherwise. if (entry.stringValue(KIO::UDSEntry::UDS_NAME) == QLatin1String(".")) { d->m_rootEntryListed = true; } listEntries(d->pendingListEntries); d->pendingListEntries.clear(); } else { listEntry(entry); } } #endif void SlaveBase::listEntry(const UDSEntry &entry) { // #366795: many slaves don't create an entry for ".", so we keep track if they do // and we provide a fallback in finished() otherwise. if (entry.stringValue(KIO::UDSEntry::UDS_NAME) == QLatin1String(".")) { d->m_rootEntryListed = true; } // We start measuring the time from the point we start filling the list if (d->pendingListEntries.isEmpty()) { d->m_timeSinceLastBatch.restart(); } d->pendingListEntries.append(entry); // If more then KIO_MAX_SEND_BATCH_TIME time is passed, emit the current batch // Also emit if we have piled up a large number of entries already, to save memory (and time) if (d->m_timeSinceLastBatch.elapsed() > KIO_MAX_SEND_BATCH_TIME || d->pendingListEntries.size() > KIO_MAX_ENTRIES_PER_BATCH) { listEntries(d->pendingListEntries); d->pendingListEntries.clear(); // Restart time d->m_timeSinceLastBatch.restart(); } } void SlaveBase::listEntries(const UDSEntryList &list) { QByteArray data; QDataStream stream(&data, QIODevice::WriteOnly); foreach (const UDSEntry &entry, list) { stream << entry; } send(MSG_LIST_ENTRIES, data); } static void sigpipe_handler(int) { // We ignore a SIGPIPE in slaves. // A SIGPIPE can happen in two cases: // 1) Communication error with application. // 2) Communication error with network. slaveWriteError = true; // Don't add anything else here, especially no debug output } void SlaveBase::setHost(QString const &, quint16, QString const &, QString const &) { } KIOCORE_EXPORT QString KIO::unsupportedActionErrorString(const QString &protocol, int cmd) { switch (cmd) { case CMD_CONNECT: return i18n("Opening connections is not supported with the protocol %1.", protocol); case CMD_DISCONNECT: return i18n("Closing connections is not supported with the protocol %1.", protocol); case CMD_STAT: return i18n("Accessing files is not supported with the protocol %1.", protocol); case CMD_PUT: return i18n("Writing to %1 is not supported.", protocol); case CMD_SPECIAL: return i18n("There are no special actions available for protocol %1.", protocol); case CMD_LISTDIR: return i18n("Listing folders is not supported for protocol %1.", protocol); case CMD_GET: return i18n("Retrieving data from %1 is not supported.", protocol); case CMD_MIMETYPE: return i18n("Retrieving mime type information from %1 is not supported.", protocol); case CMD_RENAME: return i18n("Renaming or moving files within %1 is not supported.", protocol); case CMD_SYMLINK: return i18n("Creating symlinks is not supported with protocol %1.", protocol); case CMD_COPY: return i18n("Copying files within %1 is not supported.", protocol); case CMD_DEL: return i18n("Deleting files from %1 is not supported.", protocol); case CMD_MKDIR: return i18n("Creating folders is not supported with protocol %1.", protocol); case CMD_CHMOD: return i18n("Changing the attributes of files is not supported with protocol %1.", protocol); case CMD_CHOWN: return i18n("Changing the ownership of files is not supported with protocol %1.", protocol); case CMD_SUBURL: return i18n("Using sub-URLs with %1 is not supported.", protocol); case CMD_MULTI_GET: return i18n("Multiple get is not supported with protocol %1.", protocol); case CMD_OPEN: return i18n("Opening files is not supported with protocol %1.", protocol); default: return i18n("Protocol %1 does not support action %2.", protocol, cmd); }/*end switch*/ } void SlaveBase::openConnection() { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CONNECT)); } void SlaveBase::closeConnection() { } // No response! void SlaveBase::stat(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_STAT)); } void SlaveBase::put(QUrl const &, int, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_PUT)); } void SlaveBase::special(const QByteArray &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SPECIAL)); } void SlaveBase::listDir(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_LISTDIR)); } void SlaveBase::get(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_GET)); } void SlaveBase::open(QUrl const &, QIODevice::OpenMode) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_OPEN)); } void SlaveBase::read(KIO::filesize_t) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_READ)); } void SlaveBase::write(const QByteArray &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_WRITE)); } void SlaveBase::seek(KIO::filesize_t) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SEEK)); } void SlaveBase::close() { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CLOSE)); } void SlaveBase::mimetype(QUrl const &url) { get(url); } void SlaveBase::rename(QUrl const &, QUrl const &, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_RENAME)); } void SlaveBase::symlink(QString const &, QUrl const &, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SYMLINK)); } void SlaveBase::copy(QUrl const &, QUrl const &, int, JobFlags) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_COPY)); } void SlaveBase::del(QUrl const &, bool) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_DEL)); } void SlaveBase::setLinkDest(const QUrl &, const QString &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SETLINKDEST)); } void SlaveBase::mkdir(QUrl const &, int) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MKDIR)); } void SlaveBase::chmod(QUrl const &, int) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHMOD)); } void SlaveBase::setModificationTime(QUrl const &, const QDateTime &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SETMODIFICATIONTIME)); } void SlaveBase::chown(QUrl const &, const QString &, const QString &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_CHOWN)); } void SlaveBase::setSubUrl(QUrl const &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_SUBURL)); } void SlaveBase::multiGet(const QByteArray &) { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_MULTI_GET)); } void SlaveBase::slave_status() { slaveStatus(QString(), false); } void SlaveBase::reparseConfiguration() { delete d->remotefile; d->remotefile = nullptr; } bool SlaveBase::openPasswordDialog(AuthInfo &info, const QString &errorMsg) { const int errorCode = openPasswordDialogV2(info, errorMsg); return errorCode == KJob::NoError; } int SlaveBase::openPasswordDialogV2(AuthInfo &info, const QString &errorMsg) { const long windowId = metaData(QStringLiteral("window-id")).toLong(); const unsigned long userTimestamp = metaData(QStringLiteral("user-timestamp")).toULong(); QString errorMessage; if (metaData(QStringLiteral("no-auth-prompt")).compare(QLatin1String("true"), Qt::CaseInsensitive) == 0) { errorMessage = QStringLiteral(""); } else { errorMessage = errorMsg; } AuthInfo dlgInfo(info); // Make sure the modified flag is not set. dlgInfo.setModified(false); // Prevent queryAuthInfo from caching the user supplied password since // we need the ioslaves to first authenticate against the server with // it to ensure it is valid. dlgInfo.setExtraField(QStringLiteral("skip-caching-on-query"), true); KPasswdServerClient *passwdServerClient = d->passwdServerClient(); const int errCode = passwdServerClient->queryAuthInfo(&dlgInfo, errorMessage, windowId, userTimestamp); if (errCode == KJob::NoError) { info = dlgInfo; } return errCode; } int SlaveBase::messageBox(MessageBoxType type, const QString &text, const QString &caption, const QString &buttonYes, const QString &buttonNo) { return messageBox(text, type, caption, buttonYes, buttonNo, QString()); } int SlaveBase::messageBox(const QString &text, MessageBoxType type, const QString &caption, const QString &_buttonYes, const QString &_buttonNo, const QString &dontAskAgainName) { QString buttonYes = _buttonYes.isNull() ? i18n("&Yes") : _buttonYes; QString buttonNo = _buttonNo.isNull() ? i18n("&No") : _buttonNo; //qDebug() << "messageBox " << type << " " << text << " - " << caption << buttonYes << buttonNo; KIO_DATA << static_cast(type) << text << caption << buttonYes << buttonNo << dontAskAgainName; send(INF_MESSAGEBOX, data); if (waitForAnswer(CMD_MESSAGEBOXANSWER, 0, data) != -1) { QDataStream stream(data); int answer; stream >> answer; //qDebug() << "got messagebox answer" << answer; return answer; } else { return 0; // communication failure } } bool SlaveBase::canResume(KIO::filesize_t offset) { //qDebug() << "offset=" << KIO::number(offset); d->needSendCanResume = false; KIO_DATA << KIO_FILESIZE_T(offset); send(MSG_RESUME, data); if (offset) { int cmd; if (waitForAnswer(CMD_RESUMEANSWER, CMD_NONE, data, &cmd) != -1) { //qDebug() << "returning" << (cmd == CMD_RESUMEANSWER); return cmd == CMD_RESUMEANSWER; } else { return false; } } else { // No resuming possible -> no answer to wait for return true; } } int SlaveBase::waitForAnswer(int expected1, int expected2, QByteArray &data, int *pCmd) { int cmd = 0; int result = -1; for (;;) { if (d->appConnection.hasTaskAvailable() || d->appConnection.waitForIncomingTask(-1)) { result = d->appConnection.read(&cmd, data); } if (result == -1) { //qDebug() << "read error."; return -1; } if (cmd == expected1 || cmd == expected2) { if (pCmd) { *pCmd = cmd; } return result; } if (isSubCommand(cmd)) { dispatch(cmd, data); } else { qFatal("Fatal Error: Got cmd %d, while waiting for an answer!", cmd); } } } int SlaveBase::readData(QByteArray &buffer) { int result = waitForAnswer(MSG_DATA, 0, buffer); //qDebug() << "readData: length = " << result << " "; return result; } void SlaveBase::setTimeoutSpecialCommand(int timeout, const QByteArray &data) { if (timeout > 0) { - d->nextTimeoutMsecs = timeout*1000; // from seconds to miliseconds + d->nextTimeoutMsecs = timeout*1000; // from seconds to milliseconds d->nextTimeout.start(); } else if (timeout == 0) { d->nextTimeoutMsecs = 1000; // Immediate timeout d->nextTimeout.start(); } else { d->nextTimeout.invalidate(); // Canceled } d->timeoutData = data; } void SlaveBase::dispatch(int command, const QByteArray &data) { QDataStream stream(data); QUrl url; int i; switch (command) { case CMD_HOST: { QString passwd; QString host, user; quint16 port; stream >> host >> port >> user >> passwd; d->m_state = d->InsideMethod; setHost(host, port, user, passwd); d->verifyErrorFinishedNotCalled("setHost()"); d->m_state = d->Idle; } break; case CMD_CONNECT: { openConnection(); } break; case CMD_DISCONNECT: { closeConnection(); } break; case CMD_SLAVE_STATUS: { d->m_state = d->InsideMethod; slave_status(); // TODO verify that the slave has called slaveStatus()? d->verifyErrorFinishedNotCalled("slave_status()"); d->m_state = d->Idle; } break; case CMD_SLAVE_CONNECT: { d->onHold = false; QString app_socket; QDataStream stream(data); stream >> app_socket; d->appConnection.send(MSG_SLAVE_ACK); disconnectSlave(); d->isConnectedToApp = true; connectSlave(app_socket); virtual_hook(AppConnectionMade, nullptr); } break; case CMD_SLAVE_HOLD: { QUrl url; QDataStream stream(data); stream >> url; d->onHoldUrl = url; d->onHold = true; disconnectSlave(); d->isConnectedToApp = false; // Do not close connection! connectSlave(d->poolSocket); } break; case CMD_REPARSECONFIGURATION: { d->m_state = d->InsideMethod; reparseConfiguration(); d->verifyErrorFinishedNotCalled("reparseConfiguration()"); d->m_state = d->Idle; } break; case CMD_CONFIG: { stream >> d->configData; d->rebuildConfig(); delete d->remotefile; d->remotefile = nullptr; } break; case CMD_GET: { stream >> url; d->m_state = d->InsideMethod; get(url); d->verifyState("get()"); d->m_state = d->Idle; } break; case CMD_OPEN: { stream >> url >> i; QIODevice::OpenMode mode = QFlag(i); d->m_state = d->InsideMethod; open(url, mode); //krazy:exclude=syscalls d->m_state = d->Idle; } break; case CMD_PUT: { int permissions; qint8 iOverwrite, iResume; stream >> url >> iOverwrite >> iResume >> permissions; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } if (iResume != 0) { flags |= Resume; } // Remember that we need to send canResume(), TransferJob is expecting // it. Well, in theory this shouldn't be done if resume is true. // (the resume bool is currently unused) d->needSendCanResume = true /* !resume */; d->m_state = d->InsideMethod; put(url, permissions, flags); d->verifyState("put()"); d->m_state = d->Idle; } break; case CMD_STAT: { stream >> url; d->m_state = d->InsideMethod; stat(url); //krazy:exclude=syscalls d->verifyState("stat()"); d->m_state = d->Idle; } break; case CMD_MIMETYPE: { stream >> url; d->m_state = d->InsideMethod; mimetype(url); d->verifyState("mimetype()"); d->m_state = d->Idle; } break; case CMD_LISTDIR: { stream >> url; d->m_state = d->InsideMethod; listDir(url); d->verifyState("listDir()"); d->m_state = d->Idle; } break; case CMD_MKDIR: { stream >> url >> i; d->m_state = d->InsideMethod; mkdir(url, i); //krazy:exclude=syscalls d->verifyState("mkdir()"); d->m_state = d->Idle; } break; case CMD_RENAME: { qint8 iOverwrite; QUrl url2; stream >> url >> url2 >> iOverwrite; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } d->m_state = d->InsideMethod; rename(url, url2, flags); //krazy:exclude=syscalls d->verifyState("rename()"); d->m_state = d->Idle; } break; case CMD_SYMLINK: { qint8 iOverwrite; QString target; stream >> target >> url >> iOverwrite; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } d->m_state = d->InsideMethod; symlink(target, url, flags); d->verifyState("symlink()"); d->m_state = d->Idle; } break; case CMD_COPY: { int permissions; qint8 iOverwrite; QUrl url2; stream >> url >> url2 >> permissions >> iOverwrite; JobFlags flags; if (iOverwrite != 0) { flags |= Overwrite; } d->m_state = d->InsideMethod; copy(url, url2, permissions, flags); d->verifyState("copy()"); d->m_state = d->Idle; } break; case CMD_DEL: { qint8 isFile; stream >> url >> isFile; d->m_state = d->InsideMethod; del(url, isFile != 0); d->verifyState("del()"); d->m_state = d->Idle; } break; case CMD_CHMOD: { stream >> url >> i; d->m_state = d->InsideMethod; chmod(url, i); d->verifyState("chmod()"); d->m_state = d->Idle; } break; case CMD_CHOWN: { QString owner, group; stream >> url >> owner >> group; d->m_state = d->InsideMethod; chown(url, owner, group); d->verifyState("chown()"); d->m_state = d->Idle; } break; case CMD_SETMODIFICATIONTIME: { QDateTime dt; stream >> url >> dt; d->m_state = d->InsideMethod; setModificationTime(url, dt); d->verifyState("setModificationTime()"); d->m_state = d->Idle; } break; case CMD_SPECIAL: { d->m_state = d->InsideMethod; special(data); d->verifyState("special()"); d->m_state = d->Idle; } break; case CMD_META_DATA: { //qDebug() << "(" << getpid() << ") Incoming meta-data..."; stream >> mIncomingMetaData; d->rebuildConfig(); } break; case CMD_SUBURL: { stream >> url; d->m_state = d->InsideMethod; setSubUrl(url); d->verifyErrorFinishedNotCalled("setSubUrl()"); d->m_state = d->Idle; } break; case CMD_NONE: { qCWarning(KIO_CORE) << "Got unexpected CMD_NONE!"; } break; case CMD_MULTI_GET: { d->m_state = d->InsideMethod; multiGet(data); d->verifyState("multiGet()"); d->m_state = d->Idle; } break; case CMD_FILESYSTEMFREESPACE: { stream >> url; void *data = static_cast(&url); d->m_state = d->InsideMethod; virtual_hook(GetFileSystemFreeSpace, data); d->verifyState("fileSystemFreeSpace()"); d->m_state = d->Idle; } break; default: { // Some command we don't understand. // Just ignore it, it may come from some future version of KIO. } break; } } bool SlaveBase::checkCachedAuthentication(AuthInfo &info) { KPasswdServerClient *passwdServerClient = d->passwdServerClient(); return (passwdServerClient->checkAuthInfo(&info, metaData(QStringLiteral("window-id")).toLong(), metaData(QStringLiteral("user-timestamp")).toULong())); } void SlaveBase::dispatchOpenCommand(int command, const QByteArray &data) { QDataStream stream(data); switch (command) { case CMD_READ: { KIO::filesize_t bytes; stream >> bytes; read(bytes); break; } case CMD_WRITE: { write(data); break; } case CMD_SEEK: { KIO::filesize_t offset; stream >> offset; seek(offset); break; } case CMD_NONE: break; case CMD_CLOSE: close(); // must call finish(), which will set d->inOpenLoop=false break; default: // Some command we don't understand. // Just ignore it, it may come from some future version of KIO. break; } } bool SlaveBase::cacheAuthentication(const AuthInfo &info) { KPasswdServerClient *passwdServerClient = d->passwdServerClient(); passwdServerClient->addAuthInfo(info, metaData(QStringLiteral("window-id")).toLongLong()); return true; } int SlaveBase::connectTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ConnectTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_CONNECT_TIMEOUT; } int SlaveBase::proxyConnectTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ProxyConnectTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_PROXY_CONNECT_TIMEOUT; } int SlaveBase::responseTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ResponseTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_RESPONSE_TIMEOUT; } int SlaveBase::readTimeout() { bool ok; QString tmp = metaData(QStringLiteral("ReadTimeout")); int result = tmp.toInt(&ok); if (ok) { return result; } return DEFAULT_READ_TIMEOUT; } bool SlaveBase::wasKilled() const { return d->wasKilled; } void SlaveBase::setKillFlag() { d->wasKilled = true; } void SlaveBase::send(int cmd, const QByteArray &arr) { slaveWriteError = false; if (!d->appConnection.send(cmd, arr)) // Note that slaveWriteError can also be set by sigpipe_handler { slaveWriteError = true; } if (slaveWriteError) { exit(); } } void SlaveBase::virtual_hook(int id, void *data) { Q_UNUSED(data); switch(id) { case GetFileSystemFreeSpace: { error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(mProtocol, CMD_FILESYSTEMFREESPACE)); } break; } } void SlaveBase::lookupHost(const QString &host) { KIO_DATA << host; send(MSG_HOST_INFO_REQ, data); } int SlaveBase::waitForHostInfo(QHostInfo &info) { QByteArray data; int result = waitForAnswer(CMD_HOST_INFO, 0, data); if (result == -1) { info.setError(QHostInfo::UnknownError); info.setErrorString(i18n("Unknown Error")); return result; } QDataStream stream(data); QString hostName; QList addresses; int error; QString errorString; stream >> hostName >> addresses >> error >> errorString; info.setHostName(hostName); info.setAddresses(addresses); info.setError(QHostInfo::HostInfoError(error)); info.setErrorString(errorString); return result; } PrivilegeOperationStatus SlaveBase::requestPrivilegeOperation() { if (d->m_privilegeOperationStatus == OperationNotAllowed) { QByteArray buffer; send(MSG_PRIVILEGE_EXEC); waitForAnswer(MSG_PRIVILEGE_EXEC, 0, buffer); QDataStream ds(buffer); ds >> d->m_privilegeOperationStatus >> d->m_warningCaption >> d-> m_warningMessage; } if (metaData(QStringLiteral("UnitTesting")) != QLatin1String("true") && d->m_privilegeOperationStatus == OperationAllowed && !d->m_confirmationAsked) { d->m_privilegeOperationStatus = d->askConfirmation(); d->m_confirmationAsked = true; } return KIO::PrivilegeOperationStatus(d->m_privilegeOperationStatus); } void SlaveBase::addTemporaryAuthorization(const QString &action) { d->m_tempAuths.insert(action); } diff --git a/src/filewidgets/kfilewidget.cpp b/src/filewidgets/kfilewidget.cpp index 1ab0ed40..bb56f6f1 100644 --- a/src/filewidgets/kfilewidget.cpp +++ b/src/filewidgets/kfilewidget.cpp @@ -1,2886 +1,2886 @@ // -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 1997, 1998 Richard Moore 1998 Stephan Kulow 1998 Daniel Grana 1999,2000,2001,2002,2003 Carsten Pfeiffer 2003 Clarence Dang 2007 David Faure 2008 Rafael Fernández López This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfilewidget.h" #include "../pathhelpers_p.h" #include "kfileplacesview.h" #include "kfileplacesmodel.h" #include "kfilebookmarkhandler_p.h" #include "kurlcombobox.h" #include "kurlnavigator.h" #include "kfilepreviewgenerator.h" #include "kfilewidgetdocktitlebar_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class KFileWidgetPrivate { public: explicit KFileWidgetPrivate(KFileWidget *widget) : q(widget), boxLayout(nullptr), placesDock(nullptr), placesView(nullptr), placesViewSplitter(nullptr), placesViewWidth(-1), labeledCustomWidget(nullptr), bottomCustomWidget(nullptr), autoSelectExtCheckBox(nullptr), operationMode(KFileWidget::Opening), bookmarkHandler(nullptr), toolbar(nullptr), locationEdit(nullptr), ops(nullptr), filterWidget(nullptr), autoSelectExtChecked(false), keepLocation(false), hasView(false), hasDefaultFilter(false), inAccept(false), dummyAdded(false), confirmOverwrite(false), differentHierarchyLevelItemsEntered(false), iconSizeSlider(nullptr), zoomOutAction(nullptr), zoomInAction(nullptr) { } ~KFileWidgetPrivate() { delete bookmarkHandler; // Should be deleted before ops! delete ops; } void updateLocationWhatsThis(); void updateAutoSelectExtension(); void initSpeedbar(); void setPlacesViewSplitterSizes(); void setLafBoxColumnWidth(); void initGUI(); void readViewConfig(); void writeViewConfig(); void setNonExtSelection(); void setLocationText(const QUrl &); void setLocationText(const QList &); void appendExtension(QUrl &url); void updateLocationEditExtension(const QString &); void updateFilter(); QList &parseSelectedUrls(); /** * Parses the string "line" for files. If line doesn't contain any ", the * whole line will be interpreted as one file. If the number of " is odd, * an empty list will be returned. Otherwise, all items enclosed in " " * will be returned as correct urls. */ QList tokenize(const QString &line) const; /** * Reads the recent used files and inserts them into the location combobox */ void readRecentFiles(); /** * Saves the entries from the location combobox. */ void saveRecentFiles(); /** * called when an item is highlighted/selected in multiselection mode. * handles setting the locationEdit. */ void multiSelectionChanged(); /** * Returns the absolute version of the URL specified in locationEdit. */ QUrl getCompleteUrl(const QString &) const; /** * Sets the dummy entry on the history combo box. If the dummy entry * already exists, it is overwritten with this information. */ void setDummyHistoryEntry(const QString &text, const QPixmap &icon = QPixmap(), bool usePreviousPixmapIfNull = true); /** * Removes the dummy entry of the history combo box. */ void removeDummyHistoryEntry(); /** * Asks for overwrite confirmation using a KMessageBox and returns * true if the user accepts. * * @since 4.2 */ bool toOverwrite(const QUrl &); // private slots void _k_slotLocationChanged(const QString &); void _k_urlEntered(const QUrl &); void _k_enterUrl(const QUrl &); void _k_enterUrl(const QString &); void _k_locationAccepted(const QString &); void _k_slotFilterChanged(); void _k_fileHighlighted(const KFileItem &); void _k_fileSelected(const KFileItem &); void _k_slotLoadingFinished(); void _k_fileCompletion(const QString &); void _k_toggleSpeedbar(bool); void _k_toggleBookmarks(bool); void _k_slotAutoSelectExtClicked(); void _k_placesViewSplitterMoved(int, int); void _k_activateUrlNavigator(); void _k_zoomOutIconsSize(); void _k_zoomInIconsSize(); void _k_slotIconSizeSliderMoved(int); void _k_slotIconSizeChanged(int); void _k_slotViewDoubleClicked(const QModelIndex&); void addToRecentDocuments(); QString locationEditCurrentText() const; /** * KIO::NetAccess::mostLocalUrl local replacement. * This method won't show any progress dialogs for stating, since * they are very annoying when stating. */ QUrl mostLocalUrl(const QUrl &url); void setInlinePreviewShown(bool show); KFileWidget * const q; // the last selected url QUrl url; // the selected filenames in multiselection mode -- FIXME QString filenames; // now following all kind of widgets, that I need to rebuild // the geometry management QBoxLayout *boxLayout; QGridLayout *lafBox; QVBoxLayout *vbox; QLabel *locationLabel; QWidget *opsWidget; QWidget *pathSpacer; QLabel *filterLabel; KUrlNavigator *urlNavigator; QPushButton *okButton, *cancelButton; QDockWidget *placesDock; KFilePlacesView *placesView; QSplitter *placesViewSplitter; // caches the places view width. This value will be updated when the splitter // is moved. This allows us to properly set a value when the dialog itself // is resized int placesViewWidth; QWidget *labeledCustomWidget; QWidget *bottomCustomWidget; // Automatically Select Extension stuff QCheckBox *autoSelectExtCheckBox; QString extension; // current extension for this filter QList statJobs; QList urlList; //the list of selected urls KFileWidget::OperationMode operationMode; // The file class used for KRecentDirs QString fileClass; KFileBookmarkHandler *bookmarkHandler; KActionMenu *bookmarkButton; KToolBar *toolbar; KUrlComboBox *locationEdit; KDirOperator *ops; KFileFilterCombo *filterWidget; QTimer filterDelayTimer; KFilePlacesModel *model; // whether or not the _user_ has checked the above box bool autoSelectExtChecked : 1; // indicates if the location edit should be kept or cleared when changing // directories bool keepLocation : 1; // the KDirOperators view is set in KFileWidget::show(), so to avoid // setting it again and again, we have this nice little boolean :) bool hasView : 1; bool hasDefaultFilter : 1; // necessary for the operationMode bool autoDirectoryFollowing : 1; bool inAccept : 1; // true between beginning and end of accept() bool dummyAdded : 1; // if the dummy item has been added. This prevents the combo from having a // blank item added when loaded bool confirmOverwrite : 1; bool differentHierarchyLevelItemsEntered; QSlider *iconSizeSlider; QAction *zoomOutAction; QAction *zoomInAction; // The group which stores app-specific settings. These settings are recent // files and urls. Visual settings (view mode, sorting criteria...) are not // app-specific and are stored in kdeglobals KConfigGroup configGroup; }; Q_GLOBAL_STATIC(QUrl, lastDirectory) // to set the start path static const char autocompletionWhatsThisText[] = I18N_NOOP("While typing in the text area, you may be presented " "with possible matches. " "This feature can be controlled by clicking with the right mouse button " "and selecting a preferred mode from the Text Completion menu."); // returns true if the string contains ":/" sequence, where is at least 2 alpha chars static bool containsProtocolSection(const QString &string) { int len = string.length(); static const char prot[] = ":/"; for (int i = 0; i < len;) { i = string.indexOf(QLatin1String(prot), i); if (i == -1) { return false; } int j = i - 1; for (; j >= 0; j--) { const QChar &ch(string[j]); if (ch.toLatin1() == 0 || !ch.isLetter()) { break; } if (ch.isSpace() && (i - j - 1) >= 2) { return true; } } if (j < 0 && i >= 2) { return true; // at least two letters before ":/" } i += 3; // skip : and / and one char } return false; } // this string-to-url conversion function handles relative paths, full paths and URLs // without the http-prepending that QUrl::fromUserInput does. static QUrl urlFromString(const QString& str) { if (QDir::isAbsolutePath(str)) { return QUrl::fromLocalFile(str); } QUrl url(str); if (url.isRelative()) { url.clear(); url.setPath(str); } return url; } KFileWidget::KFileWidget(const QUrl &_startDir, QWidget *parent) : QWidget(parent), d(new KFileWidgetPrivate(this)) { QUrl startDir(_startDir); // qDebug() << "startDir" << startDir; QString filename; d->okButton = new QPushButton(this); KGuiItem::assign(d->okButton, KStandardGuiItem::ok()); d->okButton->setDefault(true); d->cancelButton = new QPushButton(this); KGuiItem::assign(d->cancelButton, KStandardGuiItem::cancel()); // The dialog shows them d->okButton->hide(); d->cancelButton->hide(); d->opsWidget = new QWidget(this); QVBoxLayout *opsWidgetLayout = new QVBoxLayout(d->opsWidget); opsWidgetLayout->setMargin(0); opsWidgetLayout->setSpacing(0); //d->toolbar = new KToolBar(this, true); d->toolbar = new KToolBar(d->opsWidget, true); d->toolbar->setObjectName(QStringLiteral("KFileWidget::toolbar")); d->toolbar->setMovable(false); opsWidgetLayout->addWidget(d->toolbar); d->model = new KFilePlacesModel(this); // Resolve this now so that a 'kfiledialog:' URL, if specified, // does not get inserted into the urlNavigator history. d->url = getStartUrl(startDir, d->fileClass, filename); startDir = d->url; // Don't pass startDir to the KUrlNavigator at this stage: as well as // the above, it may also contain a file name which should not get // inserted in that form into the old-style navigation bar history. // Wait until the KIO::stat has been done later. // // The stat cannot be done before this point, bug 172678. d->urlNavigator = new KUrlNavigator(d->model, QUrl(), d->opsWidget); //d->toolbar); d->urlNavigator->setPlacesSelectorVisible(false); opsWidgetLayout->addWidget(d->urlNavigator); QUrl u; KUrlComboBox *pathCombo = d->urlNavigator->editor(); #ifdef Q_OS_WIN #if 0 foreach (const QFileInfo &drive, QFSFileEngine::drives()) { u = QUrl::fromLocalFile(drive.filePath()); pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small), i18n("Drive: %1", u.toLocalFile())); } #else #pragma message("QT5 PORT") #endif #else u = QUrl::fromLocalFile(QDir::rootPath()); pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small), u.toLocalFile()); #endif u = QUrl::fromLocalFile(QDir::homePath()); pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small), u.toLocalFile()); QUrl docPath = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); if (u.adjusted(QUrl::StripTrailingSlash) != docPath.adjusted(QUrl::StripTrailingSlash) && QDir(docPath.toLocalFile()).exists()) { pathCombo->addDefaultUrl(docPath, KIO::pixmapForUrl(docPath, 0, KIconLoader::Small), docPath.toLocalFile()); } u = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DesktopLocation)); pathCombo->addDefaultUrl(u, KIO::pixmapForUrl(u, 0, KIconLoader::Small), u.toLocalFile()); d->ops = new KDirOperator(QUrl(), d->opsWidget); d->ops->setObjectName(QStringLiteral("KFileWidget::ops")); d->ops->setIsSaving(d->operationMode == Saving); opsWidgetLayout->addWidget(d->ops); connect(d->ops, SIGNAL(urlEntered(QUrl)), SLOT(_k_urlEntered(QUrl))); connect(d->ops, SIGNAL(fileHighlighted(KFileItem)), SLOT(_k_fileHighlighted(KFileItem))); connect(d->ops, SIGNAL(fileSelected(KFileItem)), SLOT(_k_fileSelected(KFileItem))); connect(d->ops, SIGNAL(finishedLoading()), SLOT(_k_slotLoadingFinished())); d->ops->setupMenu(KDirOperator::SortActions | KDirOperator::FileActions | KDirOperator::ViewActions); KActionCollection *coll = d->ops->actionCollection(); coll->addAssociatedWidget(this); // add nav items to the toolbar // // NOTE: The order of the button icons here differs from that // found in the file manager and web browser, but has been discussed // and agreed upon on the kde-core-devel mailing list: // // http://lists.kde.org/?l=kde-core-devel&m=116888382514090&w=2 coll->action(QStringLiteral("up"))->setWhatsThis(i18n("Click this button to enter the parent folder.

" "For instance, if the current location is file:/home/konqi clicking this " "button will take you to file:/home.
")); coll->action(QStringLiteral("back"))->setWhatsThis(i18n("Click this button to move backwards one step in the browsing history.")); coll->action(QStringLiteral("forward"))->setWhatsThis(i18n("Click this button to move forward one step in the browsing history.")); coll->action(QStringLiteral("reload"))->setWhatsThis(i18n("Click this button to reload the contents of the current location.")); coll->action(QStringLiteral("mkdir"))->setShortcut(QKeySequence(Qt::Key_F10)); coll->action(QStringLiteral("mkdir"))->setWhatsThis(i18n("Click this button to create a new folder.")); QAction *goToNavigatorAction = coll->addAction(QStringLiteral("gotonavigator"), this, SLOT(_k_activateUrlNavigator())); goToNavigatorAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_L)); KToggleAction *showSidebarAction = new KToggleAction(i18n("Show Places Navigation Panel"), this); coll->addAction(QStringLiteral("toggleSpeedbar"), showSidebarAction); showSidebarAction->setShortcut(QKeySequence(Qt::Key_F9)); connect(showSidebarAction, SIGNAL(toggled(bool)), SLOT(_k_toggleSpeedbar(bool))); KToggleAction *showBookmarksAction = new KToggleAction(i18n("Show Bookmarks"), this); coll->addAction(QStringLiteral("toggleBookmarks"), showBookmarksAction); connect(showBookmarksAction, SIGNAL(toggled(bool)), SLOT(_k_toggleBookmarks(bool))); KActionMenu *menu = new KActionMenu(QIcon::fromTheme(QStringLiteral("configure")), i18n("Options"), this); coll->addAction(QStringLiteral("extra menu"), menu); menu->setWhatsThis(i18n("This is the preferences menu for the file dialog. " "Various options can be accessed from this menu including:
    " "
  • how files are sorted in the list
  • " "
  • types of view, including icon and list
  • " "
  • showing of hidden files
  • " "
  • the Places navigation panel
  • " "
  • file previews
  • " "
  • separating folders from files
")); menu->addAction(coll->action(QStringLiteral("view menu"))); menu->addSeparator(); menu->addAction(coll->action(QStringLiteral("decoration menu"))); menu->addSeparator(); menu->addAction(coll->action(QStringLiteral("show hidden"))); menu->addAction(showSidebarAction); menu->addAction(showBookmarksAction); coll->action(QStringLiteral("inline preview")); menu->addAction(coll->action(QStringLiteral("preview"))); menu->setDelayed(false); connect(menu->menu(), &QMenu::aboutToShow, d->ops, &KDirOperator::updateSelectionDependentActions); d->iconSizeSlider = new QSlider(this); d->iconSizeSlider->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed); d->iconSizeSlider->setMinimumWidth(40); d->iconSizeSlider->setOrientation(Qt::Horizontal); d->iconSizeSlider->setMinimum(0); d->iconSizeSlider->setMaximum(100); d->iconSizeSlider->installEventFilter(this); connect(d->iconSizeSlider, &QAbstractSlider::valueChanged, d->ops, &KDirOperator::setIconsZoom); connect(d->iconSizeSlider, SIGNAL(valueChanged(int)), this, SLOT(_k_slotIconSizeChanged(int))); connect(d->iconSizeSlider, SIGNAL(sliderMoved(int)), this, SLOT(_k_slotIconSizeSliderMoved(int))); connect(d->ops, &KDirOperator::currentIconSizeChanged, [this](int value) { d->iconSizeSlider->setValue(value); d->zoomOutAction->setDisabled(value <= d->iconSizeSlider->minimum()); d->zoomInAction->setDisabled(value >= d->iconSizeSlider->maximum()); }); d->zoomOutAction = new QAction(QIcon::fromTheme(QStringLiteral("file-zoom-out")), i18n("Zoom out"), this); connect(d->zoomOutAction, SIGNAL(triggered()), SLOT(_k_zoomOutIconsSize())); d->zoomInAction = new QAction(QIcon::fromTheme(QStringLiteral("file-zoom-in")), i18n("Zoom in"), this); connect(d->zoomInAction, SIGNAL(triggered()), SLOT(_k_zoomInIconsSize())); QWidget *midSpacer = new QWidget(this); midSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); d->toolbar->addAction(coll->action(QStringLiteral("back"))); d->toolbar->addAction(coll->action(QStringLiteral("forward"))); d->toolbar->addAction(coll->action(QStringLiteral("up"))); d->toolbar->addAction(coll->action(QStringLiteral("reload"))); d->toolbar->addSeparator(); d->toolbar->addAction(coll->action(QStringLiteral("inline preview"))); d->toolbar->addAction(coll->action(QStringLiteral("sorting menu"))); d->toolbar->addWidget(midSpacer); d->toolbar->addAction(d->zoomOutAction); d->toolbar->addWidget(d->iconSizeSlider); d->toolbar->addAction(d->zoomInAction); d->toolbar->addSeparator(); d->toolbar->addAction(coll->action(QStringLiteral("mkdir"))); d->toolbar->addAction(menu); d->toolbar->setToolButtonStyle(Qt::ToolButtonIconOnly); d->toolbar->setMovable(false); KUrlCompletion *pathCompletionObj = new KUrlCompletion(KUrlCompletion::DirCompletion); pathCombo->setCompletionObject(pathCompletionObj); pathCombo->setAutoDeleteCompletionObject(true); connect(d->urlNavigator, SIGNAL(urlChanged(QUrl)), this, SLOT(_k_enterUrl(QUrl))); connect(d->urlNavigator, &KUrlNavigator::returnPressed, d->ops, QOverload<>::of(&QWidget::setFocus)); QString whatsThisText; // the Location label/edit d->locationLabel = new QLabel(i18n("&Name:"), this); d->locationEdit = new KUrlComboBox(KUrlComboBox::Files, true, this); d->locationEdit->installEventFilter(this); // Properly let the dialog be resized (to smaller). Otherwise we could have // huge dialogs that can't be resized to smaller (it would be as big as the longest // item in this combo box). (ereslibre) d->locationEdit->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); connect(d->locationEdit, SIGNAL(editTextChanged(QString)), SLOT(_k_slotLocationChanged(QString))); d->updateLocationWhatsThis(); d->locationLabel->setBuddy(d->locationEdit); KUrlCompletion *fileCompletionObj = new KUrlCompletion(KUrlCompletion::FileCompletion); d->locationEdit->setCompletionObject(fileCompletionObj); d->locationEdit->setAutoDeleteCompletionObject(true); connect(fileCompletionObj, SIGNAL(match(QString)), SLOT(_k_fileCompletion(QString))); connect(d->locationEdit, SIGNAL(returnPressed(QString)), this, SLOT(_k_locationAccepted(QString))); // the Filter label/edit whatsThisText = i18n("This is the filter to apply to the file list. " "File names that do not match the filter will not be shown.

" "You may select from one of the preset filters in the " "drop down menu, or you may enter a custom filter " "directly into the text area.

" "Wildcards such as * and ? are allowed.

"); d->filterLabel = new QLabel(i18n("&Filter:"), this); d->filterLabel->setWhatsThis(whatsThisText); d->filterWidget = new KFileFilterCombo(this); // Properly let the dialog be resized (to smaller). Otherwise we could have // huge dialogs that can't be resized to smaller (it would be as big as the longest // item in this combo box). (ereslibre) d->filterWidget->setSizeAdjustPolicy(QComboBox::AdjustToMinimumContentsLength); d->filterWidget->setWhatsThis(whatsThisText); d->filterLabel->setBuddy(d->filterWidget); connect(d->filterWidget, SIGNAL(filterChanged()), SLOT(_k_slotFilterChanged())); d->filterDelayTimer.setSingleShot(true); d->filterDelayTimer.setInterval(300); connect(d->filterWidget, &QComboBox::editTextChanged, &d->filterDelayTimer, QOverload<>::of(&QTimer::start)); connect(&d->filterDelayTimer, SIGNAL(timeout()), SLOT(_k_slotFilterChanged())); // the Automatically Select Extension checkbox // (the text, visibility etc. is set in updateAutoSelectExtension(), which is called by readConfig()) d->autoSelectExtCheckBox = new QCheckBox(this); const int spacingHint = style()->pixelMetric(QStyle::PM_DefaultLayoutSpacing); d->autoSelectExtCheckBox->setStyleSheet(QStringLiteral("QCheckBox { padding-top: %1px; }").arg(spacingHint)); connect(d->autoSelectExtCheckBox, SIGNAL(clicked()), SLOT(_k_slotAutoSelectExtClicked())); d->initGUI(); // activate GM // read our configuration KSharedConfig::Ptr config = KSharedConfig::openConfig(); KConfigGroup group(config, ConfigGroup); readConfig(group); coll->action(QStringLiteral("inline preview"))->setChecked(d->ops->isInlinePreviewShown()); d->iconSizeSlider->setValue(d->ops->iconsZoom()); KFilePreviewGenerator *pg = d->ops->previewGenerator(); if (pg) { coll->action(QStringLiteral("inline preview"))->setChecked(pg->isPreviewShown()); } // getStartUrl() above will have resolved the startDir parameter into // a directory and file name in the two cases: (a) where it is a // special "kfiledialog:" URL, or (b) where it is a plain file name // only without directory or protocol. For any other startDir // specified, it is not possible to resolve whether there is a file name // present just by looking at the URL; the only way to be sure is // to stat it. bool statRes = false; if (filename.isEmpty()) { KIO::StatJob *statJob = KIO::stat(startDir, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, this); statRes = statJob->exec(); // qDebug() << "stat of" << startDir << "-> statRes" << statRes << "isDir" << statJob->statResult().isDir(); if (!statRes || !statJob->statResult().isDir()) { filename = startDir.fileName(); startDir = startDir.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); // qDebug() << "statJob -> startDir" << startDir << "filename" << filename; } } d->ops->setUrl(startDir, true); d->urlNavigator->setLocationUrl(startDir); if (d->placesView) { d->placesView->setUrl(startDir); } // We have a file name either explicitly specified, or have checked that // we could stat it and it is not a directory. Set it. if (!filename.isEmpty()) { QLineEdit *lineEdit = d->locationEdit->lineEdit(); // qDebug() << "selecting filename" << filename; if (statRes) { d->setLocationText(QUrl(filename)); } else { lineEdit->setText(filename); // Preserve this filename when clicking on the view (cf _k_fileHighlighted) lineEdit->setModified(true); } lineEdit->selectAll(); } d->locationEdit->setFocus(); } KFileWidget::~KFileWidget() { KSharedConfig::Ptr config = KSharedConfig::openConfig(); config->sync(); delete d; } void KFileWidget::setLocationLabel(const QString &text) { d->locationLabel->setText(text); } void KFileWidget::setFilter(const QString &filter) { int pos = filter.indexOf(QLatin1Char('/')); // Check for an un-escaped '/', if found // interpret as a MIME filter. if (pos > 0 && filter[pos - 1] != QLatin1Char('\\')) { QStringList filters = filter.split(QLatin1Char(' '), QString::SkipEmptyParts); setMimeFilter(filters); return; } // Strip the escape characters from // escaped '/' characters. QString copy(filter); for (pos = 0; (pos = copy.indexOf(QStringLiteral("\\/"), pos)) != -1; ++pos) { copy.remove(pos, 1); } d->ops->clearFilter(); d->filterWidget->setFilter(copy); d->ops->setNameFilter(d->filterWidget->currentFilter()); d->ops->updateDir(); d->hasDefaultFilter = false; d->filterWidget->setEditable(true); d->updateAutoSelectExtension(); } QString KFileWidget::currentFilter() const { return d->filterWidget->currentFilter(); } void KFileWidget::setMimeFilter(const QStringList &mimeTypes, const QString &defaultType) { d->filterWidget->setMimeFilter(mimeTypes, defaultType); QStringList types = d->filterWidget->currentFilter().split(QLatin1Char(' '), QString::SkipEmptyParts); //QStringList::split(" ", d->filterWidget->currentFilter()); types.append(QStringLiteral("inode/directory")); d->ops->clearFilter(); d->ops->setMimeFilter(types); d->hasDefaultFilter = !defaultType.isEmpty(); d->filterWidget->setEditable(!d->hasDefaultFilter || d->operationMode != Saving); d->updateAutoSelectExtension(); } void KFileWidget::clearFilter() { d->filterWidget->setFilter(QString()); d->ops->clearFilter(); d->hasDefaultFilter = false; d->filterWidget->setEditable(true); d->updateAutoSelectExtension(); } QString KFileWidget::currentMimeFilter() const { int i = d->filterWidget->currentIndex(); if (d->filterWidget->showsAllTypes() && i == 0) { return QString(); // The "all types" item has no mimetype } return d->filterWidget->filters().at(i); } QMimeType KFileWidget::currentFilterMimeType() { QMimeDatabase db; return db.mimeTypeForName(currentMimeFilter()); } void KFileWidget::setPreviewWidget(KPreviewWidgetBase *w) { d->ops->setPreviewWidget(w); d->ops->clearHistory(); d->hasView = true; } QUrl KFileWidgetPrivate::getCompleteUrl(const QString &_url) const { // qDebug() << "got url " << _url; const QString url = KShell::tildeExpand(_url); QUrl u; if (QDir::isAbsolutePath(url)) { u = QUrl::fromLocalFile(url); } else { QUrl relativeUrlTest(ops->url()); relativeUrlTest.setPath(concatPaths(relativeUrlTest.path(), url)); if (!ops->dirLister()->findByUrl(relativeUrlTest).isNull() || !KProtocolInfo::isKnownProtocol(relativeUrlTest)) { u = relativeUrlTest; } else { u = QUrl(url); // keep it relative } } return u; } QSize KFileWidget::sizeHint() const { int fontSize = fontMetrics().height(); const QSize goodSize(48 * fontSize, 30 * fontSize); const QSize screenSize = QApplication::desktop()->availableGeometry(this).size(); const QSize minSize(screenSize / 2); const QSize maxSize(screenSize * qreal(0.9)); return (goodSize.expandedTo(minSize).boundedTo(maxSize)); } static QString relativePathOrUrl(const QUrl &baseUrl, const QUrl &url); // Called by KFileDialog void KFileWidget::slotOk() { // qDebug() << "slotOk\n"; const QString locationEditCurrentText(KShell::tildeExpand(d->locationEditCurrentText())); QList locationEditCurrentTextList(d->tokenize(locationEditCurrentText)); KFile::Modes mode = d->ops->mode(); // if there is nothing to do, just return from here if (locationEditCurrentTextList.isEmpty()) { return; } // Make sure that one of the modes was provided if (!((mode & KFile::File) || (mode & KFile::Directory) || (mode & KFile::Files))) { mode |= KFile::File; // qDebug() << "No mode() provided"; } // if we are on file mode, and the list of provided files/folder is greater than one, inform // the user about it if (locationEditCurrentTextList.count() > 1) { if (mode & KFile::File) { KMessageBox::sorry(this, i18n("You can only select one file"), i18n("More than one file provided")); return; } /** * Logic of the next part of code (ends at "end multi relative urls"). * * We allow for instance to be at "/" and insert '"home/foo/bar.txt" "boot/grub/menu.lst"'. * Why we need to support this ? Because we provide tree views, which aren't plain. * * Now, how does this logic work. It will get the first element on the list (with no filename), * following the previous example say "/home/foo" and set it as the top most url. * * After this, it will iterate over the rest of items and check if this URL (topmost url) * contains the url being iterated. * * As you might have guessed it will do "/home/foo" against "/boot/grub" (again stripping * filename), and a false will be returned. Then we upUrl the top most url, resulting in * "/home" against "/boot/grub", what will again return false, so we upUrl again. Now we * have "/" against "/boot/grub", what returns true for us, so we can say that the closest * common ancestor of both is "/". * * This example has been written for 2 urls, but this works for any number of urls. */ if (!d->differentHierarchyLevelItemsEntered) { // avoid infinite recursion. running this int start = 0; QUrl topMostUrl; KIO::StatJob *statJob = nullptr; bool res = false; // we need to check for a valid first url, so in theory we only iterate one time over // this loop. However it can happen that the user did // "home/foo/nonexistantfile" "boot/grub/menu.lst", so we look for a good first // candidate. while (!res && start < locationEditCurrentTextList.count()) { topMostUrl = locationEditCurrentTextList.at(start); statJob = KIO::stat(topMostUrl, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, this); res = statJob->exec(); start++; } Q_ASSERT(statJob); // if this is not a dir, strip the filename. after this we have an existent and valid // dir (we stated correctly the file). if (!statJob->statResult().isDir()) { topMostUrl = topMostUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); } // now the funny part. for the rest of filenames, go and look for the closest ancestor // of all them. for (int i = start; i < locationEditCurrentTextList.count(); ++i) { QUrl currUrl = locationEditCurrentTextList.at(i); KIO::StatJob *statJob = KIO::stat(currUrl, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, this); int res = statJob->exec(); if (res) { // again, we don't care about filenames if (!statJob->statResult().isDir()) { currUrl = currUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash); } // iterate while this item is contained on the top most url while (!topMostUrl.matches(currUrl, QUrl::StripTrailingSlash) && !topMostUrl.isParentOf(currUrl)) { topMostUrl = KIO::upUrl(topMostUrl); } } } // now recalculate all paths for them being relative in base of the top most url QStringList stringList; stringList.reserve(locationEditCurrentTextList.count()); for (int i = 0; i < locationEditCurrentTextList.count(); ++i) { Q_ASSERT(topMostUrl.isParentOf(locationEditCurrentTextList[i])); stringList << relativePathOrUrl(topMostUrl, locationEditCurrentTextList[i]); } d->ops->setUrl(topMostUrl, true); const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true); d->locationEdit->lineEdit()->setText(QStringLiteral("\"%1\"").arg(stringList.join(QStringLiteral("\" \"")))); d->locationEdit->lineEdit()->blockSignals(signalsBlocked); d->differentHierarchyLevelItemsEntered = true; slotOk(); return; } /** * end multi relative urls */ } else if (!locationEditCurrentTextList.isEmpty()) { // if we are on file or files mode, and we have an absolute url written by // the user, convert it to relative if (!locationEditCurrentText.isEmpty() && !(mode & KFile::Directory) && (QDir::isAbsolutePath(locationEditCurrentText) || containsProtocolSection(locationEditCurrentText))) { QString fileName; QUrl url = urlFromString(locationEditCurrentText); if (d->operationMode == Opening) { KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, this); int res = statJob->exec(); if (res) { if (!statJob->statResult().isDir()) { fileName = url.fileName(); url = url.adjusted(QUrl::RemoveFilename); // keeps trailing slash } else { if (!url.path().endsWith(QLatin1Char('/'))) { url.setPath(url.path() + QLatin1Char('/')); } } } } else { const QUrl directory = url.adjusted(QUrl::RemoveFilename); //Check if the folder exists KIO::StatJob *statJob = KIO::stat(directory, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, this); int res = statJob->exec(); if (res) { if (statJob->statResult().isDir()) { url = url.adjusted(QUrl::StripTrailingSlash); fileName = url.fileName(); url = url.adjusted(QUrl::RemoveFilename); } } } d->ops->setUrl(url, true); const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true); d->locationEdit->lineEdit()->setText(fileName); d->locationEdit->lineEdit()->blockSignals(signalsBlocked); slotOk(); return; } } // restore it d->differentHierarchyLevelItemsEntered = false; // locationEditCurrentTextList contains absolute paths // this is the general loop for the File and Files mode. Obviously we know // that the File mode will iterate only one time here bool directoryMode = (mode & KFile::Directory); bool onlyDirectoryMode = directoryMode && !(mode & KFile::File) && !(mode & KFile::Files); QList::ConstIterator it = locationEditCurrentTextList.constBegin(); bool filesInList = false; while (it != locationEditCurrentTextList.constEnd()) { QUrl url(*it); if (d->operationMode == Saving && !directoryMode) { d->appendExtension(url); } d->url = url; KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, this); int res = statJob->exec(); if (!KUrlAuthorized::authorizeUrlAction(QStringLiteral("open"), QUrl(), url)) { QString msg = KIO::buildErrorString(KIO::ERR_ACCESS_DENIED, d->url.toDisplayString()); KMessageBox::error(this, msg); return; } // if we are on local mode, make sure we haven't got a remote base url if ((mode & KFile::LocalOnly) && !d->mostLocalUrl(d->url).isLocalFile()) { KMessageBox::sorry(this, i18n("You can only select local files"), i18n("Remote files not accepted")); return; } const auto &supportedSchemes = d->model->supportedSchemes(); if (!supportedSchemes.isEmpty() && !supportedSchemes.contains(d->url.scheme())) { KMessageBox::sorry(this, i18np("The selected URL uses an unsupported scheme. " "Please use the following scheme: %2", "The selected URL uses an unsupported scheme. " "Please use one of the following schemes: %2", supportedSchemes.size(), supportedSchemes.join(QLatin1String(", "))), i18n("Unsupported URL scheme")); return; } // if we are given a folder when not on directory mode, let's get into it if (res && !directoryMode && statJob->statResult().isDir()) { // check if we were given more than one folder, in that case we don't know to which one // cd ++it; while (it != locationEditCurrentTextList.constEnd()) { QUrl checkUrl(*it); KIO::StatJob *checkStatJob = KIO::stat(checkUrl, KIO::HideProgressInfo); KJobWidgets::setWindow(checkStatJob, this); bool res = checkStatJob->exec(); if (res && checkStatJob->statResult().isDir()) { KMessageBox::sorry(this, i18n("More than one folder has been selected and this dialog does not accept folders, so it is not possible to decide which one to enter. Please select only one folder to list it."), i18n("More than one folder provided")); return; } else if (res) { filesInList = true; } ++it; } if (filesInList) { KMessageBox::information(this, i18n("At least one folder and one file has been selected. Selected files will be ignored and the selected folder will be listed"), i18n("Files and folders selected")); } d->ops->setUrl(url, true); const bool signalsBlocked = d->locationEdit->lineEdit()->blockSignals(true); d->locationEdit->lineEdit()->setText(QString()); d->locationEdit->lineEdit()->blockSignals(signalsBlocked); return; } else if (res && onlyDirectoryMode && !statJob->statResult().isDir()) { // if we are given a file when on directory only mode, reject it return; } else if (!(mode & KFile::ExistingOnly) || res) { // if we don't care about ExistingOnly flag, add the file even if // it doesn't exist. If we care about it, don't add it to the list if (!onlyDirectoryMode || (res && statJob->statResult().isDir())) { d->urlList << url; } filesInList = true; } else { KMessageBox::sorry(this, i18n("The file \"%1\" could not be found", url.toDisplayString(QUrl::PreferLocalFile)), i18n("Cannot open file")); return; // do not emit accepted() if we had ExistingOnly flag and stat failed } if ((d->operationMode == Saving) && d->confirmOverwrite && !d->toOverwrite(url)) { return; } ++it; } // if we have reached this point and we didn't return before, that is because // we want this dialog to be accepted emit accepted(); } void KFileWidget::accept() { d->inAccept = true; // parseSelectedUrls() checks that *lastDirectory() = d->ops->url(); if (!d->fileClass.isEmpty()) { KRecentDirs::add(d->fileClass, d->ops->url().toString()); } // clear the topmost item, we insert it as full path later on as item 1 d->locationEdit->setItemText(0, QString()); const QList list = selectedUrls(); QList::const_iterator it = list.begin(); int atmost = d->locationEdit->maxItems(); //don't add more items than necessary for (; it != list.end() && atmost > 0; ++it) { const QUrl &url = *it; // we strip the last slash (-1) because KUrlComboBox does that as well // when operating in file-mode. If we wouldn't , dupe-finding wouldn't // work. QString file = url.isLocalFile() ? url.toLocalFile() : url.toDisplayString(); // remove dupes for (int i = 1; i < d->locationEdit->count(); i++) { if (d->locationEdit->itemText(i) == file) { d->locationEdit->removeItem(i--); break; } } //FIXME I don't think this works correctly when the KUrlComboBox has some default urls. //KUrlComboBox should provide a function to add an url and rotate the existing ones, keeping //track of maxItems, and we shouldn't be able to insert items as we please. d->locationEdit->insertItem(1, file); atmost--; } d->writeViewConfig(); d->saveRecentFiles(); d->addToRecentDocuments(); if (!(mode() & KFile::Files)) { // single selection emit fileSelected(d->url); } d->ops->close(); } void KFileWidgetPrivate::_k_fileHighlighted(const KFileItem &i) { if ((!i.isNull() && i.isDir()) || (locationEdit->hasFocus() && !locationEdit->currentText().isEmpty())) { // don't disturb return; } const bool modified = locationEdit->lineEdit()->isModified(); if (!(ops->mode() & KFile::Files)) { if (i.isNull()) { if (!modified) { setLocationText(QUrl()); } return; } url = i.url(); if (!locationEdit->hasFocus()) { // don't disturb while editing setLocationText(url); } emit q->fileHighlighted(url); } else { multiSelectionChanged(); emit q->selectionChanged(); } locationEdit->lineEdit()->setModified(false); } void KFileWidgetPrivate::_k_fileSelected(const KFileItem &i) { if (!i.isNull() && i.isDir()) { return; } if (!(ops->mode() & KFile::Files)) { if (i.isNull()) { setLocationText(QUrl()); return; } setLocationText(i.url()); } else { multiSelectionChanged(); emit q->selectionChanged(); } // If we are saving, let another chance to the user before accepting the dialog (or trying to // accept). This way the user can choose a file and add a "_2" for instance to the filename. // Double clicking however will override this, regardless of single/double click mouse setting, // see: _k_slotViewDoubleClicked if (operationMode == KFileWidget::Saving) { locationEdit->setFocus(); } else { q->slotOk(); } } // I know it's slow to always iterate thru the whole filelist // (d->ops->selectedItems()), but what can we do? void KFileWidgetPrivate::multiSelectionChanged() { if (locationEdit->hasFocus() && !locationEdit->currentText().isEmpty()) { // don't disturb return; } const KFileItemList list = ops->selectedItems(); if (list.isEmpty()) { setLocationText(QUrl()); return; } setLocationText(list.urlList()); } void KFileWidgetPrivate::setDummyHistoryEntry(const QString &text, const QPixmap &icon, bool usePreviousPixmapIfNull) { // setCurrentItem() will cause textChanged() being emitted, // so slotLocationChanged() will be called. Make sure we don't clear // the KDirOperator's view-selection in there QObject::disconnect(locationEdit, SIGNAL(editTextChanged(QString)), q, SLOT(_k_slotLocationChanged(QString))); bool dummyExists = dummyAdded; int cursorPosition = locationEdit->lineEdit()->cursorPosition(); if (dummyAdded) { if (!icon.isNull()) { locationEdit->setItemIcon(0, icon); locationEdit->setItemText(0, text); } else { if (!usePreviousPixmapIfNull) { locationEdit->setItemIcon(0, QPixmap()); } locationEdit->setItemText(0, text); } } else { if (!text.isEmpty()) { if (!icon.isNull()) { locationEdit->insertItem(0, icon, text); } else { if (!usePreviousPixmapIfNull) { locationEdit->insertItem(0, QPixmap(), text); } else { locationEdit->insertItem(0, text); } } dummyAdded = true; dummyExists = true; } } if (dummyExists && !text.isEmpty()) { locationEdit->setCurrentIndex(0); } locationEdit->lineEdit()->setCursorPosition(cursorPosition); QObject::connect(locationEdit, SIGNAL(editTextChanged(QString)), q, SLOT(_k_slotLocationChanged(QString))); } void KFileWidgetPrivate::removeDummyHistoryEntry() { if (!dummyAdded) { return; } // setCurrentItem() will cause textChanged() being emitted, // so slotLocationChanged() will be called. Make sure we don't clear // the KDirOperator's view-selection in there QObject::disconnect(locationEdit, SIGNAL(editTextChanged(QString)), q, SLOT(_k_slotLocationChanged(QString))); if (locationEdit->count()) { locationEdit->removeItem(0); } locationEdit->setCurrentIndex(-1); dummyAdded = false; QObject::connect(locationEdit, SIGNAL(editTextChanged(QString)), q, SLOT(_k_slotLocationChanged(QString))); } void KFileWidgetPrivate::setLocationText(const QUrl &url) { if (!url.isEmpty()) { QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon(KIO::iconNameForUrl(url), KIconLoader::Small); if (!url.isRelative()) { const QUrl directory = url.adjusted(QUrl::RemoveFilename); if (!directory.path().isEmpty()) { q->setUrl(directory, false); } else { q->setUrl(url, false); } } setDummyHistoryEntry(url.fileName(), mimeTypeIcon); } else { removeDummyHistoryEntry(); } if (operationMode == KFileWidget::Saving) { setNonExtSelection(); } } static QString relativePathOrUrl(const QUrl &baseUrl, const QUrl &url) { if (baseUrl.isParentOf(url)) { const QString basePath(QDir::cleanPath(baseUrl.path())); QString relPath(QDir::cleanPath(url.path())); relPath.remove(0, basePath.length()); if (relPath.startsWith(QLatin1Char('/'))) { relPath.remove(0, 1); } return relPath; } else { return url.toDisplayString(); } } void KFileWidgetPrivate::setLocationText(const QList &urlList) { const QUrl currUrl = ops->url(); if (urlList.count() > 1) { QString urls; foreach (const QUrl &url, urlList) { urls += QStringLiteral("\"%1\"").arg(relativePathOrUrl(currUrl, url)) + QLatin1Char(' '); } urls.chop(1); setDummyHistoryEntry(urls, QPixmap(), false); } else if (urlList.count() == 1) { const QPixmap mimeTypeIcon = KIconLoader::global()->loadMimeTypeIcon(KIO::iconNameForUrl(urlList[0]), KIconLoader::Small); setDummyHistoryEntry(relativePathOrUrl(currUrl, urlList[0]), mimeTypeIcon); } else { removeDummyHistoryEntry(); } if (operationMode == KFileWidget::Saving) { setNonExtSelection(); } } void KFileWidgetPrivate::updateLocationWhatsThis() { QString whatsThisText; if (operationMode == KFileWidget::Saving) { whatsThisText = QLatin1String("") + i18n("This is the name to save the file as.") + i18n(autocompletionWhatsThisText); } else if (ops->mode() & KFile::Files) { whatsThisText = QLatin1String("") + i18n("This is the list of files to open. More than " "one file can be specified by listing several " "files, separated by spaces.") + i18n(autocompletionWhatsThisText); } else { whatsThisText = QLatin1String("") + i18n("This is the name of the file to open.") + i18n(autocompletionWhatsThisText); } locationLabel->setWhatsThis(whatsThisText); locationEdit->setWhatsThis(whatsThisText); } void KFileWidgetPrivate::initSpeedbar() { if (placesDock) { return; } placesDock = new QDockWidget(i18nc("@title:window", "Places"), q); placesDock->setFeatures(QDockWidget::NoDockWidgetFeatures); placesDock->setTitleBarWidget(new KDEPrivate::KFileWidgetDockTitleBar(placesDock)); placesView = new KFilePlacesView(placesDock); placesView->setModel(model); placesView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); placesView->setObjectName(QStringLiteral("url bar")); QObject::connect(placesView, SIGNAL(urlChanged(QUrl)), q, SLOT(_k_enterUrl(QUrl))); // need to set the current url of the urlbar manually (not via urlEntered() // here, because the initial url of KDirOperator might be the same as the // one that will be set later (and then urlEntered() won't be emitted). // TODO: KDE5 ### REMOVE THIS when KDirOperator's initial URL (in the c'tor) is gone. placesView->setUrl(url); placesDock->setWidget(placesView); placesViewSplitter->insertWidget(0, placesDock); // initialize the size of the splitter placesViewWidth = configGroup.readEntry(SpeedbarWidth, placesView->sizeHint().width()); // Needed for when the dialog is shown with the places panel initially hidden setPlacesViewSplitterSizes(); QObject::connect(placesDock, SIGNAL(visibilityChanged(bool)), q, SLOT(_k_toggleSpeedbar(bool))); } void KFileWidgetPrivate::setPlacesViewSplitterSizes() { if (placesViewWidth > 0) { QList sizes = placesViewSplitter->sizes(); sizes[0] = placesViewWidth; sizes[1] = q->width() - placesViewWidth - placesViewSplitter->handleWidth(); placesViewSplitter->setSizes(sizes); } } void KFileWidgetPrivate::setLafBoxColumnWidth() { // In order to perfectly align the filename widget with KDirOperator's icon view // - placesViewWidth needs to account for the size of the splitter handle // - the lafBox grid layout spacing should only affect the label, but not the line edit const int adjustment = placesViewSplitter->handleWidth() - lafBox->horizontalSpacing(); lafBox->setColumnMinimumWidth(0, placesViewWidth + adjustment); } void KFileWidgetPrivate::initGUI() { delete boxLayout; // deletes all sub layouts boxLayout = new QVBoxLayout(q); boxLayout->setMargin(0); // no additional margin to the already existing placesViewSplitter = new QSplitter(q); placesViewSplitter->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); placesViewSplitter->setChildrenCollapsible(false); boxLayout->addWidget(placesViewSplitter); QObject::connect(placesViewSplitter, SIGNAL(splitterMoved(int,int)), q, SLOT(_k_placesViewSplitterMoved(int,int))); placesViewSplitter->insertWidget(0, opsWidget); vbox = new QVBoxLayout(); vbox->setMargin(0); boxLayout->addLayout(vbox); lafBox = new QGridLayout(); lafBox->addWidget(locationLabel, 0, 0, Qt::AlignVCenter | Qt::AlignRight); lafBox->addWidget(locationEdit, 0, 1, Qt::AlignVCenter); lafBox->addWidget(okButton, 0, 2, Qt::AlignVCenter); lafBox->addWidget(filterLabel, 1, 0, Qt::AlignVCenter | Qt::AlignRight); lafBox->addWidget(filterWidget, 1, 1, Qt::AlignVCenter); lafBox->addWidget(cancelButton, 1, 2, Qt::AlignVCenter); lafBox->setColumnStretch(1, 4); vbox->addLayout(lafBox); // add the Automatically Select Extension checkbox vbox->addWidget(autoSelectExtCheckBox); q->setTabOrder(ops, autoSelectExtCheckBox); q->setTabOrder(autoSelectExtCheckBox, locationEdit); q->setTabOrder(locationEdit, filterWidget); q->setTabOrder(filterWidget, okButton); q->setTabOrder(okButton, cancelButton); q->setTabOrder(cancelButton, urlNavigator); q->setTabOrder(urlNavigator, ops); } void KFileWidgetPrivate::_k_slotFilterChanged() { // qDebug(); filterDelayTimer.stop(); QString filter = filterWidget->currentFilter(); ops->clearFilter(); if (filter.contains(QLatin1Char('/'))) { QStringList types = filter.split(QLatin1Char(' '), QString::SkipEmptyParts); types.prepend(QStringLiteral("inode/directory")); ops->setMimeFilter(types); } else if (filter.contains(QLatin1Char('*')) || filter.contains(QLatin1Char('?')) || filter.contains(QLatin1Char('['))) { ops->setNameFilter(filter); } else { ops->setNameFilter(QLatin1Char('*') + filter.replace(QLatin1Char(' '), QLatin1Char('*')) + QLatin1Char('*')); } updateAutoSelectExtension(); ops->updateDir(); emit q->filterChanged(filter); } void KFileWidget::setUrl(const QUrl &url, bool clearforward) { // qDebug(); d->ops->setUrl(url, clearforward); } // Protected void KFileWidgetPrivate::_k_urlEntered(const QUrl &url) { // qDebug(); QString filename = locationEditCurrentText(); KUrlComboBox *pathCombo = urlNavigator->editor(); if (pathCombo->count() != 0) { // little hack pathCombo->setUrl(url); } bool blocked = locationEdit->blockSignals(true); if (keepLocation) { QUrl currentUrl = urlFromString(filename); locationEdit->changeUrl(0, QIcon::fromTheme(KIO::iconNameForUrl(currentUrl)), currentUrl); locationEdit->lineEdit()->setModified(true); } locationEdit->blockSignals(blocked); urlNavigator->setLocationUrl(url); // is trigged in ctor before completion object is set KUrlCompletion *completion = dynamic_cast(locationEdit->completionObject()); if (completion) { completion->setDir(url); } if (placesView) { placesView->setUrl(url); } } void KFileWidgetPrivate::_k_locationAccepted(const QString &url) { Q_UNUSED(url); // qDebug(); q->slotOk(); } void KFileWidgetPrivate::_k_enterUrl(const QUrl &url) { // qDebug(); // append '/' if needed: url combo does not add it // tokenize() expects it because it uses QUrl::adjusted(QUrl::RemoveFilename) QUrl u(url); if (!u.path().isEmpty() && !u.path().endsWith(QLatin1Char('/'))) { u.setPath(u.path() + QLatin1Char('/')); } q->setUrl(u); // We need to check window()->focusWidget() instead of locationEdit->hasFocus // because when the window is showing up locationEdit // may still not have focus but it'll be the one that will have focus when the window // gets it and we don't want to steal its focus either if (q->window()->focusWidget() != locationEdit) { ops->setFocus(); } } void KFileWidgetPrivate::_k_enterUrl(const QString &url) { // qDebug(); _k_enterUrl(urlFromString(KUrlCompletion::replacedPath(url, true, true))); } bool KFileWidgetPrivate::toOverwrite(const QUrl &url) { // qDebug(); KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, q); bool res = statJob->exec(); if (res) { int ret = KMessageBox::warningContinueCancel(q, i18n("The file \"%1\" already exists. Do you wish to overwrite it?", url.fileName()), i18n("Overwrite File?"), KStandardGuiItem::overwrite(), KStandardGuiItem::cancel(), QString(), KMessageBox::Notify | KMessageBox::Dangerous); if (ret != KMessageBox::Continue) { return false; } return true; } return true; } #ifndef KIOFILEWIDGETS_NO_DEPRECATED void KFileWidget::setSelection(const QString &url) { // qDebug() << "setSelection " << url; if (url.isEmpty()) { return; } QUrl u = d->getCompleteUrl(url); if (!u.isValid()) { // if it still is qWarning() << url << " is not a correct argument for setSelection!"; return; } setSelectedUrl(urlFromString(url)); } #endif void KFileWidget::setSelectedUrl(const QUrl &url) { // Honor protocols that do not support directory listing if (!url.isRelative() && !KProtocolManager::supportsListing(url)) { return; } d->setLocationText(url); } void KFileWidgetPrivate::_k_slotLoadingFinished() { const QString currentText = locationEdit->currentText(); if (currentText.isEmpty()) { return; } ops->blockSignals(true); QUrl u(ops->url()); if (currentText.startsWith(QLatin1Char('/'))) u.setPath(currentText); else u.setPath(concatPaths(ops->url().path(), currentText)); ops->setCurrentItem(u); ops->blockSignals(false); } void KFileWidgetPrivate::_k_fileCompletion(const QString &match) { // qDebug(); if (match.isEmpty() || locationEdit->currentText().contains(QLatin1Char('"'))) { return; } const QUrl url = urlFromString(match); const QPixmap pix = KIconLoader::global()->loadMimeTypeIcon(KIO::iconNameForUrl(url), KIconLoader::Small); setDummyHistoryEntry(locationEdit->currentText(), pix, !locationEdit->currentText().isEmpty()); } void KFileWidgetPrivate::_k_slotLocationChanged(const QString &text) { // qDebug(); locationEdit->lineEdit()->setModified(true); if (text.isEmpty() && ops->view()) { ops->view()->clearSelection(); } if (text.isEmpty()) { removeDummyHistoryEntry(); } else { setDummyHistoryEntry(text); } if (!locationEdit->lineEdit()->text().isEmpty()) { const QList urlList(tokenize(text)); ops->setCurrentItems(urlList); } updateFilter(); } QUrl KFileWidget::selectedUrl() const { // qDebug(); if (d->inAccept) { return d->url; } else { return QUrl(); } } QList KFileWidget::selectedUrls() const { // qDebug(); QList list; if (d->inAccept) { if (d->ops->mode() & KFile::Files) { list = d->parseSelectedUrls(); } else { list.append(d->url); } } return list; } QList &KFileWidgetPrivate::parseSelectedUrls() { // qDebug(); if (filenames.isEmpty()) { return urlList; } urlList.clear(); if (filenames.contains(QLatin1Char('/'))) { // assume _one_ absolute filename QUrl u; if (containsProtocolSection(filenames)) { u = QUrl(filenames); } else { u.setPath(filenames); } if (u.isValid()) { urlList.append(u); } else KMessageBox::error(q, i18n("The chosen filenames do not\n" "appear to be valid."), i18n("Invalid Filenames")); } else { urlList = tokenize(filenames); } filenames.clear(); // indicate that we parsed that one return urlList; } // FIXME: current implementation drawback: a filename can't contain quotes QList KFileWidgetPrivate::tokenize(const QString &line) const { // qDebug(); QList urls; QUrl u(ops->url()); if (!u.path().endsWith(QLatin1Char('/'))) { u.setPath(u.path() + QLatin1Char('/')); } QString name; const int count = line.count(QLatin1Char('"')); if (count == 0) { // no " " -> assume one single file if (!QDir::isAbsolutePath(line)) { u = u.adjusted(QUrl::RemoveFilename); u.setPath(u.path() + line); if (u.isValid()) { urls.append(u); } } else { urls << QUrl::fromLocalFile(line); } return urls; } int start = 0; int index1 = -1, index2 = -1; while (true) { index1 = line.indexOf(QLatin1Char('"'), start); index2 = line.indexOf(QLatin1Char('"'), index1 + 1); if (index1 < 0 || index2 < 0) { break; } // get everything between the " " name = line.mid(index1 + 1, index2 - index1 - 1); // since we use setPath we need to do this under a temporary url QUrl _u(u); QUrl currUrl(name); if (!QDir::isAbsolutePath(currUrl.url())) { _u = _u.adjusted(QUrl::RemoveFilename); _u.setPath(_u.path() + name); } else { // we allow to insert various absolute paths like: // "/home/foo/bar.txt" "/boot/grub/menu.lst" _u = currUrl; } if (_u.isValid()) { urls.append(_u); } start = index2 + 1; } return urls; } QString KFileWidget::selectedFile() const { // qDebug(); if (d->inAccept) { const QUrl url = d->mostLocalUrl(d->url); if (url.isLocalFile()) { return url.toLocalFile(); } else { KMessageBox::sorry(const_cast(this), i18n("You can only select local files."), i18n("Remote Files Not Accepted")); } } return QString(); } QStringList KFileWidget::selectedFiles() const { // qDebug(); QStringList list; if (d->inAccept) { if (d->ops->mode() & KFile::Files) { const QList urls = d->parseSelectedUrls(); QList::const_iterator it = urls.begin(); while (it != urls.end()) { QUrl url = d->mostLocalUrl(*it); if (url.isLocalFile()) { list.append(url.toLocalFile()); } ++it; } } else { // single-selection mode if (d->url.isLocalFile()) { list.append(d->url.toLocalFile()); } } } return list; } QUrl KFileWidget::baseUrl() const { return d->ops->url(); } void KFileWidget::resizeEvent(QResizeEvent *event) { QWidget::resizeEvent(event); if (d->placesDock) { // we don't want our places dock actually changing size when we resize // and qt doesn't make it easy to enforce such a thing with QSplitter d->setPlacesViewSplitterSizes(); } } void KFileWidget::showEvent(QShowEvent *event) { if (!d->hasView) { // delayed view-creation Q_ASSERT(d); Q_ASSERT(d->ops); d->ops->setView(KFile::Default); d->ops->view()->setSizePolicy(QSizePolicy(QSizePolicy::Maximum, QSizePolicy::Maximum)); d->hasView = true; connect(d->ops->view(), SIGNAL(doubleClicked(QModelIndex)), this, SLOT(_k_slotViewDoubleClicked(QModelIndex))); } d->ops->clearHistory(); QWidget::showEvent(event); } bool KFileWidget::eventFilter(QObject *watched, QEvent *event) { const bool res = QWidget::eventFilter(watched, event); QKeyEvent *keyEvent = dynamic_cast(event); if (watched == d->iconSizeSlider && keyEvent) { if (keyEvent->key() == Qt::Key_Left || keyEvent->key() == Qt::Key_Up || keyEvent->key() == Qt::Key_Right || keyEvent->key() == Qt::Key_Down) { d->_k_slotIconSizeSliderMoved(d->iconSizeSlider->value()); } } else if (watched == d->locationEdit && event->type() == QEvent::KeyPress) { if (keyEvent->modifiers() & Qt::AltModifier) { switch (keyEvent->key()) { case Qt::Key_Up: d->ops->actionCollection()->action(QStringLiteral("up"))->trigger(); break; case Qt::Key_Left: d->ops->actionCollection()->action(QStringLiteral("back"))->trigger(); break; case Qt::Key_Right: d->ops->actionCollection()->action(QStringLiteral("forward"))->trigger(); break; default: break; } } } return res; } void KFileWidget::setMode(KFile::Modes m) { // qDebug(); d->ops->setMode(m); if (d->ops->dirOnlyMode()) { d->filterWidget->setDefaultFilter(i18n("*|All Folders")); } else { d->filterWidget->setDefaultFilter(i18n("*|All Files")); } d->updateAutoSelectExtension(); } KFile::Modes KFileWidget::mode() const { return d->ops->mode(); } void KFileWidgetPrivate::readViewConfig() { ops->setViewConfig(configGroup); ops->readConfig(configGroup); KUrlComboBox *combo = urlNavigator->editor(); autoDirectoryFollowing = configGroup.readEntry(AutoDirectoryFollowing, DefaultDirectoryFollowing); KCompletion::CompletionMode cm = (KCompletion::CompletionMode) configGroup.readEntry(PathComboCompletionMode, static_cast(KCompletion::CompletionPopup)); if (cm != KCompletion::CompletionPopup) { combo->setCompletionMode(cm); } cm = (KCompletion::CompletionMode) configGroup.readEntry(LocationComboCompletionMode, static_cast(KCompletion::CompletionPopup)); if (cm != KCompletion::CompletionPopup) { locationEdit->setCompletionMode(cm); } // show or don't show the speedbar _k_toggleSpeedbar(configGroup.readEntry(ShowSpeedbar, true)); // show or don't show the bookmarks _k_toggleBookmarks(configGroup.readEntry(ShowBookmarks, false)); // does the user want Automatically Select Extension? autoSelectExtChecked = configGroup.readEntry(AutoSelectExtChecked, DefaultAutoSelectExtChecked); updateAutoSelectExtension(); // should the URL navigator use the breadcrumb navigation? urlNavigator->setUrlEditable(!configGroup.readEntry(BreadcrumbNavigation, true)); // should the URL navigator show the full path? urlNavigator->setShowFullPath(configGroup.readEntry(ShowFullPath, false)); int w1 = q->minimumSize().width(); int w2 = toolbar->sizeHint().width(); if (w1 < w2) { q->setMinimumWidth(w2); } } void KFileWidgetPrivate::writeViewConfig() { // these settings are global settings; ALL instances of the file dialog // should reflect them. // There is no way to tell KFileOperator::writeConfig() to write to // kdeglobals so we write settings to a temporary config group then copy // them all to kdeglobals KConfig tmp(QString(), KConfig::SimpleConfig); KConfigGroup tmpGroup(&tmp, ConfigGroup); KUrlComboBox *pathCombo = urlNavigator->editor(); //saveDialogSize( tmpGroup, KConfigGroup::Persistent | KConfigGroup::Global ); tmpGroup.writeEntry(PathComboCompletionMode, static_cast(pathCombo->completionMode())); tmpGroup.writeEntry(LocationComboCompletionMode, static_cast(locationEdit->completionMode())); const bool showSpeedbar = placesDock && !placesDock->isHidden(); tmpGroup.writeEntry(ShowSpeedbar, showSpeedbar); if (placesViewWidth > 0) { tmpGroup.writeEntry(SpeedbarWidth, placesViewWidth); } tmpGroup.writeEntry(ShowBookmarks, bookmarkHandler != nullptr); tmpGroup.writeEntry(AutoSelectExtChecked, autoSelectExtChecked); tmpGroup.writeEntry(BreadcrumbNavigation, !urlNavigator->isUrlEditable()); tmpGroup.writeEntry(ShowFullPath, urlNavigator->showFullPath()); ops->writeConfig(tmpGroup); // Copy saved settings to kdeglobals tmpGroup.copyTo(&configGroup, KConfigGroup::Persistent | KConfigGroup::Global); } void KFileWidgetPrivate::readRecentFiles() { // qDebug(); QObject::disconnect(locationEdit, SIGNAL(editTextChanged(QString)), q, SLOT(_k_slotLocationChanged(QString))); locationEdit->setMaxItems(configGroup.readEntry(RecentFilesNumber, DefaultRecentURLsNumber)); locationEdit->setUrls(configGroup.readPathEntry(RecentFiles, QStringList()), KUrlComboBox::RemoveBottom); locationEdit->setCurrentIndex(-1); QObject::connect(locationEdit, SIGNAL(editTextChanged(QString)), q, SLOT(_k_slotLocationChanged(QString))); KUrlComboBox *combo = urlNavigator->editor(); combo->setUrls(configGroup.readPathEntry(RecentURLs, QStringList()), KUrlComboBox::RemoveTop); combo->setMaxItems(configGroup.readEntry(RecentURLsNumber, DefaultRecentURLsNumber)); combo->setUrl(ops->url()); // since we delayed this moment, initialize the directory of the completion object to // our current directory (that was very probably set on the constructor) KUrlCompletion *completion = dynamic_cast(locationEdit->completionObject()); if (completion) { completion->setDir(ops->url()); } } void KFileWidgetPrivate::saveRecentFiles() { // qDebug(); configGroup.writePathEntry(RecentFiles, locationEdit->urls()); KUrlComboBox *pathCombo = urlNavigator->editor(); configGroup.writePathEntry(RecentURLs, pathCombo->urls()); } QPushButton *KFileWidget::okButton() const { return d->okButton; } QPushButton *KFileWidget::cancelButton() const { return d->cancelButton; } // Called by KFileDialog void KFileWidget::slotCancel() { d->writeViewConfig(); d->ops->close(); } void KFileWidget::setKeepLocation(bool keep) { d->keepLocation = keep; } bool KFileWidget::keepsLocation() const { return d->keepLocation; } void KFileWidget::setOperationMode(OperationMode mode) { // qDebug(); d->operationMode = mode; d->keepLocation = (mode == Saving); d->filterWidget->setEditable(!d->hasDefaultFilter || mode != Saving); if (mode == Opening) { // don't use KStandardGuiItem::open() here which has trailing ellipsis! d->okButton->setText(i18n("&Open")); d->okButton->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); // hide the new folder actions...usability team says they shouldn't be in open file dialog actionCollection()->removeAction(actionCollection()->action(QStringLiteral("mkdir"))); } else if (mode == Saving) { KGuiItem::assign(d->okButton, KStandardGuiItem::save()); d->setNonExtSelection(); } else { KGuiItem::assign(d->okButton, KStandardGuiItem::ok()); } d->updateLocationWhatsThis(); d->updateAutoSelectExtension(); if (d->ops) { d->ops->setIsSaving(mode == Saving); } } KFileWidget::OperationMode KFileWidget::operationMode() const { return d->operationMode; } void KFileWidgetPrivate::_k_slotAutoSelectExtClicked() { // qDebug() << "slotAutoSelectExtClicked(): " // << autoSelectExtCheckBox->isChecked() << endl; // whether the _user_ wants it on/off autoSelectExtChecked = autoSelectExtCheckBox->isChecked(); // update the current filename's extension updateLocationEditExtension(extension /* extension hasn't changed */); } void KFileWidgetPrivate::_k_placesViewSplitterMoved(int pos, int index) { // qDebug(); // we need to record the size of the splitter when the splitter changes size // so we can keep the places box the right size! if (placesDock && index == 1) { placesViewWidth = pos; // qDebug() << "setting lafBox minwidth to" << placesViewWidth; setLafBoxColumnWidth(); } } void KFileWidgetPrivate::_k_activateUrlNavigator() { // qDebug(); urlNavigator->setUrlEditable(!urlNavigator->isUrlEditable()); if (urlNavigator->isUrlEditable()) { urlNavigator->setFocus(); urlNavigator->editor()->lineEdit()->selectAll(); } } void KFileWidgetPrivate::_k_zoomOutIconsSize() { const int currValue = ops->iconsZoom(); const int futValue = qMax(0, currValue - 10); iconSizeSlider->setValue(futValue); _k_slotIconSizeSliderMoved(futValue); } void KFileWidgetPrivate::_k_zoomInIconsSize() { const int currValue = ops->iconsZoom(); const int futValue = qMin(100, currValue + 10); iconSizeSlider->setValue(futValue); _k_slotIconSizeSliderMoved(futValue); } void KFileWidgetPrivate::_k_slotIconSizeChanged(int _value) { int maxSize = KIconLoader::SizeEnormous - KIconLoader::SizeSmall; int value = (maxSize * _value / 100) + KIconLoader::SizeSmall; switch (value) { case KIconLoader::SizeSmall: case KIconLoader::SizeSmallMedium: case KIconLoader::SizeMedium: case KIconLoader::SizeLarge: case KIconLoader::SizeHuge: case KIconLoader::SizeEnormous: iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels (standard size)", value)); break; default: iconSizeSlider->setToolTip(i18n("Icon size: %1 pixels", value)); break; } } void KFileWidgetPrivate::_k_slotIconSizeSliderMoved(int _value) { // Force this to be called in case this slot is called first on the // slider move. _k_slotIconSizeChanged(_value); QPoint global(iconSizeSlider->rect().topLeft()); global.ry() += iconSizeSlider->height() / 2; QHelpEvent toolTipEvent(QEvent::ToolTip, QPoint(0, 0), iconSizeSlider->mapToGlobal(global)); QApplication::sendEvent(iconSizeSlider, &toolTipEvent); } void KFileWidgetPrivate::_k_slotViewDoubleClicked(const QModelIndex &index) { // double clicking to save should only work on files if (operationMode == KFileWidget::Saving && index.isValid() && ops->selectedItems().constFirst().isFile()) { q->slotOk(); } } static QString getExtensionFromPatternList(const QStringList &patternList) { // qDebug(); QString ret; // qDebug() << "\tgetExtension " << patternList; QStringList::ConstIterator patternListEnd = patternList.end(); for (QStringList::ConstIterator it = patternList.begin(); it != patternListEnd; ++it) { // qDebug() << "\t\ttry: \'" << (*it) << "\'"; // is this pattern like "*.BMP" rather than useless things like: // // README // *. // *.* // *.JP*G // *.JP? if ((*it).startsWith(QLatin1String("*.")) && (*it).length() > 2 && (*it).indexOf(QLatin1Char('*'), 2) < 0 && (*it).indexOf(QLatin1Char('?'), 2) < 0) { ret = (*it).mid(1); break; } } return ret; } static QString stripUndisplayable(const QString &string) { QString ret = string; ret.remove(QLatin1Char(':')); ret = KLocalizedString::removeAcceleratorMarker(ret); return ret; } //QString KFileWidget::currentFilterExtension() //{ // return d->extension; //} void KFileWidgetPrivate::updateAutoSelectExtension() { if (!autoSelectExtCheckBox) { return; } QMimeDatabase db; // // Figure out an extension for the Automatically Select Extension thing // (some Windows users apparently don't know what to do when confronted // with a text file called "COPYING" but do know what to do with // COPYING.txt ...) // // qDebug() << "Figure out an extension: "; QString lastExtension = extension; extension.clear(); // Automatically Select Extension is only valid if the user is _saving_ a _file_ if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File)) { // // Get an extension from the filter // QString filter = filterWidget->currentFilter(); if (!filter.isEmpty()) { // if the currently selected filename already has an extension which // is also included in the currently allowed extensions, keep it // otherwise use the default extension QString currentExtension = db.suffixForFileName(locationEditCurrentText()); if (currentExtension.isEmpty()) { currentExtension = locationEditCurrentText().section(QLatin1Char('.'), -1, -1); } // qDebug() << "filter:" << filter << "locationEdit:" << locationEditCurrentText() << "currentExtension:" << currentExtension; QString defaultExtension; QStringList extensionList; // e.g. "*.cpp" if (filter.indexOf(QLatin1Char('/')) < 0) { extensionList = filter.split(QLatin1Char(' '), QString::SkipEmptyParts); defaultExtension = getExtensionFromPatternList(extensionList); } // e.g. "text/html" else { QMimeType mime = db.mimeTypeForName(filter); if (mime.isValid()) { extensionList = mime.globPatterns(); defaultExtension = mime.preferredSuffix(); if (!defaultExtension.isEmpty()) { defaultExtension.prepend(QLatin1Char('.')); } } } if ((!currentExtension.isEmpty() && extensionList.contains(QLatin1String("*.") + currentExtension)) || filter == QStringLiteral("application/octet-stream")) { extension = QLatin1Char('.') + currentExtension; } else { extension = defaultExtension; } // qDebug() << "List:" << extensionList << "auto-selected extension:" << extension; } // // GUI: checkbox // QString whatsThisExtension; if (!extension.isEmpty()) { // remember: sync any changes to the string with below autoSelectExtCheckBox->setText(i18n("Automatically select filename e&xtension (%1)", extension)); whatsThisExtension = i18n("the extension %1", extension); autoSelectExtCheckBox->setEnabled(true); autoSelectExtCheckBox->setChecked(autoSelectExtChecked); } else { // remember: sync any changes to the string with above autoSelectExtCheckBox->setText(i18n("Automatically select filename e&xtension")); whatsThisExtension = i18n("a suitable extension"); autoSelectExtCheckBox->setChecked(false); autoSelectExtCheckBox->setEnabled(false); } const QString locationLabelText = stripUndisplayable(locationLabel->text()); autoSelectExtCheckBox->setWhatsThis(QLatin1String("") + i18n( "This option enables some convenient features for " "saving files with extensions:
" "
    " "
  1. Any extension specified in the %1 text " "area will be updated if you change the file type " "to save in.
    " "
  2. " "
  3. If no extension is specified in the %2 " "text area when you click " "Save, %3 will be added to the end of the " "filename (if the filename does not already exist). " "This extension is based on the file type that you " "have chosen to save in.
    " "
    " "If you do not want KDE to supply an extension for the " "filename, you can either turn this option off or you " "can suppress it by adding a period (.) to the end of " "the filename (the period will be automatically " "removed)." "
  4. " "
" "If unsure, keep this option enabled as it makes your " "files more manageable." , locationLabelText, locationLabelText, whatsThisExtension) + QLatin1String("
") ); autoSelectExtCheckBox->show(); // update the current filename's extension updateLocationEditExtension(lastExtension); } // Automatically Select Extension not valid else { autoSelectExtCheckBox->setChecked(false); autoSelectExtCheckBox->hide(); } } // Updates the extension of the filename specified in d->locationEdit if the // Automatically Select Extension feature is enabled. -// (this prevents you from accidently saving "file.kwd" as RTF, for example) +// (this prevents you from accidentally saving "file.kwd" as RTF, for example) void KFileWidgetPrivate::updateLocationEditExtension(const QString &lastExtension) { if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty()) { return; } QString urlStr = locationEditCurrentText(); if (urlStr.isEmpty()) { return; } QUrl url = getCompleteUrl(urlStr); // qDebug() << "updateLocationEditExtension (" << url << ")"; const int fileNameOffset = urlStr.lastIndexOf(QLatin1Char('/')) + 1; QString fileName = urlStr.mid(fileNameOffset); const int dot = fileName.lastIndexOf(QLatin1Char('.')); const int len = fileName.length(); if (dot > 0 && // has an extension already and it's not a hidden file // like ".hidden" (but we do accept ".hidden.ext") dot != len - 1 // and not deliberately suppressing extension ) { // exists? KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, q); bool result = statJob->exec(); if (result) { // qDebug() << "\tfile exists"; if (statJob->statResult().isDir()) { // qDebug() << "\tisDir - won't alter extension"; return; } // --- fall through --- } // // try to get rid of the current extension // // catch "double extensions" like ".tar.gz" if (lastExtension.length() && fileName.endsWith(lastExtension)) { fileName.chop(lastExtension.length()); } else if (extension.length() && fileName.endsWith(extension)) { fileName.chop(extension.length()); } // can only handle "single extensions" else { fileName.truncate(dot); } // add extension const QString newText = urlStr.leftRef(fileNameOffset) + fileName + extension; if (newText != locationEditCurrentText()) { locationEdit->setItemText(locationEdit->currentIndex(), newText); locationEdit->lineEdit()->setModified(true); } } } // Updates the filter if the extension of the filename specified in d->locationEdit is changed // (this prevents you from accidently saving "file.kwd" as RTF, for example) void KFileWidgetPrivate::updateFilter() { // qDebug(); if ((operationMode == KFileWidget::Saving) && (ops->mode() & KFile::File)) { QString urlStr = locationEditCurrentText(); if (urlStr.isEmpty()) { return; } if (filterWidget->isMimeFilter()) { QMimeDatabase db; QMimeType mime = db.mimeTypeForFile(urlStr, QMimeDatabase::MatchExtension); if (mime.isValid() && !mime.isDefault()) { if (filterWidget->currentFilter() != mime.name() && filterWidget->filters().indexOf(mime.name()) != -1) { filterWidget->setCurrentFilter(mime.name()); } } } else { QString filename = urlStr.mid(urlStr.lastIndexOf(QLatin1Char('/')) + 1); // only filename foreach (const QString &filter, filterWidget->filters()) { QStringList patterns = filter.left(filter.indexOf(QLatin1Char('|'))).split(QLatin1Char(' '), QString::SkipEmptyParts); // '*.foo *.bar|Foo type' -> '*.foo', '*.bar' foreach (const QString &p, patterns) { QRegExp rx(p); rx.setPatternSyntax(QRegExp::Wildcard); if (rx.exactMatch(filename)) { if (p != QLatin1String("*")) { // never match the catch-all filter filterWidget->setCurrentFilter(filter); } return; // do not repeat, could match a later filter } } } } } } // applies only to a file that doesn't already exist void KFileWidgetPrivate::appendExtension(QUrl &url) { // qDebug(); if (!autoSelectExtCheckBox->isChecked() || extension.isEmpty()) { return; } QString fileName = url.fileName(); if (fileName.isEmpty()) { return; } // qDebug() << "appendExtension(" << url << ")"; const int len = fileName.length(); const int dot = fileName.lastIndexOf(QLatin1Char('.')); const bool suppressExtension = (dot == len - 1); const bool unspecifiedExtension = (dot <= 0); // don't KIO::Stat if unnecessary if (!(suppressExtension || unspecifiedExtension)) { return; } // exists? KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, q); bool res = statJob->exec(); if (res) { // qDebug() << "\tfile exists - won't append extension"; return; } // suppress automatically append extension? if (suppressExtension) { // // Strip trailing dot // This allows lazy people to have autoSelectExtCheckBox->isChecked // but don't want a file extension to be appended // e.g. "README." will make a file called "README" // // If you really want a name like "README.", then type "README.." // and the trailing dot will be removed (or just stop being lazy and // turn off this feature so that you can type "README.") // // qDebug() << "\tstrip trailing dot"; QString path = url.path(); path.chop(1); url.setPath(path); } // evilmatically append extension :) if the user hasn't specified one else if (unspecifiedExtension) { // qDebug() << "\tappending extension \'" << extension << "\'..."; url = url.adjusted(QUrl::RemoveFilename); // keeps trailing slash url.setPath(url.path() + fileName + extension); // qDebug() << "\tsaving as \'" << url << "\'"; } } // adds the selected files/urls to 'recent documents' void KFileWidgetPrivate::addToRecentDocuments() { int m = ops->mode(); int atmost = KRecentDocument::maximumItems(); //don't add more than we need. KRecentDocument::add() is pretty slow if (m & KFile::LocalOnly) { const QStringList files = q->selectedFiles(); QStringList::ConstIterator it = files.begin(); for (; it != files.end() && atmost > 0; ++it) { KRecentDocument::add(QUrl::fromLocalFile(*it)); atmost--; } } else { // urls const QList urls = q->selectedUrls(); QList::ConstIterator it = urls.begin(); for (; it != urls.end() && atmost > 0; ++it) { if ((*it).isValid()) { KRecentDocument::add(*it); atmost--; } } } } KUrlComboBox *KFileWidget::locationEdit() const { return d->locationEdit; } KFileFilterCombo *KFileWidget::filterWidget() const { return d->filterWidget; } KActionCollection *KFileWidget::actionCollection() const { return d->ops->actionCollection(); } void KFileWidgetPrivate::_k_toggleSpeedbar(bool show) { if (show) { initSpeedbar(); placesDock->show(); setLafBoxColumnWidth(); // check to see if they have a home item defined, if not show the home button QUrl homeURL; homeURL.setPath(QDir::homePath()); KFilePlacesModel *model = static_cast(placesView->model()); for (int rowIndex = 0; rowIndex < model->rowCount(); rowIndex++) { QModelIndex index = model->index(rowIndex, 0); QUrl url = model->url(index); if (homeURL.matches(url, QUrl::StripTrailingSlash)) { toolbar->removeAction(ops->actionCollection()->action(QStringLiteral("home"))); break; } } } else { if (q->sender() == placesDock && placesDock && placesDock->isVisibleTo(q)) { // we didn't *really* go away! the dialog was simply hidden or // we changed virtual desktops or ... return; } if (placesDock) { placesDock->hide(); } QAction *homeAction = ops->actionCollection()->action(QStringLiteral("home")); QAction *reloadAction = ops->actionCollection()->action(QStringLiteral("reload")); if (!toolbar->actions().contains(homeAction)) { toolbar->insertAction(reloadAction, homeAction); } // reset the lafbox to not follow the width of the splitter lafBox->setColumnMinimumWidth(0, 0); } static_cast(q->actionCollection()->action(QStringLiteral("toggleSpeedbar")))->setChecked(show); // if we don't show the places panel, at least show the places menu urlNavigator->setPlacesSelectorVisible(!show); } void KFileWidgetPrivate::_k_toggleBookmarks(bool show) { if (show) { if (bookmarkHandler) { return; } bookmarkHandler = new KFileBookmarkHandler(q); q->connect(bookmarkHandler, SIGNAL(openUrl(QString)), SLOT(_k_enterUrl(QString))); bookmarkButton = new KActionMenu(QIcon::fromTheme(QStringLiteral("bookmarks")), i18n("Bookmarks"), q); bookmarkButton->setDelayed(false); q->actionCollection()->addAction(QStringLiteral("bookmark"), bookmarkButton); bookmarkButton->setMenu(bookmarkHandler->menu()); bookmarkButton->setWhatsThis(i18n("This button allows you to bookmark specific locations. " "Click on this button to open the bookmark menu where you may add, " "edit or select a bookmark.

" "These bookmarks are specific to the file dialog, but otherwise operate " "like bookmarks elsewhere in KDE.
")); toolbar->addAction(bookmarkButton); } else if (bookmarkHandler) { delete bookmarkHandler; bookmarkHandler = nullptr; delete bookmarkButton; bookmarkButton = nullptr; } static_cast(q->actionCollection()->action(QStringLiteral("toggleBookmarks")))->setChecked(show); } // static, overloaded QUrl KFileWidget::getStartUrl(const QUrl &startDir, QString &recentDirClass) { QString fileName; // result discarded return getStartUrl(startDir, recentDirClass, fileName); } // static, overloaded QUrl KFileWidget::getStartUrl(const QUrl &startDir, QString &recentDirClass, QString &fileName) { recentDirClass.clear(); fileName.clear(); QUrl ret; bool useDefaultStartDir = startDir.isEmpty(); if (!useDefaultStartDir) { if (startDir.scheme() == QLatin1String("kfiledialog")) { // The startDir URL with this protocol may be in the format: // directory() fileName() // 1. kfiledialog:///keyword "/" keyword // 2. kfiledialog:///keyword?global "/" keyword // 3. kfiledialog:///keyword/ "/" keyword // 4. kfiledialog:///keyword/?global "/" keyword // 5. kfiledialog:///keyword/filename /keyword filename // 6. kfiledialog:///keyword/filename?global /keyword filename QString keyword; QString urlDir = startDir.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path(); QString urlFile = startDir.fileName(); if (urlDir == QLatin1String("/")) { // '1'..'4' above keyword = urlFile; fileName.clear(); } else { // '5' or '6' above keyword = urlDir.mid(1); fileName = urlFile; } if (startDir.query() == QLatin1String("global")) { recentDirClass = QStringLiteral("::%1").arg(keyword); } else { recentDirClass = QStringLiteral(":%1").arg(keyword); } ret = QUrl::fromLocalFile(KRecentDirs::dir(recentDirClass)); } else { // not special "kfiledialog" URL // "foo.png" only gives us a file name, the default start dir will be used. // "file:foo.png" (from KHTML/webkit, due to fromPath()) means the same // (and is the reason why we don't just use QUrl::isRelative()). // In all other cases (startDir contains a directory path, or has no // fileName for us anyway, such as smb://), startDir is indeed a dir url. if (!startDir.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path().isEmpty() || startDir.fileName().isEmpty()) { // can use start directory ret = startDir; // will be checked by stat later // If we won't be able to list it (e.g. http), then use default if (!KProtocolManager::supportsListing(ret)) { useDefaultStartDir = true; fileName = startDir.fileName(); } } else { // file name only fileName = startDir.fileName(); useDefaultStartDir = true; } } } if (useDefaultStartDir) { if (lastDirectory()->isEmpty()) { *lastDirectory() = QUrl::fromLocalFile(QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation)); const QUrl home(QUrl::fromLocalFile(QDir::homePath())); // if there is no docpath set (== home dir), we prefer the current // directory over it. We also prefer the homedir when our CWD is // different from our homedirectory or when the document dir // does not exist if (lastDirectory()->adjusted(QUrl::StripTrailingSlash) == home.adjusted(QUrl::StripTrailingSlash) || QDir::currentPath() != QDir::homePath() || !QDir(lastDirectory()->toLocalFile()).exists()) { *lastDirectory() = QUrl::fromLocalFile(QDir::currentPath()); } } ret = *lastDirectory(); } // qDebug() << "for" << startDir << "->" << ret << "recentDirClass" << recentDirClass << "fileName" << fileName; return ret; } void KFileWidget::setStartDir(const QUrl &directory) { if (directory.isValid()) { *lastDirectory() = directory; } } void KFileWidgetPrivate::setNonExtSelection() { // Enhanced rename: Don't highlight the file extension. QString filename = locationEditCurrentText(); QMimeDatabase db; QString extension = db.suffixForFileName(filename); if (!extension.isEmpty()) { locationEdit->lineEdit()->setSelection(0, filename.length() - extension.length() - 1); } else { int lastDot = filename.lastIndexOf(QLatin1Char('.')); if (lastDot > 0) { locationEdit->lineEdit()->setSelection(0, lastDot); } else { locationEdit->lineEdit()->selectAll(); } } } KToolBar *KFileWidget::toolBar() const { return d->toolbar; } void KFileWidget::setCustomWidget(QWidget *widget) { delete d->bottomCustomWidget; d->bottomCustomWidget = widget; // add it to the dialog, below the filter list box. // Change the parent so that this widget is a child of the main widget d->bottomCustomWidget->setParent(this); d->vbox->addWidget(d->bottomCustomWidget); //d->vbox->addSpacing(3); // can't do this every time... // FIXME: This should adjust the tab orders so that the custom widget // comes after the Cancel button. The code appears to do this, but the result // somehow screws up the tab order of the file path combo box. Not a major // problem, but ideally the tab order with a custom widget should be // the same as the order without one. setTabOrder(d->cancelButton, d->bottomCustomWidget); setTabOrder(d->bottomCustomWidget, d->urlNavigator); } void KFileWidget::setCustomWidget(const QString &text, QWidget *widget) { delete d->labeledCustomWidget; d->labeledCustomWidget = widget; QLabel *label = new QLabel(text, this); label->setAlignment(Qt::AlignRight); d->lafBox->addWidget(label, 2, 0, Qt::AlignVCenter); d->lafBox->addWidget(widget, 2, 1, Qt::AlignVCenter); } KDirOperator *KFileWidget::dirOperator() { return d->ops; } void KFileWidget::readConfig(KConfigGroup &group) { d->configGroup = group; d->readViewConfig(); d->readRecentFiles(); } QString KFileWidgetPrivate::locationEditCurrentText() const { return QDir::fromNativeSeparators(locationEdit->currentText()); } QUrl KFileWidgetPrivate::mostLocalUrl(const QUrl &url) { if (url.isLocalFile()) { return url; } KIO::StatJob *statJob = KIO::stat(url, KIO::HideProgressInfo); KJobWidgets::setWindow(statJob, q); bool res = statJob->exec(); if (!res) { return url; } const QString path = statJob->statResult().stringValue(KIO::UDSEntry::UDS_LOCAL_PATH); if (!path.isEmpty()) { QUrl newUrl; newUrl.setPath(path); return newUrl; } return url; } void KFileWidgetPrivate::setInlinePreviewShown(bool show) { ops->setInlinePreviewShown(show); } void KFileWidget::setConfirmOverwrite(bool enable) { d->confirmOverwrite = enable; } void KFileWidget::setInlinePreviewShown(bool show) { d->setInlinePreviewShown(show); } QSize KFileWidget::dialogSizeHint() const { int fontSize = fontMetrics().height(); QSize goodSize(48 * fontSize, 30 * fontSize); QSize screenSize = QApplication::desktop()->availableGeometry(this).size(); QSize minSize(screenSize / 2); QSize maxSize(screenSize * qreal(0.9)); return (goodSize.expandedTo(minSize).boundedTo(maxSize)); } void KFileWidget::setViewMode(KFile::FileView mode) { d->ops->setView(mode); d->hasView = true; } void KFileWidget::setSupportedSchemes(const QStringList &schemes) { d->model->setSupportedSchemes(schemes); d->ops->setSupportedSchemes(schemes); d->urlNavigator->setCustomProtocols(schemes); } QStringList KFileWidget::supportedSchemes() const { return d->model->supportedSchemes(); } #include "moc_kfilewidget.cpp" diff --git a/src/filewidgets/kfilewidget.h b/src/filewidgets/kfilewidget.h index bda8871c..3a44dc15 100644 --- a/src/filewidgets/kfilewidget.h +++ b/src/filewidgets/kfilewidget.h @@ -1,618 +1,618 @@ // -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 1997, 1998 Richard Moore 1998 Stephan Kulow 1998 Daniel Grana 2000,2001 Carsten Pfeiffer 2001 Frerich Raabe 2007 David Faure 2008 Rafael Fernández López This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KFILEWIDGET_H #define KFILEWIDGET_H #include "kiofilewidgets_export.h" #include "kfile.h" #include class QUrl; class QPushButton; class KActionCollection; class KToolBar; class KFileWidgetPrivate; class KUrlComboBox; class KFileFilterCombo; class KPreviewWidgetBase; class QMimeType; class KConfigGroup; class KJob; class KFileItem; class KDirOperator; /** * @class KFileWidget kfilewidget.h * * File selector widget. * * This is the contents of the KDE file dialog, without the actual QDialog around it. * It can be embedded directly into applications. */ class KIOFILEWIDGETS_EXPORT KFileWidget : public QWidget { Q_OBJECT public: /** * Constructs a file selector widget. * * @param startDir This can either be: * @li An empty URL (QUrl()) to start in the current working directory, * or the last directory where a file has been selected. * @li The path or URL of a starting directory. * @li An initial file name to select, with the starting directory being * the current working directory or the last directory where a file * has been selected. * @li The path or URL of a file, specifying both the starting directory and * an initially selected file name. * @li A URL of the form @c kfiledialog:///<keyword> to start in the * directory last used by a filedialog in the same application that * specified the same keyword. * @li A URL of the form @c kfiledialog:///<keyword>/<filename> * to start in the directory last used by a filedialog in the same * application that specified the same keyword, and to initially * select the specified filename. * @li A URL of the form @c kfiledialog:///<keyword>?global to start * in the directory last used by a filedialog in any application that * specified the same keyword. * @li A URL of the form @c kfiledialog:///<keyword>/<filename>?global * to start in the directory last used by a filedialog in any * application that specified the same keyword, and to initially * select the specified filename. * * @param parent The parent widget of this widget * */ explicit KFileWidget(const QUrl &startDir, QWidget *parent = nullptr); /** * Destructor */ ~KFileWidget() override; /** * Defines some default behavior of the filedialog. * E.g. in mode @p Opening and @p Saving, the selected files/urls will * be added to the "recent documents" list. The Saving mode also implies * setKeepLocation() being set. * * @p Other means that no default actions are performed. * * @see setOperationMode * @see operationMode */ enum OperationMode { Other = 0, Opening, Saving }; /** * @returns The selected fully qualified filename. */ QUrl selectedUrl() const; /** * @returns The list of selected URLs. */ QList selectedUrls() const; /** * @returns the currently shown directory. */ QUrl baseUrl() const; /** * Returns the full path of the selected file in the local filesystem. * (Local files only) */ QString selectedFile() const; /** * Returns a list of all selected local files. */ QStringList selectedFiles() const; /** * Sets the directory to view. * * @param url URL to show. * @param clearforward Indicates whether the forward queue * should be cleared. */ void setUrl(const QUrl &url, bool clearforward = true); #if !defined(KIOFILEWIDGETS_NO_DEPRECATED) && !defined(DOXYGEN_SHOULD_SKIP_THIS) /** * Sets the file to preselect to @p pathOrUrl * * This method handles absolute paths (on Unix, but probably not correctly on Windows) * and absolute URLs as strings (but for those you should use setSelectedUrl instead). * * This method does not work with relative paths (filenames) * (it would misinterpret a ':' or a '#' in the filename). * * @deprecated since 5.33, use setSelectedUrl instead, after ensuring that * construct the QUrl correctly (e.g. use fromLocalFile for local paths). */ KIOFILEWIDGETS_DEPRECATED void setSelection(const QString &pathOrUrl); #endif /** * Sets the URL to preselect to @p url * * This method handles absolute URLs (remember to use fromLocalFile for local paths). * It also handles relative URLs, which you should construct like this: * QUrl relativeUrl; relativeUrl.setPath(fileName); * * @since 5.33 */ void setSelectedUrl(const QUrl &url); /** * Sets the operational mode of the filedialog to @p Saving, @p Opening * or @p Other. This will set some flags that are specific to loading * or saving files. E.g. setKeepLocation() makes mostly sense for * a save-as dialog. So setOperationMode( KFileWidget::Saving ); sets * setKeepLocation for example. * * The mode @p Saving, together with a default filter set via * setMimeFilter() will make the filter combobox read-only. * * The default mode is @p Opening. * * Call this method right after instantiating KFileWidget. * * @see operationMode * @see KFileWidget::OperationMode */ void setOperationMode(OperationMode); /** * @returns the current operation mode, Opening, Saving or Other. Default * is Other. * * @see operationMode * @see KFileWidget::OperationMode */ OperationMode operationMode() const; /** * Sets whether the filename/url should be kept when changing directories. * This is for example useful when having a predefined filename where * the full path for that file is searched. * * This is implicitly set when operationMode() is KFileWidget::Saving * * getSaveFileName() and getSaveUrl() set this to true by default, so that * you can type in the filename and change the directory without having * to type the name again. */ void setKeepLocation(bool keep); /** * @returns whether the contents of the location edit are kept when * changing directories. */ bool keepsLocation() const; /** * Sets the filter to be used to @p filter. * * You can set more * filters for the user to select separated by '\n'. Every * filter entry is defined through namefilter|text to display. * If no | is found in the expression, just the namefilter is * shown. Examples: * * \code * kfile->setFilter("*.cpp|C++ Source Files\n*.h|Header files"); * kfile->setFilter("*.cpp"); * kfile->setFilter("*.cpp|Sources (*.cpp)"); * kfile->setFilter("*.cpp|" + i18n("Sources (*.cpp)")); * kfile->setFilter("*.cpp *.cc *.C|C++ Source Files\n*.h *.H|Header files"); * \endcode * * Note: The text to display is not parsed in any way. So, if you * want to show the suffix to select by a specific filter, you must * repeat it. * * If the filter contains an unescaped '/', a mimetype-filter is assumed. * If you would like a '/' visible in your filter it can be escaped with * a '\'. You can specify multiple mimetypes like this (separated with * space): * * \code * kfile->setFilter( "image/png text/html text/plain" ); * kfile->setFilter( "*.cue|CUE\\/BIN Files (*.cue)" ); * \endcode * * @see filterChanged * @see setMimeFilter */ void setFilter(const QString &filter); /** * Returns the current filter as entered by the user or one of the * predefined set via setFilter(). * * @see setFilter() * @see filterChanged() */ QString currentFilter() const; /** * Returns the mimetype for the desired output format. * * This is only valid if setFilterMimeType() has been called * previously. * * @see setFilterMimeType() */ QMimeType currentFilterMimeType(); /** * Sets the filter up to specify the output type. * * @param types a list of mimetypes that can be used as output format * @param defaultType the default mimetype to use as output format, if any. * If @p defaultType is set, it will be set as the current item. * Otherwise, a first item showing all the mimetypes will be created. * Typically, @p defaultType should be empty for loading and set for saving. * * Do not use in conjunction with setFilter() */ void setMimeFilter(const QStringList &types, const QString &defaultType = QString()); /** * The mimetype for the desired output format. * * This is only valid if setMimeFilter() has been called * previously. * * @see setMimeFilter() */ QString currentMimeFilter() const; /** * Clears any mime- or namefilter. Does not reload the directory. */ void clearFilter(); /** * Adds a preview widget and enters the preview mode. * * In this mode the dialog is split and the right part contains your * preview widget. * * Ownership is transferred to KFileWidget. You need to create the * preview-widget with "new", i.e. on the heap. * * @param w The widget to be used for the preview. */ void setPreviewWidget(KPreviewWidgetBase *w); /** * Sets the mode of the dialog. * * The mode is defined as (in kfile.h): * \code * enum Mode { * File = 1, * Directory = 2, * Files = 4, * ExistingOnly = 8, * LocalOnly = 16 * }; * \endcode * You can OR the values, e.g. * \code * KFile::Modes mode = KFile::Files | * KFile::ExistingOnly | * KFile::LocalOnly ); * setMode( mode ); * \endcode */ void setMode(KFile::Modes m); /** * Returns the mode of the filedialog. * @see setMode() */ KFile::Modes mode() const; /** * Sets the text to be displayed in front of the selection. * * The default is "Location". * Most useful if you want to make clear what * the location is used for. */ void setLocationLabel(const QString &text); /** * Returns a pointer to the toolbar. * */ KToolBar *toolBar() const; /** * @returns a pointer to the OK-Button in the filedialog. * Note that the button is hidden and unconnected when using KFileWidget alone; * KFileDialog shows it and connects to it. */ QPushButton *okButton() const; /** * @returns a pointer to the Cancel-Button in the filedialog. * Note that the button is hidden and unconnected when using KFileWidget alone; * KFileDialog shows it and connects to it. */ QPushButton *cancelButton() const; /** * @returns the combobox used to type the filename or full location of the file. */ KUrlComboBox *locationEdit() const; /** * @returns the combobox that contains the filters */ KFileFilterCombo *filterWidget() const; /** * @returns a pointer to the action collection, holding all the used * KActions. */ KActionCollection *actionCollection() const; /** * This method implements the logic to determine the user's default directory * to be listed. E.g. the documents directory, home directory or a recently * used directory. * @param startDir A URL specifying the initial directory, or using the * @c kfiledialog:/// syntax to specify a last used * directory. If this URL specifies a file name, it is * ignored. Refer to the KFileWidget::KFileWidget() * documentation for the @c kfiledialog:/// URL syntax. * @param recentDirClass If the @c kfiledialog:/// syntax is used, this * will return the string to be passed to KRecentDirs::dir() and * KRecentDirs::add(). * @return The URL that should be listed by default (e.g. by KFileDialog or * KDirSelectDialog). * @see KFileWidget::KFileWidget() */ static QUrl getStartUrl(const QUrl &startDir, QString &recentDirClass); /** * Similar to getStartUrl(const QUrl& startDir,QString& recentDirClass), * but allows both the recent start directory keyword and a suggested file name * to be returned. * @param startDir A URL specifying the initial directory and/or filename, * or using the @c kfiledialog:/// syntax to specify a * last used location. * Refer to the KFileWidget::KFileWidget() * documentation for the @c kfiledialog:/// URL syntax. * @param recentDirClass If the @c kfiledialog:/// syntax is used, this * will return the string to be passed to KRecentDirs::dir() and * KRecentDirs::add(). * @param fileName The suggested file name, if specified as part of the * @p StartDir URL. * @return The URL that should be listed by default (e.g. by KFileDialog or * KDirSelectDialog). * * @see KFileWidget::KFileWidget() * @since 4.3 */ static QUrl getStartUrl(const QUrl &startDir, QString &recentDirClass, QString &fileName); /** * @internal * Used by KDirSelectDialog to share the dialog's start directory. */ static void setStartDir(const QUrl &directory); /** * Set a custom widget that should be added to the file dialog. * @param widget A widget, or a widget of widgets, for displaying custom * data in the file widget. This can be used, for example, to * display a check box with the caption "Open as read-only". * When creating this widget, you don't need to specify a parent, * since the widget's parent will be set automatically by KFileWidget. */ void setCustomWidget(QWidget *widget); /** * Sets a custom widget that should be added below the location and the filter * editors. * @param text Label of the custom widget, which is displayed below the labels * "Location:" and "Filter:". * @param widget Any kind of widget, but preferable a combo box or a line editor * to be compliant with the location and filter layout. * When creating this widget, you don't need to specify a parent, * since the widget's parent will be set automatically by KFileWidget. */ void setCustomWidget(const QString &text, QWidget *widget); /** * Sets whether the user should be asked for confirmation * when an overwrite might occur. * * @param enable Set this to true to enable checking. * @since 4.2 */ void setConfirmOverwrite(bool enable); /** * Forces the inline previews to be shown or hidden, depending on @p show. * * @param show Whether to show inline previews or not. * @since 4.2 */ void setInlinePreviewShown(bool show); /** * Provides a size hint, useful for dialogs that embed the widget. * * @return a QSize, calculated to be optimal for a dialog. * @since 5.0 */ QSize dialogSizeHint() const; /** * Sets how the view should be displayed. * * @see KFile::FileView * @since 5.0 */ void setViewMode(KFile::FileView mode); /** * Reimplemented */ QSize sizeHint() const override; /** * Set the URL schemes that the file widget should allow navigating to. * * If the returned list is empty, all schemes are supported. * * @sa QFileDialog::setSupportedSchemes * @since 5.43 */ void setSupportedSchemes(const QStringList &schemes); /** * Returns the URL schemes that the file widget should allow navigating to. * * If the returned list is empty, all schemes are supported. Examples for * schemes are @c "file" or @c "ftp". * * @sa QFileDialog::supportedSchemes * @since 5.43 */ QStringList supportedSchemes() const; public Q_SLOTS: /** * Called when clicking ok (when this widget is used in KFileDialog) * Might or might not call accept(). */ void slotOk(); void accept(); void slotCancel(); protected: void resizeEvent(QResizeEvent *event) override; void showEvent(QShowEvent *event) override; bool eventFilter(QObject *watched, QEvent *event) override; Q_SIGNALS: /** * Emitted when the user selects a file. It is only emitted in single- * selection mode. The best way to get notified about selected file(s) * is to connect to the okClicked() signal inherited from KDialog * and call selectedFile(), selectedFiles(), * selectedUrl() or selectedUrls(). * * \since 4.4 */ void fileSelected(const QUrl &); /** * Emitted when the user highlights a file. * \since 4.4 */ void fileHighlighted(const QUrl &); /** - * Emitted when the user hilights one or more files in multiselection mode. + * Emitted when the user highlights one or more files in multiselection mode. * * Note: fileHighlighted() or fileSelected() are @em not * emitted in multiselection mode. You may use selectedItems() to * ask for the current highlighted items. * @see fileSelected */ void selectionChanged(); /** * Emitted when the filter changed, i.e. the user entered an own filter * or chose one of the predefined set via setFilter(). * * @param filter contains the new filter (only the extension part, * not the explanation), i.e. "*.cpp" or "*.cpp *.cc". * * @see setFilter() * @see currentFilter() */ void filterChanged(const QString &filter); /** * Emitted by slotOk() (directly or asynchronously) once everything has * been done. Should be used by the caller to call accept(). */ void accepted(); public: /** * @returns the KDirOperator used to navigate the filesystem * @since 4.3 */ KDirOperator *dirOperator(); /** * reads the configuration for this widget from the given config group * @param group the KConfigGroup to read from * @since 4.4 */ void readConfig(KConfigGroup &group); private: friend class KFileWidgetPrivate; KFileWidgetPrivate *const d; Q_PRIVATE_SLOT(d, void _k_slotLocationChanged(const QString &)) Q_PRIVATE_SLOT(d, void _k_urlEntered(const QUrl &)) Q_PRIVATE_SLOT(d, void _k_enterUrl(const QUrl &)) Q_PRIVATE_SLOT(d, void _k_enterUrl(const QString &)) Q_PRIVATE_SLOT(d, void _k_locationAccepted(const QString &)) Q_PRIVATE_SLOT(d, void _k_slotFilterChanged()) Q_PRIVATE_SLOT(d, void _k_fileHighlighted(const KFileItem &)) Q_PRIVATE_SLOT(d, void _k_fileSelected(const KFileItem &)) Q_PRIVATE_SLOT(d, void _k_slotLoadingFinished()) Q_PRIVATE_SLOT(d, void _k_fileCompletion(const QString &)) Q_PRIVATE_SLOT(d, void _k_toggleSpeedbar(bool)) Q_PRIVATE_SLOT(d, void _k_toggleBookmarks(bool)) Q_PRIVATE_SLOT(d, void _k_slotAutoSelectExtClicked()) Q_PRIVATE_SLOT(d, void _k_placesViewSplitterMoved(int, int)) Q_PRIVATE_SLOT(d, void _k_activateUrlNavigator()) Q_PRIVATE_SLOT(d, void _k_zoomOutIconsSize()) Q_PRIVATE_SLOT(d, void _k_zoomInIconsSize()) Q_PRIVATE_SLOT(d, void _k_slotIconSizeSliderMoved(int)) Q_PRIVATE_SLOT(d, void _k_slotIconSizeChanged(int)) Q_PRIVATE_SLOT(d, void _k_slotViewDoubleClicked(const QModelIndex&)) }; #endif diff --git a/src/ioslaves/ftp/ftp.h b/src/ioslaves/ftp/ftp.h index 4203a2ac..bdf7872b 100644 --- a/src/ioslaves/ftp/ftp.h +++ b/src/ioslaves/ftp/ftp.h @@ -1,447 +1,447 @@ /* This file is part of the KDE libraries Copyright (C) 2000 David Faure This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KDELIBS_FTP_H #define KDELIBS_FTP_H #include #include #include class QTcpServer; class QTcpSocket; class QNetworkProxy; class QAuthenticator; struct FtpEntry { QString name; QString owner; QString group; QString link; KIO::filesize_t size; mode_t type; mode_t access; QDateTime date; }; //=============================================================================== // Ftp //=============================================================================== class Ftp : public QObject, public KIO::SlaveBase { Q_OBJECT public: Ftp(const QByteArray &pool, const QByteArray &app); virtual ~Ftp(); void setHost(const QString &host, quint16 port, const QString &user, const QString &pass) override; /** * Connects to a ftp server and logs us in * m_bLoggedOn is set to true if logging on was successful. * It is set to false if the connection becomes closed. * */ void openConnection() override; /** * Closes the connection */ void closeConnection() override; void stat(const QUrl &url) override; void listDir(const QUrl &url) override; void mkdir(const QUrl &url, int permissions) override; void rename(const QUrl &src, const QUrl &dst, KIO::JobFlags flags) override; void del(const QUrl &url, bool isfile) override; void chmod(const QUrl &url, int permissions) override; void get(const QUrl &url) override; void put(const QUrl &url, int permissions, KIO::JobFlags flags) override; //virtual void mimetype( const QUrl& url ); void slave_status() override; /** * Handles the case that one side of the job is a local file */ void copy(const QUrl &src, const QUrl &dest, int permissions, KIO::JobFlags flags) override; private: // ------------------------------------------------------------------------ // All the methods named ftpXyz are lowlevel methods that are not exported. // The implement functionality used by the public high-level methods. Some // low-level methods still use error() to emit errors. This behaviour is not // recommended - please return a boolean status or an error code instead! // ------------------------------------------------------------------------ /** * Status Code returned from ftpPut() and ftpGet(), used to select * source or destination url for error messages */ typedef enum { statusSuccess, statusClientError, statusServerError } StatusCode; /** * Login Mode for ftpOpenConnection */ typedef enum { loginDefered, loginExplicit, loginImplicit } LoginMode; /** * Connect and login to the FTP server. * * @param loginMode controls if login info should be sent
* loginDefered - must not be logged on, no login info is sent
* loginExplicit - must not be logged on, login info is sent
* loginImplicit - login info is sent if not logged on * * @return true on success (a login failure would return false). */ bool ftpOpenConnection(LoginMode loginMode); /** * Executes any auto login macro's as specified in a .netrc file. */ void ftpAutoLoginMacro(); /** * Called by openConnection. It logs us in. * m_initialPath is set to the current working directory * if logging on was successful. * * @param userChanged if not nullptr, will be set to true if the user name * was changed during login. * @return true on success. */ bool ftpLogin(bool *userChanged = nullptr); /** * ftpSendCmd - send a command (@p cmd) and read response * * @param maxretries number of time it should retry. Since it recursively * calls itself if it can't read the answer (this happens especially after * timeouts), we need to limit the recursiveness ;-) * * return true if any response received, false on error */ bool ftpSendCmd(const QByteArray &cmd, int maxretries = 1); /** * Use the SIZE command to get the file size. * @param mode the size depends on the transfer mode, hence this arg. * @return true on success * Gets the size into m_size. */ bool ftpSize(const QString &path, char mode); /** * Returns true if the file exists. * Implemented using the SIZE command. */ bool ftpFileExists(const QString &path); /** * Set the current working directory, but only if not yet current */ bool ftpFolder(const QString &path, bool bReportError); /** * Runs a command on the ftp server like "list" or "retr". In contrast to * ftpSendCmd a data connection is opened. The corresponding socket * sData is available for reading/writing on success. * The connection must be closed afterwards with ftpCloseCommand. * * @param mode is 'A' or 'I'. 'A' means ASCII transfer, 'I' means binary transfer. * @param errorcode the command-dependent error code to emit on error * * @return true if the command was accepted by the server. */ bool ftpOpenCommand(const char *command, const QString &path, char mode, int errorcode, KIO::fileoffset_t offset = 0); /** * The counterpart to openCommand. * Closes data sockets and then reads line sent by server at * end of command. * @return false on error (line doesn't start with '2') */ bool ftpCloseCommand(); /** * Send "TYPE I" or "TYPE A" only if required, see m_cDataMode. * * Use 'A' to select ASCII and 'I' to select BINARY mode. If * cMode is '?' the m_bTextMode flag is used to choose a mode. */ bool ftpDataMode(char cMode); //void ftpAbortTransfer(); /** * Used by ftpOpenCommand, return 0 on success or an error code */ int ftpOpenDataConnection(); /** * closes a data connection, see ftpOpenDataConnection() */ void ftpCloseDataConnection(); /** * Helper for ftpOpenDataConnection */ int ftpOpenPASVDataConnection(); /** * Helper for ftpOpenDataConnection */ int ftpOpenEPSVDataConnection(); /** * Helper for ftpOpenDataConnection */ int ftpOpenPortDataConnection(); bool ftpChmod(const QString &path, int permissions); // used by listDir bool ftpOpenDir(const QString &path); /** * Called to parse directory listings, call this until it returns false */ bool ftpReadDir(FtpEntry &ftpEnt); /** * Helper to fill an UDSEntry */ void ftpCreateUDSEntry(const QString &filename, const FtpEntry &ftpEnt, KIO::UDSEntry &entry, bool isDir); void ftpShortStatAnswer(const QString &filename, bool isDir); void ftpStatAnswerNotFound(const QString &path, const QString &filename); /** * This is the internal implementation of rename() - set put(). * * @return true on success. */ bool ftpRename(const QString &src, const QString &dst, KIO::JobFlags flags); /** * Called by openConnection. It opens the control connection to the ftp server. * * @return true on success. */ bool ftpOpenControlConnection(); bool ftpOpenControlConnection(const QString &host, int port); /** * closes the socket holding the control connection (see ftpOpenControlConnection) */ void ftpCloseControlConnection(); /** * read a response from the server (a trailing CR gets stripped) * @param iOffset -1 to read a new line from the server
* 0 to return the whole response string * >0 to return the response with iOffset chars skipped - * @return the reponse message with iOffset chars skipped (or "" if iOffset points + * @return the response message with iOffset chars skipped (or "" if iOffset points * behind the available data) */ const char *ftpResponse(int iOffset); /** * This is the internal implementation of get() - see copy(). * * IMPORTANT: the caller should call ftpCloseCommand() on return. * The function does not call error(), the caller should do this. * * @param iError set to an ERR_xxxx code on error * @param iCopyFile -1 -or- handle of a local destination file * @param hCopyOffset local file only: non-zero for resume * @return 0 for success, -1 for server error, -2 for client error */ StatusCode ftpGet(int &iError, int iCopyFile, const QUrl &url, KIO::fileoffset_t hCopyOffset); /** * This is the internal implementation of put() - see copy(). * * IMPORTANT: the caller should call ftpCloseCommand() on return. * The function does not call error(), the caller should do this. * * @param iError set to an ERR_xxxx code on error * @param iCopyFile -1 -or- handle of a local source file * @return 0 for success, -1 for server error, -2 for client error */ StatusCode ftpPut(int &iError, int iCopyFile, const QUrl &url, int permissions, KIO::JobFlags flags); /** * helper called from copy() to implement FILE -> FTP transfers * * @param iError set to an ERR_xxxx code on error * @param iCopyFile [out] handle of a local source file * @param sCopyFile path of the local source file * @return 0 for success, -1 for server error, -2 for client error */ StatusCode ftpCopyPut(int &iError, int &iCopyFile, const QString &sCopyFile, const QUrl &url, int permissions, KIO::JobFlags flags); /** * helper called from copy() to implement FTP -> FILE transfers * * @param iError set to an ERR_xxxx code on error * @param iCopyFile [out] handle of a local source file * @param sCopyFile path of the local destination file * @return 0 for success, -1 for server error, -2 for client error */ StatusCode ftpCopyGet(int &iError, int &iCopyFile, const QString &sCopyFile, const QUrl &url, int permissions, KIO::JobFlags flags); /** * Sends the mime type of the content to retrieved. * * @param iError set to an ERR_xxxx code on error * @return 0 for success, -1 for server error, -2 for client error */ StatusCode ftpSendMimeType(int &iError, const QUrl &url); /** * Fixes up an entry name so that extraneous whitespaces do not cause * problems. See bug# 88575 and bug# 300988. */ void fixupEntryName(FtpEntry *ftpEnt); /** * Calls @ref statEntry. */ bool maybeEmitStatEntry(FtpEntry &ftpEnt, const QString &search, const QString &filename, bool isDir); /** * Setup the connection to the server */ QTcpSocket *synchronousConnectToHost(const QString &host, quint16 port); private Q_SLOTS: void proxyAuthentication(const QNetworkProxy &, QAuthenticator *); void saveProxyAuthentication(); private: // data members QString m_host; int m_port; QString m_user; QString m_pass; /** * Where we end up after connecting */ QString m_initialPath; QUrl m_proxyURL; QStringList m_proxyUrls; /** * the current working directory - see ftpFolder */ QString m_currentPath; /** * the status returned by the FTP protocol, set in ftpResponse() */ int m_iRespCode; /** * the status/100 returned by the FTP protocol, set in ftpResponse() */ int m_iRespType; /** * This flag is maintained by ftpDataMode() and contains I or A after * ftpDataMode() has successfully set the mode. */ char m_cDataMode; /** * true if logged on (m_control should also be non-nullptr) */ bool m_bLoggedOn; /** * true if a "textmode" metadata key was found by ftpLogin(). This * switches the ftp data transfer mode from binary to ASCII. */ bool m_bTextMode; /** * true if a data stream is open, used in closeConnection(). * * When the user cancels a get or put command the Ftp dtor will be called, * which in turn calls closeConnection(). The later would try to send QUIT * which won't work until timeout. ftpOpenCommand sets the m_bBusy flag so * that the sockets will be closed immediately - the server should be * capable of handling this and return an error code on thru the control * connection. The m_bBusy gets cleared by the ftpCloseCommand() routine. */ bool m_bBusy; bool m_bPasv; KIO::filesize_t m_size; static const KIO::filesize_t UnknownSize; enum { epsvUnknown = 0x01, epsvAllUnknown = 0x02, eprtUnknown = 0x04, epsvAllSent = 0x10, pasvUnknown = 0x20, chmodUnknown = 0x100 }; int m_extControl; /** * control connection socket, only set if openControl() succeeded */ QTcpSocket *m_control; QByteArray m_lastControlLine; /** * data connection socket */ QTcpSocket *m_data; /** * active mode server socket */ QTcpServer *m_server; /** * proxy server authenticator */ QAuthenticator *m_socketProxyAuth; }; #endif // KDELIBS_FTP_H diff --git a/src/ioslaves/trash/kio_trash_win.cpp b/src/ioslaves/trash/kio_trash_win.cpp index a32f836a..67e06543 100644 --- a/src/ioslaves/trash/kio_trash_win.cpp +++ b/src/ioslaves/trash/kio_trash_win.cpp @@ -1,454 +1,454 @@ /* This file is part of the KDE project Copyright (C) 2004 David Faure Copyright (C) 2009 Christian Ehrlicher This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #define QT_NO_CAST_FROM_ASCII #include "kio_trash_win.h" #include "kiotrashdebug.h" #include "kioglobal_p.h" #include "kio/job.h" #include #include #include #include #include #include extern "C" { int Q_DECL_EXPORT kdemain(int argc, char **argv) { bool bNeedsUninit = (CoInitializeEx(NULL, COINIT_MULTITHREADED) == S_OK); // necessary to use other kio slaves QCoreApplication app(argc, argv); // start the slave TrashProtocol slave(argv[1], argv[2], argv[3]); slave.dispatchLoop(); if (bNeedsUninit) { CoUninitialize(); } return 0; } } static const qint64 KDE_SECONDS_SINCE_1601 = 11644473600LL; static const qint64 KDE_USEC_IN_SEC = 1000000LL; static const int WM_SHELLNOTIFY = (WM_USER + 42); #ifndef SHCNRF_InterruptLevel static const int SHCNRF_InterruptLevel = 0x0001; static const int SHCNRF_ShellLevel = 0x0002; static const int SHCNRF_RecursiveInterrupt = 0x1000; #endif static inline time_t filetimeToTime_t(const FILETIME *time) { ULARGE_INTEGER i64; i64.LowPart = time->dwLowDateTime; i64.HighPart = time->dwHighDateTime; i64.QuadPart /= KDE_USEC_IN_SEC * 10; i64.QuadPart -= KDE_SECONDS_SINCE_1601; return i64.QuadPart; } LRESULT CALLBACK trash_internal_proc(HWND hwnd, UINT message, WPARAM wp, LPARAM lp) { if (message == WM_SHELLNOTIFY) { TrashProtocol *that = (TrashProtocol *)GetWindowLongPtr(hwnd, GWLP_USERDATA); that->updateRecycleBin(); } return DefWindowProc(hwnd, message, wp, lp); } TrashProtocol::TrashProtocol(const QByteArray &protocol, const QByteArray &pool, const QByteArray &app) : SlaveBase(protocol, pool, app) , m_config(QString::fromLatin1("trashrc"), KConfig::SimpleConfig) { - // create a hidden window to receive notifications thorugh window messages + // create a hidden window to receive notifications through window messages const QString className = QLatin1String("TrashProtocol_Widget") + QString::number(quintptr(trash_internal_proc)); HINSTANCE hi = GetModuleHandle(nullptr); WNDCLASS wc; memset(&wc, 0, sizeof(WNDCLASS)); wc.lpfnWndProc = trash_internal_proc; wc.hInstance = hi; wc.lpszClassName = (LPCWSTR)className.utf16(); RegisterClass(&wc); m_notificationWindow = CreateWindow(wc.lpszClassName, // classname wc.lpszClassName, // window name 0, // style 0, 0, 0, 0, // geometry 0, // parent 0, // menu handle hi, // application 0); // windows creation data. SetWindowLongPtr(m_notificationWindow, GWLP_USERDATA, (LONG_PTR)this); // get trash IShellFolder object LPITEMIDLIST iilTrash; IShellFolder *isfDesktop; // we assume that this will always work - if not we've a bigger problem than a kio_trash crash... SHGetFolderLocation(NULL, CSIDL_BITBUCKET, 0, 0, &iilTrash); SHGetDesktopFolder(&isfDesktop); isfDesktop->BindToObject(iilTrash, NULL, IID_IShellFolder2, (void **)&m_isfTrashFolder); isfDesktop->Release(); SHGetMalloc(&m_pMalloc); // register for recycle bin notifications, have to do it for *every* single recycle bin #if 0 // TODO: this does not work for devices attached after this loop here... DWORD dwSize = GetLogicalDriveStrings(0, NULL); LPWSTR pszDrives = (LPWSTR)malloc((dwSize + 2) * sizeof(WCHAR)); #endif SHChangeNotifyEntry stPIDL; stPIDL.pidl = iilTrash; stPIDL.fRecursive = TRUE; m_hNotifyRBin = SHChangeNotifyRegister(m_notificationWindow, SHCNRF_InterruptLevel | SHCNRF_ShellLevel | SHCNRF_RecursiveInterrupt, SHCNE_ALLEVENTS, WM_SHELLNOTIFY, 1, &stPIDL); ILFree(iilTrash); updateRecycleBin(); } TrashProtocol::~TrashProtocol() { SHChangeNotifyDeregister(m_hNotifyRBin); const QString className = QLatin1String("TrashProtocol_Widget") + QString::number(quintptr(trash_internal_proc)); UnregisterClass((LPCWSTR)className.utf16(), GetModuleHandle(nullptr)); DestroyWindow(m_notificationWindow); if (m_pMalloc) { m_pMalloc->Release(); } if (m_isfTrashFolder) { m_isfTrashFolder->Release(); } } void TrashProtocol::restore(const QUrl &trashURL, const QUrl &destURL) { LPITEMIDLIST pidl = NULL; LPCONTEXTMENU pCtxMenu = NULL; const QString path = trashURL.path().mid(1).replace(QLatin1Char('/'), QLatin1Char('\\')); LPWSTR lpFile = (LPWSTR)path.utf16(); HRESULT res = m_isfTrashFolder->ParseDisplayName(0, 0, lpFile, 0, &pidl, 0); bool bOk = translateError(res); if (!bOk) { return; } res = m_isfTrashFolder->GetUIObjectOf(0, 1, (LPCITEMIDLIST *)&pidl, IID_IContextMenu, NULL, (LPVOID *)&pCtxMenu); bOk = translateError(res); if (!bOk) { return; } // this looks hacky but it's the only solution I found so far... HMENU hmenuCtx = CreatePopupMenu(); res = pCtxMenu->QueryContextMenu(hmenuCtx, 0, 1, 0x00007FFF, CMF_NORMAL); bOk = translateError(res); if (!bOk) { return; } UINT uiCommand = ~0U; char verb[MAX_PATH]; const int iMenuMax = GetMenuItemCount(hmenuCtx); for (int i = 0; i < iMenuMax; i++) { UINT uiID = GetMenuItemID(hmenuCtx, i) - 1; if ((uiID == -1) || (uiID == 0)) { continue; } res = pCtxMenu->GetCommandString(uiID, GCS_VERBA, NULL, verb, sizeof(verb)); if (FAILED(res)) { continue; } if (stricmp(verb, "undelete") == 0) { uiCommand = uiID; break; } } if (uiCommand != ~0U) { CMINVOKECOMMANDINFO cmi; memset(&cmi, 0, sizeof(CMINVOKECOMMANDINFO)); cmi.cbSize = sizeof(CMINVOKECOMMANDINFO); cmi.lpVerb = MAKEINTRESOURCEA(uiCommand); cmi.fMask = CMIC_MASK_FLAG_NO_UI; res = pCtxMenu->InvokeCommand((CMINVOKECOMMANDINFO *)&cmi); bOk = translateError(res); if (bOk) { finished(); } } DestroyMenu(hmenuCtx); pCtxMenu->Release(); ILFree(pidl); } void TrashProtocol::clearTrash() { translateError(SHEmptyRecycleBin(0, 0, 0)); finished(); } void TrashProtocol::rename(const QUrl &oldURL, const QUrl &newURL, KIO::JobFlags flags) { qCDebug(KIO_TRASH) << "TrashProtocol::rename(): old=" << oldURL << " new=" << newURL << " overwrite=" << (flags & KIO::Overwrite); if (oldURL.scheme() == QLatin1String("trash") && newURL.scheme() == QLatin1String("trash")) { error(KIO::ERR_CANNOT_RENAME, oldURL.toDisplayString()); return; } copyOrMove(oldURL, newURL, (flags & KIO::Overwrite), Move); } void TrashProtocol::copy(const QUrl &src, const QUrl &dest, int /*permissions*/, KIO::JobFlags flags) { qCDebug(KIO_TRASH) << "TrashProtocol::copy(): " << src << " " << dest; if (src.scheme() == QLatin1String("trash") && dest.scheme() == QLatin1String("trash")) { error(KIO::ERR_UNSUPPORTED_ACTION, i18n("This file is already in the trash bin.")); return; } copyOrMove(src, dest, (flags & KIO::Overwrite), Copy); } void TrashProtocol::copyOrMove(const QUrl &src, const QUrl &dest, bool overwrite, CopyOrMove action) { if (src.scheme() == QLatin1String("trash") && dest.isLocalFile()) { if (action == Move) { restore(src, dest); } else { error(KIO::ERR_UNSUPPORTED_ACTION, i18n("not supported")); } // Extracting (e.g. via dnd). Ignore original location stored in info file. return; } else if (src.isLocalFile() && dest.scheme() == QLatin1String("trash")) { UINT op = (action == Move) ? FO_DELETE : FO_COPY; if (!doFileOp(src, FO_DELETE, FOF_ALLOWUNDO)) { return; } finished(); return; } else { error(KIO::ERR_UNSUPPORTED_ACTION, i18n("Internal error in copyOrMove, should never happen")); } } void TrashProtocol::stat(const QUrl &url) { KIO::UDSEntry entry; if (url.path() == QLatin1String("/")) { STRRET strret; IShellFolder *isfDesktop; LPITEMIDLIST iilTrash; SHGetFolderLocation(NULL, CSIDL_BITBUCKET, 0, 0, &iilTrash); SHGetDesktopFolder(&isfDesktop); isfDesktop->BindToObject(iilTrash, NULL, IID_IShellFolder2, (void **)&m_isfTrashFolder); isfDesktop->GetDisplayNameOf(iilTrash, SHGDN_NORMAL, &strret); isfDesktop->Release(); ILFree(iilTrash); entry.insert(KIO::UDSEntry::UDS_NAME, QString::fromUtf16((const unsigned short *) strret.pOleStr)); entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, S_IFDIR); entry.insert(KIO::UDSEntry::UDS_ACCESS, 0700); entry.insert(KIO::UDSEntry::UDS_MIME_TYPE, QString::fromLatin1("inode/directory")); m_pMalloc->Free(strret.pOleStr); } else { // TODO: when does this happen? } statEntry(entry); finished(); } void TrashProtocol::del(const QUrl &url, bool /*isfile*/) { if (!doFileOp(url, FO_DELETE, 0)) { return; } finished(); } void TrashProtocol::listDir(const QUrl &url) { qCDebug(KIO_TRASH) << "TrashProtocol::listDir(): " << url; // There are no subfolders in Windows Trash listRoot(); } void TrashProtocol::listRoot() { IEnumIDList *l; HRESULT res = m_isfTrashFolder->EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &l); if (res != S_OK) { return; } STRRET strret; SFGAOF attribs; KIO::UDSEntry entry; LPITEMIDLIST i; WIN32_FIND_DATAW findData; while (l->Next(1, &i, NULL) == S_OK) { m_isfTrashFolder->GetDisplayNameOf(i, SHGDN_NORMAL, &strret); entry.insert(KIO::UDSEntry::UDS_DISPLAY_NAME, QString::fromUtf16((const unsigned short *)strret.pOleStr)); m_pMalloc->Free(strret.pOleStr); m_isfTrashFolder->GetDisplayNameOf(i, SHGDN_FORPARSING | SHGDN_INFOLDER, &strret); entry.insert(KIO::UDSEntry::UDS_NAME, QString::fromUtf16((const unsigned short *)strret.pOleStr)); m_pMalloc->Free(strret.pOleStr); m_isfTrashFolder->GetAttributesOf(1, (LPCITEMIDLIST *)&i, &attribs); SHGetDataFromIDList(m_isfTrashFolder, i, SHGDFIL_FINDDATA, &findData, sizeof(findData)); entry.insert(KIO::UDSEntry::UDS_SIZE, ((quint64)findData.nFileSizeLow) + (((quint64)findData.nFileSizeHigh) << 32)); entry.insert(KIO::UDSEntry::UDS_MODIFICATION_TIME, filetimeToTime_t(&findData.ftLastWriteTime)); entry.insert(KIO::UDSEntry::UDS_ACCESS_TIME, filetimeToTime_t(&findData.ftLastAccessTime)); entry.insert(KIO::UDSEntry::UDS_CREATION_TIME, filetimeToTime_t(&findData.ftCreationTime)); entry.insert(KIO::UDSEntry::UDS_EXTRA, QString::fromUtf16((const unsigned short *)strret.pOleStr)); entry.insert(KIO::UDSEntry::UDS_EXTRA + 1, QDateTime().toString(Qt::ISODate)); mode_t type = QT_STAT_REG; if ((attribs & SFGAO_FOLDER) == SFGAO_FOLDER) { type = QT_STAT_DIR; } if ((attribs & SFGAO_LINK) == SFGAO_LINK) { type = QT_STAT_LNK; } entry.insert(KIO::UDSEntry::UDS_FILE_TYPE, type); mode_t access = 0700; if ((findData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY) { type = 0300; } listEntry(entry, false); ILFree(i); } l->Release(); entry.clear(); listEntry(entry, true); finished(); } void TrashProtocol::special(const QByteArray &data) { QDataStream stream(data); int cmd; stream >> cmd; switch (cmd) { case 1: // empty trash folder clearTrash(); break; case 2: // convert old trash folder (non-windows only) finished(); break; case 3: { QUrl url; stream >> url; restore(url, QUrl()); break; } default: qCWarning(KIO_TRASH) << "Unknown command in special(): " << cmd; error(KIO::ERR_UNSUPPORTED_ACTION, QString::number(cmd)); break; } } void TrashProtocol::updateRecycleBin() { IEnumIDList *l; HRESULT res = m_isfTrashFolder->EnumObjects(0, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &l); if (res != S_OK) { return; } bool bEmpty = true; LPITEMIDLIST i; if (l->Next(1, &i, NULL) == S_OK) { bEmpty = false; ILFree(i); } KConfigGroup group = m_config.group("Status"); group.writeEntry("Empty", bEmpty); m_config.sync(); l->Release(); } void TrashProtocol::put(const QUrl &url, int /*permissions*/, KIO::JobFlags) { qCDebug(KIO_TRASH) << "put: " << url; // create deleted file. We need to get the mtime and original location from metadata... // Maybe we can find the info file for url.fileName(), in case ::rename() was called first, and failed... error(KIO::ERR_ACCESS_DENIED, url.toDisplayString()); } void TrashProtocol::get(const QUrl &url) { // TODO } bool TrashProtocol::doFileOp(const QUrl &url, UINT wFunc, FILEOP_FLAGS fFlags) { const QString path = url.path().replace(QLatin1Char('/'), QLatin1Char('\\')); // must be double-null terminated. QByteArray delBuf((path.length() + 2) * 2, 0); memcpy(delBuf.data(), path.utf16(), path.length() * 2); SHFILEOPSTRUCTW op; memset(&op, 0, sizeof(SHFILEOPSTRUCTW)); op.wFunc = wFunc; op.pFrom = (LPCWSTR)delBuf.constData(); op.fFlags = fFlags | FOF_NOCONFIRMATION | FOF_NOERRORUI; return translateError(SHFileOperationW(&op)); } bool TrashProtocol::translateError(HRESULT hRes) { // TODO! if (FAILED(hRes)) { error(KIO::ERR_DOES_NOT_EXIST, QLatin1String("fixme!")); return false; } return true; } #include "moc_kio_trash_win.cpp" diff --git a/src/kntlm/des.cpp b/src/kntlm/des.cpp index af9b240f..26058269 100644 --- a/src/kntlm/des.cpp +++ b/src/kntlm/des.cpp @@ -1,547 +1,547 @@ /* - * Sofware DES functions + * Software DES functions * * Copyright 1988-1991 Phil Karn * Copyright 2003 Nikos Mavroyanopoulos * * Taken from libmcrypt (http://mcrypt.hellug.gr/lib/index.html). * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ -/* Sofware DES functions +/* Software DES functions * written 12 Dec 1986 by Phil Karn, KA9Q; large sections adapted from * the 1977 public-domain program by Jim Gillogly * Modified for additional speed - 6 December 1988 Phil Karn * Modified for parameterized key schedules - Jan 1991 Phil Karn * Callers now allocate a key schedule as follows: * kn = (char (*)[8])malloc(sizeof(char) * 8 * 16); * or * char kn[16][8]; */ /* modified in order to use the libmcrypt API by Nikos Mavroyanopoulos * All modifications are placed under the license of libmcrypt. */ #include "des.h" #include #include static void permute_ip(unsigned char *inblock, DES_KEY *key, unsigned char *outblock); static void permute_fp(unsigned char *inblock, DES_KEY *key, unsigned char *outblock); static void perminit_ip(DES_KEY *key); static void spinit(DES_KEY *key); static void perminit_fp(DES_KEY *key); static quint32 f(DES_KEY *key, quint32 r, char *subkey); /* Tables defined in the Data Encryption Standard documents */ /* initial permutation IP */ static const char ip[] = { 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7 }; /* final permutation IP^-1 */ static const char fp[] = { 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9, 49, 17, 57, 25 }; /* expansion operation matrix * This is for reference only; it is unused in the code * as the f() function performs it implicitly for speed */ #ifdef notdef static const char ei[] = { 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, 28, 29, 28, 29, 30, 31, 32, 1 }; #endif /* permuted choice table (key) */ static const char pc1[] = { 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 }; /* number left rotations of pc1 */ static const char totrot[] = { 1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 }; /* permuted choice key (table) */ static const char pc2[] = { 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 }; /* The (in)famous S-boxes */ static const char si[8][64] = { /* S1 */ { 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 }, /* S2 */ { 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 }, /* S3 */ { 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 }, /* S4 */ { 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 }, /* S5 */ { 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 }, /* S6 */ { 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 }, /* S7 */ { 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 }, /* S8 */ { 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 }, }; /* 32-bit permutation function P used on the output of the S-boxes */ static const char p32i[] = { 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 }; /* End of DES-defined tables */ /* Lookup tables initialized once only at startup by desinit() */ /* bit 0 is left-most in byte */ static const int bytebit[] = { 0200, 0100, 040, 020, 010, 04, 02, 01 }; static const int nibblebit[] = { 010, 04, 02, 01 }; /* Allocate space and initialize DES lookup arrays * mode == 0: standard Data Encryption Algorithm */ static int desinit(DES_KEY *key) { spinit(key); perminit_ip(key); perminit_fp(key); return 0; } /* Set key (initialize key schedule array) */ int ntlm_des_set_key(DES_KEY *dkey, char *user_key, int /*len*/) { char pc1m[56]; /* place to modify pc1 into */ char pcr[56]; /* place to rotate pc1 into */ int i, j, l; int m; memset(dkey, 0, sizeof(DES_KEY)); desinit(dkey); /* Clear key schedule */ for (j = 0; j < 56; ++j) { /* convert pc1 to bits of key */ l = pc1[j] - 1; /* integer bit location */ m = l & 07; /* find bit */ pc1m[j] = (user_key[l >> 3] & /* find which key byte l is in */ bytebit[m]) /* and which bit of that byte */ ? 1 : 0; /* and store 1-bit result */ } for (i = 0; i < 16; ++i) { /* key chunk for each iteration */ for (j = 0; j < 56; ++j) { /* rotate pc1 the right amount */ pcr[j] = pc1m[(l = j + totrot[i]) < (j < 28 ? 28 : 56) ? l : l - 28]; } /* rotate left and right halves independently */ for (j = 0; j < 48; ++j) { /* select bits individually */ /* check bit that goes to kn[j] */ if (pcr[pc2[j] - 1]) { /* mask it in if it's there */ l = j % 6; dkey->kn[i][j / 6] |= bytebit[l] >> 2; } } } return 0; } /* In-place encryption of 64-bit block */ static void ntlm_des_encrypt(DES_KEY *key, unsigned char *block) { quint32 left, right; char *knp; quint32 work[2]; /* Working data storage */ permute_ip(block, key, (unsigned char *) work); /* Initial Permutation */ left = qFromBigEndian(work[0]); right = qFromBigEndian(work[1]); /* Do the 16 rounds. * The rounds are numbered from 0 to 15. On even rounds * the right half is fed to f() and the result exclusive-ORs * the left half; on odd rounds the reverse is done. */ knp = &key->kn[0][0]; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); knp += 8; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); knp += 8; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); knp += 8; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); knp += 8; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); knp += 8; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); knp += 8; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); knp += 8; left ^= f(key, right, knp); knp += 8; right ^= f(key, left, knp); /* Left/right half swap, plus byte swap if little-endian */ work[1] = qToBigEndian(left); work[0] = qToBigEndian(right); permute_fp((unsigned char *) work, key, block); /* Inverse initial permutation */ } /* Permute inblock with perm */ static void permute_ip(unsigned char *inblock, DES_KEY *key, unsigned char *outblock) { unsigned char *ib, *ob; /* ptr to input or output block */ char *p, *q; int j; /* Clear output block */ memset(outblock, 0, 8); ib = inblock; for (j = 0; j < 16; j += 2, ++ib) { /* for each input nibble */ ob = outblock; p = key->iperm[j][(*ib >> 4) & 0xf]; q = key->iperm[j + 1][*ib & 0xf]; /* and each output byte, OR the masks together */ *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; } } /* Permute inblock with perm */ static void permute_fp(unsigned char *inblock, DES_KEY *key, unsigned char *outblock) { unsigned char *ib, *ob; /* ptr to input or output block */ char *p, *q; int j; /* Clear output block */ memset(outblock, 0, 8); ib = inblock; for (j = 0; j < 16; j += 2, ++ib) { /* for each input nibble */ ob = outblock; p = key->fperm[j][(*ib >> 4) & 0xf]; q = key->fperm[j + 1][*ib & 0xf]; /* and each output byte, OR the masks together */ *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; *ob++ |= *p++ | *q++; } } /* The nonlinear function f(r,k), the heart of DES */ static quint32 f(DES_KEY *key, quint32 r, char *subkey) { quint32 *spp; quint32 rval, rt; int er; #ifdef TRACE printf("f(%08lx, %02x %02x %02x %02x %02x %02x %02x %02x) = ", r, subkey[0], subkey[1], subkey[2], subkey[3], subkey[4], subkey[5], subkey[6], subkey[7]); #endif /* Run E(R) ^ K through the combined S & P boxes. * This code takes advantage of a convenient regularity in * E, namely that each group of 6 bits in E(R) feeding * a single S-box is a contiguous segment of R. */ subkey += 7; /* Compute E(R) for each block of 6 bits, and run thru boxes */ er = ((int) r << 1) | ((r & 0x80000000) ? 1 : 0); spp = &key->sp[7][0]; rval = spp[(er ^ *subkey--) & 0x3f]; spp -= 64; rt = (quint32) r >> 3; rval |= spp[((int) rt ^ *subkey--) & 0x3f]; spp -= 64; rt >>= 4; rval |= spp[((int) rt ^ *subkey--) & 0x3f]; spp -= 64; rt >>= 4; rval |= spp[((int) rt ^ *subkey--) & 0x3f]; spp -= 64; rt >>= 4; rval |= spp[((int) rt ^ *subkey--) & 0x3f]; spp -= 64; rt >>= 4; rval |= spp[((int) rt ^ *subkey--) & 0x3f]; spp -= 64; rt >>= 4; rval |= spp[((int) rt ^ *subkey--) & 0x3f]; spp -= 64; rt >>= 4; rt |= (r & 1) << 5; rval |= spp[((int) rt ^ *subkey) & 0x3f]; #ifdef TRACE printf(" %08lx\n", rval); #endif return rval; } /* initialize a perm array */ static void perminit_ip(DES_KEY *key) { int l, j, k; int i, m; /* Clear the permutation array */ memset(key->iperm, 0, 16 * 16 * 8); for (i = 0; i < 16; ++i) /* each input nibble position */ for (j = 0; j < 16; ++j) /* each possible input nibble */ for (k = 0; k < 64; ++k) { /* each output bit position */ l = ip[k] - 1; /* where does this bit come from */ if ((l >> 2) != i) { /* does it come from input posn? */ continue; /* if not, bit k is 0 */ } if (!(j & nibblebit[l & 3])) { continue; /* any such bit in input? */ } m = k & 07; /* which bit is this in the byte */ key->iperm[i][j][k >> 3] |= bytebit[m]; } } static void perminit_fp(DES_KEY *key) { int l, j, k; int i, m; /* Clear the permutation array */ memset(key->fperm, 0, 16 * 16 * 8); for (i = 0; i < 16; ++i) /* each input nibble position */ for (j = 0; j < 16; ++j) /* each possible input nibble */ for (k = 0; k < 64; ++k) { /* each output bit position */ l = fp[k] - 1; /* where does this bit come from */ if ((l >> 2) != i) { /* does it come from input posn? */ continue; /* if not, bit k is 0 */ } if (!(j & nibblebit[l & 3])) { continue; /* any such bit in input? */ } m = k & 07; /* which bit is this in the byte */ key->fperm[i][j][k >> 3] |= bytebit[m]; } } /* Initialize the lookup table for the combined S and P boxes */ static void spinit(DES_KEY *key) { char pbox[32]; int p, i, s, j, rowcol; quint32 val; /* Compute pbox, the inverse of p32i. * This is easier to work with */ for (p = 0; p < 32; ++p) { for (i = 0; i < 32; ++i) { if (p32i[i] - 1 == p) { pbox[p] = i; break; } } } for (s = 0; s < 8; ++s) { /* For each S-box */ for (i = 0; i < 64; ++i) { /* For each possible input */ val = 0; /* The row number is formed from the first and last * bits; the column number is from the middle 4 */ rowcol = (i & 32) | ((i & 1) ? 16 : 0) | ((i >> 1) & 0xf); for (j = 0; j < 4; j++) { /* For each output bit */ if (si[s][rowcol] & (8 >> j)) { val |= 1L << (31 - pbox[4 * s + j]); } } key->sp[s][i] = val; } } } int ntlm_des_ecb_encrypt(const void *plaintext, int len, DES_KEY *akey, unsigned char output[8]) { int j; const unsigned char *plain = (const unsigned char *) plaintext; for (j = 0; j < len / 8; ++j) { memcpy(&output[j * 8], &plain[j * 8], 8); ntlm_des_encrypt(akey, &output[j * 8]); } if (j == 0 && len != 0) { return -1; /* no blocks were encrypted */ } return 0; } diff --git a/src/kntlm/des.h b/src/kntlm/des.h index ee9b4310..7c5b83dd 100644 --- a/src/kntlm/des.h +++ b/src/kntlm/des.h @@ -1,41 +1,41 @@ /* - * Sofware DES functions + * Software DES functions * * Copyright 1988-1991 Phil Karn * Copyright 2003 Nikos Mavroyanopoulos * * Taken from libmcrypt (http://mcrypt.hellug.gr/lib/index.html). * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301 USA */ #ifndef KNTLM_DES_H #define KNTLM_DES_H #include typedef struct des_key { char kn[16][8]; quint32 sp[8][64]; char iperm[16][16][8]; char fperm[16][16][8]; } DES_KEY; int ntlm_des_ecb_encrypt(const void *plaintext, int len, DES_KEY *akey, unsigned char output[8]); int ntlm_des_set_key(DES_KEY *dkey, char *user_key, int len); #endif /* KNTLM_DES_H */