diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index e08a9641..9eb7bc5d 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -1,298 +1,294 @@ project(KIOCore) include (ConfigureChecks.cmake) configure_file(config-kiocore.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kiocore.h ) configure_file(config-kmountpoint.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kmountpoint.h) # KSSL_HAVE_SSL only used in kssl/ksslsettings.cpp, but currently ifdefed out #find_package(OpenSSL) #set_package_properties(OpenSSL PROPERTIES DESCRIPTION "Support for secure network communications (SSL and TLS)" # URL "http://openssl.org" # TYPE RECOMMENDED # PURPOSE "KDE uses OpenSSL for the bulk of secure communications, including secure web browsing via HTTPS" # ) #if(OPENSSL_FOUND) # set(KSSL_HAVE_SSL 1) # include_directories(${OPENSSL_INCLUDE_DIR}) #endif() -# TODO: Remove these -remove_definitions(-DQT_NO_CAST_FROM_ASCII) -remove_definitions(-DQT_NO_CAST_FROM_BYTEARRAY) - set(kiocore_SRCS idleslave.cpp klocalsocket.cpp connectionbackend.cpp connection.cpp connectionserver.cpp krecentdocument.cpp kfileitemlistproperties.cpp tcpslavebase.cpp directorysizejob.cpp forwardingslavebase.cpp chmodjob.cpp kdiskfreespaceinfo.cpp usernotificationhandler.cpp ksambasharedata.cpp ksambashare.cpp knfsshare.cpp kfileitem.cpp davjob.cpp deletejob.cpp copyjob.cpp filejob.cpp mkdirjob.cpp mkpathjob.cpp kpasswdserverloop.cpp kpasswdserverclient.cpp kremoteencoding.cpp sessiondata.cpp slavebase.cpp dataslave.cpp dataprotocol.cpp desktopexecparser.cpp emptytrashjob.cpp authinfo.cpp slaveinterface.cpp slave.cpp job_error.cpp job.cpp filecopyjob.cpp listjob.cpp mimetypejob.cpp multigetjob.cpp restorejob.cpp simplejob.cpp specialjob.cpp statjob.cpp storedtransferjob.cpp transferjob.cpp filesystemfreespacejob.cpp scheduler.cpp slaveconfig.cpp kprotocolmanager.cpp hostinfo.cpp kdirnotify.cpp kurlauthorized.cpp kacl.cpp udsentry.cpp global.cpp metadata.cpp kprotocolinfo.cpp kprotocolinfofactory.cpp jobtracker.cpp jobuidelegateextension.cpp jobuidelegatefactory.cpp kmountpoint.cpp kcoredirlister.cpp faviconscache.cpp ksslcertificatemanager.cpp ktcpsocket.cpp kssl/ksslsettings.cpp kioglobal_p.cpp batchrenamejob.cpp ) ecm_qt_declare_logging_category(kiocore_SRCS HEADER kiocoredebug.h IDENTIFIER KIO_CORE CATEGORY_NAME kf5.kio.core) if (UNIX) set(kiocore_SRCS ${kiocore_SRCS} klocalsocket_unix.cpp kioglobal_p_unix.cpp ) endif() if (WIN32) set(kiocore_SRCS ${kiocore_SRCS} klocalsocket_win.cpp kioglobal_p_win.cpp ) endif() qt5_add_dbus_interface(kiocore_SRCS org.kde.KSlaveLauncher.xml klauncher_interface) set_source_files_properties(org.kde.KPasswdServer.xml PROPERTIES INCLUDE authinfo.h ) qt5_add_dbus_interface(kiocore_SRCS org.kde.KPasswdServer.xml kpasswdserver_interface) install(FILES org.kde.KDirNotify.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.kde.KDirNotify.xml) install(FILES org.kde.KPasswdServer.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.kde.KPasswdServer.xml) install(FILES org.kde.KSlaveLauncher.xml DESTINATION ${KDE_INSTALL_DBUSINTERFACEDIR} RENAME kf5_org.kde.KSlaveLauncher.xml) add_library(KF5KIOCore ${kiocore_SRCS}) generate_export_header(KF5KIOCore BASE_NAME KIOCore) add_library(KF5::KIOCore ALIAS KF5KIOCore) target_include_directories(KF5KIOCore PUBLIC "$" # kio_version.h "$" ) target_include_directories(KF5KIOCore INTERFACE "$") target_link_libraries(KF5KIOCore PUBLIC KF5::CoreAddons # KJob KF5::Service Qt5::Network Qt5::Concurrent # QtConcurrentRun in hostinfo.cpp Qt5::DBus PRIVATE Qt5::Xml # davjob.cpp uses QDom KF5::I18n KF5::Crash KF5::DBusAddons # KDEInitInterface ) if (UNIX) target_link_libraries(KF5KIOCore PRIVATE KF5::Auth) #SlaveBase uses KAuth::Action endif() if(ACL_FOUND) target_link_libraries(KF5KIOCore PRIVATE ${ACL_LIBS}) endif() set_target_properties(KF5KIOCore PROPERTIES VERSION ${KIO_VERSION_STRING} SOVERSION ${KIO_SOVERSION} EXPORT_NAME KIOCore ) # this should be done by cmake, see bug 371721 if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND Qt5Core_VERSION VERSION_GREATER 5.8.0) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h COMMAND "${CMAKE_CXX_COMPILER}" "-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp" > ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h ) set_property(TARGET KF5KIOCore APPEND PROPERTY AUTOMOC_MOC_OPTIONS --include ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h) set_property(TARGET KF5KIOCore APPEND PROPERTY AUTOGEN_TARGET_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h) endif() # Headers prefixed with KIO/ ecm_generate_headers(KIOCore_CamelCase_HEADERS HEADER_NAMES IdleSlave ConnectionServer TCPSlaveBase DirectorySizeJob ForwardingSlaveBase Job # ### should forward to job_base.h, not job.h... JobTracker Global ChmodJob DeleteJob CopyJob EmptyTrashJob FileJob MkdirJob MkpathJob SlaveBase SlaveConfig HostInfo MetaData UDSEntry JobUiDelegateExtension JobUiDelegateFactory SlaveInterface Slave FileCopyJob ListJob MimetypeJob MultiGetJob RestoreJob SimpleJob SpecialJob StatJob StoredTransferJob TransferJob Scheduler AuthInfo DavJob DesktopExecParser FileSystemFreeSpaceJob BatchRenameJob PREFIX KIO REQUIRED_HEADERS KIO_namespaced_HEADERS ) # Create local forwarding header for kio/job_base.h set(REGULAR_HEADER_NAME ${CMAKE_CURRENT_BINARY_DIR}/kio/job_base.h) if (NOT EXISTS ${REGULAR_HEADER_NAME}) file(WRITE ${REGULAR_HEADER_NAME} "#include \"${CMAKE_CURRENT_SOURCE_DIR}/job_base.h\"\n") endif() install(TARGETS KF5KIOCore EXPORT KF5KIOTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) list(APPEND KIO_namespaced_HEADERS http_slave_defaults.h ioslave_defaults.h job_base.h jobclasses.h ) install(FILES ${KIO_namespaced_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore/kio COMPONENT Devel ) # Headers not prefixed with KIO/ ecm_generate_headers(KIOCore_HEADERS HEADER_NAMES KACL KUrlAuthorized KCoreDirLister KRemoteEncoding KDirNotify KDiskFreeSpaceInfo KFileItem KFileItemListProperties KMountPoint KNFSShare KSambaShare KSambaShareData KPasswdServerClient KProtocolInfo KProtocolManager KRecentDocument KSslCertificateManager KTcpSocket REQUIRED_HEADERS KIOCore_HEADERS ) ecm_generate_headers(KIOCore_HEADERS HEADER_NAMES KSSLSettings RELATIVE kssl REQUIRED_HEADERS KIOCore_HEADERS ) install(FILES ${KIOCore_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore/KIO COMPONENT Devel) install(FILES ksslcertificatemanager_p.h ${KIOCore_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kiocore_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore COMPONENT Devel) install(FILES accept-languages.codes DESTINATION ${KDE_INSTALL_CONFDIR}) # make available to ecm_add_qch in parent folder set(KIOCore_QCH_SOURCES ${KIOCore_HEADERS} ${KIO_namespaced_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KIOCore LIB_NAME KF5KIOCore DEPS "KCoreAddons KService" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOCore) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/core/dataprotocol.cpp b/src/core/dataprotocol.cpp index 2891e6ec..77791d8a 100644 --- a/src/core/dataprotocol.cpp +++ b/src/core/dataprotocol.cpp @@ -1,326 +1,329 @@ // dataprotocol.cpp // ================== // // Implementation of the data protocol (rfc 2397) // // Author: Leo Savernik // Email: l.savernik@aon.at // Copyright (C) 2002, 2003 by Leo Savernik // Created: Sam Dez 28 14:11:18 CET 2002 /*************************************************************************** * * * This program 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; version 2. * * * ***************************************************************************/ +// TODO: remove me +#undef QT_NO_CAST_FROM_ASCII + #include "dataprotocol_p.h" #include "global.h" #include #include #include #include #ifdef DATAKIOSLAVE # include # include #endif #if !defined(DATAKIOSLAVE) # define DISPATCH(f) dispatch_##f #else # define DISPATCH(f) f #endif using namespace KIO; #ifdef DATAKIOSLAVE extern "C" { int kdemain(int argc, char **argv) { //qDebug() << "*** Starting kio_data "; if (argc != 4) { //qDebug() << "Usage: kio_data protocol domain-socket1 domain-socket2"; exit(-1); } DataProtocol slave(argv[2], argv[3]); slave.dispatchLoop(); //qDebug() << "*** kio_data Done"; return 0; } } #endif /** structure containing header information */ struct DataHeader { QString mime_type; // mime type of content (lowercase) MetaData attributes; // attribute/value pairs (attribute lowercase, // value unchanged) bool is_base64; // true if data is base64 encoded QByteArray url; // reference to decoded url int data_offset; // zero-indexed position within url // where the real data begins. May point beyond // the end to indicate that there is no data }; /** returns the position of the first occurrence of any of the given * characters @p c1 or comma (',') or semicolon (';') or buf.length() * if none is contained. * * @param buf buffer where to look for c * @param begin zero-indexed starting position * @param c1 character to find or '\0' to ignore */ static int find(const QByteArray &buf, int begin, const char c1) { static const char comma = ','; static const char semicolon = ';'; int pos = begin; int size = buf.length(); while (pos < size) { const char ch = buf[pos]; if (ch == comma || ch == semicolon || (c1 != '\0' && ch == c1)) { break; } pos++; }/*wend*/ return pos; } /** extracts the string between the current position @p pos and the first * occurrence of either @p c1 or comma (',') or semicolon (';') exclusively * and updates @p pos to point at the found delimiter or at the end of the * buffer if neither character occurred. * @param buf buffer where to look for * @param pos zero-indexed position within buffer * @param c1 character to find or '\0' to ignore */ static inline QString extract(const QByteArray &buf, int &pos, const char c1 = '\0') { int oldpos = pos; pos = find(buf, oldpos, c1); return buf.mid(oldpos, pos - oldpos); } /** ignores all whitespaces * @param buf buffer to operate on * @param pos position to shift to first non-whitespace character * Upon return @p pos will either point to the first non-whitespace * character or to the end of the buffer. */ static inline void ignoreWS(const QString &buf, int &pos) { int size = buf.length(); while (pos < size && buf[pos].isSpace()) { ++pos; } } /** parses a quoted string as per rfc 822. * * If trailing quote is missing, the whole rest of the buffer is returned. * @param buf buffer to operate on * @param pos position pointing to the leading quote * @return the extracted string. @p pos will be updated to point to the * character following the trailing quote. */ static QString parseQuotedString(const QString &buf, int &pos) { int size = buf.length(); QString res; res.reserve(size); // can't be larger than buf pos++; // jump over leading quote bool escaped = false; // if true means next character is literal bool parsing = true; // true as long as end quote not found while (parsing && pos < size) { const QChar ch = buf[pos++]; if (escaped) { res += ch; escaped = false; } else { switch (ch.unicode()) { case '"': parsing = false; break; case '\\': escaped = true; break; default: res += ch; break; }/*end switch*/ }/*end if*/ }/*wend*/ res.squeeze(); return res; } /** parses the header of a data url * @param url the data url * @param mimeOnly if the only interesting information is the mime type * @return DataHeader structure with the header information */ static DataHeader parseDataHeader(const QUrl &url, const bool mimeOnly) { DataHeader header_info; // initialize header info members header_info.mime_type = QStringLiteral("text/plain"); header_info.attributes.insert(QStringLiteral("charset"), QStringLiteral("us-ascii")); header_info.is_base64 = false; // decode url and save it const QByteArray &raw_url = header_info.url = QByteArray::fromPercentEncoding(url.path(QUrl::FullyEncoded).toLatin1()); const int raw_url_len = raw_url.length(); header_info.data_offset = 0; // read mime type if (raw_url_len == 0) { return header_info; } const QString mime_type = extract(raw_url, header_info.data_offset).trimmed(); if (!mime_type.isEmpty()) { header_info.mime_type = mime_type; } if (mimeOnly) { return header_info; } if (header_info.data_offset >= raw_url_len) { return header_info; } // jump over delimiter token and return if data reached if (raw_url[header_info.data_offset++] == QLatin1Char(',')) { return header_info; } // read all attributes and store them bool data_begin_reached = false; while (!data_begin_reached && header_info.data_offset < raw_url_len) { // read attribute const QString attribute = extract(raw_url, header_info.data_offset, '=').trimmed(); if (header_info.data_offset >= raw_url_len || raw_url[header_info.data_offset] != QLatin1Char('=')) { // no assignment, must be base64 option if (attribute == QLatin1String("base64")) { header_info.is_base64 = true; } } else { header_info.data_offset++; // jump over '=' token // read value ignoreWS(raw_url, header_info.data_offset); if (header_info.data_offset >= raw_url_len) { return header_info; } QString value; if (raw_url[header_info.data_offset] == QLatin1Char('"')) { value = parseQuotedString(raw_url, header_info.data_offset); ignoreWS(raw_url, header_info.data_offset); } else { value = extract(raw_url, header_info.data_offset).trimmed(); } // add attribute to map header_info.attributes[attribute.toLower()] = value; }/*end if*/ if (header_info.data_offset < raw_url_len && raw_url[header_info.data_offset] == QLatin1Char(',')) { data_begin_reached = true; } header_info.data_offset++; // jump over separator token }/*wend*/ return header_info; } #ifdef DATAKIOSLAVE DataProtocol::DataProtocol(const QByteArray &pool_socket, const QByteArray &app_socket) : SlaveBase("kio_data", pool_socket, app_socket) { #else DataProtocol::DataProtocol() { #endif //qDebug(); } /* --------------------------------------------------------------------- */ DataProtocol::~DataProtocol() { //qDebug(); } /* --------------------------------------------------------------------- */ void DataProtocol::get(const QUrl &url) { ref(); //qDebug() << this; const DataHeader hdr = parseDataHeader(url, false); const int size = hdr.url.length(); const int data_ofs = qMin(hdr.data_offset, size); // FIXME: string is copied, would be nice if we could have a reference only const QByteArray url_data = hdr.url.mid(data_ofs); QByteArray outData; if (hdr.is_base64) { // base64 stuff is expected to contain the correct charset, so we just // decode it and pass it to the receiver outData = QByteArray::fromBase64(url_data); } else { QTextCodec *codec = QTextCodec::codecForName(hdr.attributes[QStringLiteral("charset")].toLatin1()); if (codec != nullptr) { outData = codec->toUnicode(url_data).toUtf8(); } else { outData = url_data; }/*end if*/ }/*end if*/ //qDebug() << "emit mimeType@"< 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. */ +// TODO: remove me +#undef QT_NO_CAST_FROM_ASCII + #include "forwardingslavebase.h" #include "../pathhelpers_p.h" #include "deletejob.h" #include "mkdirjob.h" #include "job.h" #include #include namespace KIO { class ForwardingSlaveBasePrivate { public: ForwardingSlaveBasePrivate(QObject *eventLoopParent, ForwardingSlaveBase *qq) : q(qq) , eventLoop(eventLoopParent) {} ForwardingSlaveBase * const q; QUrl m_processedURL; QUrl m_requestedURL; QEventLoop eventLoop; bool internalRewriteUrl(const QUrl &url, QUrl &newURL); void connectJob(Job *job); void connectSimpleJob(SimpleJob *job); void connectListJob(ListJob *job); void connectTransferJob(TransferJob *job); void _k_slotResult(KJob *job); void _k_slotWarning(KJob *job, const QString &msg); void _k_slotInfoMessage(KJob *job, const QString &msg); void _k_slotTotalSize(KJob *job, qulonglong size); void _k_slotProcessedSize(KJob *job, qulonglong size); void _k_slotSpeed(KJob *job, unsigned long bytesPerSecond); // KIO::SimpleJob subclasses void _k_slotRedirection(KIO::Job *job, const QUrl &url); // KIO::ListJob void _k_slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries); // KIO::TransferJob void _k_slotData(KIO::Job *job, const QByteArray &data); void _k_slotDataReq(KIO::Job *job, QByteArray &data); void _k_slotMimetype(KIO::Job *job, const QString &type); void _k_slotCanResume(KIO::Job *job, KIO::filesize_t offset); }; ForwardingSlaveBase::ForwardingSlaveBase(const QByteArray &protocol, const QByteArray &poolSocket, const QByteArray &appSocket) : QObject(), SlaveBase(protocol, poolSocket, appSocket), d(new ForwardingSlaveBasePrivate(this, this)) { } ForwardingSlaveBase::~ForwardingSlaveBase() { delete d; } bool ForwardingSlaveBasePrivate::internalRewriteUrl(const QUrl &url, QUrl &newURL) { bool result = true; if (url.scheme() == q->mProtocol) { result = q->rewriteUrl(url, newURL); } else { newURL = url; } m_processedURL = newURL; m_requestedURL = url; return result; } void ForwardingSlaveBase::prepareUDSEntry(KIO::UDSEntry &entry, bool listing) const { //qDebug() << "listing==" << listing; const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME); QString mimetype = entry.stringValue(KIO::UDSEntry::UDS_MIME_TYPE); QUrl url; const QString urlStr = entry.stringValue(KIO::UDSEntry::UDS_URL); const bool url_found = !urlStr.isEmpty(); if (url_found) { url = QUrl(urlStr); QUrl new_url(d->m_requestedURL); if (listing) { new_url.setPath(concatPaths(new_url.path(), url.fileName())); } // ## Didn't find a way to use an iterator instead of re-doing a key lookup entry.replace(KIO::UDSEntry::UDS_URL, new_url.toString()); //qDebug() << "URL =" << url; //qDebug() << "New URL =" << new_url; } if (mimetype.isEmpty()) { QUrl new_url(d->m_processedURL); if (url_found && listing) { new_url.setPath(concatPaths(new_url.path(), url.fileName())); } else if (listing) { new_url.setPath(concatPaths(new_url.path(), name)); } QMimeDatabase db; mimetype = db.mimeTypeForUrl(new_url).name(); entry.replace(KIO::UDSEntry::UDS_MIME_TYPE, mimetype); //qDebug() << "New Mimetype = " << mimetype; } if (d->m_processedURL.isLocalFile()) { QUrl new_url(d->m_processedURL); if (listing) { new_url.setPath(concatPaths(new_url.path(), name)); } entry.replace(KIO::UDSEntry::UDS_LOCAL_PATH, new_url.toLocalFile()); } } QUrl ForwardingSlaveBase::processedUrl() const { return d->m_processedURL; } QUrl ForwardingSlaveBase::requestedUrl() const { return d->m_requestedURL; } void ForwardingSlaveBase::get(const QUrl &url) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::TransferJob *job = KIO::get(new_url, NoReload, HideProgressInfo); d->connectTransferJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } } void ForwardingSlaveBase::put(const QUrl &url, int permissions, JobFlags flags) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::TransferJob *job = KIO::put(new_url, permissions, flags | HideProgressInfo); d->connectTransferJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_MALFORMED_URL, url.toDisplayString()); } } void ForwardingSlaveBase::stat(const QUrl &url) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::SimpleJob *job = KIO::stat(new_url, KIO::HideProgressInfo); d->connectSimpleJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } } void ForwardingSlaveBase::mimetype(const QUrl &url) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::TransferJob *job = KIO::mimetype(new_url, KIO::HideProgressInfo); d->connectTransferJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } } void ForwardingSlaveBase::listDir(const QUrl &url) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::ListJob *job = KIO::listDir(new_url, KIO::HideProgressInfo); d->connectListJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } } void ForwardingSlaveBase::mkdir(const QUrl &url, int permissions) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::SimpleJob *job = KIO::mkdir(new_url, permissions); d->connectSimpleJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_MALFORMED_URL, url.toDisplayString()); } } void ForwardingSlaveBase::rename(const QUrl &src, const QUrl &dest, JobFlags flags) { //qDebug() << src << "," << dest; QUrl new_src, new_dest; if (!d->internalRewriteUrl(src, new_src)) { error(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString()); } else if (d->internalRewriteUrl(dest, new_dest)) { KIO::Job *job = KIO::rename(new_src, new_dest, flags); d->connectJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_MALFORMED_URL, dest.toDisplayString()); } } void ForwardingSlaveBase::symlink(const QString &target, const QUrl &dest, JobFlags flags) { //qDebug() << target << ", " << dest; QUrl new_dest; if (d->internalRewriteUrl(dest, new_dest)) { KIO::SimpleJob *job = KIO::symlink(target, new_dest, flags | HideProgressInfo); d->connectSimpleJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_MALFORMED_URL, dest.toDisplayString()); } } void ForwardingSlaveBase::chmod(const QUrl &url, int permissions) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::SimpleJob *job = KIO::chmod(new_url, permissions); d->connectSimpleJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } } void ForwardingSlaveBase::setModificationTime(const QUrl &url, const QDateTime &mtime) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { KIO::SimpleJob *job = KIO::setModificationTime(new_url, mtime); d->connectSimpleJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } } void ForwardingSlaveBase::copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) { //qDebug() << src << "," << dest; QUrl new_src, new_dest; if (!d->internalRewriteUrl(src, new_src)) { error(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString()); } else if (d->internalRewriteUrl(dest, new_dest)) { KIO::Job *job = KIO::file_copy(new_src, new_dest, permissions, flags | HideProgressInfo); d->connectJob(job); d->eventLoop.exec(); } else { error(KIO::ERR_MALFORMED_URL, dest.toDisplayString()); } } void ForwardingSlaveBase::del(const QUrl &url, bool isfile) { //qDebug() << url; QUrl new_url; if (d->internalRewriteUrl(url, new_url)) { if (isfile) { KIO::DeleteJob *job = KIO::del(new_url, HideProgressInfo); d->connectJob(job); } else { KIO::SimpleJob *job = KIO::rmdir(new_url); d->connectSimpleJob(job); } d->eventLoop.exec(); } else { error(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString()); } } ////////////////////////////////////////////////////////////////////////////// void ForwardingSlaveBasePrivate::connectJob(KIO::Job *job) { // We will forward the warning message, no need to let the job // display it itself job->setUiDelegate(nullptr); // Forward metadata (e.g. modification time for put()) job->setMetaData(q->allMetaData()); #if 0 // debug code //qDebug() << "transferring metadata:"; const MetaData md = allMetaData(); for (MetaData::const_iterator it = md.begin(); it != md.end(); ++it) //qDebug() << it.key() << " = " << it.data(); #endif q->connect(job, SIGNAL(result(KJob*)), SLOT(_k_slotResult(KJob*))); q->connect(job, SIGNAL(warning(KJob*,QString,QString)), SLOT(_k_slotWarning(KJob*,QString))); q->connect(job, SIGNAL(infoMessage(KJob*,QString,QString)), SLOT(_k_slotInfoMessage(KJob*,QString))); q->connect(job, SIGNAL(totalSize(KJob*,qulonglong)), SLOT(_k_slotTotalSize(KJob*,qulonglong))); q->connect(job, SIGNAL(processedSize(KJob*,qulonglong)), SLOT(_k_slotProcessedSize(KJob*,qulonglong))); q->connect(job, SIGNAL(speed(KJob*,ulong)), SLOT(_k_slotSpeed(KJob*,ulong))); } void ForwardingSlaveBasePrivate::connectSimpleJob(KIO::SimpleJob *job) { connectJob(job); if (job->metaObject()->indexOfSignal("redirection(KIO::Job*,QUrl)") > -1) { q->connect(job, SIGNAL(redirection(KIO::Job*,QUrl)), SLOT(_k_slotRedirection(KIO::Job*,QUrl))); } } void ForwardingSlaveBasePrivate::connectListJob(KIO::ListJob *job) { connectSimpleJob(job); q->connect(job, SIGNAL(entries(KIO::Job*,KIO::UDSEntryList)), SLOT(_k_slotEntries(KIO::Job*,KIO::UDSEntryList))); } void ForwardingSlaveBasePrivate::connectTransferJob(KIO::TransferJob *job) { connectSimpleJob(job); q->connect(job, SIGNAL(data(KIO::Job*,QByteArray)), SLOT(_k_slotData(KIO::Job*,QByteArray))); q->connect(job, SIGNAL(dataReq(KIO::Job*,QByteArray&)), SLOT(_k_slotDataReq(KIO::Job*,QByteArray&))); q->connect(job, SIGNAL(mimetype(KIO::Job*,QString)), SLOT(_k_slotMimetype(KIO::Job*,QString))); q->connect(job, SIGNAL(canResume(KIO::Job*,KIO::filesize_t)), SLOT(_k_slotCanResume(KIO::Job*,KIO::filesize_t))); } ////////////////////////////////////////////////////////////////////////////// void ForwardingSlaveBasePrivate::_k_slotResult(KJob *job) { if (job->error() != 0) { q->error(job->error(), job->errorText()); } else { KIO::StatJob *stat_job = qobject_cast(job); if (stat_job != nullptr) { KIO::UDSEntry entry = stat_job->statResult(); q->prepareUDSEntry(entry); q->statEntry(entry); } q->finished(); } eventLoop.exit(); } void ForwardingSlaveBasePrivate::_k_slotWarning(KJob * /*job*/, const QString &msg) { q->warning(msg); } void ForwardingSlaveBasePrivate::_k_slotInfoMessage(KJob * /*job*/, const QString &msg) { q->infoMessage(msg); } void ForwardingSlaveBasePrivate::_k_slotTotalSize(KJob * /*job*/, qulonglong size) { q->totalSize(size); } void ForwardingSlaveBasePrivate::_k_slotProcessedSize(KJob * /*job*/, qulonglong size) { q->processedSize(size); } void ForwardingSlaveBasePrivate::_k_slotSpeed(KJob * /*job*/, unsigned long bytesPerSecond) { q->speed(bytesPerSecond); } void ForwardingSlaveBasePrivate::_k_slotRedirection(KIO::Job *job, const QUrl &url) { q->redirection(url); // We've been redirected stop everything. job->kill(KJob::Quietly); q->finished(); eventLoop.exit(); } void ForwardingSlaveBasePrivate::_k_slotEntries(KIO::Job * /*job*/, const KIO::UDSEntryList &entries) { KIO::UDSEntryList final_entries = entries; KIO::UDSEntryList::iterator it = final_entries.begin(); const KIO::UDSEntryList::iterator end = final_entries.end(); for (; it != end; ++it) { q->prepareUDSEntry(*it, true); } q->listEntries(final_entries); } void ForwardingSlaveBasePrivate::_k_slotData(KIO::Job * /*job*/, const QByteArray &_data) { q->data(_data); } void ForwardingSlaveBasePrivate::_k_slotDataReq(KIO::Job * /*job*/, QByteArray &data) { q->dataReq(); q->readData(data); } void ForwardingSlaveBasePrivate::_k_slotMimetype(KIO::Job * /*job*/, const QString &type) { q->mimeType(type); } void ForwardingSlaveBasePrivate::_k_slotCanResume(KIO::Job * /*job*/, KIO::filesize_t offset) { q->canResume(offset); } } #include "moc_forwardingslavebase.cpp" diff --git a/src/core/kremoteencoding.cpp b/src/core/kremoteencoding.cpp index 361e5320..628636c5 100644 --- a/src/core/kremoteencoding.cpp +++ b/src/core/kremoteencoding.cpp @@ -1,129 +1,132 @@ /* This file is part of the KDE libraries Copyright (C) 2003 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_BYTEARRAY + #include "kremoteencoding.h" #include #include #include class KRemoteEncodingPrivate { public: KRemoteEncodingPrivate() : m_codec(nullptr) { } QTextCodec *m_codec; }; KRemoteEncoding::KRemoteEncoding(const char *name) : d(new KRemoteEncodingPrivate) { setEncoding(name); } KRemoteEncoding::~KRemoteEncoding() { delete d; } QString KRemoteEncoding::decode(const QByteArray &name) const { #ifdef CHECK_UTF8 if (d->m_codec->mibEnum() == 106 && !KStringHandler::isUtf8(name)) { return QLatin1String(name); } #endif QString result = d->m_codec->toUnicode(name); if (d->m_codec->fromUnicode(result) != name) // fallback in case of decoding failure { return QLatin1String(name); } return result; } QByteArray KRemoteEncoding::encode(const QString &name) const { QByteArray result = d->m_codec->fromUnicode(name); if (d->m_codec->toUnicode(result) != name) { return name.toLatin1(); } return result; } QByteArray KRemoteEncoding::encode(const QUrl &url) const { return encode(url.path()); } QByteArray KRemoteEncoding::directory(const QUrl &url, bool ignore_trailing_slash) const { QUrl dirUrl(url); if (ignore_trailing_slash && dirUrl.path().endsWith(QLatin1Char('/'))) { dirUrl = dirUrl.adjusted(QUrl::StripTrailingSlash); } const QString dir = dirUrl.adjusted(QUrl::RemoveFilename).path(); return encode(dir); } QByteArray KRemoteEncoding::fileName(const QUrl &url) const { return encode(url.fileName()); } const char *KRemoteEncoding::encoding() const { return d->m_codec->name(); } int KRemoteEncoding::encodingMib() const { return d->m_codec->mibEnum(); } void KRemoteEncoding::setEncoding(const char *name) { // don't delete codecs if (name) { d->m_codec = QTextCodec::codecForName(name); } if (d->m_codec == nullptr) { d->m_codec = QTextCodec::codecForMib(106); // fallback to UTF-8 } if (d->m_codec == nullptr) { d->m_codec = QTextCodec::codecForMib(4 /* latin-1 */); } Q_ASSERT(d->m_codec); /*qDebug() << "setting encoding" << d->m_codec->name() << "for name=" << name;*/ } void KRemoteEncoding::virtual_hook(int, void *) { } diff --git a/src/core/slavebase.cpp b/src/core/slavebase.cpp index 5f71b59f..a3d6841d 100644 --- a/src/core/slavebase.cpp +++ b/src/core/slavebase.cpp @@ -1,1479 +1,1482 @@ /* * 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); } ~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->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/CMakeLists.txt b/src/filewidgets/CMakeLists.txt index c4cd5f2b..982b0b1b 100644 --- a/src/filewidgets/CMakeLists.txt +++ b/src/filewidgets/CMakeLists.txt @@ -1,109 +1,106 @@ project(KIOFileWidgets) find_package(KF5Bookmarks ${KF5_DEP_VERSION} REQUIRED) find_package(KF5XmlGui ${KF5_DEP_VERSION} REQUIRED) configure_file(config-kiofilewidgets.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kiofilewidgets.h) -# TODO: Remove these -remove_definitions(-DQT_NO_CAST_FROM_ASCII) - set(kiofilewidgets_SRCS kstatusbarofflineindicator.cpp kfilemetapreview.cpp kimagefilepreview.cpp kpreviewwidgetbase.cpp krecentdirs.cpp defaultviewadapter.cpp kdiroperator.cpp kdiroperatordetailview.cpp kdirsortfilterproxymodel.cpp #used in combination with kdirmodel.cpp kencodingfiledialog.cpp kfilebookmarkhandler.cpp kfilecopytomenu.cpp kfilecustomdialog.cpp kfilefiltercombo.cpp kfilewidget.cpp kfilewidgetdocktitlebar.cpp kfileplacesitem.cpp kfileplacesmodel.cpp kfileplacesview.cpp kfileplaceeditdialog.cpp kfilepreviewgenerator.cpp knameandurlinputdialog.cpp knewfilemenu.cpp kurlnavigatordropdownbutton.cpp kurlnavigatorbuttonbase.cpp kurlnavigatorbutton.cpp kurlnavigatorplacesselector.cpp kurlnavigatorprotocolcombo.cpp kurlnavigatortogglebutton.cpp kurlnavigator.cpp kurlnavigatormenu.cpp kurlnavigatorpathselectoreventfilter.cpp ) qt5_add_resources(kiofilewidgets_SRCS ../new_file_templates/templates.qrc) add_library(KF5KIOFileWidgets ${kiofilewidgets_SRCS}) generate_export_header(KF5KIOFileWidgets BASE_NAME KIOFileWidgets) add_library(KF5::KIOFileWidgets ALIAS KF5KIOFileWidgets) target_include_directories(KF5KIOFileWidgets INTERFACE "$") target_link_libraries(KF5KIOFileWidgets PUBLIC KF5::KIOWidgets KF5::Bookmarks # in KFilePlacesModel's API KF5::ItemViews # kdirsortfilterproxymodel KF5::XmlGui # for KActionCollection, used by KFileWidget/KDirOperator KF5::Solid # KFilePlacesModel/KFilePlacesView PRIVATE KF5::IconThemes # KIconLoader KF5::I18n ) set_target_properties(KF5KIOFileWidgets PROPERTIES VERSION ${KIO_VERSION_STRING} SOVERSION ${KIO_SOVERSION} EXPORT_NAME KIOFileWidgets ) ecm_generate_headers(KIOFileWidgets_HEADERS HEADER_NAMES KAbstractViewAdapter KImageFilePreview KPreviewWidgetBase KRecentDirs KStatusBarOfflineIndicator KDirOperator KDirSortFilterProxyModel KFileCopyToMenu KFileFilterCombo KFilePlacesModel KFilePlacesView KFilePreviewGenerator KFileWidget KUrlNavigator KNewFileMenu KNameAndUrlInputDialog KEncodingFileDialog REQUIRED_HEADERS KIOFileWidgets_HEADERS ) install(TARGETS KF5KIOFileWidgets EXPORT KF5KIOTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${KIOFileWidgets_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/kiofilewidgets_export.h DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOFileWidgets COMPONENT Devel) # make available to ecm_add_qch in parent folder set(KIOFileWidgets_QCH_SOURCES ${KIOFileWidgets_HEADERS} PARENT_SCOPE) include(ECMGeneratePriFile) ecm_generate_pri_file(BASE_NAME KIOFileWidgets LIB_NAME KF5KIOFileWidgets DEPS "KIOWidgets KBookmarks KXmlGui Solid" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KIOFileWidgets) install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/filewidgets/kencodingfiledialog.cpp b/src/filewidgets/kencodingfiledialog.cpp index dd4f0b33..b2825b92 100644 --- a/src/filewidgets/kencodingfiledialog.cpp +++ b/src/filewidgets/kencodingfiledialog.cpp @@ -1,284 +1,287 @@ // -*- c++ -*- /* This file is part of the KDE libraries Copyright (C) 2003 Joseph Wenninger 2003 Andras Mantia 2013 Teo Mrnjavac 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. */ +// TODO: remove me +#undef QT_NO_CAST_FROM_ASCII + #include "kencodingfiledialog.h" #include "kfilewidget.h" #include #include #include #include #include #include #include #include #include #include #include #include struct KEncodingFileDialogPrivate { KEncodingFileDialogPrivate() : cfgGroup(KSharedConfig::openConfig(), ConfigGroup) {} KComboBox *encoding; KFileWidget *w; KConfigGroup cfgGroup; }; KEncodingFileDialog::KEncodingFileDialog(const QUrl &startDir, const QString &encoding, const QString &filter, const QString &caption, QFileDialog::AcceptMode type, QWidget *parent) : QDialog(parent, Qt::Dialog) , d(new KEncodingFileDialogPrivate) { d->w = new KFileWidget(startDir, this); d->w->setFilter(filter); if (type == QFileDialog::AcceptOpen) { d->w->setOperationMode(KFileWidget::Opening); } else { d->w->setOperationMode(KFileWidget::Saving); } setWindowTitle(caption); //ops->clearHistory(); KWindowConfig::restoreWindowSize(windowHandle(), d->cfgGroup); QBoxLayout *mainLayout = new QVBoxLayout; setLayout(mainLayout); mainLayout->addWidget(d->w); d->w->okButton()->show(); connect(d->w->okButton(), &QAbstractButton::clicked, this, &KEncodingFileDialog::slotOk); d->w->cancelButton()->show(); connect(d->w->cancelButton(), &QAbstractButton::clicked, this, &KEncodingFileDialog::slotCancel); connect(d->w, &KFileWidget::accepted, this, &KEncodingFileDialog::accept); d->encoding = new KComboBox(this); d->w->setCustomWidget(i18n("Encoding:"), d->encoding); d->encoding->clear(); QString sEncoding = encoding; QString systemEncoding = QTextCodec::codecForLocale()->name(); if (sEncoding.isEmpty() || sEncoding == QLatin1String("System")) { sEncoding = systemEncoding; } const QStringList encodings(KCharsets::charsets()->availableEncodingNames()); int insert = 0, system = 0; bool foundRequested = false; foreach (const QString &encoding, encodings) { bool found = false; QTextCodec *codecForEnc = KCharsets::charsets()->codecForName(encoding, found); if (found) { d->encoding->addItem(encoding); if ((codecForEnc->name() == sEncoding) || (encoding == sEncoding)) { d->encoding->setCurrentIndex(insert); foundRequested = true; } if ((codecForEnc->name() == systemEncoding) || (encoding == systemEncoding)) { system = insert; } insert++; } } if (!foundRequested) { d->encoding->setCurrentIndex(system); } } KEncodingFileDialog::~KEncodingFileDialog() { delete d; } QString KEncodingFileDialog::selectedEncoding() const { if (d->encoding) { return d->encoding->currentText(); } else { return QString(); } } KEncodingFileDialog::Result KEncodingFileDialog::getOpenFileNameAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KEncodingFileDialog dlg(startDir, encoding, filter, caption.isNull() ? i18n("Open") : caption, QFileDialog::AcceptOpen, parent); dlg.d->w->setMode(KFile::File | KFile::LocalOnly); dlg.exec(); Result res; res.fileNames << dlg.d->w->selectedFile(); res.encoding = dlg.selectedEncoding(); return res; } KEncodingFileDialog::Result KEncodingFileDialog::getOpenFileNamesAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KEncodingFileDialog dlg(startDir, encoding, filter, caption.isNull() ? i18n("Open") : caption, QFileDialog::AcceptOpen, parent); dlg.d->w->setMode(KFile::Files | KFile::LocalOnly); dlg.exec(); Result res; res.fileNames = dlg.d->w->selectedFiles(); res.encoding = dlg.selectedEncoding(); return res; } KEncodingFileDialog::Result KEncodingFileDialog::getOpenUrlAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KEncodingFileDialog dlg(startDir, encoding, filter, caption.isNull() ? i18n("Open") : caption, QFileDialog::AcceptOpen, parent); dlg.d->w->setMode(KFile::File); dlg.exec(); Result res; res.URLs << dlg.d->w->selectedUrl(); res.encoding = dlg.selectedEncoding(); return res; } KEncodingFileDialog::Result KEncodingFileDialog::getOpenUrlsAndEncoding(const QString &encoding, const QUrl &startDir, const QString &filter, QWidget *parent, const QString &caption) { KEncodingFileDialog dlg(startDir, encoding, filter, caption.isNull() ? i18n("Open") : caption, QFileDialog::AcceptOpen, parent); dlg.d->w->setMode(KFile::Files); dlg.exec(); Result res; res.URLs = dlg.d->w->selectedUrls(); res.encoding = dlg.selectedEncoding(); return res; } KEncodingFileDialog::Result KEncodingFileDialog::getSaveFileNameAndEncoding(const QString &encoding, const QUrl &dir, const QString &filter, QWidget *parent, const QString &caption) { KEncodingFileDialog dlg(dir, encoding, filter, caption.isNull() ? i18n("Save As") : caption, QFileDialog::AcceptSave, parent); dlg.d->w->setMode(KFile::File); dlg.exec(); QString filename = dlg.d->w->selectedFile(); if (!filename.isEmpty()) { KRecentDocument::add(QUrl::fromLocalFile(filename)); } Result res; res.fileNames << filename; res.encoding = dlg.selectedEncoding(); return res; } KEncodingFileDialog::Result KEncodingFileDialog::getSaveUrlAndEncoding(const QString &encoding, const QUrl &dir, const QString &filter, QWidget *parent, const QString &caption) { KEncodingFileDialog dlg(dir, encoding, filter, caption.isNull() ? i18n("Save As") : caption, QFileDialog::AcceptSave, parent); dlg.d->w->setMode(KFile::File); Result res; if (dlg.exec() == QDialog::Accepted) { QUrl url = dlg.d->w->selectedUrl(); if (url.isValid()) { KRecentDocument::add(url); } res.URLs << url; res.encoding = dlg.selectedEncoding(); } return res; } QSize KEncodingFileDialog::sizeHint() const { return d->w->dialogSizeHint(); } void KEncodingFileDialog::hideEvent(QHideEvent *e) { KWindowConfig::saveWindowSize(windowHandle(), d->cfgGroup, KConfigBase::Persistent); QDialog::hideEvent(e); } void KEncodingFileDialog::accept() { d->w->accept(); QDialog::accept(); } void KEncodingFileDialog::slotOk() { d->w->slotOk(); } void KEncodingFileDialog::slotCancel() { d->w->slotCancel(); reject(); } #include "moc_kencodingfiledialog.cpp" diff --git a/src/filewidgets/kfileplacesmodel.cpp b/src/filewidgets/kfileplacesmodel.cpp index c7f155fe..bd5f4798 100644 --- a/src/filewidgets/kfileplacesmodel.cpp +++ b/src/filewidgets/kfileplacesmodel.cpp @@ -1,1278 +1,1282 @@ /* This file is part of the KDE project Copyright (C) 2007 Kevin Ottens Copyright (C) 2007 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. */ + +// TODO: remove me +#undef QT_NO_CAST_FROM_ASCII + #include "kfileplacesmodel.h" #include "kfileplacesitem_p.h" #ifdef _WIN32_WCE #include "Windows.h" #include "WinBase.h" #endif #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 namespace { QString stateNameForGroupType(KFilePlacesModel::GroupType type) { switch (type) { case KFilePlacesModel::PlacesType: return QStringLiteral("GroupState-Places-IsHidden"); case KFilePlacesModel::RemoteType: return QStringLiteral("GroupState-Remote-IsHidden"); case KFilePlacesModel::RecentlySavedType: return QStringLiteral("GroupState-RecentlySaved-IsHidden"); case KFilePlacesModel::SearchForType: return QStringLiteral("GroupState-SearchFor-IsHidden"); case KFilePlacesModel::DevicesType: return QStringLiteral("GroupState-Devices-IsHidden"); case KFilePlacesModel::RemovableDevicesType: return QStringLiteral("GroupState-RemovableDevices-IsHidden"); default: Q_UNREACHABLE(); } } static bool isFileIndexingEnabled() { KConfig config(QStringLiteral("baloofilerc")); KConfigGroup basicSettings = config.group("Basic Settings"); return basicSettings.readEntry("Indexing-Enabled", true); } static QString timelineDateString(int year, int month, int day = 0) { const QString dateFormat = QStringLiteral("%1-%2"); QString date = dateFormat.arg(year).arg(month, 2, 10, QLatin1Char('0')); if (day > 0) { date += QStringLiteral("-%1").arg(day, 2, 10, QLatin1Char('0')); } return date; } static QUrl createTimelineUrl(const QUrl &url) { // based on dolphin urls const QString timelinePrefix = QStringLiteral("timeline:") + QLatin1Char('/'); QUrl timelineUrl; const QString path = url.toDisplayString(QUrl::PreferLocalFile); if (path.endsWith(QLatin1String("/yesterday"))) { const QDate date = QDate::currentDate().addDays(-1); const int year = date.year(); const int month = date.month(); const int day = date.day(); timelineUrl = QUrl(timelinePrefix + timelineDateString(year, month) + QLatin1Char('/') + timelineDateString(year, month, day)); } else if (path.endsWith(QLatin1String("/thismonth"))) { const QDate date = QDate::currentDate(); timelineUrl = QUrl(timelinePrefix + timelineDateString(date.year(), date.month())); } else if (path.endsWith(QLatin1String("/lastmonth"))) { const QDate date = QDate::currentDate().addMonths(-1); timelineUrl = QUrl(timelinePrefix + timelineDateString(date.year(), date.month())); } else { Q_ASSERT(path.endsWith(QLatin1String("/today"))); timelineUrl = url; } return timelineUrl; } static QUrl createSearchUrl(const QUrl &url) { QUrl searchUrl = url; const QString path = url.toDisplayString(QUrl::PreferLocalFile); const QStringList validSearchPaths = { QStringLiteral("/documents"), QStringLiteral("/images"), QStringLiteral("/audio"), QStringLiteral("/videos") }; for (const QString &validPath : validSearchPaths) { if (path.endsWith(validPath)) { searchUrl.setScheme(QStringLiteral("baloosearch")); return searchUrl; } } qWarning() << "Invalid search url:" << url; return searchUrl; } } class Q_DECL_HIDDEN KFilePlacesModel::Private { public: explicit Private(KFilePlacesModel *self) : q(self), bookmarkManager(nullptr), fileIndexingEnabled(isFileIndexingEnabled()) { } ~Private() { qDeleteAll(items); } KFilePlacesModel * const q; QList items; QVector availableDevices; QMap setupInProgress; QStringList supportedSchemes; Solid::Predicate predicate; KBookmarkManager *bookmarkManager; const bool fileIndexingEnabled; QString alternativeApplicationName; void reloadAndSignal(); QList loadBookmarkList(); int findNearestPosition(int source, int target); void _k_initDeviceList(); void _k_deviceAdded(const QString &udi); void _k_deviceRemoved(const QString &udi); void _k_itemChanged(const QString &udi); void _k_reloadBookmarks(); void _k_storageSetupDone(Solid::ErrorType error, const QVariant &errorData); void _k_storageTeardownDone(Solid::ErrorType error, const QVariant &errorData); private: bool isBalooUrl(const QUrl &url) const; }; KFilePlacesModel::KFilePlacesModel(const QString &alternativeApplicationName, QObject *parent) : QAbstractItemModel(parent), d(new Private(this)) { const QString file = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QLatin1String("/user-places.xbel"); d->bookmarkManager = KBookmarkManager::managerForExternalFile(file); d->alternativeApplicationName = alternativeApplicationName; // Let's put some places in there if it's empty. KBookmarkGroup root = d->bookmarkManager->root(); const auto setDefaultMetadataItemForGroup = [&root](KFilePlacesModel::GroupType type) { root.setMetaDataItem(stateNameForGroupType(type), QStringLiteral("false")); }; if (root.first().isNull() || !QFile::exists(file)) { // NOTE: The context for these I18N_NOOP2 calls has to be "KFile System Bookmarks". // The real i18nc call is made later, with this context, so the two must match. // // createSystemBookmark actually does nothing with its third argument, // but we have to give it something so the I18N_NOOP2 calls stay here for now. // // (coles, 13th May 2009) KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Home"), I18N_NOOP2("KFile System Bookmarks", "Home"), QUrl::fromLocalFile(QDir::homePath()), QStringLiteral("user-home")); // Some distros may not create various standard XDG folders by default // so check for their existence before adding bookmarks for them const QString desktopFolder = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); if (QDir(desktopFolder).exists()) { KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Desktop"), I18N_NOOP2("KFile System Bookmarks", "Desktop"), QUrl::fromLocalFile(desktopFolder), QStringLiteral("user-desktop")); } const QString downloadFolder = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation); if (QDir(downloadFolder).exists()) { KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Downloads"), I18N_NOOP2("KFile System Bookmarks", "Downloads"), QUrl::fromLocalFile(downloadFolder), QStringLiteral("folder-downloads")); } KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Network"), I18N_NOOP2("KFile System Bookmarks", "Network"), QUrl(QStringLiteral("remote:/")), QStringLiteral("folder-network")); #if defined(_WIN32_WCE) // adding drives const QString driveIcon = QStringLiteral("drive-harddisk"); foreach (const QFileInfo &info, QDir::drives()) { KFilePlacesItem::createSystemBookmark(d->bookmarkManager, info.absoluteFilePath(), info.absoluteFilePath(), QUrl::fromLocalFile(info.absoluteFilePath()), driveIcon); } #elif !defined(Q_OS_WIN) KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Root"), I18N_NOOP2("KFile System Bookmarks", "Root"), QUrl::fromLocalFile(QStringLiteral("/")), QStringLiteral("folder-red")); #endif KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Trash"), I18N_NOOP2("KFile System Bookmarks", "Trash"), QUrl(QStringLiteral("trash:/")), QStringLiteral("user-trash")); setDefaultMetadataItemForGroup(PlacesType); setDefaultMetadataItemForGroup(RemoteType); setDefaultMetadataItemForGroup(DevicesType); setDefaultMetadataItemForGroup(RemovableDevicesType); // Force bookmarks to be saved. If on open/save dialog and the bookmarks are not saved, QFile::exists // will always return false, which opening/closing all the time the open/save dialog would case the // bookmarks to be added once each time, having lots of times each bookmark. (ereslibre) d->bookmarkManager->saveAs(file); } // if baloo is enabled, add new urls even if the bookmark file is not empty if (d->fileIndexingEnabled && root.metaDataItem(QStringLiteral("withBaloo")) != QLatin1String("true")) { root.setMetaDataItem(QStringLiteral("withBaloo"), QStringLiteral("true")); KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Today"), I18N_NOOP2("KFile System Bookmarks", "Today"), QUrl(QStringLiteral("timeline:/today")), QStringLiteral("go-jump-today")); KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Yesterday"), I18N_NOOP2("KFile System Bookmarks", "Yesterday"), QUrl(QStringLiteral("timeline:/yesterday")), QStringLiteral("view-calendar-day")); KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Documents"), I18N_NOOP2("KFile System Bookmarks", "Documents"), QUrl(QStringLiteral("search:/documents")), QStringLiteral("folder-text")); KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Images"), I18N_NOOP2("KFile System Bookmarks", "Images"), QUrl(QStringLiteral("search:/images")), QStringLiteral("folder-images")); KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Audio Files"), I18N_NOOP2("KFile System Bookmarks", "Audio Files"), QUrl(QStringLiteral("search:/audio")), QStringLiteral("folder-sound")); KFilePlacesItem::createSystemBookmark(d->bookmarkManager, QStringLiteral("Videos"), I18N_NOOP2("KFile System Bookmarks", "Videos"), QUrl(QStringLiteral("search:/videos")), QStringLiteral("folder-videos")); setDefaultMetadataItemForGroup(SearchForType); setDefaultMetadataItemForGroup(RecentlySavedType); d->bookmarkManager->save(); } QString predicate(QString::fromLatin1("[[[[ StorageVolume.ignored == false AND [ StorageVolume.usage == 'FileSystem' OR StorageVolume.usage == 'Encrypted' ]]" " OR " "[ IS StorageAccess AND StorageDrive.driveType == 'Floppy' ]]" " OR " "OpticalDisc.availableContent & 'Audio' ]" " OR " "StorageAccess.ignored == false ]")); if (KProtocolInfo::isKnownProtocol(QStringLiteral("mtp"))) { predicate.prepend(QLatin1Char('[')); predicate.append(QLatin1String(" OR PortableMediaPlayer.supportedProtocols == 'mtp']")); } d->predicate = Solid::Predicate::fromString(predicate); Q_ASSERT(d->predicate.isValid()); connect(d->bookmarkManager, SIGNAL(changed(QString,QString)), this, SLOT(_k_reloadBookmarks())); connect(d->bookmarkManager, SIGNAL(bookmarksChanged(QString)), this, SLOT(_k_reloadBookmarks())); d->_k_reloadBookmarks(); QTimer::singleShot(0, this, SLOT(_k_initDeviceList())); } KFilePlacesModel::KFilePlacesModel(QObject *parent) : KFilePlacesModel({}, parent) { } KFilePlacesModel::~KFilePlacesModel() { delete d; } QUrl KFilePlacesModel::url(const QModelIndex &index) const { return data(index, UrlRole).toUrl(); } bool KFilePlacesModel::setupNeeded(const QModelIndex &index) const { return data(index, SetupNeededRole).toBool(); } QIcon KFilePlacesModel::icon(const QModelIndex &index) const { return data(index, Qt::DecorationRole).value(); } QString KFilePlacesModel::text(const QModelIndex &index) const { return data(index, Qt::DisplayRole).toString(); } bool KFilePlacesModel::isHidden(const QModelIndex &index) const { //Note: we do not want to show an index if its parent is hidden return data(index, HiddenRole).toBool() || isGroupHidden(index); } bool KFilePlacesModel::isGroupHidden(const GroupType type) const { const QString hidden = d->bookmarkManager->root().metaDataItem(stateNameForGroupType(type)); return hidden == QStringLiteral("true") ? true : false; } bool KFilePlacesModel::isGroupHidden(const QModelIndex &index) const { if (!index.isValid()) { return false; } KFilePlacesItem *item = static_cast(index.internalPointer()); return isGroupHidden(item->groupType()); } bool KFilePlacesModel::isDevice(const QModelIndex &index) const { if (!index.isValid()) { return false; } KFilePlacesItem *item = static_cast(index.internalPointer()); return item->isDevice(); } Solid::Device KFilePlacesModel::deviceForIndex(const QModelIndex &index) const { if (!index.isValid()) { return Solid::Device(); } KFilePlacesItem *item = static_cast(index.internalPointer()); if (item->isDevice()) { return item->device(); } else { return Solid::Device(); } } KBookmark KFilePlacesModel::bookmarkForIndex(const QModelIndex &index) const { if (!index.isValid()) { return KBookmark(); } KFilePlacesItem *item = static_cast(index.internalPointer()); return item->bookmark(); } KFilePlacesModel::GroupType KFilePlacesModel::groupType(const QModelIndex &index) const { if (!index.isValid()) { return UnknownType; } KFilePlacesItem *item = static_cast(index.internalPointer()); return item->groupType(); } QModelIndexList KFilePlacesModel::groupIndexes(const KFilePlacesModel::GroupType type) const { if (type == UnknownType) { return QModelIndexList(); } QModelIndexList indexes; const int rows = rowCount(); for (int row = 0; row < rows ; ++row) { const QModelIndex current = index(row, 0); if (groupType(current) == type) { indexes << current; } } return indexes; } QVariant KFilePlacesModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } KFilePlacesItem *item = static_cast(index.internalPointer()); if (role == KFilePlacesModel::GroupHiddenRole) { return isGroupHidden(item->groupType()); } else { return item->data(role); } } QModelIndex KFilePlacesModel::index(int row, int column, const QModelIndex &parent) const { if (row < 0 || column != 0 || row >= d->items.size()) { return QModelIndex(); } if (parent.isValid()) { return QModelIndex(); } return createIndex(row, column, d->items.at(row)); } QModelIndex KFilePlacesModel::parent(const QModelIndex &child) const { Q_UNUSED(child); return QModelIndex(); } int KFilePlacesModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; } else { return d->items.size(); } } int KFilePlacesModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) // We only know 1 piece of information for a particular entry return 1; } QModelIndex KFilePlacesModel::closestItem(const QUrl &url) const { int foundRow = -1; int maxLength = 0; // Search the item which is equal to the URL or at least is a parent URL. // If there are more than one possible item URL candidates, choose the item // which covers the bigger range of the URL. for (int row = 0; row < d->items.size(); ++row) { KFilePlacesItem *item = d->items[row]; const QUrl itemUrl(item->data(UrlRole).toUrl()); if (itemUrl.matches(url, QUrl::StripTrailingSlash) || itemUrl.isParentOf(url)) { const int length = itemUrl.toString().length(); if (length > maxLength) { foundRow = row; maxLength = length; } } } if (foundRow == -1) { return QModelIndex(); } else { return createIndex(foundRow, 0, d->items[foundRow]); } } void KFilePlacesModel::Private::_k_initDeviceList() { Solid::DeviceNotifier *notifier = Solid::DeviceNotifier::instance(); connect(notifier, SIGNAL(deviceAdded(QString)), q, SLOT(_k_deviceAdded(QString))); connect(notifier, SIGNAL(deviceRemoved(QString)), q, SLOT(_k_deviceRemoved(QString))); const QList &deviceList = Solid::Device::listFromQuery(predicate); foreach (const Solid::Device &device, deviceList) { availableDevices << device.udi(); } _k_reloadBookmarks(); } void KFilePlacesModel::Private::_k_deviceAdded(const QString &udi) { Solid::Device d(udi); if (predicate.matches(d)) { availableDevices << udi; _k_reloadBookmarks(); } } void KFilePlacesModel::Private::_k_deviceRemoved(const QString &udi) { auto it = std::find(availableDevices.begin(), availableDevices.end(), udi); if (it != availableDevices.end()) { availableDevices.erase(it); _k_reloadBookmarks(); } } void KFilePlacesModel::Private::_k_itemChanged(const QString &id) { for (int row = 0; row < items.size(); ++row) { if (items.at(row)->id() == id) { QModelIndex index = q->index(row, 0); emit q->dataChanged(index, index); } } } void KFilePlacesModel::Private::_k_reloadBookmarks() { QList currentItems = loadBookmarkList(); QList::Iterator it_i = items.begin(); QList::Iterator it_c = currentItems.begin(); QList::Iterator end_i = items.end(); QList::Iterator end_c = currentItems.end(); while (it_i != end_i || it_c != end_c) { if (it_i == end_i && it_c != end_c) { int row = items.count(); q->beginInsertRows(QModelIndex(), row, row); it_i = items.insert(it_i, *it_c); ++it_i; it_c = currentItems.erase(it_c); end_i = items.end(); end_c = currentItems.end(); q->endInsertRows(); } else if (it_i != end_i && it_c == end_c) { int row = items.indexOf(*it_i); q->beginRemoveRows(QModelIndex(), row, row); delete *it_i; it_i = items.erase(it_i); end_i = items.end(); end_c = currentItems.end(); q->endRemoveRows(); } else if ((*it_i)->id() == (*it_c)->id()) { bool shouldEmit = !((*it_i)->bookmark() == (*it_c)->bookmark()); (*it_i)->setBookmark((*it_c)->bookmark()); if (shouldEmit) { int row = items.indexOf(*it_i); QModelIndex idx = q->index(row, 0); emit q->dataChanged(idx, idx); } ++it_i; ++it_c; } else if ((*it_i)->id() != (*it_c)->id()) { int row = items.indexOf(*it_i); if (it_i + 1 != end_i && (*(it_i + 1))->id() == (*it_c)->id()) { // if the next one matches, it's a remove q->beginRemoveRows(QModelIndex(), row, row); delete *it_i; it_i = items.erase(it_i); end_i = items.end(); end_c = currentItems.end(); q->endRemoveRows(); } else { q->beginInsertRows(QModelIndex(), row, row); it_i = items.insert(it_i, *it_c); ++it_i; it_c = currentItems.erase(it_c); end_i = items.end(); end_c = currentItems.end(); q->endInsertRows(); } } } qDeleteAll(currentItems); currentItems.clear(); } bool KFilePlacesModel::Private::isBalooUrl(const QUrl &url) const { const QString scheme = url.scheme(); return ((scheme == QLatin1String("timeline")) || (scheme == QLatin1String("search"))); } QList KFilePlacesModel::Private::loadBookmarkList() { QList items; KBookmarkGroup root = bookmarkManager->root(); KBookmark bookmark = root.first(); QVector devices = availableDevices; while (!bookmark.isNull()) { const QString udi = bookmark.metaDataItem(QStringLiteral("UDI")); const QUrl url = bookmark.url(); if (!udi.isEmpty() || url.isValid()) { QString appName = bookmark.metaDataItem(QStringLiteral("OnlyInApp")); auto it = std::find(devices.begin(), devices.end(), udi); bool deviceAvailable = (it != devices.end()); if (deviceAvailable) { devices.erase(it); } bool allowedHere = appName.isEmpty() || ((appName == QCoreApplication::instance()->applicationName()) || (appName == alternativeApplicationName)); bool isSupportedUrl = isBalooUrl(url) ? fileIndexingEnabled : true; bool isSupportedScheme = supportedSchemes.isEmpty() || supportedSchemes.contains(url.scheme()); if (isSupportedScheme && ((isSupportedUrl && udi.isEmpty() && allowedHere) || deviceAvailable)) { KFilePlacesItem *item; if (deviceAvailable) { item = new KFilePlacesItem(bookmarkManager, bookmark.address(), udi); // TODO: Update bookmark internal element } else { item = new KFilePlacesItem(bookmarkManager, bookmark.address()); } connect(item, SIGNAL(itemChanged(QString)), q, SLOT(_k_itemChanged(QString))); items << item; } } bookmark = root.next(bookmark); } // Add bookmarks for the remaining devices, they were previously unknown foreach (const QString &udi, devices) { bookmark = KFilePlacesItem::createDeviceBookmark(bookmarkManager, udi); if (!bookmark.isNull()) { KFilePlacesItem *item = new KFilePlacesItem(bookmarkManager, bookmark.address(), udi); connect(item, SIGNAL(itemChanged(QString)), q, SLOT(_k_itemChanged(QString))); // TODO: Update bookmark internal element items << item; } } // return a sorted list based on groups qStableSort(items.begin(), items.end(), [](KFilePlacesItem *itemA, KFilePlacesItem *itemB) { return (itemA->groupType() < itemB->groupType()); }); return items; } int KFilePlacesModel::Private::findNearestPosition(int source, int target) { const KFilePlacesItem *item = items.at(source); const KFilePlacesModel::GroupType groupType = item->groupType(); int newTarget = qMin(target, items.count() - 1); // moving inside the same group is ok if ((items.at(newTarget)->groupType() == groupType)) { return target; } if (target > source) { // moving down, move it to the end of the group int groupFooter = source; while (items.at(groupFooter)->groupType() == groupType) { groupFooter++; // end of the list move it there if (groupFooter == items.count()) { break; } } target = groupFooter; } else { // moving up, move it to beginning of the group int groupHead = source; while (items.at(groupHead)->groupType() == groupType) { groupHead--; // beginning of the list move it there if (groupHead == 0) { break; } } target = groupHead; } return target; } void KFilePlacesModel::Private::reloadAndSignal() { bookmarkManager->emitChanged(bookmarkManager->root()); // ... we'll get relisted anyway } Qt::DropActions KFilePlacesModel::supportedDropActions() const { return Qt::ActionMask; } Qt::ItemFlags KFilePlacesModel::flags(const QModelIndex &index) const { Qt::ItemFlags res = Qt::ItemIsSelectable | Qt::ItemIsEnabled; if (index.isValid()) { res |= Qt::ItemIsDragEnabled; } if (!index.isValid()) { res |= Qt::ItemIsDropEnabled; } return res; } static QString _k_internalMimetype(const KFilePlacesModel *const self) { return QStringLiteral("application/x-kfileplacesmodel-") + QString::number(reinterpret_cast(self)); } QStringList KFilePlacesModel::mimeTypes() const { QStringList types; types << _k_internalMimetype(this) << QStringLiteral("text/uri-list"); return types; } QMimeData *KFilePlacesModel::mimeData(const QModelIndexList &indexes) const { QList urls; QByteArray itemData; QDataStream stream(&itemData, QIODevice::WriteOnly); foreach (const QModelIndex &index, indexes) { QUrl itemUrl = url(index); if (itemUrl.isValid()) { urls << itemUrl; } stream << index.row(); } QMimeData *mimeData = new QMimeData(); if (!urls.isEmpty()) { mimeData->setUrls(urls); } mimeData->setData(_k_internalMimetype(this), itemData); return mimeData; } bool KFilePlacesModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { if (action == Qt::IgnoreAction) { return true; } if (column > 0) { return false; } if (row == -1 && parent.isValid()) { return false; // Don't allow to move an item onto another one, // too easy for the user to mess something up // If we really really want to allow copying files this way, // let's do it in the views to get the good old drop menu } if (data->hasFormat(_k_internalMimetype(this))) { // The operation is an internal move QByteArray itemData = data->data(_k_internalMimetype(this)); QDataStream stream(&itemData, QIODevice::ReadOnly); int itemRow; stream >> itemRow; if (!movePlace(itemRow, row)) { return false; } } else if (data->hasFormat(QStringLiteral("text/uri-list"))) { // The operation is an add QMimeDatabase db; KBookmark afterBookmark; if (row == -1) { // The dropped item is moved or added to the last position KFilePlacesItem *lastItem = d->items.last(); afterBookmark = lastItem->bookmark(); } else { // The dropped item is moved or added before position 'row', ie after position 'row-1' if (row > 0) { KFilePlacesItem *afterItem = d->items[row - 1]; afterBookmark = afterItem->bookmark(); } } const QList urls = KUrlMimeData::urlsFromMimeData(data); KBookmarkGroup group = d->bookmarkManager->root(); foreach (const QUrl &url, urls) { // TODO: use KIO::stat in order to get the UDS_DISPLAY_NAME too KIO::MimetypeJob *job = KIO::mimetype(url); QString mimeString; if (!job->exec()) { mimeString = QStringLiteral("unknown"); } else { mimeString = job->mimetype(); } QMimeType mimetype = db.mimeTypeForName(mimeString); if (!mimetype.isValid()) { qWarning() << "URL not added to Places as mimetype could not be determined!"; continue; } if (!mimetype.inherits(QStringLiteral("inode/directory"))) { // Only directories are allowed continue; } KFileItem item(url, mimetype.name(), S_IFDIR); KBookmark bookmark = KFilePlacesItem::createBookmark(d->bookmarkManager, url.fileName(), url, item.iconName()); group.moveBookmark(bookmark, afterBookmark); afterBookmark = bookmark; } } else { // Oops, shouldn't happen thanks to mimeTypes() qWarning() << ": received wrong mimedata, " << data->formats(); return false; } refresh(); return true; } void KFilePlacesModel::refresh() const { d->reloadAndSignal(); } QUrl KFilePlacesModel::convertedUrl(const QUrl &url) { QUrl newUrl = url; if (url.scheme() == QLatin1String("timeline")) { newUrl = createTimelineUrl(url); } else if (url.scheme() == QLatin1String("search")) { newUrl = createSearchUrl(url); } return newUrl; } void KFilePlacesModel::addPlace(const QString &text, const QUrl &url, const QString &iconName, const QString &appName) { addPlace(text, url, iconName, appName, QModelIndex()); } void KFilePlacesModel::addPlace(const QString &text, const QUrl &url, const QString &iconName, const QString &appName, const QModelIndex &after) { KBookmark bookmark = KFilePlacesItem::createBookmark(d->bookmarkManager, text, url, iconName); if (!appName.isEmpty()) { bookmark.setMetaDataItem(QStringLiteral("OnlyInApp"), appName); } if (after.isValid()) { KFilePlacesItem *item = static_cast(after.internalPointer()); d->bookmarkManager->root().moveBookmark(bookmark, item->bookmark()); } refresh(); } void KFilePlacesModel::editPlace(const QModelIndex &index, const QString &text, const QUrl &url, const QString &iconName, const QString &appName) { if (!index.isValid()) { return; } KFilePlacesItem *item = static_cast(index.internalPointer()); if (item->isDevice()) { return; } KBookmark bookmark = item->bookmark(); if (bookmark.isNull()) { return; } bool changed = false; if (text != bookmark.fullText()) { bookmark.setFullText(text); changed = true; } if (url != bookmark.url()) { bookmark.setUrl(url); changed = true; } if (iconName != bookmark.icon()) { bookmark.setIcon(iconName); changed = true; } const QString onlyInApp = bookmark.metaDataItem(QStringLiteral("OnlyInApp")); if (appName != onlyInApp) { bookmark.setMetaDataItem(QStringLiteral("OnlyInApp"), appName); changed = true; } if (changed) { refresh(); emit dataChanged(index, index); } } void KFilePlacesModel::removePlace(const QModelIndex &index) const { if (!index.isValid()) { return; } KFilePlacesItem *item = static_cast(index.internalPointer()); if (item->isDevice()) { return; } KBookmark bookmark = item->bookmark(); if (bookmark.isNull()) { return; } d->bookmarkManager->root().deleteBookmark(bookmark); refresh(); } void KFilePlacesModel::setPlaceHidden(const QModelIndex &index, bool hidden) { if (!index.isValid()) { return; } KFilePlacesItem *item = static_cast(index.internalPointer()); if (item->bookmark().isNull() || item->isHidden() == hidden) { return; } const bool groupHidden = isGroupHidden(item->groupType()); const bool hidingChildOnShownParent = hidden && !groupHidden; const bool showingChildOnShownParent = !hidden && !groupHidden; if (hidingChildOnShownParent || showingChildOnShownParent) { item->setHidden(hidden); d->reloadAndSignal(); emit dataChanged(index, index); } } void KFilePlacesModel::setGroupHidden(const GroupType type, bool hidden) { if (isGroupHidden(type) == hidden) return; d->bookmarkManager->root().setMetaDataItem(stateNameForGroupType(type), (hidden ? QStringLiteral("true") : QStringLiteral("false"))); d->reloadAndSignal(); emit groupHiddenChanged(type, hidden); } bool KFilePlacesModel::movePlace(int itemRow, int row) { KBookmark afterBookmark; if ((itemRow < 0) || (itemRow >= d->items.count())) { return false; } if (row >= d->items.count()) { row = -1; } if (row == -1) { // The dropped item is moved or added to the last position KFilePlacesItem *lastItem = d->items.last(); afterBookmark = lastItem->bookmark(); } else { // The dropped item is moved or added before position 'row', ie after position 'row-1' if (row > 0) { KFilePlacesItem *afterItem = d->items[row - 1]; afterBookmark = afterItem->bookmark(); } } KFilePlacesItem *item = d->items[itemRow]; KBookmark bookmark = item->bookmark(); int destRow = row == -1 ? d->items.count() : row; // avoid move item away from its group destRow = d->findNearestPosition(itemRow, destRow); // The item is not moved when the drop indicator is on either item edge if (itemRow == destRow || itemRow + 1 == destRow) { return false; } beginMoveRows(QModelIndex(), itemRow, itemRow, QModelIndex(), destRow); d->bookmarkManager->root().moveBookmark(bookmark, afterBookmark); // Move item ourselves so that _k_reloadBookmarks() does not consider // the move as a remove + insert. // // 2nd argument of QList::move() expects the final destination index, // but 'row' is the value of the destination index before the moved // item has been removed from its original position. That is why we // adjust if necessary. d->items.move(itemRow, itemRow < destRow ? (destRow - 1) : destRow); endMoveRows(); return true; } int KFilePlacesModel::hiddenCount() const { int rows = rowCount(); int hidden = 0; for (int i = 0; i < rows; ++i) { if (isHidden(index(i, 0))) { hidden++; } } return hidden; } QAction *KFilePlacesModel::teardownActionForIndex(const QModelIndex &index) const { Solid::Device device = deviceForIndex(index); if (device.is() && device.as()->isAccessible()) { Solid::StorageDrive *drive = device.as(); if (drive == nullptr) { drive = device.parent().as(); } bool hotpluggable = false; bool removable = false; if (drive != nullptr) { hotpluggable = drive->isHotpluggable(); removable = drive->isRemovable(); } QString iconName; QString text; QString label = data(index, Qt::DisplayRole).toString().replace(QLatin1Char('&'), QLatin1String("&&")); if (device.is()) { text = i18n("&Release '%1'", label); } else if (removable || hotpluggable) { text = i18n("&Safely Remove '%1'", label); iconName = QStringLiteral("media-eject"); } else { text = i18n("&Unmount '%1'", label); iconName = QStringLiteral("media-eject"); } if (!iconName.isEmpty()) { return new QAction(QIcon::fromTheme(iconName), text, nullptr); } else { return new QAction(text, nullptr); } } return nullptr; } QAction *KFilePlacesModel::ejectActionForIndex(const QModelIndex &index) const { Solid::Device device = deviceForIndex(index); if (device.is()) { QString label = data(index, Qt::DisplayRole).toString().replace(QLatin1Char('&'), QLatin1String("&&")); QString text = i18n("&Eject '%1'", label); return new QAction(QIcon::fromTheme(QStringLiteral("media-eject")), text, nullptr); } return nullptr; } void KFilePlacesModel::requestTeardown(const QModelIndex &index) { Solid::Device device = deviceForIndex(index); Solid::StorageAccess *access = device.as(); if (access != nullptr) { connect(access, SIGNAL(teardownDone(Solid::ErrorType,QVariant,QString)), this, SLOT(_k_storageTeardownDone(Solid::ErrorType,QVariant))); access->teardown(); } } void KFilePlacesModel::requestEject(const QModelIndex &index) { Solid::Device device = deviceForIndex(index); Solid::OpticalDrive *drive = device.parent().as(); if (drive != nullptr) { connect(drive, SIGNAL(ejectDone(Solid::ErrorType,QVariant,QString)), this, SLOT(_k_storageTeardownDone(Solid::ErrorType,QVariant))); drive->eject(); } else { QString label = data(index, Qt::DisplayRole).toString().replace(QLatin1Char('&'), QLatin1String("&&")); QString message = i18n("The device '%1' is not a disk and cannot be ejected.", label); emit errorMessage(message); } } void KFilePlacesModel::requestSetup(const QModelIndex &index) { Solid::Device device = deviceForIndex(index); if (device.is() && !d->setupInProgress.contains(device.as()) && !device.as()->isAccessible()) { Solid::StorageAccess *access = device.as(); d->setupInProgress[access] = index; connect(access, SIGNAL(setupDone(Solid::ErrorType,QVariant,QString)), this, SLOT(_k_storageSetupDone(Solid::ErrorType,QVariant))); access->setup(); } } void KFilePlacesModel::Private::_k_storageSetupDone(Solid::ErrorType error, const QVariant &errorData) { QPersistentModelIndex index = setupInProgress.take(q->sender()); if (!index.isValid()) { return; } if (!error) { emit q->setupDone(index, true); } else { if (errorData.isValid()) { emit q->errorMessage(i18n("An error occurred while accessing '%1', the system responded: %2", q->text(index), errorData.toString())); } else { emit q->errorMessage(i18n("An error occurred while accessing '%1'", q->text(index))); } emit q->setupDone(index, false); } } void KFilePlacesModel::Private::_k_storageTeardownDone(Solid::ErrorType error, const QVariant &errorData) { if (error && errorData.isValid()) { emit q->errorMessage(errorData.toString()); } } void KFilePlacesModel::setSupportedSchemes(const QStringList &schemes) { d->supportedSchemes = schemes; d->_k_reloadBookmarks(); } QStringList KFilePlacesModel::supportedSchemes() const { return d->supportedSchemes; } #include "moc_kfileplacesmodel.cpp" diff --git a/src/kcms/CMakeLists.txt b/src/kcms/CMakeLists.txt index bfac720d..0d3e7560 100644 --- a/src/kcms/CMakeLists.txt +++ b/src/kcms/CMakeLists.txt @@ -1,5 +1,2 @@ -# TODO: Remove these -remove_definitions(-DQT_NO_CAST_FROM_ASCII) - add_subdirectory(webshortcuts) add_subdirectory(kio) diff --git a/src/kcms/kio/kcookiespolicies.cpp b/src/kcms/kio/kcookiespolicies.cpp index 95438625..bcc5cfb3 100644 --- a/src/kcms/kio/kcookiespolicies.cpp +++ b/src/kcms/kio/kcookiespolicies.cpp @@ -1,458 +1,461 @@ /** * kcookiespolicies.cpp - Cookies configuration * * Original Authors * Copyright (c) Waldo Bastian * Copyright (c) 1999 David Faure * Copyright (c) 2008 Urs Wolfer * * Re-written by: * Copyright (c) 2000- Dawit Alemayehu * * 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. */ +// TODO: remove me +#undef QT_NO_CAST_FROM_ASCII + // Own #include "kcookiespolicies.h" // Local #include "ksaveioconfig.h" // Qt #include #include #include #include // KDE #include #include #include #include #include #include // QUrl::fromAce/toAce don't accept a domain that starts with a '.', like we do here. // So we use these wrappers. QString tolerantFromAce (const QByteArray& _domain) { QByteArray domain (_domain); const bool hasDot = domain.startsWith ('.'); if (hasDot) domain.remove (0, 1); QString ret = QUrl::fromAce(domain); if (hasDot) { ret.prepend(QLatin1Char('.')); } return ret; } static QByteArray tolerantToAce (const QString& _domain) { QString domain (_domain); const bool hasDot = domain.startsWith(QLatin1Char('.')); if (hasDot) domain.remove (0, 1); QByteArray ret = QUrl::toAce (domain); if (hasDot) { ret.prepend ('.'); } return ret; } KCookiesPolicies::KCookiesPolicies (QWidget* parent) : KCModule (parent), mSelectedItemsCount(0) { mUi.setupUi (this); mUi.kListViewSearchLine->setTreeWidget (mUi.policyTreeWidget); QList columns; columns.append (0); mUi.kListViewSearchLine->setSearchColumns (columns); mUi.pbNew->setIcon (QIcon::fromTheme(QStringLiteral("list-add"))); mUi.pbChange->setIcon (QIcon::fromTheme(QStringLiteral("edit-rename"))); mUi.pbDelete->setIcon (QIcon::fromTheme(QStringLiteral("list-remove"))); mUi.pbDeleteAll->setIcon (QIcon::fromTheme(QStringLiteral("edit-delete"))); // Connect the main swicth :) Enable/disable cookie support connect (mUi.cbEnableCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::cookiesEnabled); connect (mUi.cbEnableCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); // Connect the preference check boxes... connect (mUi.cbRejectCrossDomainCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); connect (mUi.cbAutoAcceptSessionCookies, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); connect (mUi.rbPolicyAsk, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); connect (mUi.rbPolicyAccept, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); connect (mUi.rbPolicyAcceptForSession, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); connect (mUi.rbPolicyReject, &QAbstractButton::toggled, this, &KCookiesPolicies::configChanged); // Connect signals from the domain specific policy listview. connect (mUi.policyTreeWidget, &QTreeWidget::itemSelectionChanged, this, &KCookiesPolicies::selectionChanged); connect (mUi.policyTreeWidget, &QTreeWidget::itemDoubleClicked, this, QOverload<>::of(&KCookiesPolicies::changePressed)); // Connect the buttons... connect(mUi.pbNew, &QAbstractButton::clicked, this, QOverload<>::of(&KCookiesPolicies::addPressed)); connect(mUi.pbChange, &QAbstractButton::clicked, this, QOverload<>::of(&KCookiesPolicies::changePressed)); connect(mUi.pbDelete, &QAbstractButton::clicked, this, &KCookiesPolicies::deletePressed); connect(mUi.pbDeleteAll, &QAbstractButton::clicked, this, &KCookiesPolicies::deleteAllPressed); } KCookiesPolicies::~KCookiesPolicies() { } void KCookiesPolicies::configChanged () { //kDebug() << "KCookiesPolicies::configChanged..."; emit changed (true); } void KCookiesPolicies::cookiesEnabled (bool enable) { mUi.bgDefault->setEnabled (enable); mUi.bgPreferences->setEnabled (enable); mUi.gbDomainSpecific->setEnabled (enable); } void KCookiesPolicies::setPolicy (const QString& domain) { QTreeWidgetItemIterator it (mUi.policyTreeWidget); bool hasExistingPolicy = false; while (*it) { if ((*it)->text(0) == domain) { hasExistingPolicy = true; break; } ++it; } if (hasExistingPolicy) { changePressed((*it), false); } else { addPressed(domain); } } void KCookiesPolicies::changePressed() { changePressed(mUi.policyTreeWidget->currentItem()); } void KCookiesPolicies::addPressed() { addPressed(QString()); } void KCookiesPolicies::changePressed(QTreeWidgetItem* item, bool state) { Q_ASSERT(item); const QString oldDomain(item->text (0)); KCookiesPolicySelectionDlg pdlg (this); pdlg.setWindowTitle (i18nc ("@title:window", "Change Cookie Policy")); pdlg.setPolicy (KCookieAdvice::strToAdvice (mDomainPolicyMap.value(oldDomain))); pdlg.setEnableHostEdit (state, oldDomain); if (pdlg.exec() && !pdlg.domain().isEmpty()) { const QString newDomain = tolerantFromAce (pdlg.domain().toLatin1()); int advice = pdlg.advice(); if (newDomain == oldDomain || !handleDuplicate (newDomain, advice)) { mDomainPolicyMap[newDomain] = KCookieAdvice::adviceToStr(advice); item->setText(0, newDomain); item->setText(1, i18n (mDomainPolicyMap.value(newDomain))); configChanged(); } } } void KCookiesPolicies::addPressed(const QString& domain, bool state) { KCookiesPolicySelectionDlg pdlg (this); pdlg.setWindowTitle (i18nc ("@title:window", "New Cookie Policy")); pdlg.setEnableHostEdit(state, domain); if (mUi.rbPolicyAccept->isChecked()) pdlg.setPolicy (KCookieAdvice::Reject); else pdlg.setPolicy (KCookieAdvice::Accept); if (pdlg.exec() && !pdlg.domain().isEmpty()) { const QString domain = tolerantFromAce (pdlg.domain().toLatin1()); int advice = pdlg.advice(); if (!handleDuplicate (domain, advice)) { const char* strAdvice = KCookieAdvice::adviceToStr (advice); QTreeWidgetItem* item = new QTreeWidgetItem (mUi.policyTreeWidget, QStringList() << domain << i18n (strAdvice)); mDomainPolicyMap.insert (item->text(0), strAdvice); configChanged(); updateButtons(); } } } bool KCookiesPolicies::handleDuplicate (const QString& domain, int advice) { QTreeWidgetItem* item = mUi.policyTreeWidget->topLevelItem (0); while (item != nullptr) { if (item->text (0) == domain) { const int res = KMessageBox::warningContinueCancel (this, i18n ("A policy already exists for" "
%1
" "Do you want to replace it?
", domain), i18nc ("@title:window", "Duplicate Policy"), KGuiItem (i18n ("Replace"))); if (res == KMessageBox::Continue) { mDomainPolicyMap[domain] = KCookieAdvice::adviceToStr(advice); item->setText (0, domain); item->setText (1, i18n (mDomainPolicyMap.value(domain))); configChanged(); return true; } else return true; // User Cancelled!! } item = mUi.policyTreeWidget->itemBelow (item); } return false; } void KCookiesPolicies::deletePressed() { QTreeWidgetItem* nextItem = nullptr; Q_FOREACH (QTreeWidgetItem * item, mUi.policyTreeWidget->selectedItems()) { nextItem = mUi.policyTreeWidget->itemBelow (item); if (!nextItem) nextItem = mUi.policyTreeWidget->itemAbove (item); mDomainPolicyMap.remove (item->text(0)); delete item; } if (nextItem) nextItem->setSelected (true); updateButtons(); configChanged(); } void KCookiesPolicies::deleteAllPressed() { mDomainPolicyMap.clear(); mUi.policyTreeWidget->clear(); updateButtons(); configChanged(); } void KCookiesPolicies::updateButtons() { bool hasItems = mUi.policyTreeWidget->topLevelItemCount() > 0; mUi.pbChange->setEnabled((hasItems && mSelectedItemsCount == 1)); mUi.pbDelete->setEnabled((hasItems && mSelectedItemsCount > 0)); mUi.pbDeleteAll->setEnabled(hasItems); } void KCookiesPolicies::updateDomainList (const QStringList& domainConfig) { mUi.policyTreeWidget->clear(); QStringList::ConstIterator it = domainConfig.begin(); for (; it != domainConfig.end(); ++it) { QString domain; KCookieAdvice::Value advice = KCookieAdvice::Dunno; splitDomainAdvice (*it, domain, advice); if (!domain.isEmpty()) { QStringList items; items << tolerantFromAce(domain.toLatin1()) << i18n(KCookieAdvice::adviceToStr(advice)); QTreeWidgetItem* item = new QTreeWidgetItem (mUi.policyTreeWidget, items); mDomainPolicyMap[item->text(0)] = KCookieAdvice::adviceToStr(advice); } } mUi.policyTreeWidget->sortItems(0, Qt::AscendingOrder); } void KCookiesPolicies::selectionChanged () { mSelectedItemsCount = mUi.policyTreeWidget->selectedItems().count(); updateButtons (); } void KCookiesPolicies::load() { mSelectedItemsCount = 0; KConfig cfg (QStringLiteral("kcookiejarrc")); KConfigGroup group = cfg.group ("Cookie Policy"); bool enableCookies = group.readEntry ("Cookies", true); mUi.cbEnableCookies->setChecked (enableCookies); cookiesEnabled (enableCookies); // Warning: the default values are duplicated in kcookiejar.cpp KCookieAdvice::Value advice = KCookieAdvice::strToAdvice (group.readEntry ( "CookieGlobalAdvice", "Accept")); switch (advice) { case KCookieAdvice::Accept: mUi.rbPolicyAccept->setChecked (true); break; case KCookieAdvice::AcceptForSession: mUi.rbPolicyAcceptForSession->setChecked (true); break; case KCookieAdvice::Reject: mUi.rbPolicyReject->setChecked (true); break; case KCookieAdvice::Ask: case KCookieAdvice::Dunno: default: mUi.rbPolicyAsk->setChecked (true); } bool enable = group.readEntry ("RejectCrossDomainCookies", true); mUi.cbRejectCrossDomainCookies->setChecked (enable); bool sessionCookies = group.readEntry ("AcceptSessionCookies", true); mUi.cbAutoAcceptSessionCookies->setChecked (sessionCookies); updateDomainList (group.readEntry ("CookieDomainAdvice", QStringList())); if (enableCookies) { updateButtons(); } } void KCookiesPolicies::save() { KConfig cfg (QStringLiteral("kcookiejarrc")); KConfigGroup group = cfg.group ("Cookie Policy"); bool state = mUi.cbEnableCookies->isChecked(); group.writeEntry ("Cookies", state); state = mUi.cbRejectCrossDomainCookies->isChecked(); group.writeEntry ("RejectCrossDomainCookies", state); state = mUi.cbAutoAcceptSessionCookies->isChecked(); group.writeEntry ("AcceptSessionCookies", state); QString advice; if (mUi.rbPolicyAccept->isChecked()) advice = KCookieAdvice::adviceToStr (KCookieAdvice::Accept); else if (mUi.rbPolicyAcceptForSession->isChecked()) advice = KCookieAdvice::adviceToStr (KCookieAdvice::AcceptForSession); else if (mUi.rbPolicyReject->isChecked()) advice = KCookieAdvice::adviceToStr (KCookieAdvice::Reject); else advice = KCookieAdvice::adviceToStr (KCookieAdvice::Ask); group.writeEntry ("CookieGlobalAdvice", advice); QStringList domainConfig; QMapIterator it (mDomainPolicyMap); while (it.hasNext()) { it.next(); QString policy = tolerantToAce(it.key()); policy += QLatin1Char (':'); policy += QLatin1String (it.value()); domainConfig << policy; } group.writeEntry ("CookieDomainAdvice", domainConfig); group.sync(); // Update the cookiejar... if (!mUi.cbEnableCookies->isChecked()) { QDBusInterface kded (QStringLiteral("org.kde.kcookiejar5"), QStringLiteral("/modules/kcookiejar"), QStringLiteral("org.kde.KCookieServer"), QDBusConnection::sessionBus()); kded.call (QStringLiteral("shutdown")); } else { QDBusInterface kded (QStringLiteral("org.kde.kcookiejar5"), QStringLiteral("/modules/kcookiejar"), QStringLiteral("org.kde.KCookieServer"), QDBusConnection::sessionBus()); QDBusReply reply = kded.call (QStringLiteral("reloadPolicy")); if (!reply.isValid()) KMessageBox::sorry (nullptr, i18n ("Unable to communicate with the cookie handler service.\n" "Any changes you made will not take effect until the service " "is restarted.")); } // Force running io-slave to reload configurations... KSaveIOConfig::updateRunningIOSlaves (this); emit changed (false); } void KCookiesPolicies::defaults() { mUi.cbEnableCookies->setChecked (true); mUi.rbPolicyAsk->setChecked (true); mUi.rbPolicyAccept->setChecked (false); mUi.rbPolicyAcceptForSession->setChecked (false); mUi.rbPolicyReject->setChecked (false); mUi.cbRejectCrossDomainCookies->setChecked (true); mUi.cbAutoAcceptSessionCookies->setChecked (false); mUi.policyTreeWidget->clear(); mDomainPolicyMap.clear(); cookiesEnabled (mUi.cbEnableCookies->isChecked()); updateButtons(); } void KCookiesPolicies::splitDomainAdvice (const QString& cfg, QString& domain, KCookieAdvice::Value& advice) { int sepPos = cfg.lastIndexOf(QLatin1Char(':')); // Ignore any policy that does not contain a domain... if (sepPos <= 0) return; domain = cfg.left (sepPos); advice = KCookieAdvice::strToAdvice (cfg.mid (sepPos + 1)); } QString KCookiesPolicies::quickHelp() const { return i18n ("

Cookies

Cookies contain information that KDE" " application using the HTTP protocol (like Konqueror) stores" " on your computer from a remote Internet server. This means" " that a web server can store information about you and your" " browsing activities on your machine for later use. You might" " consider this an invasion of privacy.

However, cookies are" " useful in certain situations. For example, they are often used" " by Internet shops, so you can 'put things into a shopping" " basket'. Some sites require you have a browser that supports" " cookies.

Because most people want a compromise between privacy" " and the benefits cookies offer, KDE offers you the ability to" " customize the way it handles cookies. You might, for example" " want to set KDE's default policy to ask you whenever a server" " wants to set a cookie or simply reject or accept everything." " For example, you might choose to accept all cookies from your" " favorite shopping web site. For this all you have to do is" " either browse to that particular site and when you are presented" " with the cookie dialog box, click on This domain under" " the 'apply to' tab and choose accept or simply specify the name" " of the site in the Domain Specific Policy tab and set" " it to accept. This enables you to receive cookies from trusted" " web sites without being asked every time KDE receives a cookie.

" ); } diff --git a/src/kcms/kio/smbrodlg.cpp b/src/kcms/kio/smbrodlg.cpp index eb3b2f25..be45418c 100644 --- a/src/kcms/kio/smbrodlg.cpp +++ b/src/kcms/kio/smbrodlg.cpp @@ -1,185 +1,188 @@ /* This file is part of the KDE project Copyright (C) 2000 Alexander Neundorf 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. */ +// TODO: remove me +#undef QT_NO_CAST_FROM_ASCII + // Own #include "smbrodlg.h" // Qt #include #include #include // KDE #include #include #include #include #include #include //#include #include K_PLUGIN_FACTORY_DECLARATION(KioConfigFactory) SMBRoOptions::SMBRoOptions(QWidget *parent, const QVariantList &) : KCModule(parent) { QGridLayout *layout = new QGridLayout(this ); QLabel *label=new QLabel(i18n("These settings apply to network browsing only."),this); layout->addWidget(label,0,0, 1, 2 ); m_userLe=new QLineEdit(this); label=new QLabel(i18n("Default user name:"),this); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); label->setBuddy( m_userLe ); layout->addWidget(label,1,0); layout->addWidget(m_userLe,1,1); m_passwordLe=new QLineEdit(this); m_passwordLe->setEchoMode(QLineEdit::Password); label=new QLabel(i18n("Default password:"),this); label->setAlignment(Qt::AlignRight | Qt::AlignVCenter); label->setBuddy( m_passwordLe ); layout->addWidget(label,2,0); layout->addWidget(m_passwordLe,2,1); /* m_workgroupLe=new QLineEdit(this); label=new QLabel(m_workgroupLe,i18n("Workgroup:"),this); layout->addWidget(label,3,0); layout->addWidget(m_workgroupLe,3,1); m_showHiddenShares=new QCheckBox(i18n("Show hidden shares"),this); layout->addWidget(m_showHiddenShares,4,0, 1, 2 ); m_encodingList = new KComboBox( false, this ); QStringList _strList = KCharsets::charsets()->availableEncodingNames(); m_encodingList->addItems( _strList ); label = new QLabel( i18n( "MS Windows encoding:" ), this ); label->setBuddy( m_encodingList ); layout->addWidget( label, 3, 0 ); layout->addWidget( m_encodingList, 3, 1 ); */ layout->addWidget(new QWidget(this),4,0); // connect(m_showHiddenShares, SIGNAL(toggled(bool)), this, SLOT(changed())); connect(m_userLe, &QLineEdit::textChanged, this, &SMBRoOptions::changed); connect(m_passwordLe, &QLineEdit::textChanged, this, &SMBRoOptions::changed); // connect(m_workgroupLe, SIGNAL(textChanged(QString)), this, SLOT(changed())); // connect( m_encodingList, SIGNAL(activated(QString)), this , SLOT(changed()) ); layout->setRowStretch(4, 1); } SMBRoOptions::~SMBRoOptions() { } void SMBRoOptions::load() { KConfig *cfg = new KConfig(QStringLiteral("kioslaverc")); KConfigGroup group = cfg->group("Browser Settings/SMBro" ); m_userLe->setText(group.readEntry("User")); // m_workgroupLe->setText(group.readEntry("Workgroup")); // m_showHiddenShares->setChecked(group.readEntry("ShowHiddenShares", QVariant(false)).toBool()); //QStringList _strList = KCharsets::charsets()->availableEncodingNames(); //QString m_encoding = QTextCodec::codecForLocale()->name(); //m_encodingList->setCurrentIndex( _strList.indexOf( group.readEntry( "Encoding", m_encoding.toLower() ) ) ); // unscramble QString scrambled = group.readEntry( "Password" ); QString password; const int passwordLength = scrambled.length() / 3; password.reserve(passwordLength); for (int i=0; isetText(password); delete cfg; } void SMBRoOptions::save() { KConfig *cfg = new KConfig(QStringLiteral("kioslaverc")); KConfigGroup group = cfg->group("Browser Settings/SMBro" ); group.writeEntry( "User", m_userLe->text()); // group.writeEntry( "Workgroup", m_workgroupLe->text()); // group.writeEntry( "ShowHiddenShares", m_showHiddenShares->isChecked()); // group.writeEntry( "Encoding", m_encodingList->currentText() ); //taken from Nicola Brodu's smb ioslave //it's not really secure, but at //least better than storing the plain password QString password(m_passwordLe->text()); QString scrambled; for (int i=0; i> 10; unsigned int a2 = (num & 0x3E0) >> 5; unsigned int a3 = (num & 0x1F); scrambled += (char)(a1+'0'); scrambled += (char)(a2+'A'); scrambled += (char)(a3+'0'); } group.writeEntry( "Password", scrambled); delete cfg; } void SMBRoOptions::defaults() { m_userLe->setText(QString()); m_passwordLe->setText(QString()); // m_workgroupLe->setText(""); // m_showHiddenShares->setChecked(false); } void SMBRoOptions::changed() { emit KCModule::changed(true); } QString SMBRoOptions::quickHelp() const { return i18n("

Windows Shares

Applications using the " "SMB kioslave (like Konqueror) are able to access shared Microsoft " "Windows file systems, if properly configured.

You can specify " "here the credentials used to access the shared resources. " "Passwords will be stored locally, and scrambled so as to render them " "unreadable to the human eye. For security reasons, you may not want to " "do that, as entries with passwords are clearly indicated as such.

"); } diff --git a/src/kioexec/CMakeLists.txt b/src/kioexec/CMakeLists.txt index 3b6d2fbc..16f6daa4 100644 --- a/src/kioexec/CMakeLists.txt +++ b/src/kioexec/CMakeLists.txt @@ -1,49 +1,46 @@ -# TODO: Remove these -remove_definitions(-DQT_NO_CAST_FROM_ASCII) - set(kioexecd_SRCS kioexecd.cpp) qt5_generate_dbus_interface(${CMAKE_CURRENT_SOURCE_DIR}/kioexecd.h org.kde.KIOExecd.xml) qt5_add_dbus_adaptor(kioexecd_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KIOExecd.xml kioexecd.h KIOExecd) ecm_qt_declare_logging_category(kioexecd_SRCS HEADER kioexecdebug.h IDENTIFIER KIOEXEC CATEGORY_NAME kf5.kio.execd) kcoreaddons_add_plugin(kioexecd SOURCES ${kioexecd_SRCS} JSON kioexecd.json INSTALL_NAMESPACE "kf5/kiod") target_link_libraries(kioexecd KF5::I18n KF5::DBusAddons KF5::WidgetsAddons KF5::KIOCore) kdbusaddons_generate_dbus_service_file(kiod5 org.kde.kioexecd ${KDE_INSTALL_FULL_LIBEXECDIR_KF5}) # next target set(kioexec_SRCS main.cpp) qt5_add_dbus_interface(kioexec_SRCS ${CMAKE_CURRENT_BINARY_DIR}/org.kde.KIOExecd.xml kioexecdinterface) add_executable(kioexec ${kioexec_SRCS}) configure_file(config-kioexec.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kioexec.h) ecm_mark_nongui_executable(kioexec) target_link_libraries(kioexec Qt5::Widgets KF5::I18n KF5::KIOWidgets # KRun KF5::WidgetsAddons # KMessageBox KF5::WindowSystem # KStartupInfo KF5::DBusAddons # KDBusService ) if (HAVE_X11) target_link_libraries(kioexec Qt5::X11Extras) endif() install(TARGETS kioexec DESTINATION ${KDE_INSTALL_LIBEXECDIR_KF5} ) diff --git a/src/kioexec/main.cpp b/src/kioexec/main.cpp index f8343578..8feb16ab 100644 --- a/src/kioexec/main.cpp +++ b/src/kioexec/main.cpp @@ -1,312 +1,315 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2000-2005 David Faure Copyright (C) 2001 Waldo Bastian 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; see the file COPYING. 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 "main.h" #include "kio_version.h" #include "kioexecdinterface.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if HAVE_X11 #include #endif static const char description[] = I18N_NOOP("KIO Exec - Opens remote files, watches modifications, asks for upload"); KIOExec::KIOExec(const QStringList &args, bool tempFiles, const QString &suggestedFileName) : mExited(false) , mTempFiles(tempFiles) , mUseDaemon(false) , mSuggestedFileName(suggestedFileName) , expectedCounter(0) , command(args.first()) , jobCounter(0) { qDebug() << "command=" << command << "args=" << args; for (int i = 1; i < args.count(); i++) { const QUrl urlArg = QUrl::fromUserInput(args.value(i)); if (!urlArg.isValid()) { KMessageBox::error(nullptr, i18n("Invalid URL: %1", args.value(i))); exit(1); } KIO::StatJob* mostlocal = KIO::mostLocalUrl(urlArg); bool b = mostlocal->exec(); if (!b) { KMessageBox::error(nullptr, i18n("File not found: %1", urlArg.toDisplayString())); exit(1); } Q_ASSERT(b); const QUrl url = mostlocal->mostLocalUrl(); //kDebug() << "url=" << url.url() << " filename=" << url.fileName(); // A local file, not an URL ? // => It is not encoded and not shell escaped, too. if (url.isLocalFile()) { FileInfo file; file.path = url.toLocalFile(); file.url = url; fileList.append(file); } else { // It is an URL if (!url.isValid()) { KMessageBox::error(nullptr, i18n("The URL %1\nis malformed" , url.url())); } else if (mTempFiles) { KMessageBox::error(nullptr, i18n("Remote URL %1\nnot allowed with --tempfiles switch" , url.toDisplayString())); } else { // We must fetch the file QString fileName = KIO::encodeFileName(url.fileName()); if (!suggestedFileName.isEmpty()) fileName = suggestedFileName; // Build the destination filename, in ~/.cache/kioexec/krun/ // Unlike KDE-1.1, we put the filename at the end so that the extension is kept // (Some programs rely on it) QString krun_writable = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QStringLiteral("/krun/%1_%2/").arg(QCoreApplication::applicationPid()).arg(jobCounter++); QDir().mkpath(krun_writable); // error handling will be done by the job QString tmp = krun_writable + fileName; FileInfo file; file.path = tmp; file.url = url; fileList.append(file); expectedCounter++; const QUrl dest = QUrl::fromLocalFile(tmp); qDebug() << "Copying" << url << " to" << dest; KIO::Job *job = KIO::file_copy(url, dest); jobList.append(job); connect(job, &KJob::result, this, &KIOExec::slotResult); } } } if (mTempFiles) { slotRunApp(); return; } counter = 0; if (counter == expectedCounter) { slotResult(nullptr); } } void KIOExec::slotResult(KJob *job) { if (job) { KIO::FileCopyJob *copyJob = static_cast(job); const QString path = copyJob->destUrl().path(); if (job->error()) { // That error dialog would be queued, i.e. not immediate... //job->showErrorDialog(); if ((job->error() != KIO::ERR_USER_CANCELED)) KMessageBox::error(nullptr, job->errorString()); auto it = std::find_if(fileList.begin(), fileList.end(), [&path](const FileInfo &i) { return (i.path == path); }); if (it != fileList.end()) { fileList.erase(it); } else { qDebug() << path << " not found in list"; } } else { // Tell kioexecd to watch the file for changes. const QString dest = copyJob->srcUrl().toString(); qDebug() << "Telling kioexecd to watch path" << path << "dest" << dest; OrgKdeKIOExecdInterface kioexecd(QStringLiteral("org.kde.kioexecd"), QStringLiteral("/modules/kioexecd"), QDBusConnection::sessionBus()); kioexecd.watch(path, dest); mUseDaemon = !kioexecd.lastError().isValid(); if (!mUseDaemon) { qDebug() << "Not using kioexecd"; } } } counter++; if (counter < expectedCounter) { return; } qDebug() << "All files downloaded, will call slotRunApp shortly"; // We know we can run the app now - but let's finish the job properly first. QTimer::singleShot(0, this, &KIOExec::slotRunApp); jobList.clear(); } void KIOExec::slotRunApp() { if (fileList.isEmpty()) { qDebug() << "No files downloaded -> exiting"; mExited = true; QApplication::exit(1); return; } KService service(QStringLiteral("dummy"), command, QString()); QList list; // Store modification times QList::Iterator it = fileList.begin(); for (; it != fileList.end() ; ++it) { QFileInfo info(QFile::encodeName(it->path)); it->time = info.lastModified(); QUrl url = QUrl::fromLocalFile(it->path); list << url; } KIO::DesktopExecParser execParser(service, list); QStringList params = execParser.resultingArguments(); qDebug() << "EXEC " << params.join(QStringLiteral(" ")); // propagate the startup identification to the started process KStartupInfoId id; QByteArray startupId; #if HAVE_X11 if (QX11Info::isPlatformX11()) { startupId = QX11Info::nextStartupId(); } #endif id.initId(startupId); id.setupStartupEnv(); QString exe(params.takeFirst()); const int exit_code = QProcess::execute(exe, params); KStartupInfo::resetStartupEnv(); qDebug() << "EXEC done"; // Test whether one of the files changed for (it = fileList.begin(); it != fileList.end(); ++it) { QString src = it->path; const QUrl dest = it->url; QFileInfo info(src); const bool uploadChanges = !mUseDaemon && !dest.isLocalFile(); if (info.exists() && (it->time != info.lastModified())) { if (mTempFiles) { if (KMessageBox::questionYesNo(nullptr, i18n("The supposedly temporary file\n%1\nhas been modified.\nDo you still want to delete it?", dest.toDisplayString(QUrl::PreferLocalFile)), i18n("File Changed"), KStandardGuiItem::del(), KGuiItem(i18n("Do Not Delete"))) != KMessageBox::Yes) continue; // don't delete the temp file } else if (uploadChanges) { // no upload when it's already a local file or kioexecd already did it. if (KMessageBox::questionYesNo(nullptr, i18n("The file\n%1\nhas been modified.\nDo you want to upload the changes?" , dest.toDisplayString()), i18n("File Changed"), KGuiItem(i18n("Upload")), KGuiItem(i18n("Do Not Upload"))) == KMessageBox::Yes) { qDebug() << "src='" << src << "' dest='" << dest << "'"; // Do it the synchronous way. KIO::CopyJob* job = KIO::copy(QUrl::fromLocalFile(src), dest); if (!job->exec()) { KMessageBox::error(nullptr, job->errorText()); continue; // don't delete the temp file } } } } if ((uploadChanges || mTempFiles) && exit_code == 0) { // Wait for a reasonable time so that even if the application forks on startup (like OOo or amarok) // it will have time to start up and read the file before it gets deleted. #130709. qDebug() << "sleeping..."; QThread::currentThread()->sleep(180); // 3 mn const QString parentDir = info.path(); qDebug() << "about to delete" << parentDir << "containing" << info.fileName(); QFile(QFile::encodeName(src)).remove(); QDir().rmdir(parentDir); } } mExited = true; QApplication::exit(exit_code); } int main(int argc, char **argv) { QApplication app(argc, argv); KAboutData aboutData(QStringLiteral("kioexec"), i18n("KIOExec"), QStringLiteral(KIO_VERSION_STRING), i18n(description), KAboutLicense::GPL, i18n("(c) 1998-2000,2003 The KFM/Konqueror Developers")); aboutData.addAuthor(i18n("David Faure"), QString(), QStringLiteral("faure@kde.org")); aboutData.addAuthor(i18n("Stephan Kulow"), QString(), QStringLiteral("coolo@kde.org")); aboutData.addAuthor(i18n("Bernhard Rosenkraenzer"), QString(), QStringLiteral("bero@arklinux.org")); aboutData.addAuthor(i18n("Waldo Bastian"), QString(), QStringLiteral("bastian@kde.org")); aboutData.addAuthor(i18n("Oswald Buddenhagen"), QString(), QStringLiteral("ossi@kde.org")); KAboutData::setApplicationData(aboutData); KDBusService service(KDBusService::Multiple); QCommandLineParser parser; parser.addHelpOption(); parser.addVersionOption(); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("tempfiles") , i18n("Treat URLs as local files and delete them afterwards"))); parser.addOption(QCommandLineOption(QStringList() << QStringLiteral("suggestedfilename"), i18n("Suggested file name for the downloaded file"), QStringLiteral("filename"))); parser.addPositionalArgument(QStringLiteral("command"), i18n("Command to execute")); parser.addPositionalArgument(QStringLiteral("urls"), i18n("URL(s) or local file(s) used for 'command'")); app.setQuitOnLastWindowClosed(false); aboutData.setupCommandLine(&parser); parser.process(app); aboutData.processCommandLine(&parser); if (parser.positionalArguments().count() < 1) { parser.showHelp(-1); return -1; } const bool tempfiles = parser.isSet(QStringLiteral("tempfiles")); const QString suggestedfilename = parser.value(QStringLiteral("suggestedfilename")); KIOExec exec(parser.positionalArguments(), tempfiles, suggestedfilename); // Don't go into the event loop if we already want to exit (#172197) if (exec.exited()) { return 0; } return app.exec(); }