Advance Wayland and KDE package bring-up
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
add_subdirectory(core)
|
||||
|
||||
# KIOCore-only executables
|
||||
if (NOT KIOCORE_ONLY)
|
||||
if (NOT ANDROID)
|
||||
add_subdirectory(kioworkers)
|
||||
add_subdirectory(schemehandlers)
|
||||
endif()
|
||||
|
||||
if (HAVE_QTDBUS)
|
||||
add_subdirectory(kiod)
|
||||
add_subdirectory(kssld)
|
||||
endif()
|
||||
add_subdirectory(kioworker)
|
||||
endif()
|
||||
|
||||
if (NOT KIOCORE_ONLY)
|
||||
add_subdirectory(gui)
|
||||
add_subdirectory(widgets)
|
||||
|
||||
if (HAVE_QTDBUS)
|
||||
add_subdirectory(kpasswdserver)
|
||||
add_subdirectory(kioexec)
|
||||
endif()
|
||||
|
||||
if (NOT ANDROID)
|
||||
add_subdirectory(filewidgets)
|
||||
add_subdirectory(urifilters)
|
||||
endif()
|
||||
|
||||
set(NON_KIOCORE_LINK_QCHS
|
||||
Qt6Widgets_QCH
|
||||
Qt6Network_QCH
|
||||
KF6Completion_QCH
|
||||
KF6WidgetsAddons_QCH
|
||||
KF6JobWidgets_QCH
|
||||
KF6Bookmarks_QCH
|
||||
KF6ItemViews_QCH
|
||||
KF6Solid_QCH
|
||||
KF6WindowSystem_QCH
|
||||
)
|
||||
|
||||
if(BUILD_DESIGNERPLUGIN AND HAVE_QTDBUS)
|
||||
add_subdirectory(designer)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT KIO
|
||||
FILE kio.categories
|
||||
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
||||
)
|
||||
|
||||
if (BUILD_QCH)
|
||||
macro(_make_absolute var_name base_path)
|
||||
set(_result)
|
||||
foreach(_path ${${var_name}})
|
||||
if(IS_ABSOLUTE "${_path}")
|
||||
list(APPEND _result "${_path}")
|
||||
else()
|
||||
list(APPEND _result "${base_path}/${_path}")
|
||||
endif()
|
||||
endforeach()
|
||||
set(${var_name} ${_result})
|
||||
endmacro()
|
||||
|
||||
_make_absolute(KIOCore_QCH_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/core")
|
||||
_make_absolute(KIOGui_QCH_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/gui")
|
||||
_make_absolute(KIOWidgets_QCH_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/widgets")
|
||||
_make_absolute(KIOFileWidgets_QCH_SOURCES "${CMAKE_CURRENT_SOURCE_DIR}/filewidgets")
|
||||
|
||||
ecm_add_qch(
|
||||
KF6KIO_QCH
|
||||
NAME KIO
|
||||
BASE_NAME KF6KIO
|
||||
VERSION ${KF_VERSION}
|
||||
ORG_DOMAIN org.kde
|
||||
SOURCES # using only public headers, to cover only public API
|
||||
${KIOCore_QCH_SOURCES}
|
||||
${KIOGui_QCH_SOURCES}
|
||||
${KIOWidgets_QCH_SOURCES}
|
||||
${KIOFileWidgets_QCH_SOURCES}
|
||||
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
|
||||
IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics"
|
||||
LINK_QCHS
|
||||
KF6CoreAddons_QCH
|
||||
KF6Service_QCH
|
||||
${NON_KIOCORE_LINK_QCHS}
|
||||
BLANK_MACROS
|
||||
KIOCORE_EXPORT
|
||||
KIOCORE_DEPRECATED_EXPORT
|
||||
KIOCORE_DEPRECATED
|
||||
"KIOCORE_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOCORE_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
"KIOCORE_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOCORE_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
KIOGUI_EXPORT
|
||||
KIOGUI_DEPRECATED_EXPORT
|
||||
KIOGUI_DEPRECATED
|
||||
"KIOGUI_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOGUI_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t))"
|
||||
"KIOGUI_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOGUI_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t))"
|
||||
KIOWIDGETS_EXPORT
|
||||
KIOWIDGETS_DEPRECATED_EXPORT
|
||||
KIOWIDGETS_DEPRECATED
|
||||
"KIOWIDGETS_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOWIDGETS_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
"KIOWIDGETS_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOWIDGETS_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t)"
|
||||
KIOFILEWIDGETS_EXPORT
|
||||
KIOFILEWIDGETS_DEPRECATED_EXPORT
|
||||
KIOFILEWIDGETS_DEPRECATED
|
||||
"KIOFILEWIDGETS_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOFILEWIDGETS_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t))"
|
||||
"KIOFILEWIDGETS_ENUMERATOR_DEPRECATED_VERSION(x, y, t)"
|
||||
"KIOFILEWIDGETS_ENUMERATOR_DEPRECATED_VERSION_BELATED(x, y, xt, yt, t))"
|
||||
PREDEFINED_MACROS
|
||||
"KIOCORE_ENABLE_DEPRECATED_SINCE(x, y)=1"
|
||||
"KIOCORE_BUILD_DEPRECATED_SINCE(x, y)=1"
|
||||
"KIOGUI_ENABLE_DEPRECATED_SINCE(x, y)=1"
|
||||
"KIOGUI_BUILD_DEPRECATED_SINCE(x, y)=1"
|
||||
"KIOWIDGETS_ENABLE_DEPRECATED_SINCE(x, y)=1"
|
||||
"KIOWIDGETS_BUILD_DEPRECATED_SINCE(x, y)=1"
|
||||
"KIOFILEWIDGETS_ENABLE_DEPRECATED_SINCE(x, y)=1"
|
||||
"KIOFILEWIDGETS_BUILD_DEPRECATED_SINCE(x, y)=1"
|
||||
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
@@ -0,0 +1,11 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Invoke the extractrc script on all .ui, .rc, and .kcfg files in the sources.
|
||||
# The results are stored in a pseudo .cpp file to be picked up by xgettext.
|
||||
lst=`find . -name \*.rc -o -name \*.ui -o -name \*.kcfg`
|
||||
if [ -n "$lst" ] ; then
|
||||
$EXTRACTRC $lst >> rc.cpp
|
||||
fi
|
||||
|
||||
# Run xgettext to extract strings from all source files.
|
||||
$XGETTEXT `find . -name \*.cpp -o -name \*.h -o -name \*.qml` -o $podir/kio6.pot
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2000-2002 Till Adam <adam@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef ACLHELPERS_P_H
|
||||
#define ACLHELPERS_P_H
|
||||
|
||||
/*************************************
|
||||
*
|
||||
* ACL handling helpers
|
||||
*
|
||||
*************************************/
|
||||
|
||||
#include <config-kiocore.h>
|
||||
|
||||
#include "utils_p.h"
|
||||
#include <KIO/UDSEntry>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#if HAVE_SYS_ACL_H
|
||||
#include <sys/acl.h>
|
||||
#endif
|
||||
#if HAVE_ACL_LIBACL_H
|
||||
#include <acl/libacl.h>
|
||||
#endif
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
* WARNING: DO NOT USE outside KIO Framework
|
||||
*/
|
||||
namespace ACLPortability
|
||||
{
|
||||
/// @internal
|
||||
__attribute__((unused)) static inline int acl_cmp(acl_t acl1, acl_t acl2)
|
||||
{
|
||||
#ifdef Q_OS_FREEBSD
|
||||
return ::acl_cmp_np(acl1, acl2);
|
||||
#else
|
||||
return ::acl_cmp(acl1, acl2);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @internal
|
||||
__attribute__((unused)) static inline acl_t acl_from_mode(const mode_t mode)
|
||||
{
|
||||
#ifdef Q_OS_FREEBSD
|
||||
return ::acl_from_mode_np(mode);
|
||||
#else
|
||||
return ::acl_from_mode(mode);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @internal
|
||||
static inline int acl_equiv_mode(acl_t acl, mode_t *mode_p)
|
||||
{
|
||||
#ifdef Q_OS_FREEBSD
|
||||
return ::acl_equiv_mode_np(acl, mode_p);
|
||||
#else
|
||||
return ::acl_equiv_mode(acl, mode_p);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @internal
|
||||
__attribute__((unused)) static inline int acl_get_perm(acl_permset_t permset_d, acl_perm_t perm)
|
||||
{
|
||||
#ifdef Q_OS_FREEBSD
|
||||
return ::acl_get_perm_np(permset_d, perm);
|
||||
#else
|
||||
return ::acl_get_perm(permset_d, perm);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// @internal
|
||||
__attribute__((unused)) static inline int acl_extended_file(const char *path_p)
|
||||
{
|
||||
#ifdef Q_OS_FREEBSD
|
||||
return ::acl_extended_file_np(path_p);
|
||||
#else
|
||||
return ::acl_extended_file(path_p);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace ACLPortability
|
||||
} // namespace KIO
|
||||
|
||||
static QString aclToText(acl_t acl)
|
||||
{
|
||||
ssize_t size = 0;
|
||||
char *txt = acl_to_text(acl, &size);
|
||||
const QString ret = QString::fromLatin1(txt, size);
|
||||
acl_free(txt);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Append an atom indicating whether the file has extended acl information
|
||||
* and if withACL is specified also one with the acl itself. If it's a directory
|
||||
* and it has a default ACL, also append that. */
|
||||
__attribute__((unused)) static void appendACLAtoms(const QByteArray &path, KIO::UDSEntry &entry, mode_t type)
|
||||
{
|
||||
// first check for a noop
|
||||
if (KIO::ACLPortability::acl_extended_file(path.data()) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
acl_t acl = nullptr;
|
||||
acl_t defaultAcl = nullptr;
|
||||
// do we have an acl for the file, and/or a default acl for the dir, if it is one?
|
||||
acl = acl_get_file(path.data(), ACL_TYPE_ACCESS);
|
||||
/* Sadly libacl does not provided a means of checking for extended ACL and default
|
||||
* ACL separately. Since a directory can have both, we need to check again. */
|
||||
if (Utils::isDirMask(type)) {
|
||||
if (acl) {
|
||||
if (KIO::ACLPortability::acl_equiv_mode(acl, nullptr) == 0) {
|
||||
acl_free(acl);
|
||||
acl = nullptr;
|
||||
}
|
||||
}
|
||||
defaultAcl = acl_get_file(path.data(), ACL_TYPE_DEFAULT);
|
||||
}
|
||||
|
||||
if (acl || defaultAcl) {
|
||||
// qDebug() << path.constData() << "has extended ACL entries";
|
||||
entry.replace(KIO::UDSEntry::UDS_EXTENDED_ACL, 1);
|
||||
|
||||
if (acl) {
|
||||
const QString str = aclToText(acl);
|
||||
entry.replace(KIO::UDSEntry::UDS_ACL_STRING, str);
|
||||
// qDebug() << path.constData() << "ACL:" << str;
|
||||
acl_free(acl);
|
||||
}
|
||||
|
||||
if (defaultAcl) {
|
||||
const QString str = aclToText(defaultAcl);
|
||||
entry.replace(KIO::UDSEntry::UDS_DEFAULT_ACL_STRING, str);
|
||||
// qDebug() << path.constData() << "DEFAULT ACL:" << str;
|
||||
acl_free(defaultAcl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // ACLHELPERS_P_H
|
||||
@@ -0,0 +1,330 @@
|
||||
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)
|
||||
|
||||
configure_file(../kioworkers/file/config-stat-unix.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-stat-unix.h)
|
||||
|
||||
add_library(KF6KIOCore)
|
||||
add_library(KF6::KIOCore ALIAS KF6KIOCore)
|
||||
|
||||
set_target_properties(KF6KIOCore PROPERTIES
|
||||
VERSION ${KIO_VERSION}
|
||||
SOVERSION ${KIO_SOVERSION}
|
||||
EXPORT_NAME KIOCore
|
||||
)
|
||||
|
||||
target_sources(KF6KIOCore PRIVATE
|
||||
connectionbackend.cpp
|
||||
connection.cpp
|
||||
connectionserver.cpp
|
||||
krecentdocument.cpp
|
||||
krecentdirs.cpp
|
||||
kfileitemlistproperties.cpp
|
||||
directorysizejob.cpp
|
||||
chmodjob.cpp
|
||||
usernotificationhandler.cpp
|
||||
ksambasharedata.cpp
|
||||
ksambashare.cpp
|
||||
knfsshare.cpp
|
||||
kfileitem.cpp
|
||||
davjob.cpp
|
||||
deletejob.cpp
|
||||
copyjob.cpp
|
||||
filejob.cpp
|
||||
mkdirjob.cpp
|
||||
mkpathjob.cpp
|
||||
kremoteencoding.cpp
|
||||
slavebase.cpp
|
||||
workerbase.cpp
|
||||
dataworker.cpp
|
||||
dataprotocol.cpp
|
||||
desktopexecparser.cpp
|
||||
emptytrashjob.cpp
|
||||
authinfo.cpp
|
||||
job_error.cpp
|
||||
job.cpp
|
||||
filecopyjob.cpp
|
||||
listjob.cpp
|
||||
mimetypejob.cpp
|
||||
mimetypefinderjob.cpp
|
||||
restorejob.cpp
|
||||
simplejob.cpp
|
||||
specialjob.cpp
|
||||
statjob.cpp
|
||||
namefinderjob.cpp
|
||||
storedtransferjob.cpp
|
||||
transferjob.cpp
|
||||
filesystemfreespacejob.cpp
|
||||
scheduler.cpp
|
||||
kprotocolmanager.cpp
|
||||
kurlauthorized.cpp
|
||||
kacl.cpp
|
||||
udsentry.cpp
|
||||
global.cpp
|
||||
metadata.cpp
|
||||
kprotocolinfo.cpp
|
||||
kprotocolinfofactory.cpp
|
||||
jobtracker.cpp
|
||||
jobuidelegateextension.cpp
|
||||
jobuidelegatefactory.cpp
|
||||
askuseractioninterface.cpp
|
||||
kmountpoint.cpp
|
||||
kcoredirlister.cpp
|
||||
faviconscache.cpp
|
||||
untrustedprogramhandlerinterface.cpp
|
||||
kioglobal_p.cpp
|
||||
batchrenamejob.cpp
|
||||
worker.cpp
|
||||
workerinterface.cpp
|
||||
workerconfig.cpp
|
||||
workerfactory.cpp
|
||||
workerthread.cpp
|
||||
kfilefilter.cpp
|
||||
koverlayiconplugin.cpp
|
||||
openwith.cpp
|
||||
ksslerroruidata.cpp
|
||||
)
|
||||
|
||||
set(KIOCore_HEADERS_dbus)
|
||||
if (HAVE_QTDBUS)
|
||||
target_sources(KF6KIOCore PRIVATE
|
||||
kpasswdserverloop.cpp
|
||||
kpasswdserverclient.cpp
|
||||
kdirnotify.cpp
|
||||
forwardingworkerbase.cpp
|
||||
)
|
||||
set(KIOCore_HEADERS_dbus KDirNotify)
|
||||
endif()
|
||||
|
||||
ecm_qt_declare_logging_category(KF6KIOCore
|
||||
HEADER kiocoredebug.h
|
||||
IDENTIFIER KIO_CORE
|
||||
CATEGORY_NAME kf.kio.core
|
||||
OLD_CATEGORY_NAMES kf5.kio.core
|
||||
DESCRIPTION "KIOCore (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER KIO_COPYJOB_DEBUG
|
||||
CATEGORY_NAME kf.kio.core.copyjob
|
||||
OLD_CATEGORY_NAMES kf5.kio.core.copyjob
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "KIO::CopyJob (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER KIO_CORE_DIRLISTER
|
||||
CATEGORY_NAME kf.kio.core.dirlister
|
||||
OLD_CATEGORY_NAMES kf5.kio.core.dirlister
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "KCoreDirLister (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_qt_export_logging_category(
|
||||
IDENTIFIER KIO_CORE_SAMBASHARE
|
||||
CATEGORY_NAME kf.kio.core.sambashare
|
||||
OLD_CATEGORY_NAMES kf5.kio.core.sambashare
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "sambashare (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6KIOCore
|
||||
HEADER kiocoreconnectiondebug.h
|
||||
IDENTIFIER KIO_CORE_CONNECTION
|
||||
CATEGORY_NAME kf.kio.core.connection
|
||||
DEFAULT_SEVERITY Warning
|
||||
DESCRIPTION "Connection (KIO)"
|
||||
EXPORT KIO
|
||||
)
|
||||
|
||||
if (UNIX)
|
||||
target_sources(KF6KIOCore PRIVATE
|
||||
kioglobal_p_unix.cpp
|
||||
)
|
||||
endif()
|
||||
if (WIN32)
|
||||
target_sources(KF6KIOCore PRIVATE
|
||||
kioglobal_p_win.cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
set(kiocore_dbus_SRCS)
|
||||
if (HAVE_QTDBUS)
|
||||
qt_add_dbus_interface(kiocore_dbus_SRCS org.kde.KIOFuse.VFS.xml kiofuse_interface)
|
||||
|
||||
set_source_files_properties(org.kde.KPasswdServer.xml
|
||||
PROPERTIES INCLUDE authinfo.h
|
||||
)
|
||||
qt_add_dbus_interface(kiocore_dbus_SRCS org.kde.KPasswdServer.xml kpasswdserver_interface)
|
||||
endif()
|
||||
|
||||
target_sources(KF6KIOCore PRIVATE
|
||||
${kiocore_dbus_SRCS}
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF6KIOCore
|
||||
BASE_NAME KIOCore
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
VERSION_BASE_NAME KIO
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS 5.101 6.3 6.4 6.6 6.9
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_include_directories(KF6KIOCore PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/..>" # kio_version.h
|
||||
)
|
||||
|
||||
target_include_directories(KF6KIOCore INTERFACE
|
||||
"$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KIOCore>"
|
||||
"$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KIO>"
|
||||
)
|
||||
|
||||
target_link_libraries(KF6KIOCore
|
||||
PUBLIC
|
||||
KF6::CoreAddons # KJob
|
||||
PRIVATE
|
||||
Qt6::Xml # davjob.cpp uses QDom
|
||||
Qt6::Concurrent
|
||||
KF6::ConfigCore
|
||||
KF6::I18n
|
||||
KF6::Service
|
||||
)
|
||||
|
||||
if (NOT ANDROID)
|
||||
target_link_libraries(KF6KIOCore PRIVATE KF6::Crash)
|
||||
endif()
|
||||
|
||||
if (HAVE_QTDBUS)
|
||||
target_link_libraries(KF6KIOCore PUBLIC ${DBUS_LIB})
|
||||
endif()
|
||||
|
||||
if (TARGET KF6::AuthCore)
|
||||
target_link_libraries(KF6KIOCore PRIVATE KF6::AuthCore) #SlaveBase uses KAuth::Action
|
||||
endif()
|
||||
|
||||
if(ACL_FOUND)
|
||||
target_link_libraries(KF6KIOCore PRIVATE ${ACL_LIBS})
|
||||
endif()
|
||||
|
||||
if(HAVE_LIB_MOUNT)
|
||||
target_link_libraries(KF6KIOCore PRIVATE LibMount::LibMount)
|
||||
endif()
|
||||
|
||||
# this should be done by cmake, see bug 371721
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
add_custom_command(
|
||||
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h
|
||||
COMMAND "${CMAKE_CXX_COMPILER}" "${CMAKE_CXX_COMPILER_ARG1}" "-dM" "-E" "-c" "${CMAKE_ROOT}/Modules/CMakeCXXCompilerABI.cpp" > ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h
|
||||
)
|
||||
set_property(TARGET KF6KIOCore APPEND PROPERTY AUTOMOC_MOC_OPTIONS --include ${CMAKE_CURRENT_BINARY_DIR}/moc_predefs.h)
|
||||
set_property(TARGET KF6KIOCore 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
|
||||
DirectorySizeJob
|
||||
Job # ### should forward to job_base.h, not job.h...
|
||||
JobTracker
|
||||
Global
|
||||
ChmodJob
|
||||
DeleteJob
|
||||
CopyJob
|
||||
EmptyTrashJob
|
||||
FileJob
|
||||
MkdirJob
|
||||
MkpathJob
|
||||
MetaData
|
||||
UDSEntry
|
||||
JobUiDelegateExtension
|
||||
JobUiDelegateFactory
|
||||
AskUserActionInterface
|
||||
FileCopyJob
|
||||
ListJob
|
||||
MimetypeJob
|
||||
MimeTypeFinderJob
|
||||
RestoreJob
|
||||
SimpleJob
|
||||
SpecialJob
|
||||
StatJob
|
||||
NameFinderJob
|
||||
StoredTransferJob
|
||||
TransferJob
|
||||
AuthInfo
|
||||
DavJob
|
||||
DesktopExecParser
|
||||
FileSystemFreeSpaceJob
|
||||
BatchRenameJob
|
||||
WorkerBase
|
||||
WorkerFactory
|
||||
ForwardingWorkerBase
|
||||
UntrustedProgramHandlerInterface
|
||||
OpenWith
|
||||
|
||||
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 KF6KIOCore EXPORT KF6KIOTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
list(APPEND KIO_namespaced_HEADERS
|
||||
ioworker_defaults.h
|
||||
job_base.h
|
||||
)
|
||||
|
||||
install(FILES
|
||||
${KIO_namespaced_HEADERS}
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KIOCore/kio COMPONENT Devel
|
||||
)
|
||||
|
||||
# Headers not prefixed with KIO/
|
||||
ecm_generate_headers(KIOCore_HEADERS
|
||||
HEADER_NAMES
|
||||
KACL
|
||||
KUrlAuthorized
|
||||
KCoreDirLister
|
||||
KRemoteEncoding
|
||||
KFileItem
|
||||
KFileItemListProperties
|
||||
KMountPoint
|
||||
KSambaShare
|
||||
KSambaShareData
|
||||
KPasswdServerClient
|
||||
KProtocolInfo
|
||||
KProtocolManager
|
||||
KRecentDocument
|
||||
KRecentDirs
|
||||
KSslErrorUiData
|
||||
KFileFilter
|
||||
KOverlayIconPlugin
|
||||
${KIOCore_HEADERS_dbus}
|
||||
|
||||
REQUIRED_HEADERS KIOCore_HEADERS
|
||||
)
|
||||
|
||||
install(FILES ${KIOCore_CamelCase_HEADERS} DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KIOCore/KIO COMPONENT Devel)
|
||||
|
||||
install(FILES
|
||||
${KIOCore_HEADERS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kiocore_export.h
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KIOCore COMPONENT Devel)
|
||||
|
||||
# make available to ecm_add_qch in parent folder
|
||||
set(KIOCore_QCH_SOURCES ${KIOCore_HEADERS} ${KIO_namespaced_HEADERS} PARENT_SCOPE)
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
include(CheckIncludeFile)
|
||||
include(CheckIncludeFiles)
|
||||
include(CheckStructHasMember)
|
||||
include(CheckFunctionExists)
|
||||
include(CheckSymbolExists)
|
||||
|
||||
check_struct_has_member("struct sockaddr" sa_len "sys/socket.h" HAVE_STRUCT_SOCKADDR_SA_LEN)
|
||||
|
||||
### KMountPoint
|
||||
|
||||
check_function_exists(getmntinfo HAVE_GETMNTINFO)
|
||||
|
||||
check_include_files("sys/param.h;sys/mount.h" HAVE_SYS_MOUNT_H)
|
||||
|
||||
check_include_files(fstab.h HAVE_FSTAB_H)
|
||||
check_include_files(sys/param.h HAVE_SYS_PARAM_H)
|
||||
|
||||
check_cxx_source_compiles("
|
||||
#include <sys/types.h>
|
||||
#include <sys/statvfs.h>
|
||||
int main(){
|
||||
struct statvfs *mntbufp;
|
||||
int flags;
|
||||
return getmntinfo(&mntbufp, flags);
|
||||
}
|
||||
" GETMNTINFO_USES_STATVFS )
|
||||
|
||||
check_symbol_exists("__GLIBC__" "stdlib.h" LIBC_IS_GLIBC)
|
||||
if (LIBC_IS_GLIBC)
|
||||
check_cxx_source_compiles("
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
int main() {
|
||||
struct statx buf;
|
||||
statx(AT_FDCWD, \"/foo\", AT_EMPTY_PATH, STATX_BASIC_STATS, &buf);
|
||||
return 0;
|
||||
}
|
||||
" HAVE_STATX)
|
||||
else()
|
||||
set(HAVE_STATX 0)
|
||||
endif()
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2020 Ahmad Samir <a.samirh78@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "askuseractioninterface.h"
|
||||
|
||||
class KIO::AskUserActionInterfacePrivate
|
||||
{
|
||||
public:
|
||||
AskUserActionInterfacePrivate()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
KIO::AskUserActionInterface::AskUserActionInterface(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
KIO::AskUserActionInterface::~AskUserActionInterface()
|
||||
{
|
||||
}
|
||||
|
||||
#include "moc_askuseractioninterface.cpp"
|
||||
@@ -0,0 +1,250 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2020 Ahmad Samir <a.samirh78@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef ASKUSERACTIONINTERFACE_H
|
||||
#define ASKUSERACTIONINTERFACE_H
|
||||
|
||||
#include <kio/jobuidelegateextension.h> // RenameDialog_Result, SkipDialog_Result
|
||||
#include <kiocore_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KJob;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class AskUserActionInterfacePrivate;
|
||||
|
||||
/**
|
||||
* @class KIO::AskUserActionInterface askuseractioninterface.h <KIO/AskUserActionInterface>
|
||||
*
|
||||
* @brief The AskUserActionInterface class allows a KIO::Job to prompt the user
|
||||
* for a decision when e.g.\ copying directories/files and there is a conflict
|
||||
* (e.g.\ a file with the same name already exists at the destination).
|
||||
*
|
||||
* The methods in this interface are similar to their counterparts in
|
||||
* KIO::JobUiDelegateExtension, the main difference is that AskUserActionInterface
|
||||
* shows the dialogs using show() or open(), rather than exec(), the latter creates
|
||||
* a nested event loop which could lead to crashes.
|
||||
*
|
||||
* @sa KIO::JobUiDelegateExtension
|
||||
*
|
||||
* @since 5.78
|
||||
*/
|
||||
class KIOCORE_EXPORT AskUserActionInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
explicit AskUserActionInterface(QObject *parent = nullptr);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~AskUserActionInterface() override;
|
||||
|
||||
/**
|
||||
* @relates KIO::RenameDialog
|
||||
*
|
||||
* Constructs a modal, parent-less "rename" dialog, to prompt the user for a decision
|
||||
* in case of conflicts, while copying/moving files. The dialog is shown using open(),
|
||||
* rather than exec() (the latter creates a nested eventloop which could lead to crashes).
|
||||
* You will need to connect to the askUserRenameResult() signal to get the dialog's result
|
||||
* (exit code). The exit code is one of KIO::RenameDialog_Result.
|
||||
*
|
||||
* @see KIO::RenameDialog_Result enum.
|
||||
*
|
||||
* @param job the job that called this method
|
||||
* @param title the title for the dialog box
|
||||
* @param src the URL of the file/dir being copied/moved
|
||||
* @param dest the URL of the destination file/dir, i.e. the one that already exists
|
||||
* @param options parameters for the dialog (which buttons to show... etc), OR'ed values
|
||||
* from the KIO::RenameDialog_Options enum
|
||||
* @param sizeSrc size of the source file
|
||||
* @param sizeDest size of the destination file
|
||||
* @param ctimeSrc creation time of the source file
|
||||
* @param ctimeDest creation time of the destination file
|
||||
* @param mtimeSrc modification time of the source file
|
||||
* @param mtimeDest modification time of the destination file
|
||||
*/
|
||||
virtual void askUserRename(KJob *job,
|
||||
const QString &title,
|
||||
const QUrl &src,
|
||||
const QUrl &dest,
|
||||
KIO::RenameDialog_Options options,
|
||||
KIO::filesize_t sizeSrc,
|
||||
KIO::filesize_t sizeDest,
|
||||
const QDateTime &ctimeSrc = {},
|
||||
const QDateTime &ctimeDest = {},
|
||||
const QDateTime &mtimeSrc = {},
|
||||
const QDateTime &mtimeDest = {}) = 0;
|
||||
|
||||
/**
|
||||
* @relates KIO::SkipDialog
|
||||
*
|
||||
* You need to connect to the askUserSkipResult signal to get the dialog's
|
||||
* result.
|
||||
*
|
||||
* @param job the job that called this method
|
||||
* @param options parameters for the dialog (which buttons to show... etc), OR'ed
|
||||
* values from the KIO::SkipDialog_Options enum
|
||||
* @param error_text the error text to show to the user (usually the string returned
|
||||
* by KJob::errorText())
|
||||
*/
|
||||
virtual void askUserSkip(KJob *job, KIO::SkipDialog_Options options, const QString &errorText) = 0;
|
||||
|
||||
/**
|
||||
* The type of deletion.
|
||||
*
|
||||
* Used by askUserDelete().
|
||||
*/
|
||||
enum DeletionType {
|
||||
Delete, /// Delete the files/directories directly, i.e. without moving them to Trash
|
||||
Trash, /// Move the files/directories to Trash
|
||||
EmptyTrash, /// Empty the Trash
|
||||
/**
|
||||
* This is the same as Delete, but more text is added to the message to inform
|
||||
* the user that moving to Trash was tried but failed due to size constraints.
|
||||
* Typical use case is re-asking the user about deleting instead of Trashing.
|
||||
* @since 5.100
|
||||
*/
|
||||
DeleteInsteadOfTrash,
|
||||
};
|
||||
|
||||
/**
|
||||
* Deletion confirmation type.
|
||||
*
|
||||
* Used by askUserDelete().
|
||||
*/
|
||||
enum ConfirmationType {
|
||||
DefaultConfirmation, ///< Do not ask if the user has previously set the "Do not ask again"
|
||||
///< checkbox (which is is shown in the message dialog invoked by askUserDelete())
|
||||
ForceConfirmation, ///< Always ask the user for confirmation
|
||||
};
|
||||
|
||||
/**
|
||||
* Ask for confirmation before moving @p urls (files/directories) to the Trash,
|
||||
* emptying the Trash, or directly deleting files (i.e. without moving to Trash).
|
||||
*
|
||||
* Note that this method is not called automatically by KIO jobs. It's the
|
||||
* application's responsibility to ask the user for confirmation before calling
|
||||
* KIO::del() or KIO::trash().
|
||||
*
|
||||
* You need to connect to the askUserDeleteResult signal to get the dialog's result
|
||||
* (exit code).
|
||||
*
|
||||
* @param urls the urls about to be moved to the Trash or deleted directly
|
||||
* @param deletionType the type of deletion (Delete for real deletion, Trash otherwise),
|
||||
* see the DeletionType enum
|
||||
* @param confirmationType The type of deletion confirmation, see the ConfirmationType enum.
|
||||
* Normally set to DefaultConfirmation
|
||||
* @param parent the parent widget of the message box
|
||||
*/
|
||||
virtual void askUserDelete(const QList<QUrl> &urls,
|
||||
DeletionType deletionType,
|
||||
ConfirmationType confirmationType,
|
||||
QWidget *parent = nullptr) = 0; // TODO KF6: replace QWidget* with QObject*
|
||||
|
||||
enum MessageDialogType {
|
||||
QuestionTwoActions = 1, ///< @since 5.100
|
||||
QuestionTwoActionsCancel = 2, ///< @since 5.100
|
||||
WarningTwoActions = 3, ///< @since 5.100
|
||||
WarningTwoActionsCancel = 4, ///< @since 5.100
|
||||
WarningContinueCancel = 5,
|
||||
Information = 7,
|
||||
Error = 9,
|
||||
};
|
||||
|
||||
/**
|
||||
* This function allows for the delegation of user prompts from the KIO worker.
|
||||
*
|
||||
* @param type the desired type of message box, see the MessageDialogType enum
|
||||
* @param text the message to show to the user
|
||||
* @param title the title of the message dialog box
|
||||
* @param primaryActionText the text for the primary action
|
||||
* @param secondatyActionText the text for the secondary action
|
||||
* @param primaryActionIconName the icon to show on the primary action
|
||||
* @param secondatyActionIconName the icon to show on the secondary action
|
||||
* @param dontAskAgainName the config key name used to store the result from
|
||||
* 'Do not ask again' checkbox
|
||||
* @param details more details about the message shown to the user
|
||||
* @param parent the parent widget of the dialog
|
||||
*/
|
||||
virtual void requestUserMessageBox(MessageDialogType type,
|
||||
const QString &text,
|
||||
const QString &title,
|
||||
const QString &primaryActionText,
|
||||
const QString &secondatyActionText,
|
||||
const QString &primaryActionIconName = {},
|
||||
const QString &secondatyActionIconName = {},
|
||||
const QString &dontAskAgainName = {},
|
||||
const QString &details = {},
|
||||
QWidget *parent = nullptr) = 0; // TODO KF6: replace QWidget* with QObject*, document "widget or window"
|
||||
|
||||
virtual void askIgnoreSslErrors(const QVariantMap &sslErrorData, QWidget *parent) = 0;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Implementations of this interface must emit this signal when the rename dialog
|
||||
* finishes, to notify the caller of the dialog's result.
|
||||
*
|
||||
* @param result the exit code from the rename dialog, one of the KIO::RenameDialog_Result
|
||||
* enum
|
||||
* @param newUrl the new destination URL set by the user
|
||||
* @param parentJob the job that invoked the dialog
|
||||
*/
|
||||
void askUserRenameResult(KIO::RenameDialog_Result result, const QUrl &newUrl, KJob *parentJob);
|
||||
|
||||
/**
|
||||
* Implementations of this interface must emit this signal when the skip dialog
|
||||
* finishes, to notify the caller of the dialog's result.
|
||||
*
|
||||
* @param result the exit code from the skip dialog, one of the KIO::SkipDialog_Result enum
|
||||
* @param parentJob the job that invoked the dialog
|
||||
*/
|
||||
void askUserSkipResult(KIO::SkipDialog_Result result, KJob *parentJob);
|
||||
|
||||
/**
|
||||
* Implementations of this interface must emit this signal when the dialog invoked
|
||||
* by askUserDelete() finishes, to notify the caller of the user's decision.
|
||||
*
|
||||
* @param allowDelete set to true if the user confirmed the delete operation, otherwise
|
||||
* set to false
|
||||
* @param urls a list of urls to delete/trash
|
||||
* @param deletionType the deletion type to use, one of KIO::AskUserActionInterface::DeletionType
|
||||
* @param parent the parent widget passed to askUserDelete(), for request identification
|
||||
*/
|
||||
void askUserDeleteResult(bool allowDelete,
|
||||
const QList<QUrl> &urls,
|
||||
KIO::AskUserActionInterface::DeletionType deletionType,
|
||||
QWidget *parent); // TODO KF6: replace QWidget* with QObject*
|
||||
|
||||
/**
|
||||
* Implementations of this interface must emit this signal when the dialog invoked
|
||||
* by requestUserMessageBox() finishes, to notify the caller of the dialog's result
|
||||
* (exit code).
|
||||
*
|
||||
* @param result the exit code of the dialog, one of KIO::WorkerBase::ButtonCode enum
|
||||
*/
|
||||
void messageBoxResult(int result); // TODO KF6: add a QObject* to identify requests? Or return an int from the request method and pass it back here?
|
||||
|
||||
void askIgnoreSslErrorsResult(int result);
|
||||
|
||||
private:
|
||||
std::unique_ptr<AskUserActionInterfacePrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#endif // ASKUSERACTIONINTERFACE_H
|
||||
@@ -0,0 +1,232 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2001 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "authinfo.h"
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusMetaType>
|
||||
#endif
|
||||
#include <QDataStream>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <QStandardPaths>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
//////
|
||||
|
||||
class ExtraField
|
||||
{
|
||||
public:
|
||||
ExtraField()
|
||||
: flags(AuthInfo::ExtraFieldNoFlags)
|
||||
{
|
||||
}
|
||||
|
||||
ExtraField(const ExtraField &other)
|
||||
: customTitle(other.customTitle)
|
||||
, flags(other.flags)
|
||||
, value(other.value)
|
||||
{
|
||||
}
|
||||
|
||||
ExtraField &operator=(const ExtraField &other)
|
||||
{
|
||||
customTitle = other.customTitle;
|
||||
flags = other.flags;
|
||||
value = other.value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
QString customTitle; // reserved for future use
|
||||
AuthInfo::FieldFlags flags;
|
||||
QVariant value;
|
||||
};
|
||||
Q_DECLARE_METATYPE(ExtraField)
|
||||
|
||||
static QDataStream &operator<<(QDataStream &s, const ExtraField &extraField)
|
||||
{
|
||||
s << extraField.customTitle;
|
||||
s << static_cast<int>(extraField.flags);
|
||||
s << extraField.value;
|
||||
return s;
|
||||
}
|
||||
|
||||
static QDataStream &operator>>(QDataStream &s, ExtraField &extraField)
|
||||
{
|
||||
s >> extraField.customTitle;
|
||||
int i;
|
||||
s >> i;
|
||||
extraField.flags = AuthInfo::FieldFlags(i);
|
||||
s >> extraField.value;
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
static QDBusArgument &operator<<(QDBusArgument &argument, const ExtraField &extraField)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << extraField.customTitle << static_cast<int>(extraField.flags) << QDBusVariant(extraField.value);
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
static const QDBusArgument &operator>>(const QDBusArgument &argument, ExtraField &extraField)
|
||||
{
|
||||
QDBusVariant value;
|
||||
int flag;
|
||||
|
||||
argument.beginStructure();
|
||||
argument >> extraField.customTitle >> flag >> value;
|
||||
argument.endStructure();
|
||||
|
||||
extraField.value = value.variant();
|
||||
extraField.flags = KIO::AuthInfo::FieldFlags(flag);
|
||||
return argument;
|
||||
}
|
||||
#endif
|
||||
|
||||
class KIO::AuthInfoPrivate
|
||||
{
|
||||
public:
|
||||
QMap<QString, ExtraField> extraFields;
|
||||
};
|
||||
|
||||
//////
|
||||
|
||||
AuthInfo::AuthInfo()
|
||||
: d(new AuthInfoPrivate())
|
||||
{
|
||||
modified = false;
|
||||
readOnly = false;
|
||||
verifyPath = false;
|
||||
keepPassword = false;
|
||||
AuthInfo::registerMetaTypes();
|
||||
}
|
||||
|
||||
AuthInfo::AuthInfo(const AuthInfo &info)
|
||||
: d(new AuthInfoPrivate())
|
||||
{
|
||||
(*this) = info;
|
||||
AuthInfo::registerMetaTypes();
|
||||
}
|
||||
|
||||
AuthInfo::~AuthInfo() = default;
|
||||
|
||||
AuthInfo &AuthInfo::operator=(const AuthInfo &info)
|
||||
{
|
||||
url = info.url;
|
||||
username = info.username;
|
||||
password = info.password;
|
||||
prompt = info.prompt;
|
||||
caption = info.caption;
|
||||
comment = info.comment;
|
||||
commentLabel = info.commentLabel;
|
||||
realmValue = info.realmValue;
|
||||
digestInfo = info.digestInfo;
|
||||
verifyPath = info.verifyPath;
|
||||
readOnly = info.readOnly;
|
||||
keepPassword = info.keepPassword;
|
||||
modified = info.modified;
|
||||
d->extraFields = info.d->extraFields;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool AuthInfo::isModified() const
|
||||
{
|
||||
return modified;
|
||||
}
|
||||
|
||||
void AuthInfo::setModified(bool flag)
|
||||
{
|
||||
modified = flag;
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
void AuthInfo::setExtraField(const QString &fieldName, const QVariant &value)
|
||||
{
|
||||
d->extraFields[fieldName].value = value;
|
||||
}
|
||||
|
||||
void AuthInfo::setExtraFieldFlags(const QString &fieldName, const FieldFlags flags)
|
||||
{
|
||||
d->extraFields[fieldName].flags = flags;
|
||||
}
|
||||
|
||||
QVariant AuthInfo::getExtraField(const QString &fieldName) const
|
||||
{
|
||||
const auto it = d->extraFields.constFind(fieldName);
|
||||
if (it == d->extraFields.constEnd()) {
|
||||
return QVariant();
|
||||
}
|
||||
return it->value;
|
||||
}
|
||||
|
||||
AuthInfo::FieldFlags AuthInfo::getExtraFieldFlags(const QString &fieldName) const
|
||||
{
|
||||
const auto it = d->extraFields.constFind(fieldName);
|
||||
if (it == d->extraFields.constEnd()) {
|
||||
return AuthInfo::ExtraFieldNoFlags;
|
||||
}
|
||||
return it->flags;
|
||||
}
|
||||
|
||||
void AuthInfo::registerMetaTypes()
|
||||
{
|
||||
qRegisterMetaType<ExtraField>();
|
||||
qRegisterMetaType<KIO::AuthInfo>();
|
||||
#ifdef WITH_QTDBUS
|
||||
qDBusRegisterMetaType<ExtraField>();
|
||||
qDBusRegisterMetaType<KIO::AuthInfo>();
|
||||
#endif
|
||||
}
|
||||
|
||||
/////
|
||||
|
||||
QDataStream &KIO::operator<<(QDataStream &s, const AuthInfo &a)
|
||||
{
|
||||
s << quint8(1) << a.url << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue << a.digestInfo << a.verifyPath
|
||||
<< a.readOnly << a.keepPassword << a.modified << a.d->extraFields;
|
||||
return s;
|
||||
}
|
||||
|
||||
QDataStream &KIO::operator>>(QDataStream &s, AuthInfo &a)
|
||||
{
|
||||
quint8 version;
|
||||
s >> version >> a.url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo >> a.verifyPath
|
||||
>> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields;
|
||||
return s;
|
||||
}
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
QDBusArgument &KIO::operator<<(QDBusArgument &argument, const AuthInfo &a)
|
||||
{
|
||||
argument.beginStructure();
|
||||
argument << quint8(1) << a.url.toString() << a.username << a.password << a.prompt << a.caption << a.comment << a.commentLabel << a.realmValue
|
||||
<< a.digestInfo << a.verifyPath << a.readOnly << a.keepPassword << a.modified << a.d->extraFields;
|
||||
argument.endStructure();
|
||||
return argument;
|
||||
}
|
||||
|
||||
const QDBusArgument &KIO::operator>>(const QDBusArgument &argument, AuthInfo &a)
|
||||
{
|
||||
QString url;
|
||||
quint8 version;
|
||||
|
||||
argument.beginStructure();
|
||||
argument >> version >> url >> a.username >> a.password >> a.prompt >> a.caption >> a.comment >> a.commentLabel >> a.realmValue >> a.digestInfo
|
||||
>> a.verifyPath >> a.readOnly >> a.keepPassword >> a.modified >> a.d->extraFields;
|
||||
argument.endStructure();
|
||||
|
||||
a.url = QUrl(url);
|
||||
return argument;
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,283 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2001 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_AUTHINFO_H
|
||||
#define KIO_AUTHINFO_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
#include <QVariant> // Q_DECLARE_METATYPE
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QDBusArgument;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class AuthInfoPrivate;
|
||||
|
||||
/**
|
||||
* @class KIO::AuthInfo authinfo.h <KIO/AuthInfo>
|
||||
*
|
||||
* This class is intended to make it easier to prompt for, cache
|
||||
* and retrieve authorization information.
|
||||
*
|
||||
* When using this class to cache, retrieve or prompt authentication
|
||||
* information, you only need to set the necessary attributes. For
|
||||
* example, to check whether a password is already cached, the only
|
||||
* required information is the URL of the resource and optionally
|
||||
* whether or not a path match should be performed. Similarly, to
|
||||
* prompt for password you only need to optionally set the prompt,
|
||||
* username (if already supplied), comment and commentLabel fields.
|
||||
*
|
||||
* <em>SPECIAL NOTE:</em> If you extend this class to add additional
|
||||
* parameters do not forget to overload the stream insertion and
|
||||
* extraction operators ("<<" and ">>") so that the added data can
|
||||
* be correctly serialized.
|
||||
*
|
||||
* @short A two way messaging class for passing authentication information.
|
||||
* @author Dawit Alemayehu <adawit@kde.org>
|
||||
*/
|
||||
class KIOCORE_EXPORT AuthInfo
|
||||
{
|
||||
KIOCORE_EXPORT friend QDataStream &operator<<(QDataStream &s, const AuthInfo &a);
|
||||
KIOCORE_EXPORT friend QDataStream &operator>>(QDataStream &s, AuthInfo &a);
|
||||
|
||||
KIOCORE_EXPORT friend QDBusArgument &operator<<(QDBusArgument &argument, const AuthInfo &a);
|
||||
KIOCORE_EXPORT friend const QDBusArgument &operator>>(const QDBusArgument &argument, AuthInfo &a);
|
||||
|
||||
public:
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
AuthInfo();
|
||||
|
||||
/**
|
||||
* Copy constructor.
|
||||
*/
|
||||
AuthInfo(const AuthInfo &info);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~AuthInfo();
|
||||
|
||||
/**
|
||||
* Custom assignment operator.
|
||||
*/
|
||||
AuthInfo &operator=(const AuthInfo &info);
|
||||
|
||||
/**
|
||||
* Use this method to check if the object was modified.
|
||||
* @return true if the object has been modified
|
||||
*/
|
||||
bool isModified() const;
|
||||
|
||||
/**
|
||||
* Use this method to indicate that this object has been modified.
|
||||
* @param flag true to mark the object as modified, false to clear
|
||||
*/
|
||||
void setModified(bool flag);
|
||||
|
||||
/**
|
||||
* The URL for which authentication is to be stored.
|
||||
*
|
||||
* This field is required when attempting to cache authorization
|
||||
* and retrieve it. However, it is not needed when prompting
|
||||
* the user for authorization info.
|
||||
*
|
||||
* This setting is @em required except when prompting the
|
||||
* user for password.
|
||||
*/
|
||||
QUrl url;
|
||||
|
||||
/**
|
||||
* This is @em required for caching.
|
||||
*/
|
||||
QString username;
|
||||
|
||||
/**
|
||||
* This is @em required for caching.
|
||||
*/
|
||||
QString password;
|
||||
|
||||
/**
|
||||
* Information to be displayed when prompting
|
||||
* the user for authentication information.
|
||||
*
|
||||
* @note If this field is not set, the authentication
|
||||
* dialog simply displays the preset default prompt.
|
||||
*
|
||||
* This setting is @em optional and empty by default.
|
||||
*/
|
||||
QString prompt;
|
||||
|
||||
/**
|
||||
* The text to displayed in the title bar of
|
||||
* the password prompting dialog.
|
||||
*
|
||||
* @note If this field is not set, the authentication
|
||||
* dialog simply displays the preset default caption.
|
||||
*
|
||||
* This setting is @em optional and empty by default.
|
||||
*/
|
||||
QString caption;
|
||||
|
||||
/**
|
||||
* Additional comment to be displayed when prompting
|
||||
* the user for authentication information.
|
||||
*
|
||||
* This field allows you to display a short (no more than
|
||||
* 80 characters) extra description in the password prompt
|
||||
* dialog. For example, this field along with the
|
||||
* commentLabel can be used to describe the server that
|
||||
* requested the authentication:
|
||||
*
|
||||
* \code
|
||||
* Server: Squid Proxy @ foo.com
|
||||
* \endcode
|
||||
*
|
||||
* where "Server:" is the commentLabel and the rest is the
|
||||
* actual comment. Note that it is always better to use
|
||||
* the @p commentLabel field as it will be placed properly
|
||||
* in the dialog rather than to include it within the actual
|
||||
* comment.
|
||||
*
|
||||
* This setting is @em optional and empty by default.
|
||||
*/
|
||||
QString comment;
|
||||
|
||||
/**
|
||||
* Descriptive label to be displayed in front of the
|
||||
* comment when prompting the user for password.
|
||||
*
|
||||
* This setting is @em optional and only applicable when
|
||||
* the comment field is also set.
|
||||
*/
|
||||
QString commentLabel;
|
||||
|
||||
/**
|
||||
* A unique identifier that allows caching of multiple
|
||||
* passwords for different resources in the same server.
|
||||
*
|
||||
* Mostly this setting is applicable to the HTTP protocol
|
||||
* whose authentication scheme explicitly defines the use
|
||||
* of such a unique key. However, any protocol that can
|
||||
* generate or supply a unique id can effectively use it
|
||||
* to distinguish passwords.
|
||||
*
|
||||
* This setting is @em optional and not set by default.
|
||||
*/
|
||||
QString realmValue;
|
||||
|
||||
/**
|
||||
* Field to store any extra authentication information for
|
||||
* protocols that need it.
|
||||
*
|
||||
* This setting is @em optional and mostly applicable for HTTP
|
||||
* protocol. However, any protocol can make use of it to
|
||||
* store extra info.
|
||||
*/
|
||||
QString digestInfo;
|
||||
|
||||
/**
|
||||
* Flag that, if set, indicates whether a path match should be
|
||||
* performed when requesting for cached authorization.
|
||||
*
|
||||
* A path is deemed to be a match if it is equal to or is a subset
|
||||
* of the cached path. For example, if stored path is "/foo/bar"
|
||||
* and the request's path set to "/foo/bar/acme", then it is a match
|
||||
* whereas it would not if the request's path was set to "/foo".
|
||||
*
|
||||
* This setting is @em optional and false by default.
|
||||
*/
|
||||
bool verifyPath;
|
||||
|
||||
/**
|
||||
* Flag which if set forces the username field to be read-only.
|
||||
*
|
||||
* This setting is @em optional and false by default.
|
||||
*/
|
||||
bool readOnly;
|
||||
|
||||
/**
|
||||
* Flag to indicate the persistence of the given password.
|
||||
*
|
||||
* This is a two-way flag, when set before calling openPasswordDialog
|
||||
* it makes the "keep Password" check box visible to the user.
|
||||
* In return the flag will indicate the state of the check box.
|
||||
* By default if the flag is checked the password will be cached
|
||||
* for the entire life of the current KDE session otherwise the
|
||||
* cached password is deleted right after the application using
|
||||
* it has been closed.
|
||||
*/
|
||||
bool keepPassword;
|
||||
|
||||
/**
|
||||
* Flags for extra fields
|
||||
*/
|
||||
enum FieldFlags {
|
||||
ExtraFieldNoFlags = 0,
|
||||
ExtraFieldReadOnly = 1 << 1,
|
||||
ExtraFieldMandatory = 1 << 2,
|
||||
};
|
||||
|
||||
/**
|
||||
* Set Extra Field Value.
|
||||
* Currently supported extra-fields:
|
||||
* "domain" (QString),
|
||||
* "anonymous" (bool)
|
||||
* Setting it to an invalid QVariant() will disable the field.
|
||||
* Extra Fields are disabled by default.
|
||||
*/
|
||||
void setExtraField(const QString &fieldName, const QVariant &value);
|
||||
|
||||
/**
|
||||
* Set Extra Field Flags
|
||||
*/
|
||||
void setExtraFieldFlags(const QString &fieldName, const FieldFlags flags);
|
||||
|
||||
/**
|
||||
* Get Extra Field Value
|
||||
* Check QVariant::isValid() to find out if the field exists.
|
||||
*/
|
||||
QVariant getExtraField(const QString &fieldName) const;
|
||||
|
||||
/**
|
||||
* Get Extra Field Flags
|
||||
*/
|
||||
AuthInfo::FieldFlags getExtraFieldFlags(const QString &fieldName) const;
|
||||
|
||||
/**
|
||||
* Register the meta-types for AuthInfo. This is called from
|
||||
* AuthInfo's constructor but needed by daemons on the D-Bus such
|
||||
* as kpasswdserver.
|
||||
*/
|
||||
static void registerMetaTypes();
|
||||
|
||||
protected:
|
||||
bool modified;
|
||||
|
||||
private:
|
||||
friend class ::KIO::AuthInfoPrivate;
|
||||
std::unique_ptr<AuthInfoPrivate> const d;
|
||||
};
|
||||
|
||||
KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const AuthInfo &a);
|
||||
KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, AuthInfo &a);
|
||||
|
||||
KIOCORE_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const AuthInfo &a);
|
||||
KIOCORE_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, AuthInfo &a);
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KIO::AuthInfo)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,222 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2017 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "batchrenamejob.h"
|
||||
|
||||
#include "copyjob.h"
|
||||
#include "job_p.h"
|
||||
|
||||
#include <QMimeDatabase>
|
||||
#include <QTimer>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <set>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
class KIO::BatchRenameJobPrivate : public KIO::JobPrivate
|
||||
{
|
||||
public:
|
||||
BatchRenameJobPrivate(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, JobFlags flags)
|
||||
: JobPrivate()
|
||||
, m_srcList(src)
|
||||
, m_newName(newName)
|
||||
, m_index(index)
|
||||
, m_placeHolder(placeHolder)
|
||||
, m_listIterator(m_srcList.constBegin())
|
||||
, m_allExtensionsDifferent(true)
|
||||
, m_useIndex(true)
|
||||
, m_appendIndex(false)
|
||||
, m_flags(flags)
|
||||
{
|
||||
// There occur four cases when renaming multiple files,
|
||||
// 1. All files have different extension and $newName contains a valid placeholder.
|
||||
// 2. At least two files have same extension and $newName contains a valid placeholder.
|
||||
// In these two cases the placeholder character will be replaced by an integer($index).
|
||||
// 3. All files have different extension and new name contains an invalid placeholder
|
||||
// (this means either $newName doesn't contain the placeholder or the placeholders
|
||||
// are not in a connected sequence).
|
||||
// In this case nothing is substituted and all files have the same $newName.
|
||||
// 4. At least two files have same extension and $newName contains an invalid placeholder.
|
||||
// In this case $index is appended to $newName.
|
||||
|
||||
// Check for extensions.
|
||||
std::set<QString> extensions;
|
||||
QMimeDatabase db;
|
||||
for (const QUrl &url : std::as_const(m_srcList)) {
|
||||
const QString extension = db.suffixForFileName(url.path());
|
||||
const auto [it, isInserted] = extensions.insert(extension);
|
||||
if (!isInserted) {
|
||||
m_allExtensionsDifferent = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Check for exactly one placeholder character or exactly one sequence of placeholders.
|
||||
int pos = newName.indexOf(placeHolder);
|
||||
if (pos != -1) {
|
||||
while (pos < newName.size() && newName.at(pos) == placeHolder) {
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
const bool validPlaceholder = (newName.indexOf(placeHolder, pos) == -1);
|
||||
|
||||
if (!validPlaceholder) {
|
||||
if (!m_allExtensionsDifferent) {
|
||||
m_appendIndex = true;
|
||||
} else {
|
||||
m_useIndex = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QList<QUrl> m_srcList;
|
||||
QString m_newName;
|
||||
int m_index;
|
||||
QChar m_placeHolder;
|
||||
QList<QUrl>::const_iterator m_listIterator;
|
||||
bool m_allExtensionsDifferent;
|
||||
bool m_useIndex;
|
||||
bool m_appendIndex;
|
||||
QUrl m_oldUrl;
|
||||
QUrl m_newUrl; // for fileRenamed signal
|
||||
const JobFlags m_flags;
|
||||
QTimer m_reportTimer;
|
||||
|
||||
Q_DECLARE_PUBLIC(BatchRenameJob)
|
||||
|
||||
void slotStart();
|
||||
void slotReport();
|
||||
|
||||
QString indexedName(const QString &name, int index, QChar placeHolder) const;
|
||||
|
||||
static inline BatchRenameJob *newJob(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, JobFlags flags)
|
||||
{
|
||||
BatchRenameJob *job = new BatchRenameJob(*new BatchRenameJobPrivate(src, newName, index, placeHolder, flags));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
if (!(flags & NoPrivilegeExecution)) {
|
||||
job->d_func()->m_privilegeExecutionEnabled = true;
|
||||
job->d_func()->m_operationType = Rename;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
BatchRenameJob::BatchRenameJob(BatchRenameJobPrivate &dd)
|
||||
: Job(dd)
|
||||
{
|
||||
Q_D(BatchRenameJob);
|
||||
connect(&d->m_reportTimer, &QTimer::timeout, this, [this]() {
|
||||
d_func()->slotReport();
|
||||
});
|
||||
d->m_reportTimer.start(200);
|
||||
|
||||
QTimer::singleShot(0, this, [this] {
|
||||
d_func()->slotStart();
|
||||
});
|
||||
}
|
||||
|
||||
BatchRenameJob::~BatchRenameJob()
|
||||
{
|
||||
}
|
||||
|
||||
QString BatchRenameJobPrivate::indexedName(const QString &name, int index, QChar placeHolder) const
|
||||
{
|
||||
if (!m_useIndex) {
|
||||
return name;
|
||||
}
|
||||
|
||||
QString newName = name;
|
||||
QString indexString = QString::number(index);
|
||||
|
||||
if (m_appendIndex) {
|
||||
newName.append(indexString);
|
||||
return newName;
|
||||
}
|
||||
|
||||
// Insert leading zeros if necessary
|
||||
const int minIndexLength = name.count(placeHolder);
|
||||
indexString.prepend(QString(minIndexLength - indexString.length(), QLatin1Char('0')));
|
||||
|
||||
// Replace the index placeholders by the indexString
|
||||
const int placeHolderStart = newName.indexOf(placeHolder);
|
||||
newName.replace(placeHolderStart, minIndexLength, indexString);
|
||||
|
||||
return newName;
|
||||
}
|
||||
|
||||
void BatchRenameJobPrivate::slotStart()
|
||||
{
|
||||
Q_Q(BatchRenameJob);
|
||||
|
||||
if (m_listIterator == m_srcList.constBegin()) { // emit total
|
||||
q->setTotalAmount(KJob::Items, m_srcList.count());
|
||||
}
|
||||
|
||||
if (m_listIterator != m_srcList.constEnd()) {
|
||||
QString newName = indexedName(m_newName, m_index, m_placeHolder);
|
||||
const QUrl oldUrl = *m_listIterator;
|
||||
QMimeDatabase db;
|
||||
const QString extension = db.suffixForFileName(oldUrl.path());
|
||||
if (!extension.isEmpty()) {
|
||||
newName += QLatin1Char('.') + extension;
|
||||
}
|
||||
|
||||
m_oldUrl = oldUrl;
|
||||
m_newUrl = oldUrl.adjusted(QUrl::RemoveFilename);
|
||||
m_newUrl.setPath(m_newUrl.path() + KIO::encodeFileName(newName));
|
||||
|
||||
KIO::Job *job = KIO::moveAs(oldUrl, m_newUrl, KIO::HideProgressInfo);
|
||||
job->setParentJob(q);
|
||||
q->addSubjob(job);
|
||||
} else {
|
||||
m_reportTimer.stop();
|
||||
slotReport();
|
||||
q->emitResult();
|
||||
}
|
||||
}
|
||||
|
||||
void BatchRenameJobPrivate::slotReport()
|
||||
{
|
||||
Q_Q(BatchRenameJob);
|
||||
|
||||
const auto processed = m_listIterator - m_srcList.constBegin();
|
||||
|
||||
q->setProcessedAmount(KJob::Items, processed);
|
||||
q->emitPercent(processed, m_srcList.count());
|
||||
|
||||
emitRenaming(q, m_oldUrl, m_newUrl);
|
||||
}
|
||||
|
||||
void BatchRenameJob::slotResult(KJob *job)
|
||||
{
|
||||
Q_D(BatchRenameJob);
|
||||
if (job->error()) {
|
||||
d->m_reportTimer.stop();
|
||||
d->slotReport();
|
||||
KIO::Job::slotResult(job);
|
||||
return;
|
||||
}
|
||||
|
||||
removeSubjob(job);
|
||||
|
||||
Q_EMIT fileRenamed(*d->m_listIterator, d->m_newUrl);
|
||||
++d->m_listIterator;
|
||||
++d->m_index;
|
||||
d->slotStart();
|
||||
}
|
||||
|
||||
BatchRenameJob *KIO::batchRename(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, KIO::JobFlags flags)
|
||||
{
|
||||
return BatchRenameJobPrivate::newJob(src, newName, index, placeHolder, flags);
|
||||
}
|
||||
|
||||
#include "moc_batchrenamejob.cpp"
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2017 Chinmoy Ranjan Pradhan <chinmoyrp65@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef BATCHRENAMEJOB_H
|
||||
#define BATCHRENAMEJOB_H
|
||||
|
||||
#include "job_base.h"
|
||||
#include "kiocore_export.h"
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class BatchRenameJobPrivate;
|
||||
|
||||
/**
|
||||
* @class KIO::BatchRenameJob batchrenamejob.h <KIO/BatchRenameJob>
|
||||
*
|
||||
* A KIO job that renames multiple files in one go.
|
||||
*
|
||||
* @since 5.42
|
||||
*/
|
||||
class KIOCORE_EXPORT BatchRenameJob : public Job
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~BatchRenameJob() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Signals that a file was renamed.
|
||||
*/
|
||||
void fileRenamed(const QUrl &oldUrl, const QUrl &newUrl);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotResult(KJob *job) override;
|
||||
|
||||
protected:
|
||||
/// @internal
|
||||
KIOCORE_NO_EXPORT explicit BatchRenameJob(BatchRenameJobPrivate &dd);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(BatchRenameJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Renames multiple files at once.
|
||||
*
|
||||
* The new filename is obtained by replacing the characters represented by
|
||||
* @p placeHolder by the index @p index.
|
||||
* E.g. Calling batchRename({"file:///Test.jpg"}, "Test #" 12, '#') renames
|
||||
* the file to "Test 12.jpg". A connected sequence of placeholders results in
|
||||
* leading zeros. batchRename({"file:///Test.jpg"}, "Test ####" 12, '#') renames
|
||||
* the file to "Test 0012.jpg". And if no placeholder is there then @p index is
|
||||
* appended to @p newName. Calling batchRename({"file:///Test.jpg"}, "NewTest" 12, '#')
|
||||
* renames the file to "NewTest12.jpg".
|
||||
*
|
||||
* @param src The list of items to rename.
|
||||
* @param newName The base name to use in all new filenames.
|
||||
* @param index The integer(incremented after renaming a file) to add to the base name.
|
||||
* @param placeHolder The character(s) which @p index will replace.
|
||||
*
|
||||
* @return A pointer to the job handling the operation.
|
||||
* @since 5.42
|
||||
*/
|
||||
KIOCORE_EXPORT BatchRenameJob *batchRename(const QList<QUrl> &src, const QString &newName, int index, QChar placeHolder, JobFlags flags = DefaultFlags);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "chmodjob.h"
|
||||
#include "../utils_p.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KUser>
|
||||
#include <QDebug>
|
||||
|
||||
#include "askuseractioninterface.h"
|
||||
#include "job_p.h"
|
||||
#include "jobuidelegatefactory.h"
|
||||
#include "kioglobal_p.h"
|
||||
#include "listjob.h"
|
||||
|
||||
#include <stack>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
struct ChmodInfo {
|
||||
QUrl url;
|
||||
int permissions;
|
||||
};
|
||||
|
||||
enum ChmodJobState {
|
||||
CHMODJOB_STATE_LISTING,
|
||||
CHMODJOB_STATE_CHMODING,
|
||||
};
|
||||
|
||||
class ChmodJobPrivate : public KIO::JobPrivate
|
||||
{
|
||||
public:
|
||||
ChmodJobPrivate(const KFileItemList &lstItems, int permissions, int mask, KUserId newOwner, KGroupId newGroup, bool recursive)
|
||||
: state(CHMODJOB_STATE_LISTING)
|
||||
, m_permissions(permissions)
|
||||
, m_mask(mask)
|
||||
, m_newOwner(newOwner)
|
||||
, m_newGroup(newGroup)
|
||||
, m_recursive(recursive)
|
||||
, m_bAutoSkipFiles(false)
|
||||
, m_lstItems(lstItems)
|
||||
{
|
||||
}
|
||||
|
||||
ChmodJobState state;
|
||||
int m_permissions;
|
||||
int m_mask;
|
||||
KUserId m_newOwner;
|
||||
KGroupId m_newGroup;
|
||||
bool m_recursive;
|
||||
bool m_bAutoSkipFiles;
|
||||
KFileItemList m_lstItems;
|
||||
std::stack<ChmodInfo> m_infos;
|
||||
|
||||
void chmodNextFile();
|
||||
void slotEntries(KIO::Job *, const KIO::UDSEntryList &);
|
||||
void processList();
|
||||
|
||||
Q_DECLARE_PUBLIC(ChmodJob)
|
||||
|
||||
static inline ChmodJob *
|
||||
newJob(const KFileItemList &lstItems, int permissions, int mask, KUserId newOwner, KGroupId newGroup, bool recursive, JobFlags flags)
|
||||
{
|
||||
ChmodJob *job = new ChmodJob(*new ChmodJobPrivate(lstItems, permissions, mask, newOwner, newGroup, recursive));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
if (!(flags & NoPrivilegeExecution)) {
|
||||
job->d_func()->m_privilegeExecutionEnabled = true;
|
||||
job->d_func()->m_operationType = ChangeAttr;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
ChmodJob::ChmodJob(ChmodJobPrivate &dd)
|
||||
: KIO::Job(dd)
|
||||
{
|
||||
Q_D(ChmodJob);
|
||||
auto processFunc = [d]() {
|
||||
d->processList();
|
||||
};
|
||||
QMetaObject::invokeMethod(this, processFunc, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
ChmodJob::~ChmodJob()
|
||||
{
|
||||
}
|
||||
|
||||
void ChmodJobPrivate::processList()
|
||||
{
|
||||
Q_Q(ChmodJob);
|
||||
while (!m_lstItems.isEmpty()) {
|
||||
const KFileItem item = m_lstItems.first();
|
||||
if (!item.isLink()) { // don't do anything with symlinks
|
||||
// File or directory -> remember to chmod
|
||||
ChmodInfo info;
|
||||
info.url = item.url();
|
||||
// This is a toplevel file, we apply changes directly (no +X emulation here)
|
||||
const mode_t permissions = item.permissions() & 0777; // get rid of "set gid" and other special flags
|
||||
info.permissions = (m_permissions & m_mask) | (permissions & ~m_mask);
|
||||
/*//qDebug() << "toplevel url:" << info.url << "\n current permissions=" << QString::number(permissions,8)
|
||||
<< "\n wanted permission=" << QString::number(m_permissions,8)
|
||||
<< "\n with mask=" << QString::number(m_mask,8)
|
||||
<< "\n with ~mask (mask bits we keep) =" << QString::number((uint)~m_mask,8)
|
||||
<< "\n bits we keep =" << QString::number(permissions & ~m_mask,8)
|
||||
<< "\n new permissions = " << QString::number(info.permissions,8);*/
|
||||
m_infos.push(std::move(info));
|
||||
// qDebug() << "processList : Adding info for " << info.url;
|
||||
// Directory and recursive -> list
|
||||
if (item.isDir() && m_recursive) {
|
||||
// qDebug() << "ChmodJob::processList dir -> listing";
|
||||
KIO::ListJob *listJob = KIO::listRecursive(item.url(), KIO::HideProgressInfo);
|
||||
q->connect(listJob, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &entries) {
|
||||
slotEntries(job, entries);
|
||||
});
|
||||
q->addSubjob(listJob);
|
||||
return; // we'll come back later, when this one's finished
|
||||
}
|
||||
}
|
||||
m_lstItems.removeFirst();
|
||||
}
|
||||
// qDebug() << "ChmodJob::processList -> going to STATE_CHMODING";
|
||||
// We have finished, move on
|
||||
state = CHMODJOB_STATE_CHMODING;
|
||||
chmodNextFile();
|
||||
}
|
||||
|
||||
void ChmodJobPrivate::slotEntries(KIO::Job *, const KIO::UDSEntryList &list)
|
||||
{
|
||||
KIO::UDSEntryList::ConstIterator it = list.begin();
|
||||
KIO::UDSEntryList::ConstIterator end = list.end();
|
||||
for (; it != end; ++it) {
|
||||
const KIO::UDSEntry &entry = *it;
|
||||
const bool isLink = !entry.stringValue(KIO::UDSEntry::UDS_LINK_DEST).isEmpty();
|
||||
const QString relativePath = entry.stringValue(KIO::UDSEntry::UDS_NAME);
|
||||
if (!isLink && relativePath != QLatin1String("..")) {
|
||||
const mode_t permissions = entry.numberValue(KIO::UDSEntry::UDS_ACCESS) & 0777; // get rid of "set gid" and other special flags
|
||||
|
||||
ChmodInfo info;
|
||||
info.url = m_lstItems.first().url(); // base directory
|
||||
info.url.setPath(Utils::concatPaths(info.url.path(), relativePath));
|
||||
int mask = m_mask;
|
||||
// Emulate -X: only give +x to files that had a +x bit already
|
||||
// So the check is the opposite : if the file had no x bit, don't touch x bits
|
||||
// For dirs this doesn't apply
|
||||
if (!entry.isDir()) {
|
||||
int newPerms = m_permissions & mask;
|
||||
if ((newPerms & 0111) && !(permissions & 0111)) {
|
||||
// don't interfere with mandatory file locking
|
||||
if (newPerms & 02000) {
|
||||
mask = mask & ~0101;
|
||||
} else {
|
||||
mask = mask & ~0111;
|
||||
}
|
||||
}
|
||||
}
|
||||
info.permissions = (m_permissions & mask) | (permissions & ~mask);
|
||||
/*//qDebug() << info.url << "\n current permissions=" << QString::number(permissions,8)
|
||||
<< "\n wanted permission=" << QString::number(m_permissions,8)
|
||||
<< "\n with mask=" << QString::number(mask,8)
|
||||
<< "\n with ~mask (mask bits we keep) =" << QString::number((uint)~mask,8)
|
||||
<< "\n bits we keep =" << QString::number(permissions & ~mask,8)
|
||||
<< "\n new permissions = " << QString::number(info.permissions,8);*/
|
||||
// Push this info on top of the stack so it's handled first.
|
||||
// This way, the toplevel dirs are done last.
|
||||
m_infos.push(std::move(info));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ChmodJobPrivate::chmodNextFile()
|
||||
{
|
||||
auto processNextFunc = [this]() {
|
||||
chmodNextFile();
|
||||
};
|
||||
|
||||
Q_Q(ChmodJob);
|
||||
if (!m_infos.empty()) {
|
||||
ChmodInfo info = m_infos.top();
|
||||
m_infos.pop();
|
||||
// First update group / owner (if local file)
|
||||
// (permissions have to be set after, in case of suid and sgid)
|
||||
if (info.url.isLocalFile() && (m_newOwner.isValid() || m_newGroup.isValid())) {
|
||||
QString path = info.url.toLocalFile();
|
||||
if (!KIOPrivate::changeOwnership(path, m_newOwner, m_newGroup)) {
|
||||
auto *askUserActionInterface = KIO::delegateExtension<AskUserActionInterface *>(q);
|
||||
if (!askUserActionInterface) {
|
||||
Q_EMIT q->warning(q, i18n("Could not modify the ownership of file %1", path));
|
||||
} else if (!m_bAutoSkipFiles) {
|
||||
SkipDialog_Options options;
|
||||
if (m_infos.size() > 1) {
|
||||
options |= SkipDialog_MultipleItems;
|
||||
}
|
||||
|
||||
auto skipSignal = &AskUserActionInterface::askUserSkipResult;
|
||||
q->connect(askUserActionInterface, skipSignal, q, [=, this](KIO::SkipDialog_Result result, KJob *parentJob) {
|
||||
Q_ASSERT(q == parentJob);
|
||||
q->disconnect(askUserActionInterface, skipSignal, q, nullptr);
|
||||
|
||||
switch (result) {
|
||||
case Result_AutoSkip:
|
||||
m_bAutoSkipFiles = true;
|
||||
// fall through
|
||||
Q_FALLTHROUGH();
|
||||
case Result_Skip:
|
||||
QMetaObject::invokeMethod(q, processNextFunc, Qt::QueuedConnection);
|
||||
return;
|
||||
case Result_Retry:
|
||||
m_infos.push(std::move(info));
|
||||
QMetaObject::invokeMethod(q, processNextFunc, Qt::QueuedConnection);
|
||||
return;
|
||||
case Result_Cancel:
|
||||
default:
|
||||
q->setError(ERR_USER_CANCELED);
|
||||
q->emitResult();
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
askUserActionInterface->askUserSkip(q,
|
||||
options,
|
||||
xi18n("Could not modify the ownership of file <filename>%1</filename>. You have "
|
||||
"insufficient access to the file to perform the change.",
|
||||
path));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*qDebug() << "chmod'ing" << info.url << "to" << QString::number(info.permissions,8);*/
|
||||
KIO::SimpleJob *job = KIO::chmod(info.url, info.permissions);
|
||||
job->setParentJob(q);
|
||||
// copy the metadata for acl and default acl
|
||||
const QString aclString = q->queryMetaData(QStringLiteral("ACL_STRING"));
|
||||
const QString defaultAclString = q->queryMetaData(QStringLiteral("DEFAULT_ACL_STRING"));
|
||||
if (!aclString.isEmpty()) {
|
||||
job->addMetaData(QStringLiteral("ACL_STRING"), aclString);
|
||||
}
|
||||
if (!defaultAclString.isEmpty()) {
|
||||
job->addMetaData(QStringLiteral("DEFAULT_ACL_STRING"), defaultAclString);
|
||||
}
|
||||
q->addSubjob(job);
|
||||
} else { // We have finished
|
||||
q->emitResult();
|
||||
}
|
||||
}
|
||||
|
||||
void ChmodJob::slotResult(KJob *job)
|
||||
{
|
||||
Q_D(ChmodJob);
|
||||
removeSubjob(job);
|
||||
if (job->error()) {
|
||||
setError(job->error());
|
||||
setErrorText(job->errorText());
|
||||
emitResult();
|
||||
return;
|
||||
}
|
||||
// qDebug() << "d->m_lstItems:" << d->m_lstItems.count();
|
||||
switch (d->state) {
|
||||
case CHMODJOB_STATE_LISTING:
|
||||
d->m_lstItems.removeFirst();
|
||||
// qDebug() << "-> processList";
|
||||
d->processList();
|
||||
return;
|
||||
case CHMODJOB_STATE_CHMODING:
|
||||
// qDebug() << "-> chmodNextFile";
|
||||
d->chmodNextFile();
|
||||
return;
|
||||
default:
|
||||
Q_ASSERT(false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ChmodJob *KIO::chmod(const KFileItemList &lstItems, int permissions, int mask, const QString &owner, const QString &group, bool recursive, JobFlags flags)
|
||||
{
|
||||
KUserId uid = KUserId::fromName(owner);
|
||||
KGroupId gid = KGroupId::fromName(group);
|
||||
return ChmodJobPrivate::newJob(lstItems, permissions, mask, uid, gid, recursive, flags);
|
||||
}
|
||||
|
||||
#include "moc_chmodjob.cpp"
|
||||
@@ -0,0 +1,77 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_CHMODJOB_H
|
||||
#define KIO_CHMODJOB_H
|
||||
|
||||
#include "global.h"
|
||||
#include "job_base.h"
|
||||
#include "kiocore_export.h"
|
||||
#include <kfileitem.h>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class ChmodJobPrivate;
|
||||
/**
|
||||
* @class KIO::ChmodJob chmodjob.h <KIO/ChmodJob>
|
||||
*
|
||||
* This job changes permissions on a list of files or directories,
|
||||
* optionally in a recursive manner.
|
||||
* @see KIO::chmod()
|
||||
*/
|
||||
class KIOCORE_EXPORT ChmodJob : public KIO::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
~ChmodJob() override;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotResult(KJob *job) override;
|
||||
|
||||
protected:
|
||||
KIOCORE_NO_EXPORT explicit ChmodJob(ChmodJobPrivate &dd);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(ChmodJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a job that changes permissions/ownership on several files or directories,
|
||||
* optionally recursively.
|
||||
* This version of chmod uses a KFileItemList so that it directly knows
|
||||
* what to do with the items. TODO: a version that takes a QList<QUrl>,
|
||||
* and a general job that stats each url and returns a KFileItemList.
|
||||
*
|
||||
* Note that change of ownership is only supported for local files.
|
||||
*
|
||||
* Inside directories, the "x" bits will only be changed for files that had
|
||||
* at least one "x" bit before, and for directories.
|
||||
* This emulates the behavior of chmod +X.
|
||||
*
|
||||
* @param lstItems The file items representing several files or directories.
|
||||
* @param permissions the permissions we want to set
|
||||
* @param mask the bits we are allowed to change.
|
||||
* For instance, if mask is 0077, we don't change
|
||||
* the "user" bits, only "group" and "others".
|
||||
* @param newOwner If non-empty, the new owner for the files
|
||||
* @param newGroup If non-empty, the new group for the files
|
||||
* @param recursive whether to open directories recursively
|
||||
* @param flags We support HideProgressInfo here
|
||||
* @return The job handling the operation.
|
||||
*/
|
||||
KIOCORE_EXPORT ChmodJob *chmod(const KFileItemList &lstItems,
|
||||
int permissions,
|
||||
int mask,
|
||||
const QString &newOwner,
|
||||
const QString &newGroup,
|
||||
bool recursive,
|
||||
JobFlags flags = DefaultFlags);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2013 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KIO_COMMANDS_P_H
|
||||
#define KIO_COMMANDS_P_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
* Commands that can be invoked by a job.
|
||||
*/
|
||||
enum Command {
|
||||
CMD_HOST = '0', // 48
|
||||
CMD_CONNECT = '1', // 49
|
||||
CMD_DISCONNECT = '2', // 50
|
||||
CMD_WORKER_STATUS = '3', // 51
|
||||
CMD_NONE = 'A', // 65
|
||||
// Unused: 'B', // 66
|
||||
CMD_GET = 'C', // 67
|
||||
CMD_PUT = 'D', // 68
|
||||
CMD_STAT = 'E', // 69
|
||||
CMD_MIMETYPE = 'F', // 70
|
||||
CMD_LISTDIR = 'G', // 71
|
||||
CMD_MKDIR = 'H', // 72
|
||||
CMD_RENAME = 'I', // 73
|
||||
CMD_COPY = 'J', // 74
|
||||
CMD_DEL = 'K', // 75
|
||||
CMD_CHMOD = 'L', // 76
|
||||
CMD_SPECIAL = 'M', // 77
|
||||
CMD_SETMODIFICATIONTIME = 'N', // 78
|
||||
CMD_REPARSECONFIGURATION = 'O', // 79
|
||||
CMD_META_DATA = 'P', // 80
|
||||
CMD_SYMLINK = 'Q', // 81
|
||||
// Unused: 'R', // 82
|
||||
CMD_MESSAGEBOXANSWER = 'S', // 83
|
||||
CMD_RESUMEANSWER = 'T', // 84
|
||||
CMD_CONFIG = 'U', // 85
|
||||
// unused 'V', // 86
|
||||
CMD_SETLINKDEST = 'W', // 87
|
||||
CMD_OPEN = 'X', // 88
|
||||
CMD_CHOWN = 'Y', // 89
|
||||
CMD_READ = 'Z', // 90
|
||||
CMD_WRITE = 91,
|
||||
CMD_SEEK = 92,
|
||||
CMD_CLOSE = 93,
|
||||
CMD_HOST_INFO = 94,
|
||||
CMD_FILESYSTEMFREESPACE = 95,
|
||||
CMD_TRUNCATE = 96,
|
||||
CMD_SSLERRORANSWER,
|
||||
// Add new ones here once a release is done, to avoid breaking binary compatibility.
|
||||
// Note that protocol-specific commands shouldn't be added here, but should use special.
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
#cmakedefine01 HAVE_STRUCT_SOCKADDR_SA_LEN
|
||||
|
||||
/* Defined if system has POSIX ACL support. */
|
||||
#cmakedefine01 HAVE_POSIX_ACL
|
||||
/* Defined if acl/libacl.h exists */
|
||||
#cmakedefine01 HAVE_ACL_LIBACL_H
|
||||
/* Defined if sys/acl.h exists */
|
||||
#cmakedefine01 HAVE_SYS_ACL_H
|
||||
|
||||
#define KDE_INSTALL_FULL_LIBEXECDIR_KF "${KDE_INSTALL_FULL_LIBEXECDIR_KF}"
|
||||
|
||||
#cmakedefine01 KIO_ASSERT_WORKER_STATES
|
||||
@@ -0,0 +1,10 @@
|
||||
#cmakedefine01 HAVE_GETMNTINFO
|
||||
#cmakedefine01 GETMNTINFO_USES_STATVFS
|
||||
|
||||
#cmakedefine01 HAVE_LIB_MOUNT
|
||||
|
||||
#cmakedefine01 HAVE_SYS_MOUNT_H
|
||||
#cmakedefine01 HAVE_FSTAB_H
|
||||
#cmakedefine01 HAVE_SYS_PARAM_H
|
||||
|
||||
#cmakedefine01 HAVE_VOLMGT
|
||||
@@ -0,0 +1,249 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
|
||||
SPDX-FileCopyrightText: 2024 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "connection_p.h"
|
||||
#include "connectionbackend_p.h"
|
||||
#include "kiocoredebug.h"
|
||||
#include <QDebug>
|
||||
|
||||
#include <cerrno>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
void ConnectionPrivate::dequeue()
|
||||
{
|
||||
if (!backend || suspended) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const Task &task : std::as_const(outgoingTasks)) {
|
||||
q->sendnow(task.cmd, task.data);
|
||||
}
|
||||
outgoingTasks.clear();
|
||||
|
||||
if (!incomingTasks.isEmpty()) {
|
||||
Q_EMIT q->readyRead();
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionPrivate::commandReceived(const Task &task)
|
||||
{
|
||||
// qDebug() << this << "Command" << task.cmd << "added to the queue";
|
||||
if (!suspended && incomingTasks.isEmpty() && readMode == Connection::ReadMode::EventDriven) {
|
||||
auto dequeueFunc = [this]() {
|
||||
dequeue();
|
||||
};
|
||||
QMetaObject::invokeMethod(q, dequeueFunc, Qt::QueuedConnection);
|
||||
}
|
||||
incomingTasks.append(task);
|
||||
}
|
||||
|
||||
void ConnectionPrivate::disconnected()
|
||||
{
|
||||
q->close();
|
||||
if (readMode == Connection::ReadMode::EventDriven) {
|
||||
QMetaObject::invokeMethod(q, &Connection::readyRead, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionPrivate::setBackend(ConnectionBackend *b)
|
||||
{
|
||||
delete backend;
|
||||
backend = b;
|
||||
if (backend) {
|
||||
q->connect(backend, &ConnectionBackend::commandReceived, q, [this](const Task &task) {
|
||||
commandReceived(task);
|
||||
});
|
||||
q->connect(backend, &ConnectionBackend::disconnected, q, [this]() {
|
||||
disconnected();
|
||||
});
|
||||
backend->setSuspended(suspended);
|
||||
}
|
||||
}
|
||||
|
||||
Connection::Connection(Type type, QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new ConnectionPrivate)
|
||||
, m_type(type)
|
||||
{
|
||||
d->q = this;
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void Connection::suspend()
|
||||
{
|
||||
// qDebug() << this << "Suspended";
|
||||
d->suspended = true;
|
||||
if (d->backend) {
|
||||
d->backend->setSuspended(true);
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::resume()
|
||||
{
|
||||
// send any outgoing or incoming commands that may be in queue
|
||||
if (d->readMode == Connection::ReadMode::EventDriven) {
|
||||
auto dequeueFunc = [this]() {
|
||||
d->dequeue();
|
||||
};
|
||||
QMetaObject::invokeMethod(this, dequeueFunc, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// qDebug() << this << "Resumed";
|
||||
d->suspended = false;
|
||||
if (d->backend) {
|
||||
d->backend->setSuspended(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::close()
|
||||
{
|
||||
if (d->backend) {
|
||||
d->backend->disconnect(this);
|
||||
d->backend->deleteLater();
|
||||
d->backend = nullptr;
|
||||
}
|
||||
d->outgoingTasks.clear();
|
||||
d->incomingTasks.clear();
|
||||
}
|
||||
|
||||
bool Connection::isConnected() const
|
||||
{
|
||||
return d->backend && d->backend->state == ConnectionBackend::Connected;
|
||||
}
|
||||
|
||||
bool Connection::inited() const
|
||||
{
|
||||
return d->backend;
|
||||
}
|
||||
|
||||
bool Connection::suspended() const
|
||||
{
|
||||
return d->suspended;
|
||||
}
|
||||
|
||||
void Connection::connectToRemote(const QUrl &address)
|
||||
{
|
||||
// qDebug() << "Connection requested to" << address;
|
||||
const QString scheme = address.scheme();
|
||||
|
||||
if (scheme == QLatin1String("local")) {
|
||||
d->setBackend(new ConnectionBackend(this));
|
||||
} else {
|
||||
qCWarning(KIO_CORE) << "Unknown protocol requested:" << scheme << "(" << address << ")";
|
||||
Q_ASSERT(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// connection succeeded
|
||||
if (!d->backend->connectToRemote(address)) {
|
||||
// qCWarning(KIO_CORE) << "could not connect to" << address << "using scheme" << scheme;
|
||||
delete d->backend;
|
||||
d->backend = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
d->dequeue();
|
||||
}
|
||||
|
||||
bool Connection::send(int cmd, const QByteArray &data)
|
||||
{
|
||||
// Remember that a Connection instance exists in the Application and the Worker. If the application terminates
|
||||
// we potentially get disconnected while looping on data to send in the worker, terminate the worker when this
|
||||
// happens. Specifically while reading a possible answer from the Application we may get socketDisconnected()
|
||||
// we'll never get an answer in that case.
|
||||
if (m_type == Type::Worker && !inited()) {
|
||||
qCWarning(KIO_CORE) << "Connection::send() called with connection not inited";
|
||||
return false;
|
||||
}
|
||||
if (!inited() || !d->outgoingTasks.isEmpty()) {
|
||||
Task task;
|
||||
task.cmd = cmd;
|
||||
task.data = data;
|
||||
d->outgoingTasks.append(std::move(task));
|
||||
return true;
|
||||
} else {
|
||||
return sendnow(cmd, data);
|
||||
}
|
||||
}
|
||||
|
||||
bool Connection::sendnow(int cmd, const QByteArray &data)
|
||||
{
|
||||
if (!d->backend) {
|
||||
qCWarning(KIO_CORE) << "Connection::sendnow has no backend";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (data.size() > 0xffffff) {
|
||||
qCWarning(KIO_CORE) << "Connection::sendnow too much data";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isConnected()) {
|
||||
qCWarning(KIO_CORE) << "Connection::sendnow not connected";
|
||||
return false;
|
||||
}
|
||||
|
||||
// qDebug() << this << "Sending command" << cmd << "of size" << data.size();
|
||||
return d->backend->sendCommand(cmd, data);
|
||||
}
|
||||
|
||||
bool Connection::hasTaskAvailable() const
|
||||
{
|
||||
return !d->incomingTasks.isEmpty();
|
||||
}
|
||||
|
||||
bool Connection::waitForIncomingTask(int ms)
|
||||
{
|
||||
if (!isConnected()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (d->backend) {
|
||||
return d->backend->waitForIncomingTask(ms);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int Connection::read(int *_cmd, QByteArray &data)
|
||||
{
|
||||
// if it's still empty, then it's an error
|
||||
if (d->incomingTasks.isEmpty()) {
|
||||
// qCWarning(KIO_CORE) << this << "Task list is empty!";
|
||||
return -1;
|
||||
}
|
||||
const Task &task = d->incomingTasks.constFirst();
|
||||
// qDebug() << this << "Command" << task.cmd << "removed from the queue (size" << task.data.size() << ")";
|
||||
*_cmd = task.cmd;
|
||||
data = task.data;
|
||||
|
||||
d->incomingTasks.removeFirst();
|
||||
|
||||
// if we didn't empty our reading queue, emit again
|
||||
if (!d->suspended && !d->incomingTasks.isEmpty() && d->readMode == Connection::ReadMode::EventDriven) {
|
||||
auto dequeueFunc = [this]() {
|
||||
d->dequeue();
|
||||
};
|
||||
QMetaObject::invokeMethod(this, dequeueFunc, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
return data.size();
|
||||
}
|
||||
|
||||
void Connection::setReadMode(ReadMode readMode)
|
||||
{
|
||||
d->readMode = readMode;
|
||||
}
|
||||
|
||||
#include "moc_connection_p.cpp"
|
||||
@@ -0,0 +1,170 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2024 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_CONNECTION_P_H
|
||||
#define KIO_CONNECTION_P_H
|
||||
|
||||
#include "connectionbackend_p.h"
|
||||
#include <QList>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class ConnectionServer;
|
||||
class ConnectionPrivate;
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* This class provides a simple means for IPC between two applications
|
||||
* via a pipe.
|
||||
* It handles a queue of commands to be sent which makes it possible to
|
||||
* queue data before an actual connection has been established.
|
||||
*/
|
||||
class Connection : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum class ReadMode {
|
||||
Polled, /// Any new tasks will be polled
|
||||
EventDriven, /// We need to emit signals when we have pending events. Requires a working QEventLoop
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
Application, /// This is the connection of the application side
|
||||
Worker, /// This is the connection of the worker side
|
||||
};
|
||||
/**
|
||||
* Creates a new connection.
|
||||
* @see connectToRemote, listenForRemote
|
||||
*/
|
||||
explicit Connection(Type type, QObject *parent = nullptr);
|
||||
~Connection() override;
|
||||
|
||||
/**
|
||||
* Connects to the remote address.
|
||||
* @param address a local:// or tcp:// URL.
|
||||
*/
|
||||
void connectToRemote(const QUrl &address);
|
||||
|
||||
/// Closes the connection.
|
||||
void close();
|
||||
|
||||
bool isConnected() const;
|
||||
|
||||
/**
|
||||
* Checks whether the connection has been initialized.
|
||||
* @return true if the initialized
|
||||
* @see init()
|
||||
*/
|
||||
bool inited() const;
|
||||
|
||||
/**
|
||||
* Sends/queues the given command to be sent.
|
||||
* @param cmd the command to set
|
||||
* @param arr the bytes to send
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool send(int cmd, const QByteArray &arr = QByteArray());
|
||||
|
||||
/**
|
||||
* Sends the given command immediately.
|
||||
* @param _cmd the command to set
|
||||
* @param data the bytes to send
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
bool sendnow(int _cmd, const QByteArray &data);
|
||||
|
||||
/**
|
||||
* Returns true if there are packets to be read immediately,
|
||||
* false if waitForIncomingTask must be called before more data
|
||||
* is available.
|
||||
*/
|
||||
bool hasTaskAvailable() const;
|
||||
|
||||
/**
|
||||
* Waits for one more command to be handled and ready.
|
||||
*
|
||||
* @param ms the time to wait in milliseconds
|
||||
* @returns true if one command can be read, false if we timed out
|
||||
*/
|
||||
bool waitForIncomingTask(int ms = 30000);
|
||||
|
||||
/**
|
||||
* Receive data.
|
||||
*
|
||||
* @param _cmd the received command will be written here
|
||||
* @param data the received data will be written here
|
||||
|
||||
* @return >=0 indicates the received data size upon success
|
||||
* -1 indicates error
|
||||
*/
|
||||
int read(int *_cmd, QByteArray &data);
|
||||
|
||||
/**
|
||||
* Don't handle incoming data until resumed.
|
||||
*/
|
||||
void suspend();
|
||||
|
||||
/**
|
||||
* Resume handling of incoming data.
|
||||
*/
|
||||
void resume();
|
||||
|
||||
/**
|
||||
* Returns status of connection.
|
||||
* @return true if suspended, false otherwise
|
||||
*/
|
||||
bool suspended() const;
|
||||
|
||||
void setReadMode(ReadMode mode);
|
||||
|
||||
Q_SIGNALS:
|
||||
void readyRead();
|
||||
|
||||
private:
|
||||
friend class ConnectionPrivate;
|
||||
friend class ConnectionServer;
|
||||
std::unique_ptr<class ConnectionPrivate> const d;
|
||||
Type m_type;
|
||||
};
|
||||
|
||||
// Separated from Connection only for historical reasons - they are both private now
|
||||
class ConnectionPrivate
|
||||
{
|
||||
public:
|
||||
inline ConnectionPrivate()
|
||||
: backend(nullptr)
|
||||
, q(nullptr)
|
||||
, suspended(false)
|
||||
, readMode(Connection::ReadMode::EventDriven)
|
||||
{
|
||||
}
|
||||
|
||||
void dequeue();
|
||||
void commandReceived(const Task &task);
|
||||
void disconnected();
|
||||
void setBackend(ConnectionBackend *b);
|
||||
|
||||
QList<Task> outgoingTasks;
|
||||
QList<Task> incomingTasks;
|
||||
ConnectionBackend *backend;
|
||||
Connection *q;
|
||||
bool suspended;
|
||||
Connection::ReadMode readMode;
|
||||
};
|
||||
|
||||
class ConnectionServerPrivate;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 David Faure <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
|
||||
SPDX-FileCopyrightText: 2024 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "connectionbackend_p.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
ConnectionBackend::ConnectionBackend(QObject *parent)
|
||||
: QObject(parent)
|
||||
, state(Idle)
|
||||
, socket(nullptr)
|
||||
, localServer(nullptr)
|
||||
, signalEmitted(false)
|
||||
{
|
||||
}
|
||||
|
||||
ConnectionBackend::~ConnectionBackend() = default;
|
||||
|
||||
void ConnectionBackend::setSuspended(bool enable)
|
||||
{
|
||||
(void)enable;
|
||||
}
|
||||
|
||||
bool ConnectionBackend::connectToRemote(const QUrl &url)
|
||||
{
|
||||
(void)url;
|
||||
errorString = i18n("Local IPC is unavailable on Redox without QtNetwork");
|
||||
state = Idle;
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectionBackend::ConnectionResult ConnectionBackend::listenForRemote()
|
||||
{
|
||||
state = Idle;
|
||||
errorString = i18n("Local IPC is unavailable on Redox without QtNetwork");
|
||||
return {false, errorString};
|
||||
}
|
||||
|
||||
bool ConnectionBackend::waitForIncomingTask(int ms)
|
||||
{
|
||||
(void)ms;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConnectionBackend::sendCommand(int cmd, const QByteArray &data) const
|
||||
{
|
||||
(void)cmd;
|
||||
(void)data;
|
||||
return false;
|
||||
}
|
||||
|
||||
ConnectionBackend *ConnectionBackend::nextPendingConnection()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void ConnectionBackend::socketReadyRead()
|
||||
{
|
||||
}
|
||||
|
||||
void ConnectionBackend::socketDisconnected()
|
||||
{
|
||||
state = Idle;
|
||||
Q_EMIT disconnected();
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
|
||||
SPDX-FileCopyrightText: 2024 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_CONNECTIONBACKEND_P_H
|
||||
#define KIO_CONNECTIONBACKEND_P_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
class QLocalServer;
|
||||
class QLocalSocket;
|
||||
|
||||
class QTcpServer;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
struct Task {
|
||||
int cmd = -1;
|
||||
long len = 0;
|
||||
QByteArray data{};
|
||||
};
|
||||
|
||||
class ConnectionBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
enum {
|
||||
Idle,
|
||||
Listening,
|
||||
Connected
|
||||
} state;
|
||||
QUrl address;
|
||||
QString errorString;
|
||||
|
||||
static const int HeaderSize = 10;
|
||||
static const int StandardBufferSize = 32 * 1024;
|
||||
|
||||
private:
|
||||
QLocalSocket *socket;
|
||||
QLocalServer *localServer;
|
||||
std::optional<Task> pendingTask = std::nullopt;
|
||||
bool signalEmitted;
|
||||
|
||||
Q_SIGNALS:
|
||||
void disconnected();
|
||||
void commandReceived(const KIO::Task &task);
|
||||
void newConnection();
|
||||
|
||||
public:
|
||||
explicit ConnectionBackend(QObject *parent = nullptr);
|
||||
~ConnectionBackend() override;
|
||||
|
||||
struct ConnectionResult {
|
||||
bool success = true;
|
||||
QString error;
|
||||
};
|
||||
|
||||
void setSuspended(bool enable);
|
||||
bool connectToRemote(const QUrl &url);
|
||||
ConnectionResult listenForRemote();
|
||||
bool waitForIncomingTask(int ms);
|
||||
bool sendCommand(int command, const QByteArray &data) const;
|
||||
ConnectionBackend *nextPendingConnection();
|
||||
|
||||
public Q_SLOTS:
|
||||
void socketReadyRead();
|
||||
void socketDisconnected();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "connectionserver.h"
|
||||
#include "connection_p.h"
|
||||
#include "connectionbackend_p.h"
|
||||
#include "kiocoredebug.h"
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
ConnectionServer::ConnectionServer(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
ConnectionServer::~ConnectionServer() = default;
|
||||
|
||||
void ConnectionServer::listenForRemote()
|
||||
{
|
||||
backend = new ConnectionBackend(this);
|
||||
if (auto result = backend->listenForRemote(); !result.success) {
|
||||
qCWarning(KIO_CORE) << "ConnectionServer::listenForRemote failed:" << result.error;
|
||||
delete backend;
|
||||
backend = nullptr;
|
||||
return;
|
||||
}
|
||||
|
||||
connect(backend, &ConnectionBackend::newConnection, this, &ConnectionServer::newConnection);
|
||||
// qDebug() << "Listening on" << d->backend->address;
|
||||
}
|
||||
|
||||
QUrl ConnectionServer::address() const
|
||||
{
|
||||
if (backend) {
|
||||
return backend->address;
|
||||
}
|
||||
return QUrl();
|
||||
}
|
||||
|
||||
bool ConnectionServer::isListening() const
|
||||
{
|
||||
return backend && backend->state == ConnectionBackend::Listening;
|
||||
}
|
||||
|
||||
void ConnectionServer::setNextPendingConnection(Connection *conn)
|
||||
{
|
||||
ConnectionBackend *newBackend = backend->nextPendingConnection();
|
||||
Q_ASSERT(newBackend);
|
||||
|
||||
conn->d->setBackend(newBackend);
|
||||
newBackend->setParent(conn);
|
||||
|
||||
conn->d->dequeue();
|
||||
}
|
||||
|
||||
#include "moc_connectionserver.cpp"
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_CONNECTIONSERVER_H
|
||||
#define KIO_CONNECTIONSERVER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
|
||||
#include "connectionbackend_p.h"
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class ConnectionServerPrivate;
|
||||
class Connection;
|
||||
|
||||
/*
|
||||
* This class provides a way to obtaining KIO::Connection connections.
|
||||
*/
|
||||
class ConnectionServer : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit ConnectionServer(QObject *parent = nullptr);
|
||||
~ConnectionServer() override;
|
||||
|
||||
/**
|
||||
* Sets this connection to listen mode. Use address() to obtain the
|
||||
* address this is listening on.
|
||||
*/
|
||||
void listenForRemote();
|
||||
bool isListening() const;
|
||||
|
||||
/**
|
||||
* Returns the address for this connection if it is listening, an empty
|
||||
* address if not.
|
||||
*/
|
||||
QUrl address() const;
|
||||
|
||||
void setNextPendingConnection(Connection *conn);
|
||||
|
||||
Q_SIGNALS:
|
||||
void newConnection();
|
||||
|
||||
private:
|
||||
ConnectionBackend *backend = nullptr;
|
||||
};
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,380 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2006 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_COPYJOB_H
|
||||
#define KIO_COPYJOB_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
||||
#include "job_base.h"
|
||||
#include "kiocore_export.h"
|
||||
|
||||
class QDateTime;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
|
||||
class CopyJobPrivate;
|
||||
/**
|
||||
* @class KIO::CopyJob copyjob.h <KIO/CopyJob>
|
||||
*
|
||||
* CopyJob is used to move, copy or symlink files and directories.
|
||||
* Don't create the job directly, but use KIO::copy(),
|
||||
* KIO::move(), KIO::link() and friends.
|
||||
*
|
||||
* @see KIO::copy()
|
||||
* @see KIO::copyAs()
|
||||
* @see KIO::move()
|
||||
* @see KIO::moveAs()
|
||||
* @see KIO::link()
|
||||
* @see KIO::linkAs()
|
||||
*/
|
||||
class KIOCORE_EXPORT CopyJob : public Job
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Defines the mode of the operation
|
||||
*/
|
||||
enum CopyMode {
|
||||
Copy,
|
||||
Move,
|
||||
Link
|
||||
};
|
||||
|
||||
~CopyJob() override;
|
||||
|
||||
/**
|
||||
* Returns the mode of the operation (copy, move, or link),
|
||||
* depending on whether KIO::copy(), KIO::move() or KIO::link() was called.
|
||||
*/
|
||||
CopyMode operationMode() const;
|
||||
|
||||
/**
|
||||
* Returns the list of source URLs.
|
||||
* @return the list of source URLs.
|
||||
*/
|
||||
QList<QUrl> srcUrls() const;
|
||||
|
||||
/**
|
||||
* Returns the destination URL.
|
||||
* @return the destination URL
|
||||
*/
|
||||
QUrl destUrl() const;
|
||||
|
||||
/**
|
||||
* By default the permissions of the copied files will be those of the source files.
|
||||
*
|
||||
* But when copying "template" files to "new" files, people prefer the umask
|
||||
* to apply, rather than the template's permissions.
|
||||
* For that case, call setDefaultPermissions(true)
|
||||
*/
|
||||
void setDefaultPermissions(bool b);
|
||||
|
||||
/**
|
||||
* Skip copying or moving any file when the destination already exists,
|
||||
* instead of the default behavior (interactive mode: showing a dialog to the user,
|
||||
* non-interactive mode: aborting with an error).
|
||||
* Initially added for a unit test.
|
||||
* \since 4.2
|
||||
*/
|
||||
void setAutoSkip(bool autoSkip);
|
||||
|
||||
/**
|
||||
* Rename files automatically when the destination already exists,
|
||||
* instead of the default behavior (interactive mode: showing a dialog to the user,
|
||||
* non-interactive mode: aborting with an error).
|
||||
* Initially added for a unit test.
|
||||
* \since 4.7
|
||||
*/
|
||||
void setAutoRename(bool autoRename);
|
||||
|
||||
/**
|
||||
* Reuse any directory that already exists, instead of the default behavior
|
||||
* (interactive mode: showing a dialog to the user,
|
||||
* non-interactive mode: aborting with an error).
|
||||
* \since 4.2
|
||||
*/
|
||||
void setWriteIntoExistingDirectories(bool overwriteAllDirs);
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons
|
||||
*/
|
||||
bool doSuspend() override;
|
||||
|
||||
/**
|
||||
* Reimplemented for internal reasons
|
||||
*/
|
||||
bool doResume() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Sends the number of processed files.
|
||||
* @param job the job that emitted this signal
|
||||
* @param files the number of processed files
|
||||
*/
|
||||
void processedFiles(KIO::Job *job, unsigned long files);
|
||||
/**
|
||||
* Sends the number of processed directories.
|
||||
* @param job the job that emitted this signal
|
||||
* @param dirs the number of processed dirs
|
||||
*/
|
||||
void processedDirs(KIO::Job *job, unsigned long dirs);
|
||||
|
||||
/**
|
||||
* The job is copying a file or directory.
|
||||
*
|
||||
* Note: This signal is used for progress dialogs, it's not emitted for
|
||||
* every file or directory (this would be too slow), but every 200ms.
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param src the URL of the file or directory that is currently
|
||||
* being copied
|
||||
* @param dest the destination of the current operation
|
||||
*/
|
||||
void copying(KIO::Job *job, const QUrl &src, const QUrl &dest);
|
||||
/**
|
||||
* The job is creating a symbolic link.
|
||||
*
|
||||
* Note: This signal is used for progress dialogs, it's not emitted for
|
||||
* every file or directory (this would be too slow), but every 200ms.
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param target the URL of the file or directory that is currently
|
||||
* being linked
|
||||
* @param to the destination of the current operation
|
||||
*/
|
||||
void linking(KIO::Job *job, const QString &target, const QUrl &to);
|
||||
/**
|
||||
* The job is moving a file or directory.
|
||||
*
|
||||
* Note: This signal is used for progress dialogs, it's not emitted for
|
||||
* every file or directory (this would be too slow), but every 200ms.
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param from the URL of the file or directory that is currently
|
||||
* being moved
|
||||
* @param to the destination of the current operation
|
||||
*/
|
||||
void moving(KIO::Job *job, const QUrl &from, const QUrl &to);
|
||||
/**
|
||||
* The job is creating the directory @p dir.
|
||||
*
|
||||
* This signal is emitted for every directory being created.
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param dir the directory that is currently being created
|
||||
*/
|
||||
void creatingDir(KIO::Job *job, const QUrl &dir);
|
||||
/**
|
||||
* The user chose to rename @p from to @p to.
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param from the original name
|
||||
* @param to the new name
|
||||
*/
|
||||
void renamed(KIO::Job *job, const QUrl &from, const QUrl &to);
|
||||
|
||||
/**
|
||||
* The job emits this signal when copying or moving a file or directory successfully finished.
|
||||
* This signal is mainly for the Undo feature.
|
||||
* If you simply want to know when a copy job is done, use result().
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param from the source URL
|
||||
* @param to the destination URL
|
||||
* @param mtime the modification time of the source file, hopefully set on the destination file
|
||||
* too (when the KIO worker supports it).
|
||||
* @param directory indicates whether a file or directory was successfully copied/moved.
|
||||
* true for a directory, false for file
|
||||
* @param renamed indicates that the destination URL was created using a
|
||||
* rename operation (i.e. fast directory moving). true if is has been renamed
|
||||
*/
|
||||
void copyingDone(KIO::Job *job, const QUrl &from, const QUrl &to, const QDateTime &mtime, bool directory, bool renamed);
|
||||
/**
|
||||
* The job is copying or moving a symbolic link, that points to target.
|
||||
* The new link is created in @p to. The existing one is/was in @p from.
|
||||
* This signal is mainly for the Undo feature.
|
||||
* @param job the job that emitted this signal
|
||||
* @param from the source URL
|
||||
* @param target the target
|
||||
* @param to the destination URL
|
||||
*/
|
||||
void copyingLinkDone(KIO::Job *job, const QUrl &from, const QString &target, const QUrl &to);
|
||||
protected Q_SLOTS:
|
||||
void slotResult(KJob *job) override;
|
||||
|
||||
protected:
|
||||
KIOCORE_NO_EXPORT explicit CopyJob(CopyJobPrivate &dd);
|
||||
void emitResult();
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(CopyJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy a file or directory @p src into the destination @p dest,
|
||||
* which can be a file (including the final filename) or a directory
|
||||
* (into which @p src will be copied).
|
||||
*
|
||||
* This emulates the cp command completely.
|
||||
*
|
||||
* @param src the file or directory to copy
|
||||
* @param dest the destination
|
||||
* @param flags copy() supports HideProgressInfo and Overwrite.
|
||||
* Note: Overwrite has the meaning of both "write into existing directories" and
|
||||
* "overwrite existing files". However if "dest" exists, then src is copied
|
||||
* into a subdir of dest, just like "cp" does. Use copyAs if you don't want that.
|
||||
*
|
||||
* @return the job handling the operation
|
||||
* @see copyAs()
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *copy(const QUrl &src, const QUrl &dest, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Copy a file or directory @p src into the destination @p dest,
|
||||
* which is the destination name in any case, even for a directory.
|
||||
*
|
||||
* As opposed to copy(), this doesn't emulate cp, but is the only
|
||||
* way to copy a directory, giving it a new name and getting an error
|
||||
* box if a directory already exists with the same name (or writing the
|
||||
* contents of @p src into @p dest, when using Overwrite).
|
||||
*
|
||||
* @param src the file or directory to copy
|
||||
* @param dest the destination
|
||||
* @param flags copyAs() supports HideProgressInfo and Overwrite.
|
||||
* Note: Overwrite has the meaning of both "write into existing directories" and
|
||||
* "overwrite existing files".
|
||||
*
|
||||
* * @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *copyAs(const QUrl &src, const QUrl &dest, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Copy a list of file/dirs @p src into a destination directory @p dest.
|
||||
*
|
||||
* @param src the list of files and/or directories
|
||||
* @param dest the destination
|
||||
* @param flags copy() supports HideProgressInfo and Overwrite.
|
||||
* Note: Overwrite has the meaning of both "write into existing directories" and
|
||||
* "overwrite existing files". However if "dest" exists, then src is copied
|
||||
* into a subdir of dest, just like "cp" does.
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *copy(const QList<QUrl> &src, const QUrl &dest, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Moves a file or directory @p src to the given destination @p dest.
|
||||
*
|
||||
* @param src the file or directory to copy
|
||||
* @param dest the destination
|
||||
* @param flags move() supports HideProgressInfo and Overwrite.
|
||||
* Note: Overwrite has the meaning of both "write into existing directories" and
|
||||
* "overwrite existing files". However if "dest" exists, then src is copied
|
||||
* into a subdir of dest, just like "cp" does.
|
||||
* @return the job handling the operation
|
||||
* @see copy()
|
||||
* @see moveAs()
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *move(const QUrl &src, const QUrl &dest, JobFlags flags = DefaultFlags);
|
||||
/**
|
||||
* Moves a file or directory @p src to the given destination @p dest. Unlike move()
|
||||
* this operation will not move @p src into @p dest when @p dest exists: it will
|
||||
* either fail, or move the contents of @p src into it if Overwrite is set.
|
||||
*
|
||||
* @param src the file or directory to copy
|
||||
* @param dest the destination
|
||||
* @param flags moveAs() supports HideProgressInfo and Overwrite.
|
||||
* Note: Overwrite has the meaning of both "write into existing directories" and
|
||||
* "overwrite existing files".
|
||||
* @return the job handling the operation
|
||||
* @see copyAs()
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *moveAs(const QUrl &src, const QUrl &dest, JobFlags flags = DefaultFlags);
|
||||
/**
|
||||
* Moves a list of files or directories @p src to the given destination @p dest.
|
||||
*
|
||||
* @param src the list of files or directories to copy
|
||||
* @param dest the destination
|
||||
* @param flags move() supports HideProgressInfo and Overwrite.
|
||||
* Note: Overwrite has the meaning of both "write into existing directories" and
|
||||
* "overwrite existing files". However if "dest" exists, then src is copied
|
||||
* into a subdir of dest, just like "cp" does.
|
||||
* @return the job handling the operation
|
||||
* @see copy()
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *move(const QList<QUrl> &src, const QUrl &dest, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Create a link.
|
||||
* If the protocols and hosts are the same, a Unix symlink will be created.
|
||||
* Otherwise, a .desktop file of Type Link and pointing to the src URL will be created.
|
||||
*
|
||||
* @param src The existing file or directory, 'target' of the link.
|
||||
* @param destDir Destination directory where the link will be created.
|
||||
* @param flags link() supports HideProgressInfo only
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *link(const QUrl &src, const QUrl &destDir, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Create several links
|
||||
* If the protocols and hosts are the same, a Unix symlink will be created.
|
||||
* Otherwise, a .desktop file of Type Link and pointing to the src URL will be created.
|
||||
*
|
||||
* @param src The existing files or directories, 'targets' of the link.
|
||||
* @param destDir Destination directory where the links will be created.
|
||||
* @param flags link() supports HideProgressInfo only
|
||||
* @return the job handling the operation
|
||||
* @see link()
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *link(const QList<QUrl> &src, const QUrl &destDir, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Create a link. Unlike link() this operation will fail when @p dest is an existing
|
||||
* directory rather than the final name for the link.
|
||||
* If the protocols and hosts are the same, a Unix symlink will be created.
|
||||
* Otherwise, a .desktop file of Type Link and pointing to the src URL will be created.
|
||||
*
|
||||
* @param src The existing file or directory, 'target' of the link.
|
||||
* @param dest Destination (i.e. the final symlink)
|
||||
* @param flags linkAs() supports HideProgressInfo only
|
||||
* @return the job handling the operation
|
||||
* @see link ()
|
||||
* @see copyAs()
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *linkAs(const QUrl &src, const QUrl &dest, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Trash a file or directory.
|
||||
* This is currently only supported for local files and directories.
|
||||
* Use QUrl::fromLocalFile to create a URL from a local file path.
|
||||
*
|
||||
* @param src file to delete
|
||||
* @param flags trash() supports HideProgressInfo only
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *trash(const QUrl &src, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Trash a list of files or directories.
|
||||
* This is currently only supported for local files and directories.
|
||||
*
|
||||
* @param src the files to delete
|
||||
* @param flags trash() supports HideProgressInfo only
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT CopyJob *trash(const QList<QUrl> &src, JobFlags flags = DefaultFlags);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,270 @@
|
||||
/*
|
||||
Implementation of the data protocol (rfc 2397)
|
||||
|
||||
SPDX-FileCopyrightText: 2002, 2003 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "dataprotocol_p.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "metadata.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QStringDecoder>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
/** 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 QString::fromLatin1(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 QByteArray &buf, int &pos)
|
||||
{
|
||||
int size = buf.length();
|
||||
while (pos < size && (buf[pos] == ' ' || buf[pos] == '\t')) {
|
||||
++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 QByteArray &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 = QLatin1Char(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++] == ',') {
|
||||
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] != '=') {
|
||||
// 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] == '"') {
|
||||
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] == ',') {
|
||||
data_begin_reached = true;
|
||||
}
|
||||
header_info.data_offset++; // jump over separator token
|
||||
} /*wend*/
|
||||
|
||||
return header_info;
|
||||
}
|
||||
|
||||
DataProtocol::DataProtocol()
|
||||
{
|
||||
}
|
||||
|
||||
DataProtocol::~DataProtocol() = default;
|
||||
|
||||
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 {
|
||||
QStringDecoder codec(hdr.attributes[QStringLiteral("charset")].toLatin1().constData());
|
||||
if (codec.isValid()) {
|
||||
outData = QString(codec.decode(url_data)).toUtf8();
|
||||
} else {
|
||||
outData = url_data;
|
||||
} /*end if*/
|
||||
} /*end if*/
|
||||
|
||||
// qDebug() << "emit mimeType@"<<this;
|
||||
Q_EMIT mimeType(hdr.mime_type);
|
||||
// qDebug() << "emit totalSize@"<<this;
|
||||
Q_EMIT totalSize(outData.size());
|
||||
|
||||
// qDebug() << "emit setMetaData@"<<this;
|
||||
setAllMetaData(hdr.attributes);
|
||||
|
||||
// qDebug() << "emit sendMetaData@"<<this;
|
||||
sendMetaData();
|
||||
// qDebug() << "(1) queue size " << dispatchQueue.size();
|
||||
// empiric studies have shown that this shouldn't be queued & dispatched
|
||||
Q_EMIT data(outData);
|
||||
// qDebug() << "(2) queue size " << dispatchQueue.size();
|
||||
dispatch_data(QByteArray{});
|
||||
// qDebug() << "(3) queue size " << dispatchQueue.size();
|
||||
dispatch_finished();
|
||||
// qDebug() << "(4) queue size " << dispatchQueue.size();
|
||||
deref();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
void DataProtocol::mimetype(const QUrl &url)
|
||||
{
|
||||
ref();
|
||||
Q_EMIT mimeType(parseDataHeader(url, true).mime_type);
|
||||
Q_EMIT finished();
|
||||
deref();
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------- */
|
||||
|
||||
#if !defined(TESTKIO)
|
||||
#include "moc_dataprotocol_p.cpp"
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
Interface of the KDE data protocol core operations
|
||||
|
||||
SPDX-FileCopyrightText: 2002 Leo Savernik <l.savernik@aon.at>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef DATAPROTOCOL_H
|
||||
#define DATAPROTOCOL_H
|
||||
|
||||
// dataprotocol.* interprets the following defines
|
||||
// TESTKIO: define for test-driving
|
||||
// Both defines are mutually exclusive. Defining none of them compiles
|
||||
// DataProtocol for internal usage within libkiocore.
|
||||
|
||||
/* Wondering what this is all about? Leo explained it to me:
|
||||
*
|
||||
* That's simple, you can compile it into a standalone executable that is
|
||||
* registered like any other KIO worker.
|
||||
*
|
||||
* However, given that data-urls don't depend on any external data it seemed
|
||||
* overkill, therefore I added a special hack that the kio-dataworker is invoked
|
||||
* in-process on the client side.
|
||||
*/
|
||||
|
||||
class QByteArray;
|
||||
|
||||
class QUrl;
|
||||
|
||||
#if !defined(TESTKIO)
|
||||
#include "dataworker_p.h"
|
||||
#endif
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/** This KIO worker provides support of data urls as specified by rfc 2397
|
||||
* @see https://www.ietf.org/rfc/rfc2397.txt
|
||||
* @author Leo Savernik
|
||||
*/
|
||||
#if defined(TESTKIO)
|
||||
class DataProtocol : public TestWorker
|
||||
{
|
||||
#else
|
||||
class DataProtocol : public DataWorker
|
||||
{
|
||||
Q_OBJECT
|
||||
#endif
|
||||
|
||||
public:
|
||||
DataProtocol();
|
||||
|
||||
#if defined(TESTKIO)
|
||||
void mimetype(const QUrl &url);
|
||||
void get(const QUrl &url);
|
||||
#else
|
||||
void mimetype(const QUrl &url) override;
|
||||
void get(const QUrl &url) override;
|
||||
#endif
|
||||
~DataProtocol() override;
|
||||
};
|
||||
|
||||
} /*end namespace*/
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
Derived from worker.cpp
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "commands_p.h"
|
||||
#include "dataprotocol_p.h"
|
||||
#include "dataworker_p.h"
|
||||
#include "workerbase.h"
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
static constexpr int s_kioDataPollInterval = 0;
|
||||
|
||||
// don't forget to sync DISPATCH_DECL in dataworker_p.h
|
||||
/* clang-format off */
|
||||
#define DISPATCH_IMPL(type) \
|
||||
void DataWorker::dispatch_##type() \
|
||||
{ \
|
||||
if (_suspended) { \
|
||||
QueueStruct q(Queue_##type); \
|
||||
q.size = -1; \
|
||||
dispatchQueue.push_back(q); \
|
||||
if (!timer->isActive()) { \
|
||||
timer->start(s_kioDataPollInterval); \
|
||||
} \
|
||||
} else \
|
||||
Q_EMIT type(); \
|
||||
}
|
||||
|
||||
// don't forget to sync DISPATCH_DECL1 in dataworker_p.h
|
||||
#define DISPATCH_IMPL1(type, paramtype, paramname) \
|
||||
void DataWorker::dispatch_##type(paramtype paramname) \
|
||||
{ \
|
||||
if (_suspended) { \
|
||||
QueueStruct q(Queue_##type); \
|
||||
q.paramname = paramname; \
|
||||
dispatchQueue.push_back(q); \
|
||||
if (!timer->isActive()) { \
|
||||
timer->start(s_kioDataPollInterval); \
|
||||
} \
|
||||
} else \
|
||||
Q_EMIT type(paramname); \
|
||||
}
|
||||
|
||||
DataWorker::DataWorker()
|
||||
: Worker(QStringLiteral("data"))
|
||||
{
|
||||
// qDebug() << this;
|
||||
_suspended = false;
|
||||
timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout, this, &DataWorker::dispatchNext);
|
||||
}
|
||||
|
||||
DataWorker::~DataWorker()
|
||||
{
|
||||
// qDebug() << this;
|
||||
}
|
||||
|
||||
void DataWorker::suspend()
|
||||
{
|
||||
_suspended = true;
|
||||
// qDebug() << this;
|
||||
timer->stop();
|
||||
}
|
||||
|
||||
void DataWorker::resume()
|
||||
{
|
||||
_suspended = false;
|
||||
// qDebug() << this;
|
||||
// aarrrgh! This makes the once hyper fast and efficient data protocol
|
||||
// implementation slow as molasses. But it wouldn't work otherwise,
|
||||
// and I don't want to start messing around with threads
|
||||
timer->start(s_kioDataPollInterval);
|
||||
}
|
||||
|
||||
// finished is a special case. If we emit it right away, then
|
||||
// TransferJob::start can delete the job even before the end of the method
|
||||
void DataWorker::dispatch_finished()
|
||||
{
|
||||
QueueStruct q(Queue_finished);
|
||||
q.size = -1;
|
||||
dispatchQueue.push_back(q);
|
||||
if (!timer->isActive()) {
|
||||
timer->start(s_kioDataPollInterval);
|
||||
}
|
||||
}
|
||||
|
||||
void DataWorker::dispatchNext()
|
||||
{
|
||||
if (dispatchQueue.empty()) {
|
||||
timer->stop();
|
||||
return;
|
||||
}
|
||||
|
||||
const QueueStruct &q = dispatchQueue.front();
|
||||
// qDebug() << this << "dispatching" << q.type << dispatchQueue.size() << "left";
|
||||
switch (q.type) {
|
||||
case Queue_mimeType:
|
||||
Q_EMIT mimeType(q.s);
|
||||
break;
|
||||
case Queue_totalSize:
|
||||
Q_EMIT totalSize(q.size);
|
||||
break;
|
||||
case Queue_sendMetaData:
|
||||
sendMetaData();
|
||||
break;
|
||||
case Queue_data:
|
||||
Q_EMIT data(q.ba);
|
||||
break;
|
||||
case Queue_finished:
|
||||
Q_EMIT finished();
|
||||
break;
|
||||
} /*end switch*/
|
||||
|
||||
dispatchQueue.pop_front();
|
||||
}
|
||||
|
||||
void DataWorker::send(int cmd, const QByteArray &arr)
|
||||
{
|
||||
QDataStream stream(arr);
|
||||
|
||||
QUrl url;
|
||||
|
||||
switch (cmd) {
|
||||
case CMD_GET: {
|
||||
stream >> url;
|
||||
get(url);
|
||||
break;
|
||||
}
|
||||
case CMD_MIMETYPE: {
|
||||
stream >> url;
|
||||
mimetype(url);
|
||||
break;
|
||||
}
|
||||
// ignore these (must not emit error, otherwise SIGSEGV occurs)
|
||||
case CMD_REPARSECONFIGURATION:
|
||||
case CMD_META_DATA:
|
||||
break;
|
||||
default:
|
||||
Q_EMIT error(ERR_UNSUPPORTED_ACTION, unsupportedActionErrorString(QStringLiteral("data"), cmd));
|
||||
} /*end switch*/
|
||||
}
|
||||
|
||||
bool DataWorker::suspended()
|
||||
{
|
||||
return _suspended;
|
||||
}
|
||||
|
||||
void DataWorker::setHost(const QString & /*host*/, quint16 /*port*/, const QString & /*user*/, const QString & /*passwd*/)
|
||||
{
|
||||
// irrelevant -> will be ignored
|
||||
}
|
||||
|
||||
void DataWorker::setConfig(const MetaData & /*config*/)
|
||||
{
|
||||
// FIXME: decide to handle this directly or not at all
|
||||
#if 0
|
||||
QByteArray data;
|
||||
QDataStream stream(data, QIODevice::WriteOnly);
|
||||
stream << config;
|
||||
slaveconn.send(CMD_CONFIG, data);
|
||||
#endif
|
||||
}
|
||||
|
||||
void DataWorker::setAllMetaData(const MetaData &md)
|
||||
{
|
||||
meta_data = md;
|
||||
}
|
||||
|
||||
void DataWorker::sendMetaData()
|
||||
{
|
||||
Q_EMIT metaData(meta_data);
|
||||
}
|
||||
|
||||
DISPATCH_IMPL1(mimeType, const QString &, s)
|
||||
DISPATCH_IMPL1(totalSize, KIO::filesize_t, size)
|
||||
DISPATCH_IMPL(sendMetaData)
|
||||
DISPATCH_IMPL1(data, const QByteArray &, ba)
|
||||
|
||||
#undef DISPATCH_IMPL
|
||||
#undef DISPATCH_IMPL1
|
||||
|
||||
#include "moc_dataworker_p.cpp"
|
||||
@@ -0,0 +1,119 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2003 Leo Savernik <l.savernik@aon.at>
|
||||
Derived from worker_p.h
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KIO_DATAWORKER_P_H
|
||||
#define KIO_DATAWORKER_P_H
|
||||
|
||||
#include "global.h"
|
||||
#include "worker_p.h"
|
||||
|
||||
class QTimer;
|
||||
|
||||
// don't forget to sync DISPATCH_IMPL in dataworker.cpp
|
||||
#define DISPATCH_DECL(type) void dispatch_##type();
|
||||
|
||||
// don't forget to sync DISPATCH_IMPL1 in dataworker.cpp
|
||||
#define DISPATCH_DECL1(type, paramtype, param) void dispatch_##type(paramtype param);
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* This class provides a high performance implementation for the data
|
||||
* url scheme (rfc2397).
|
||||
*
|
||||
* @internal
|
||||
* Do not use this class in external applications. It is an implementation
|
||||
* detail of KIO and subject to change without notice.
|
||||
* @author Leo Savernik
|
||||
*/
|
||||
class DataWorker : public KIO::Worker
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
DataWorker();
|
||||
|
||||
~DataWorker() override;
|
||||
|
||||
void setHost(const QString &host, quint16 port, const QString &user, const QString &passwd) override;
|
||||
void setConfig(const MetaData &config) override;
|
||||
|
||||
void suspend() override;
|
||||
void resume() override;
|
||||
bool suspended() override;
|
||||
void send(int cmd, const QByteArray &arr = QByteArray()) override;
|
||||
|
||||
// pure virtual methods that are defined by the actual protocol
|
||||
virtual void get(const QUrl &url) = 0;
|
||||
virtual void mimetype(const QUrl &url) = 0;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Sets metadata
|
||||
* @internal
|
||||
*/
|
||||
void setAllMetaData(const MetaData &);
|
||||
/**
|
||||
* Sends metadata set with setAllMetaData
|
||||
* @internal
|
||||
*/
|
||||
void sendMetaData();
|
||||
|
||||
// queuing methods
|
||||
/** identifiers of functions to be queued */
|
||||
enum QueueType {
|
||||
Queue_mimeType = 1,
|
||||
Queue_totalSize,
|
||||
Queue_sendMetaData,
|
||||
Queue_data,
|
||||
Queue_finished,
|
||||
};
|
||||
/** structure for queuing. It is very primitive, it doesn't
|
||||
* even try to conserve memory.
|
||||
*/
|
||||
struct QueueStruct {
|
||||
QueueType type;
|
||||
QString s;
|
||||
KIO::filesize_t size;
|
||||
QByteArray ba;
|
||||
|
||||
QueueStruct()
|
||||
{
|
||||
}
|
||||
QueueStruct(QueueType type)
|
||||
: type(type)
|
||||
{
|
||||
}
|
||||
};
|
||||
typedef QList<QueueStruct> DispatchQueue;
|
||||
DispatchQueue dispatchQueue;
|
||||
|
||||
DISPATCH_DECL1(mimeType, const QString &, s)
|
||||
DISPATCH_DECL1(totalSize, KIO::filesize_t, size)
|
||||
DISPATCH_DECL(sendMetaData)
|
||||
DISPATCH_DECL1(data, const QByteArray &, ba)
|
||||
DISPATCH_DECL(finished)
|
||||
|
||||
protected Q_SLOTS:
|
||||
/** dispatches next queued method. Does nothing if there are no
|
||||
* queued methods.
|
||||
*/
|
||||
void dispatchNext();
|
||||
|
||||
private:
|
||||
MetaData meta_data;
|
||||
bool _suspended;
|
||||
QTimer *timer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#undef DISPATCH_DECL
|
||||
#undef DISPATCH_DECL1
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,134 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Jan-Pascal van Best <janpascal@vanbest.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "davjob.h"
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QDomDocument>
|
||||
|
||||
#include "httpmethod_p.h"
|
||||
#include "job_p.h"
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
/** @internal */
|
||||
class KIO::DavJobPrivate : public KIO::TransferJobPrivate
|
||||
{
|
||||
public:
|
||||
explicit DavJobPrivate(const QUrl &url)
|
||||
: TransferJobPrivate(url, KIO::CMD_SPECIAL, QByteArray(), QByteArray())
|
||||
{
|
||||
}
|
||||
QByteArray savedStaticData;
|
||||
QByteArray str_response;
|
||||
|
||||
Q_DECLARE_PUBLIC(DavJob)
|
||||
|
||||
static inline DavJob *newJob(const QUrl &url, int method, const QString &request, JobFlags flags)
|
||||
{
|
||||
DavJob *job = new DavJob(*new DavJobPrivate(url), method, request);
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
DavJob::DavJob(DavJobPrivate &dd, int method, const QString &request)
|
||||
: TransferJob(dd)
|
||||
{
|
||||
// We couldn't set the args when calling the parent constructor,
|
||||
// so do it now.
|
||||
Q_D(DavJob);
|
||||
QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly);
|
||||
stream << (int)7 << d->m_url << method;
|
||||
// Same for static data
|
||||
if (!request.isEmpty()) {
|
||||
d->staticData = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\r\n" + request.toUtf8();
|
||||
d->staticData.chop(1);
|
||||
d->savedStaticData = d->staticData;
|
||||
stream << static_cast<qint64>(d->staticData.size());
|
||||
} else {
|
||||
stream << static_cast<qint64>(-1);
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray DavJob::responseData() const
|
||||
{
|
||||
return d_func()->str_response;
|
||||
}
|
||||
|
||||
void DavJob::slotData(const QByteArray &data)
|
||||
{
|
||||
Q_D(DavJob);
|
||||
if (d->m_redirectionURL.isEmpty() || !d->m_redirectionURL.isValid() || error()) {
|
||||
unsigned int oldSize = d->str_response.size();
|
||||
d->str_response.resize(oldSize + data.size());
|
||||
memcpy(d->str_response.data() + oldSize, data.data(), data.size());
|
||||
}
|
||||
}
|
||||
|
||||
void DavJob::slotFinished()
|
||||
{
|
||||
Q_D(DavJob);
|
||||
// qDebug() << d->str_response;
|
||||
if (!d->m_redirectionURL.isEmpty() && d->m_redirectionURL.isValid() && (d->m_command == CMD_SPECIAL)) {
|
||||
QDataStream istream(d->m_packedArgs);
|
||||
int s_cmd;
|
||||
int s_method;
|
||||
qint64 s_size;
|
||||
QUrl s_url;
|
||||
istream >> s_cmd;
|
||||
istream >> s_url;
|
||||
istream >> s_method;
|
||||
istream >> s_size;
|
||||
// PROPFIND
|
||||
if ((s_cmd == 7) && (s_method == (int)KIO::DAV_PROPFIND)) {
|
||||
d->m_packedArgs.truncate(0);
|
||||
QDataStream stream(&d->m_packedArgs, QIODevice::WriteOnly);
|
||||
stream << (int)7 << d->m_redirectionURL << (int)KIO::DAV_PROPFIND << s_size;
|
||||
}
|
||||
}
|
||||
TransferJob::slotFinished();
|
||||
d->staticData = d->savedStaticData; // Need to send DAV request to this host too
|
||||
}
|
||||
|
||||
/* Convenience methods */
|
||||
DavJob *KIO::davPropFind(const QUrl &url, const QString &properties, const QString &depth, JobFlags flags)
|
||||
{
|
||||
DavJob *job = DavJobPrivate::newJob(url, (int)KIO::DAV_PROPFIND, properties, flags);
|
||||
job->addMetaData(QStringLiteral("davDepth"), depth);
|
||||
return job;
|
||||
}
|
||||
|
||||
DavJob *KIO::davPropPatch(const QUrl &url, const QString &properties, JobFlags flags)
|
||||
{
|
||||
return DavJobPrivate::newJob(url, (int)KIO::DAV_PROPPATCH, properties, flags);
|
||||
}
|
||||
|
||||
DavJob *KIO::davSearch(const QUrl &url, const QString &nsURI, const QString &qName, const QString &query, JobFlags flags)
|
||||
{
|
||||
QDomDocument doc;
|
||||
QDomElement searchrequest = doc.createElementNS(QStringLiteral("DAV:"), QStringLiteral("searchrequest"));
|
||||
QDomElement searchelement = doc.createElementNS(nsURI, qName);
|
||||
QDomText text = doc.createTextNode(query);
|
||||
searchelement.appendChild(text);
|
||||
searchrequest.appendChild(searchelement);
|
||||
doc.appendChild(searchrequest);
|
||||
return DavJobPrivate::newJob(url, KIO::DAV_SEARCH, doc.toString(), flags);
|
||||
}
|
||||
|
||||
DavJob *KIO::davReport(const QUrl &url, const QString &report, const QString &depth, JobFlags flags)
|
||||
{
|
||||
DavJob *job = DavJobPrivate::newJob(url, (int)KIO::DAV_REPORT, report, flags);
|
||||
job->addMetaData(QStringLiteral("davDepth"), depth);
|
||||
return job;
|
||||
}
|
||||
|
||||
#include "moc_davjob.cpp"
|
||||
@@ -0,0 +1,112 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Jan-Pascal van Best <janpascal@vanbest.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_DAVJOB_H
|
||||
#define KIO_DAVJOB_H
|
||||
|
||||
#include "global.h"
|
||||
#include "kiocore_export.h"
|
||||
#include "transferjob.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
|
||||
class DavJobPrivate;
|
||||
/**
|
||||
* @class KIO::DavJob davjob.h <KIO/DavJob>
|
||||
*
|
||||
* The transfer job pumps data into and/or out of a KIO worker.
|
||||
* Data is sent to the worker on request of the worker ( dataReq).
|
||||
* If data coming from the worker can not be handled, the
|
||||
* reading of data from the worker should be suspended.
|
||||
* @see KIO::davPropFind()
|
||||
* @see KIO::davPropPatch()
|
||||
* @see KIO::davSearch()
|
||||
*/
|
||||
class KIOCORE_EXPORT DavJob : public TransferJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Returns the reponse data.
|
||||
* @since 5.86
|
||||
*/
|
||||
QByteArray responseData() const;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotFinished() override;
|
||||
void slotData(const QByteArray &data) override;
|
||||
|
||||
protected:
|
||||
KIOCORE_NO_EXPORT DavJob(DavJobPrivate &dd, int, const QString &);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(DavJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new DavJob that issues a PROPFIND command. PROPFIND retrieves
|
||||
* the properties of the resource identified by the given @p url.
|
||||
*
|
||||
* @param url the URL of the resource
|
||||
* @param properties a propfind document that describes the properties that
|
||||
* should be retrieved
|
||||
* @param depth the depth of the request. Can be "0", "1" or "infinity"
|
||||
* @param flags We support HideProgressInfo here
|
||||
* @return the new DavJob
|
||||
* @since 5.84
|
||||
*/
|
||||
KIOCORE_EXPORT DavJob *davPropFind(const QUrl &url, const QString &properties, const QString &depth, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Creates a new DavJob that issues a PROPPATCH command. PROPPATCH sets
|
||||
* the properties of the resource identified by the given @p url.
|
||||
*
|
||||
* @param url the URL of the resource
|
||||
* @param properties a PROPPACTCH document that describes the properties that
|
||||
* should be modified and its new values
|
||||
* @param flags We support HideProgressInfo here
|
||||
* @return the new DavJob
|
||||
* @since 5.84
|
||||
*/
|
||||
KIOCORE_EXPORT DavJob *davPropPatch(const QUrl &url, const QString &properties, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Creates a new DavJob that issues a SEARCH command.
|
||||
*
|
||||
* @param url the URL of the resource
|
||||
* @param nsURI the URI of the search method's qualified name
|
||||
* @param qName the local part of the search method's qualified name
|
||||
* @param query the search string
|
||||
* @param flags We support HideProgressInfo here
|
||||
* @return the new DavJob
|
||||
*/
|
||||
KIOCORE_EXPORT DavJob *davSearch(const QUrl &url, const QString &nsURI, const QString &qName, const QString &query, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Creates a new DavJob that issues a REPORT command.
|
||||
*
|
||||
* @param url the URL of the resource
|
||||
* @param report a REPORT document that describes the request to make
|
||||
* @param depth the depth of the request. Can be "0", "1" or "infinity"
|
||||
* @param flags We support HideProgressInfo here
|
||||
* @return the new DavJob
|
||||
*/
|
||||
KIOCORE_EXPORT DavJob *davReport(const QUrl &url, const QString &report, const QString &depth, JobFlags flags = DefaultFlags);
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,618 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "deletejob.h"
|
||||
|
||||
#include "../utils_p.h"
|
||||
#include "job.h" // buildErrorString
|
||||
#include "kcoredirlister.h"
|
||||
#include "kprotocolmanager.h"
|
||||
#include "listjob.h"
|
||||
#include "statjob.h"
|
||||
#include <KDirWatch>
|
||||
#include <kdirnotify.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <kio/jobuidelegatefactory.h>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QMetaObject>
|
||||
#include <QPointer>
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
|
||||
#include "job_p.h"
|
||||
|
||||
extern bool kio_resolve_local_urls; // from copyjob.cpp, abused here to save a symbol.
|
||||
|
||||
static bool isHttpProtocol(const QString &protocol)
|
||||
{
|
||||
return (protocol.startsWith(QLatin1String("webdav"), Qt::CaseInsensitive) || protocol.startsWith(QLatin1String("http"), Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
enum DeleteJobState {
|
||||
DELETEJOB_STATE_STATING,
|
||||
DELETEJOB_STATE_DELETING_FILES,
|
||||
DELETEJOB_STATE_DELETING_DIRS,
|
||||
};
|
||||
|
||||
class DeleteJobIOWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Q_SIGNALS:
|
||||
void rmfileResult(bool succeeded, bool isLink);
|
||||
void rmddirResult(bool succeeded);
|
||||
|
||||
public Q_SLOTS:
|
||||
|
||||
/**
|
||||
* Deletes the file @p url points to
|
||||
* The file must be a LocalFile
|
||||
*/
|
||||
void rmfile(const QUrl &url, bool isLink)
|
||||
{
|
||||
Q_EMIT rmfileResult(QFile::remove(url.toLocalFile()), isLink);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the directory @p url points to
|
||||
* The directory must be a LocalFile
|
||||
*/
|
||||
void rmdir(const QUrl &url)
|
||||
{
|
||||
Q_EMIT rmddirResult(QDir().rmdir(url.toLocalFile()));
|
||||
}
|
||||
};
|
||||
|
||||
class DeleteJobPrivate : public KIO::JobPrivate
|
||||
{
|
||||
public:
|
||||
explicit DeleteJobPrivate(const QList<QUrl> &src)
|
||||
: state(DELETEJOB_STATE_STATING)
|
||||
, m_processedFiles(0)
|
||||
, m_processedDirs(0)
|
||||
, m_totalFilesDirs(0)
|
||||
, m_srcList(src)
|
||||
, m_currentStat(m_srcList.begin())
|
||||
, m_reportTimer(nullptr)
|
||||
{
|
||||
}
|
||||
DeleteJobState state;
|
||||
int m_processedFiles;
|
||||
int m_processedDirs;
|
||||
int m_totalFilesDirs;
|
||||
QUrl m_currentURL;
|
||||
QList<QUrl> files;
|
||||
QList<QUrl> symlinks;
|
||||
QList<QUrl> dirs;
|
||||
QList<QUrl> m_srcList;
|
||||
QList<QUrl>::iterator m_currentStat;
|
||||
QSet<QString> m_parentDirs;
|
||||
QTimer *m_reportTimer;
|
||||
DeleteJobIOWorker *m_ioworker = nullptr;
|
||||
QThread *m_thread = nullptr;
|
||||
|
||||
void statNextSrc();
|
||||
DeleteJobIOWorker *worker();
|
||||
void currentSourceStated(bool isDir, bool isLink);
|
||||
void finishedStatPhase();
|
||||
void deleteNextFile();
|
||||
void deleteNextDir();
|
||||
void restoreDirWatch() const;
|
||||
void slotReport();
|
||||
void slotStart();
|
||||
void slotEntries(KIO::Job *, const KIO::UDSEntryList &list);
|
||||
|
||||
/// Callback of worker rmfile
|
||||
void rmFileResult(bool result, bool isLink);
|
||||
/// Callback of worker rmdir
|
||||
void rmdirResult(bool result);
|
||||
void deleteFileUsingJob(const QUrl &url, bool isLink);
|
||||
void deleteDirUsingJob(const QUrl &url);
|
||||
|
||||
~DeleteJobPrivate() override;
|
||||
|
||||
Q_DECLARE_PUBLIC(DeleteJob)
|
||||
|
||||
static inline DeleteJob *newJob(const QList<QUrl> &src, JobFlags flags)
|
||||
{
|
||||
DeleteJob *job = new DeleteJob(*new DeleteJobPrivate(src));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
if (!(flags & NoPrivilegeExecution)) {
|
||||
job->d_func()->m_privilegeExecutionEnabled = true;
|
||||
job->d_func()->m_operationType = Delete;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
DeleteJob::DeleteJob(DeleteJobPrivate &dd)
|
||||
: Job(dd)
|
||||
{
|
||||
Q_D(DeleteJob);
|
||||
|
||||
d->m_reportTimer = new QTimer(this);
|
||||
connect(d->m_reportTimer, &QTimer::timeout, this, [d]() {
|
||||
d->slotReport();
|
||||
});
|
||||
// this will update the report dialog with 5 Hz, I think this is fast enough, aleXXX
|
||||
d->m_reportTimer->start(200);
|
||||
|
||||
QTimer::singleShot(0, this, [d]() {
|
||||
d->slotStart();
|
||||
});
|
||||
}
|
||||
|
||||
DeleteJob::~DeleteJob()
|
||||
{
|
||||
}
|
||||
|
||||
DeleteJobPrivate::~DeleteJobPrivate()
|
||||
{
|
||||
if (m_thread) {
|
||||
m_thread->quit();
|
||||
m_thread->wait();
|
||||
delete m_thread;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QUrl> DeleteJob::urls() const
|
||||
{
|
||||
return d_func()->m_srcList;
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::slotStart()
|
||||
{
|
||||
statNextSrc();
|
||||
}
|
||||
|
||||
DeleteJobIOWorker *DeleteJobPrivate::worker()
|
||||
{
|
||||
Q_Q(DeleteJob);
|
||||
|
||||
if (!m_ioworker) {
|
||||
m_thread = new QThread();
|
||||
|
||||
m_ioworker = new DeleteJobIOWorker;
|
||||
m_ioworker->moveToThread(m_thread);
|
||||
QObject::connect(m_thread, &QThread::finished, m_ioworker, &QObject::deleteLater);
|
||||
QObject::connect(m_ioworker, &DeleteJobIOWorker::rmfileResult, q, [=, this](bool result, bool isLink) {
|
||||
this->rmFileResult(result, isLink);
|
||||
});
|
||||
QObject::connect(m_ioworker, &DeleteJobIOWorker::rmddirResult, q, [=, this](bool result) {
|
||||
this->rmdirResult(result);
|
||||
});
|
||||
m_thread->start();
|
||||
}
|
||||
|
||||
return m_ioworker;
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::slotReport()
|
||||
{
|
||||
Q_Q(DeleteJob);
|
||||
Q_EMIT q->deleting(q, m_currentURL);
|
||||
|
||||
// TODO: maybe we could skip everything else when (flags & HideProgressInfo) ?
|
||||
JobPrivate::emitDeleting(q, m_currentURL);
|
||||
|
||||
switch (state) {
|
||||
case DELETEJOB_STATE_STATING:
|
||||
q->setTotalAmount(KJob::Files, files.count());
|
||||
q->setTotalAmount(KJob::Directories, dirs.count());
|
||||
break;
|
||||
case DELETEJOB_STATE_DELETING_DIRS:
|
||||
q->setProcessedAmount(KJob::Directories, m_processedDirs);
|
||||
q->emitPercent(m_processedFiles + m_processedDirs, m_totalFilesDirs);
|
||||
break;
|
||||
case DELETEJOB_STATE_DELETING_FILES:
|
||||
q->setProcessedAmount(KJob::Files, m_processedFiles);
|
||||
q->emitPercent(m_processedFiles, m_totalFilesDirs);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::slotEntries(KIO::Job *job, const UDSEntryList &list)
|
||||
{
|
||||
UDSEntryList::ConstIterator it = list.begin();
|
||||
const UDSEntryList::ConstIterator end = list.end();
|
||||
for (; it != end; ++it) {
|
||||
const UDSEntry &entry = *it;
|
||||
const QString displayName = entry.stringValue(KIO::UDSEntry::UDS_NAME);
|
||||
|
||||
Q_ASSERT(!displayName.isEmpty());
|
||||
if (displayName != QLatin1String("..") && displayName != QLatin1String(".")) {
|
||||
QUrl url;
|
||||
const QString urlStr = entry.stringValue(KIO::UDSEntry::UDS_URL);
|
||||
if (!urlStr.isEmpty()) {
|
||||
url = QUrl(urlStr);
|
||||
} else {
|
||||
url = static_cast<SimpleJob *>(job)->url(); // assumed to be a dir
|
||||
url.setPath(Utils::concatPaths(url.path(), displayName));
|
||||
}
|
||||
|
||||
// qDebug() << displayName << "(" << url << ")";
|
||||
if (entry.isLink()) {
|
||||
symlinks.append(url);
|
||||
} else if (entry.isDir()) {
|
||||
dirs.append(url);
|
||||
} else {
|
||||
files.append(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::statNextSrc()
|
||||
{
|
||||
Q_Q(DeleteJob);
|
||||
// qDebug();
|
||||
if (m_currentStat != m_srcList.end()) {
|
||||
m_currentURL = (*m_currentStat);
|
||||
|
||||
// if the file system doesn't support deleting, we do not even stat
|
||||
if (!KProtocolManager::supportsDeleting(m_currentURL)) {
|
||||
QPointer<DeleteJob> that = q;
|
||||
++m_currentStat;
|
||||
Q_EMIT q->warning(q, buildErrorString(ERR_CANNOT_DELETE, m_currentURL.toDisplayString()));
|
||||
if (that) {
|
||||
statNextSrc();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// Stat it
|
||||
state = DELETEJOB_STATE_STATING;
|
||||
|
||||
// Fast path for KFileItems in directory views
|
||||
while (m_currentStat != m_srcList.end()) {
|
||||
m_currentURL = (*m_currentStat);
|
||||
const KFileItem cachedItem = KCoreDirLister::cachedItemForUrl(m_currentURL);
|
||||
if (cachedItem.isNull()) {
|
||||
break;
|
||||
}
|
||||
// qDebug() << "Found cached info about" << m_currentURL << "isDir=" << cachedItem.isDir() << "isLink=" << cachedItem.isLink();
|
||||
currentSourceStated(cachedItem.isDir(), cachedItem.isLink());
|
||||
++m_currentStat;
|
||||
}
|
||||
|
||||
// Hook for unit test to disable the fast path.
|
||||
if (!kio_resolve_local_urls) {
|
||||
// Fast path for local files
|
||||
// (using a loop, instead of a huge recursion)
|
||||
while (m_currentStat != m_srcList.end() && (*m_currentStat).isLocalFile()) {
|
||||
m_currentURL = (*m_currentStat);
|
||||
QFileInfo fileInfo(m_currentURL.toLocalFile());
|
||||
currentSourceStated(fileInfo.isDir(), fileInfo.isSymLink());
|
||||
++m_currentStat;
|
||||
}
|
||||
}
|
||||
if (m_currentStat == m_srcList.end()) {
|
||||
// Done, jump to the last else of this method
|
||||
statNextSrc();
|
||||
} else {
|
||||
KIO::SimpleJob *job = KIO::stat(m_currentURL, StatJob::SourceSide, KIO::StatBasic, KIO::HideProgressInfo);
|
||||
// qDebug() << "stat'ing" << m_currentURL;
|
||||
q->addSubjob(job);
|
||||
}
|
||||
} else {
|
||||
if (!q->hasSubjobs()) { // don't go there yet if we're still listing some subdirs
|
||||
finishedStatPhase();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::finishedStatPhase()
|
||||
{
|
||||
m_totalFilesDirs = files.count() + symlinks.count() + dirs.count();
|
||||
slotReport();
|
||||
// Now we know which dirs hold the files we're going to delete.
|
||||
// To speed things up and prevent double-notification, we disable KDirWatch
|
||||
// on those dirs temporarily (using KDirWatch::self, that's the instance
|
||||
// used by e.g. kdirlister).
|
||||
for (const QString &dir : std::as_const(m_parentDirs)) {
|
||||
KDirWatch::self()->stopDirScan(dir);
|
||||
}
|
||||
state = DELETEJOB_STATE_DELETING_FILES;
|
||||
deleteNextFile();
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::rmFileResult(bool result, bool isLink)
|
||||
{
|
||||
if (result) {
|
||||
m_processedFiles++;
|
||||
|
||||
if (isLink) {
|
||||
symlinks.removeFirst();
|
||||
} else {
|
||||
files.removeFirst();
|
||||
}
|
||||
|
||||
deleteNextFile();
|
||||
} else {
|
||||
// fallback if QFile::remove() failed (we'll use the job's error handling in that case)
|
||||
deleteFileUsingJob(m_currentURL, isLink);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::deleteFileUsingJob(const QUrl &url, bool isLink)
|
||||
{
|
||||
Q_Q(DeleteJob);
|
||||
|
||||
SimpleJob *job;
|
||||
if (isHttpProtocol(url.scheme())) {
|
||||
job = KIO::http_delete(url, KIO::HideProgressInfo);
|
||||
} else {
|
||||
job = KIO::file_delete(url, KIO::HideProgressInfo);
|
||||
job->setParentJob(q);
|
||||
}
|
||||
|
||||
if (isLink) {
|
||||
symlinks.removeFirst();
|
||||
} else {
|
||||
files.removeFirst();
|
||||
}
|
||||
|
||||
q->addSubjob(job);
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::deleteNextFile()
|
||||
{
|
||||
// qDebug();
|
||||
|
||||
// if there is something else to delete
|
||||
// the loop is run using callbacks slotResult and rmFileResult
|
||||
if (!files.isEmpty() || !symlinks.isEmpty()) {
|
||||
// Take first file to delete out of list
|
||||
QList<QUrl>::iterator it = files.begin();
|
||||
const bool isLink = (it == files.end()); // No more files
|
||||
if (isLink) {
|
||||
it = symlinks.begin(); // Pick up a symlink to delete
|
||||
}
|
||||
m_currentURL = (*it);
|
||||
|
||||
// If local file, try do it directly
|
||||
if (m_currentURL.isLocalFile()) {
|
||||
// separate thread will do the work
|
||||
DeleteJobIOWorker *w = worker();
|
||||
auto rmfileFunc = [this, w, isLink]() {
|
||||
w->rmfile(m_currentURL, isLink);
|
||||
};
|
||||
QMetaObject::invokeMethod(w, rmfileFunc, Qt::QueuedConnection);
|
||||
} else {
|
||||
// if remote, use a job
|
||||
deleteFileUsingJob(m_currentURL, isLink);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
state = DELETEJOB_STATE_DELETING_DIRS;
|
||||
deleteNextDir();
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::rmdirResult(bool result)
|
||||
{
|
||||
if (result) {
|
||||
m_processedDirs++;
|
||||
dirs.removeLast();
|
||||
deleteNextDir();
|
||||
} else {
|
||||
// fallback
|
||||
deleteDirUsingJob(m_currentURL);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::deleteDirUsingJob(const QUrl &url)
|
||||
{
|
||||
Q_Q(DeleteJob);
|
||||
|
||||
// Call rmdir - works for KIO workers with canDeleteRecursive too,
|
||||
// CMD_DEL will trigger the recursive deletion in the worker.
|
||||
SimpleJob *job = KIO::rmdir(url);
|
||||
job->setParentJob(q);
|
||||
job->addMetaData(QStringLiteral("recurse"), QStringLiteral("true"));
|
||||
dirs.removeLast();
|
||||
q->addSubjob(job);
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::deleteNextDir()
|
||||
{
|
||||
Q_Q(DeleteJob);
|
||||
|
||||
if (!dirs.isEmpty()) { // some dirs to delete ?
|
||||
|
||||
// the loop is run using callbacks slotResult and rmdirResult
|
||||
// Take first dir to delete out of list - last ones first !
|
||||
QList<QUrl>::iterator it = --dirs.end();
|
||||
m_currentURL = (*it);
|
||||
// If local dir, try to rmdir it directly
|
||||
if (m_currentURL.isLocalFile()) {
|
||||
// delete it on separate worker thread
|
||||
DeleteJobIOWorker *w = worker();
|
||||
auto rmdirFunc = [this, w]() {
|
||||
w->rmdir(m_currentURL);
|
||||
};
|
||||
QMetaObject::invokeMethod(w, rmdirFunc, Qt::QueuedConnection);
|
||||
} else {
|
||||
deleteDirUsingJob(m_currentURL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-enable watching on the dirs that held the deleted files
|
||||
restoreDirWatch();
|
||||
|
||||
// Finished - tell the world
|
||||
if (!m_srcList.isEmpty()) {
|
||||
// qDebug() << "KDirNotify'ing FilesRemoved" << m_srcList;
|
||||
#ifdef WITH_QTDBUS
|
||||
org::kde::KDirNotify::emitFilesRemoved(m_srcList);
|
||||
#endif
|
||||
}
|
||||
if (m_reportTimer != nullptr) {
|
||||
m_reportTimer->stop();
|
||||
}
|
||||
// display final numbers
|
||||
q->setProcessedAmount(KJob::Directories, m_processedDirs);
|
||||
q->setProcessedAmount(KJob::Files, m_processedFiles);
|
||||
q->emitPercent(m_processedFiles + m_processedDirs, m_totalFilesDirs);
|
||||
|
||||
q->emitResult();
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::restoreDirWatch() const
|
||||
{
|
||||
const auto itEnd = m_parentDirs.constEnd();
|
||||
for (auto it = m_parentDirs.constBegin(); it != itEnd; ++it) {
|
||||
KDirWatch::self()->restartDirScan(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteJobPrivate::currentSourceStated(bool isDir, bool isLink)
|
||||
{
|
||||
Q_Q(DeleteJob);
|
||||
const QUrl url = (*m_currentStat);
|
||||
if (isDir && !isLink) {
|
||||
// Add toplevel dir in list of dirs
|
||||
dirs.append(url);
|
||||
if (url.isLocalFile()) {
|
||||
// We are about to delete this dir, no need to watch it
|
||||
// Maybe we should ask kdirwatch to remove all watches recursively?
|
||||
// But then there would be no feedback (things disappearing progressively) during huge deletions
|
||||
KDirWatch::self()->stopDirScan(url.adjusted(QUrl::StripTrailingSlash).toLocalFile());
|
||||
}
|
||||
if (!KProtocolManager::canDeleteRecursive(url)) {
|
||||
// qDebug() << url << "is a directory, let's list it";
|
||||
ListJob *newjob = KIO::listRecursive(url, KIO::HideProgressInfo);
|
||||
newjob->addMetaData(QStringLiteral("details"), QString::number(KIO::StatBasic));
|
||||
newjob->setUnrestricted(true); // No KIOSK restrictions
|
||||
QObject::connect(newjob, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) {
|
||||
slotEntries(job, list);
|
||||
});
|
||||
q->addSubjob(newjob);
|
||||
// Note that this listing job will happen in parallel with other stat jobs.
|
||||
}
|
||||
} else {
|
||||
if (isLink) {
|
||||
// qDebug() << "Target is a symlink";
|
||||
symlinks.append(url);
|
||||
} else {
|
||||
// qDebug() << "Target is a file";
|
||||
files.append(url);
|
||||
}
|
||||
}
|
||||
if (url.isLocalFile()) {
|
||||
const QString parentDir = url.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).path();
|
||||
m_parentDirs.insert(parentDir);
|
||||
}
|
||||
}
|
||||
|
||||
void DeleteJob::slotResult(KJob *job)
|
||||
{
|
||||
Q_D(DeleteJob);
|
||||
switch (d->state) {
|
||||
case DELETEJOB_STATE_STATING:
|
||||
removeSubjob(job);
|
||||
|
||||
// Was this a stat job or a list job? We do both in parallel.
|
||||
if (StatJob *statJob = qobject_cast<StatJob *>(job)) {
|
||||
// Was there an error while stating ?
|
||||
if (job->error()) {
|
||||
// Probably : doesn't exist
|
||||
Job::slotResult(job); // will set the error and emit result(this)
|
||||
d->restoreDirWatch();
|
||||
return;
|
||||
}
|
||||
|
||||
const UDSEntry &entry = statJob->statResult();
|
||||
// Is it a file or a dir ?
|
||||
const bool isLink = entry.isLink();
|
||||
const bool isDir = entry.isDir();
|
||||
d->currentSourceStated(isDir, isLink);
|
||||
|
||||
++d->m_currentStat;
|
||||
d->statNextSrc();
|
||||
} else {
|
||||
if (job->error()) {
|
||||
// Try deleting nonetheless, it may be empty (and non-listable)
|
||||
}
|
||||
if (!hasSubjobs()) {
|
||||
d->finishedStatPhase();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DELETEJOB_STATE_DELETING_FILES:
|
||||
// Propagate the subjob's metadata (a SimpleJob) to the real DeleteJob
|
||||
// FIXME: setMetaData() in the KIO API only allows access to outgoing metadata,
|
||||
// but we need to alter the incoming one
|
||||
d->m_incomingMetaData = dynamic_cast<KIO::Job *>(job)->metaData();
|
||||
|
||||
if (job->error()) {
|
||||
Job::slotResult(job); // will set the error and emit result(this)
|
||||
d->restoreDirWatch();
|
||||
return;
|
||||
}
|
||||
removeSubjob(job);
|
||||
Q_ASSERT(!hasSubjobs());
|
||||
d->m_processedFiles++;
|
||||
|
||||
d->deleteNextFile();
|
||||
break;
|
||||
case DELETEJOB_STATE_DELETING_DIRS:
|
||||
if (job->error()) {
|
||||
Job::slotResult(job); // will set the error and emit result(this)
|
||||
d->restoreDirWatch();
|
||||
return;
|
||||
}
|
||||
removeSubjob(job);
|
||||
Q_ASSERT(!hasSubjobs());
|
||||
d->m_processedDirs++;
|
||||
// emit processedAmount( this, KJob::Directories, d->m_processedDirs );
|
||||
// emitPercent( d->m_processedFiles + d->m_processedDirs, d->m_totalFilesDirs );
|
||||
|
||||
d->deleteNextDir();
|
||||
break;
|
||||
default:
|
||||
Q_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
DeleteJob *KIO::del(const QUrl &src, JobFlags flags)
|
||||
{
|
||||
QList<QUrl> srcList;
|
||||
srcList.append(src);
|
||||
DeleteJob *job = DeleteJobPrivate::newJob(srcList, flags);
|
||||
if (job->uiDelegateExtension()) {
|
||||
job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
DeleteJob *KIO::del(const QList<QUrl> &src, JobFlags flags)
|
||||
{
|
||||
DeleteJob *job = DeleteJobPrivate::newJob(src, flags);
|
||||
if (job->uiDelegateExtension()) {
|
||||
job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
#include "deletejob.moc"
|
||||
#include "moc_deletejob.cpp"
|
||||
@@ -0,0 +1,111 @@
|
||||
// -*- c++ -*-
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2006 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_DELETEJOB_H
|
||||
#define KIO_DELETEJOB_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include "global.h"
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include "job_base.h"
|
||||
|
||||
class QTimer;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class DeleteJobPrivate;
|
||||
/**
|
||||
* @class KIO::DeleteJob deletejob.h <KIO/DeleteJob>
|
||||
*
|
||||
* A more complex Job to delete files and directories.
|
||||
* Don't create the job directly, but use KIO::del() instead.
|
||||
*
|
||||
* @see KIO::del()
|
||||
*/
|
||||
class KIOCORE_EXPORT DeleteJob : public Job
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~DeleteJob() override;
|
||||
|
||||
/**
|
||||
* Returns the list of URLs.
|
||||
* @return the list of URLs.
|
||||
*/
|
||||
QList<QUrl> urls() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
/**
|
||||
* Emitted when the total number of files is known.
|
||||
* @param job the job that emitted this signal
|
||||
* @param files the total number of files
|
||||
*/
|
||||
void totalFiles(KJob *job, unsigned long files);
|
||||
/**
|
||||
* Emitted when the total number of directories is known.
|
||||
* @param job the job that emitted this signal
|
||||
* @param dirs the total number of directories
|
||||
*/
|
||||
void totalDirs(KJob *job, unsigned long dirs);
|
||||
|
||||
/**
|
||||
* Sends the number of processed files.
|
||||
* @param job the job that emitted this signal
|
||||
* @param files the number of processed files
|
||||
*/
|
||||
void processedFiles(KIO::Job *job, unsigned long files);
|
||||
/**
|
||||
* Sends the number of processed directories.
|
||||
* @param job the job that emitted this signal
|
||||
* @param dirs the number of processed dirs
|
||||
*/
|
||||
void processedDirs(KIO::Job *job, unsigned long dirs);
|
||||
|
||||
/**
|
||||
* Sends the URL of the file that is currently being deleted.
|
||||
* @param job the job that emitted this signal
|
||||
* @param file the URL of the file or directory that is being
|
||||
* deleted
|
||||
*/
|
||||
void deleting(KIO::Job *job, const QUrl &file);
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotResult(KJob *job) override;
|
||||
|
||||
protected:
|
||||
KIOCORE_NO_EXPORT explicit DeleteJob(DeleteJobPrivate &dd);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(DeleteJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Delete a file or directory.
|
||||
*
|
||||
* @param src file to delete
|
||||
* @param flags We support HideProgressInfo here
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT DeleteJob *del(const QUrl &src, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Deletes a list of files or directories.
|
||||
*
|
||||
* @param src the files to delete
|
||||
* @param flags We support HideProgressInfo here
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT DeleteJob *del(const QList<QUrl> &src, JobFlags flags = DefaultFlags);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,597 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2006-2013 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2009 Michael Pyne <michael.pyne@kdemail.net>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "desktopexecparser.h"
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
#include "kiofuse_interface.h"
|
||||
#endif
|
||||
|
||||
#include <KApplicationTrader>
|
||||
#include <KConfigGroup>
|
||||
#include <KDesktopFile>
|
||||
#include <KLocalizedString>
|
||||
#include <KMacroExpander>
|
||||
#include <KService>
|
||||
#include <KSharedConfig>
|
||||
#include <KShell>
|
||||
#include <kprotocolinfo.h> // KF6 TODO remove after moving hasSchemeHandler to OpenUrlJob
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusReply>
|
||||
#endif
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QStandardPaths>
|
||||
#include <QUrl>
|
||||
|
||||
#include <config-kiocore.h> // KDE_INSTALL_FULL_LIBEXECDIR_KF
|
||||
|
||||
#include "kiocoredebug.h"
|
||||
|
||||
class KRunMX1 : public KMacroExpanderBase
|
||||
{
|
||||
public:
|
||||
explicit KRunMX1(const KService &_service)
|
||||
: KMacroExpanderBase(QLatin1Char('%'))
|
||||
, service(_service)
|
||||
{
|
||||
}
|
||||
|
||||
bool hasUrls = false;
|
||||
bool hasSpec = false;
|
||||
bool hasError = false;
|
||||
|
||||
protected:
|
||||
int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
|
||||
|
||||
private:
|
||||
const KService &service;
|
||||
};
|
||||
|
||||
int KRunMX1::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
|
||||
{
|
||||
if (str.length() == pos + 1) {
|
||||
// Internally, the stack of KMacroExpanderBase is empty and thus it thinks everything is successfully parsed
|
||||
// This could be the case if the escape char "%" in this case, is at the end of the string
|
||||
// See BUG: 495606
|
||||
hasError = true;
|
||||
return 0;
|
||||
}
|
||||
uint option = str[pos + 1].unicode();
|
||||
switch (option) {
|
||||
case 'c':
|
||||
ret << service.name().replace(QLatin1Char('%'), QLatin1String("%%"));
|
||||
break;
|
||||
case 'k':
|
||||
ret << service.entryPath().replace(QLatin1Char('%'), QLatin1String("%%"));
|
||||
break;
|
||||
case 'i':
|
||||
ret << QStringLiteral("--icon") << service.icon().replace(QLatin1Char('%'), QLatin1String("%%"));
|
||||
break;
|
||||
case 'm':
|
||||
// ret << "-miniicon" << service.icon().replace( '%', "%%" );
|
||||
qCWarning(KIO_CORE) << "-miniicon isn't supported anymore (service" << service.name() << ')';
|
||||
break;
|
||||
case 'u':
|
||||
case 'U':
|
||||
hasUrls = true;
|
||||
Q_FALLTHROUGH();
|
||||
/* fallthrough */
|
||||
case 'f':
|
||||
case 'F':
|
||||
case 'n':
|
||||
case 'N':
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'v':
|
||||
hasSpec = true;
|
||||
Q_FALLTHROUGH();
|
||||
/* fallthrough */
|
||||
default:
|
||||
return -2; // subst with same and skip
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
class KRunMX2 : public KMacroExpanderBase
|
||||
{
|
||||
public:
|
||||
explicit KRunMX2(const QList<QUrl> &_urls)
|
||||
: KMacroExpanderBase(QLatin1Char('%'))
|
||||
, ignFile(false)
|
||||
, urls(_urls)
|
||||
{
|
||||
}
|
||||
|
||||
bool ignFile;
|
||||
|
||||
protected:
|
||||
int expandEscapedMacro(const QString &str, int pos, QStringList &ret) override;
|
||||
|
||||
private:
|
||||
void subst(int option, const QUrl &url, QStringList &ret);
|
||||
|
||||
const QList<QUrl> &urls;
|
||||
};
|
||||
|
||||
void KRunMX2::subst(int option, const QUrl &url, QStringList &ret)
|
||||
{
|
||||
switch (option) {
|
||||
case 'u':
|
||||
ret << ((url.isLocalFile() && url.fragment().isNull() && url.query().isNull()) ? QDir::toNativeSeparators(url.toLocalFile()) : url.toString());
|
||||
break;
|
||||
case 'd':
|
||||
ret << url.adjusted(QUrl::RemoveFilename).path();
|
||||
break;
|
||||
case 'f':
|
||||
ret << QDir::toNativeSeparators(url.toLocalFile());
|
||||
break;
|
||||
case 'n':
|
||||
ret << url.fileName();
|
||||
break;
|
||||
case 'v':
|
||||
if (url.isLocalFile() && QFile::exists(url.toLocalFile())) {
|
||||
ret << KDesktopFile(url.toLocalFile()).desktopGroup().readEntry("Dev");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
int KRunMX2::expandEscapedMacro(const QString &str, int pos, QStringList &ret)
|
||||
{
|
||||
uint option = str[pos + 1].unicode();
|
||||
switch (option) {
|
||||
case 'f':
|
||||
case 'u':
|
||||
case 'n':
|
||||
case 'd':
|
||||
case 'v':
|
||||
if (urls.isEmpty()) {
|
||||
if (!ignFile) {
|
||||
// qCDebug(KIO_CORE) << "No URLs supplied to single-URL service" << str;
|
||||
}
|
||||
} else if (urls.count() > 1) {
|
||||
qCWarning(KIO_CORE) << urls.count() << "URLs supplied to single-URL service" << str;
|
||||
} else {
|
||||
subst(option, urls.first(), ret);
|
||||
}
|
||||
break;
|
||||
case 'F':
|
||||
case 'U':
|
||||
case 'N':
|
||||
case 'D':
|
||||
option += 'a' - 'A';
|
||||
for (const QUrl &url : urls) {
|
||||
subst(option, url, ret);
|
||||
}
|
||||
break;
|
||||
case '%':
|
||||
ret = QStringList(QStringLiteral("%"));
|
||||
break;
|
||||
default:
|
||||
return -2; // subst with same and skip
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
|
||||
QStringList KIO::DesktopExecParser::supportedProtocols(const KService &service)
|
||||
{
|
||||
QStringList supportedProtocols = service.supportedProtocols();
|
||||
|
||||
KRunMX1 mx1(service);
|
||||
QString exec = service.exec();
|
||||
if (mx1.expandMacrosShellQuote(exec) && !mx1.hasUrls) {
|
||||
if (!supportedProtocols.isEmpty()) {
|
||||
qCWarning(KIO_CORE) << service.entryPath() << "contains supported protocols but doesn't use %u or %U in its Exec line! This is inconsistent.";
|
||||
}
|
||||
return QStringList();
|
||||
} else {
|
||||
if (supportedProtocols.isEmpty()) {
|
||||
// compat mode: assume KIO if not set and it's a KDE app (or a KDE service)
|
||||
const QStringList categories = service.property<QStringList>(QStringLiteral("Categories"));
|
||||
if (categories.contains(QLatin1String("KDE")) || !service.isApplication() || service.entryPath().isEmpty() /*temp service*/) {
|
||||
supportedProtocols.append(QStringLiteral("KIO"));
|
||||
} else { // if no KDE app, be a bit over-generic
|
||||
supportedProtocols.append(QStringLiteral("http"));
|
||||
supportedProtocols.append(QStringLiteral("https")); // #253294
|
||||
supportedProtocols.append(QStringLiteral("ftp"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// qCDebug(KIO_CORE) << "supportedProtocols:" << supportedProtocols;
|
||||
return supportedProtocols;
|
||||
}
|
||||
|
||||
bool KIO::DesktopExecParser::isProtocolInSupportedList(const QUrl &url, const QStringList &supportedProtocols)
|
||||
{
|
||||
return url.isLocalFile() //
|
||||
|| supportedProtocols.contains(QLatin1String("KIO")) //
|
||||
|| supportedProtocols.contains(url.scheme(), Qt::CaseInsensitive);
|
||||
}
|
||||
|
||||
// We have up to two sources of data, for protocols not handled by KIO workers (so called "helper") :
|
||||
// 1) the exec line of the .protocol file, if there's one
|
||||
// 2) the application associated with x-scheme-handler/<protocol> if there's one
|
||||
bool KIO::DesktopExecParser::hasSchemeHandler(const QUrl &url) // KF6 TODO move to OpenUrlJob
|
||||
{
|
||||
if (KProtocolInfo::isHelperProtocol(url)) {
|
||||
return true;
|
||||
}
|
||||
const KService::Ptr service = KApplicationTrader::preferredService(QLatin1String("x-scheme-handler/") + url.scheme());
|
||||
if (service) {
|
||||
qCDebug(KIO_CORE) << QLatin1String("preferred service for x-scheme-handler/") + url.scheme() << service->desktopEntryName();
|
||||
}
|
||||
return service;
|
||||
}
|
||||
|
||||
class KIO::DesktopExecParserPrivate
|
||||
{
|
||||
public:
|
||||
DesktopExecParserPrivate(const KService &_service, const QList<QUrl> &_urls)
|
||||
: service(_service)
|
||||
, urls(_urls)
|
||||
, tempFiles(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool isUrlSupported(const QUrl &url, const QStringList &supportedProtocols);
|
||||
|
||||
const KService &service;
|
||||
QList<QUrl> urls;
|
||||
bool tempFiles;
|
||||
QString suggestedFileName;
|
||||
QString m_errorString;
|
||||
};
|
||||
|
||||
KIO::DesktopExecParser::DesktopExecParser(const KService &service, const QList<QUrl> &urls)
|
||||
: d(new DesktopExecParserPrivate(service, urls))
|
||||
{
|
||||
}
|
||||
|
||||
KIO::DesktopExecParser::~DesktopExecParser()
|
||||
{
|
||||
}
|
||||
|
||||
void KIO::DesktopExecParser::setUrlsAreTempFiles(bool tempFiles)
|
||||
{
|
||||
d->tempFiles = tempFiles;
|
||||
}
|
||||
|
||||
void KIO::DesktopExecParser::setSuggestedFileName(const QString &suggestedFileName)
|
||||
{
|
||||
d->suggestedFileName = suggestedFileName;
|
||||
}
|
||||
|
||||
static const QString kioexecPath()
|
||||
{
|
||||
QString kioexec = QCoreApplication::applicationDirPath() + QLatin1String("/kioexec");
|
||||
if (!QFileInfo::exists(kioexec)) {
|
||||
kioexec = QStringLiteral(KDE_INSTALL_FULL_LIBEXECDIR_KF "/kioexec");
|
||||
}
|
||||
Q_ASSERT(QFileInfo::exists(kioexec));
|
||||
return kioexec;
|
||||
}
|
||||
|
||||
static QString findNonExecutableProgram(const QString &executable)
|
||||
{
|
||||
// Relative to current dir, or absolute path
|
||||
const QFileInfo fi(executable);
|
||||
if (fi.exists() && !fi.isExecutable()) {
|
||||
return executable;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
// This is a *very* simplified version of QStandardPaths::findExecutable
|
||||
const QStringList searchPaths = QString::fromLocal8Bit(qgetenv("PATH")).split(QDir::listSeparator(), Qt::SkipEmptyParts);
|
||||
for (const QString &searchPath : searchPaths) {
|
||||
const QString candidate = searchPath + QLatin1Char('/') + executable;
|
||||
const QFileInfo fileInfo(candidate);
|
||||
if (fileInfo.exists()) {
|
||||
if (fileInfo.isExecutable()) {
|
||||
qWarning() << "Internal program error. QStandardPaths::findExecutable couldn't find" << executable << "but our own logic found it at"
|
||||
<< candidate << ". Please report a bug at https://bugs.kde.org";
|
||||
} else {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return QString();
|
||||
}
|
||||
|
||||
bool KIO::DesktopExecParserPrivate::isUrlSupported(const QUrl &url, const QStringList &protocols)
|
||||
{
|
||||
if (KIO::DesktopExecParser::isProtocolInSupportedList(url, protocols)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// supportedProtocols() only checks whether the .desktop file has MimeType=x-scheme-handler/xxx
|
||||
// We also want to check whether the app has been set as default/associated in mimeapps.list
|
||||
const auto handlers = KApplicationTrader::queryByMimeType(QLatin1String("x-scheme-handler/") + url.scheme());
|
||||
for (const KService::Ptr &handler : handlers) {
|
||||
if (handler->desktopEntryName() == service.desktopEntryName()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
QStringList KIO::DesktopExecParser::resultingArguments() const
|
||||
{
|
||||
QString exec = d->service.exec();
|
||||
if (exec.isEmpty()) {
|
||||
d->m_errorString = i18n("No Exec field in %1", d->service.entryPath());
|
||||
qCWarning(KIO_CORE) << "No Exec field in" << d->service.entryPath();
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
// Extract the name of the binary to execute from the full Exec line, to see if it exists
|
||||
const QString binary = executablePath(exec);
|
||||
QString executableFullPath;
|
||||
if (!binary.isEmpty()) { // skip all this if the Exec line is a complex shell command
|
||||
if (QDir::isRelativePath(binary)) {
|
||||
// Resolve the executable to ensure that helpers in libexec are found.
|
||||
// Too bad for commands that need a shell - they must reside in $PATH.
|
||||
executableFullPath = QStandardPaths::findExecutable(binary);
|
||||
if (executableFullPath.isEmpty()) {
|
||||
executableFullPath = QFile::decodeName(KDE_INSTALL_FULL_LIBEXECDIR_KF "/") + binary;
|
||||
}
|
||||
} else {
|
||||
executableFullPath = binary;
|
||||
}
|
||||
|
||||
// Now check that the binary exists and has the executable flag
|
||||
if (!QFileInfo(executableFullPath).isExecutable()) {
|
||||
// Does it really not exist, or is it non-executable (on Unix)? (bug #415567)
|
||||
const QString nonExecutable = findNonExecutableProgram(binary);
|
||||
if (nonExecutable.isEmpty()) {
|
||||
d->m_errorString = i18n("Could not find the program '%1'", binary);
|
||||
} else {
|
||||
if (QDir::isRelativePath(binary)) {
|
||||
d->m_errorString = i18n("The program '%1' was found at '%2' but it is missing executable permissions.", binary, nonExecutable);
|
||||
} else {
|
||||
d->m_errorString = i18n("The program '%1' is missing executable permissions.", nonExecutable);
|
||||
}
|
||||
}
|
||||
return QStringList();
|
||||
}
|
||||
}
|
||||
|
||||
QStringList result;
|
||||
bool appHasTempFileOption;
|
||||
|
||||
KRunMX1 mx1(d->service);
|
||||
KRunMX2 mx2(d->urls);
|
||||
|
||||
if (!mx1.expandMacrosShellQuote(exec) || mx1.hasError) { // Error in shell syntax
|
||||
d->m_errorString = i18n("Syntax error in command %1 coming from %2", exec, d->service.entryPath());
|
||||
qCWarning(KIO_CORE) << "Syntax error in command" << d->service.exec() << ", service" << d->service.name();
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
// FIXME: the current way of invoking kioexec disables term and su use
|
||||
|
||||
// Check if we need "tempexec" (kioexec in fact)
|
||||
appHasTempFileOption = d->tempFiles && d->service.property<bool>(QStringLiteral("X-KDE-HasTempFileOption"));
|
||||
if (d->tempFiles && !appHasTempFileOption && d->urls.size()) {
|
||||
result << kioexecPath() << QStringLiteral("--tempfiles") << exec;
|
||||
if (!d->suggestedFileName.isEmpty()) {
|
||||
result << QStringLiteral("--suggestedfilename");
|
||||
result << d->suggestedFileName;
|
||||
}
|
||||
result += QUrl::toStringList(d->urls);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
// Return true for non-KIO desktop files with explicit X-KDE-Protocols list, like vlc, for the special case below
|
||||
auto isNonKIO = [this]() {
|
||||
const QStringList protocols = d->service.property<QStringList>(QStringLiteral("X-KDE-Protocols"));
|
||||
return !protocols.isEmpty() && !protocols.contains(QLatin1String("KIO"));
|
||||
};
|
||||
|
||||
// Check if we need kioexec, or KIOFuse
|
||||
bool useKioexec = false;
|
||||
|
||||
org::kde::KIOFuse::VFS kiofuse_iface(QStringLiteral("org.kde.KIOFuse"), QStringLiteral("/org/kde/KIOFuse"), QDBusConnection::sessionBus());
|
||||
struct MountRequest {
|
||||
QDBusPendingReply<QString> reply;
|
||||
int urlIndex;
|
||||
};
|
||||
QList<MountRequest> requests;
|
||||
requests.reserve(d->urls.count());
|
||||
|
||||
const QStringList appSupportedProtocols = supportedProtocols(d->service);
|
||||
for (int i = 0; i < d->urls.count(); ++i) {
|
||||
const QUrl url = d->urls.at(i);
|
||||
const bool supported = mx1.hasUrls ? d->isUrlSupported(url, appSupportedProtocols) : url.isLocalFile();
|
||||
if (!supported) {
|
||||
// If FUSE fails, and there is no scheme handler, we'll have to fallback to kioexec
|
||||
useKioexec = true;
|
||||
}
|
||||
|
||||
// NOTE: Some non-KIO apps may support the URLs (e.g. VLC supports smb://)
|
||||
// but will not have the password if they are not in the URL itself.
|
||||
// Hence convert URL to KIOFuse equivalent in case there is a password.
|
||||
// @see https://pointieststick.com/2018/01/17/videos-on-samba-shares/
|
||||
// @see https://bugs.kde.org/show_bug.cgi?id=330192
|
||||
if (!supported || (!url.userName().isEmpty() && url.password().isEmpty() && isNonKIO())) {
|
||||
requests.push_back({kiofuse_iface.mountUrl(url.toString()), i});
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &request : requests) {
|
||||
request.reply.waitForFinished();
|
||||
}
|
||||
const bool fuseError = std::any_of(requests.cbegin(), requests.cend(), [](const MountRequest &request) {
|
||||
return request.reply.isError();
|
||||
});
|
||||
|
||||
if (fuseError && useKioexec) {
|
||||
// We need to run the app through kioexec
|
||||
result << kioexecPath();
|
||||
if (d->tempFiles) {
|
||||
result << QStringLiteral("--tempfiles");
|
||||
}
|
||||
if (!d->suggestedFileName.isEmpty()) {
|
||||
result << QStringLiteral("--suggestedfilename");
|
||||
result << d->suggestedFileName;
|
||||
}
|
||||
result << exec;
|
||||
result += QUrl::toStringList(d->urls);
|
||||
return result;
|
||||
}
|
||||
|
||||
// At this point we know we're not using kioexec, so feel free to replace
|
||||
// KIO URLs with their KIOFuse local path.
|
||||
for (const auto &request : std::as_const(requests)) {
|
||||
if (!request.reply.isError()) {
|
||||
d->urls[request.urlIndex] = QUrl::fromLocalFile(request.reply.value());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (appHasTempFileOption) {
|
||||
exec += QLatin1String(" --tempfile");
|
||||
}
|
||||
|
||||
// Did the user forget to append something like '%f'?
|
||||
// If so, then assume that '%f' is the right choice => the application
|
||||
// accepts only local files.
|
||||
if (!mx1.hasSpec) {
|
||||
exec += QLatin1String(" %f");
|
||||
mx2.ignFile = true;
|
||||
}
|
||||
|
||||
mx2.expandMacrosShellQuote(exec); // syntax was already checked, so don't check return value
|
||||
|
||||
/*
|
||||
1 = need_shell, 2 = terminal, 4 = su
|
||||
|
||||
0 << split(cmd)
|
||||
1 << "sh" << "-c" << cmd
|
||||
2 << split(term) << "-e" << split(cmd)
|
||||
3 << split(term) << "-e" << "sh" << "-c" << cmd
|
||||
|
||||
4 << "kdesu" << "-u" << user << "-c" << cmd
|
||||
5 << "kdesu" << "-u" << user << "-c" << ("sh -c " + quote(cmd))
|
||||
6 << split(term) << "-e" << "su" << user << "-c" << cmd
|
||||
7 << split(term) << "-e" << "su" << user << "-c" << ("sh -c " + quote(cmd))
|
||||
|
||||
"sh -c" is needed in the "su" case, too, as su uses the user's login shell, not sh.
|
||||
this could be optimized with the -s switch of some su versions (e.g., debian linux).
|
||||
*/
|
||||
|
||||
if (d->service.terminal()) {
|
||||
KConfigGroup cg(KSharedConfig::openConfig(), QStringLiteral("General"));
|
||||
QString terminal = cg.readPathEntry("TerminalApplication", QStringLiteral("konsole"));
|
||||
|
||||
const bool isKonsole = (terminal == QLatin1String("konsole"));
|
||||
QStringList terminalParts = KShell::splitArgs(terminal);
|
||||
QString terminalPath;
|
||||
if (!terminalParts.isEmpty()) {
|
||||
terminalPath = QStandardPaths::findExecutable(terminalParts.at(0));
|
||||
}
|
||||
|
||||
if (terminalPath.isEmpty()) {
|
||||
d->m_errorString = i18n("Terminal %1 not found while trying to run %2", terminal, d->service.entryPath());
|
||||
qCWarning(KIO_CORE) << "Terminal" << terminal << "not found, service" << d->service.name();
|
||||
return QStringList();
|
||||
}
|
||||
terminalParts[0] = terminalPath;
|
||||
terminal = KShell::joinArgs(terminalParts);
|
||||
if (isKonsole) {
|
||||
if (!d->service.workingDirectory().isEmpty()) {
|
||||
terminal += QLatin1String(" --workdir ") + KShell::quoteArg(d->service.workingDirectory());
|
||||
}
|
||||
terminal += QLatin1String(" -qwindowtitle '%c'");
|
||||
if (!d->service.icon().isEmpty()) {
|
||||
terminal += QLatin1String(" -qwindowicon ") + KShell::quoteArg(d->service.icon().replace(QLatin1Char('%'), QLatin1String("%%")));
|
||||
}
|
||||
}
|
||||
terminal += QLatin1Char(' ') + d->service.terminalOptions();
|
||||
if (!mx1.expandMacrosShellQuote(terminal) || mx1.hasError) {
|
||||
d->m_errorString = i18n("Syntax error in command %1 while trying to run %2", terminal, d->service.entryPath());
|
||||
qCWarning(KIO_CORE) << "Syntax error in command" << terminal << ", service" << d->service.name();
|
||||
return QStringList();
|
||||
}
|
||||
mx2.expandMacrosShellQuote(terminal);
|
||||
result = KShell::splitArgs(terminal); // assuming that the term spec never needs a shell!
|
||||
result << QStringLiteral("-e");
|
||||
}
|
||||
|
||||
KShell::Errors err;
|
||||
QStringList execlist = KShell::splitArgs(exec, KShell::AbortOnMeta | KShell::TildeExpand, &err);
|
||||
if (!executableFullPath.isEmpty()) {
|
||||
execlist[0] = executableFullPath;
|
||||
}
|
||||
|
||||
if (d->service.substituteUid()) {
|
||||
if (d->service.terminal()) {
|
||||
result << QStringLiteral("su");
|
||||
} else {
|
||||
QString kdesu = QFile::decodeName(KDE_INSTALL_FULL_LIBEXECDIR_KF "/kdesu");
|
||||
if (!QFile::exists(kdesu)) {
|
||||
kdesu = QStandardPaths::findExecutable(QStringLiteral("kdesu"));
|
||||
}
|
||||
if (!QFile::exists(kdesu)) {
|
||||
// Insert kdesu as string so we show a nice warning: 'Could not launch kdesu'
|
||||
result << QStringLiteral("kdesu");
|
||||
return result;
|
||||
} else {
|
||||
result << kdesu << QStringLiteral("-u");
|
||||
}
|
||||
}
|
||||
|
||||
result << d->service.username() << QStringLiteral("-c");
|
||||
if (err == KShell::FoundMeta) {
|
||||
exec = QLatin1String("/bin/sh -c ") + KShell::quoteArg(exec);
|
||||
} else {
|
||||
exec = KShell::joinArgs(execlist);
|
||||
}
|
||||
result << exec;
|
||||
} else {
|
||||
if (err == KShell::FoundMeta) {
|
||||
result << QStringLiteral("/bin/sh") << QStringLiteral("-c") << exec;
|
||||
} else {
|
||||
result += execlist;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString KIO::DesktopExecParser::errorMessage() const
|
||||
{
|
||||
return d->m_errorString;
|
||||
}
|
||||
|
||||
// static
|
||||
QString KIO::DesktopExecParser::executableName(const QString &execLine)
|
||||
{
|
||||
const QString bin = executablePath(execLine);
|
||||
return bin.mid(bin.lastIndexOf(QLatin1Char('/')) + 1);
|
||||
}
|
||||
|
||||
// static
|
||||
QString KIO::DesktopExecParser::executablePath(const QString &execLine)
|
||||
{
|
||||
// Remove parameters and/or trailing spaces.
|
||||
const QStringList args = KShell::splitArgs(execLine, KShell::AbortOnMeta | KShell::TildeExpand);
|
||||
auto it = std::find_if(args.cbegin(), args.cend(), [](const QString &arg) {
|
||||
return !arg.contains(QLatin1Char('='));
|
||||
});
|
||||
return it != args.cend() ? *it : QString{};
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2013 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KIO_DESKTOPEXECPARSER_H
|
||||
#define KIO_DESKTOPEXECPARSER_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QScopedPointer>
|
||||
#include <QStringList>
|
||||
|
||||
class QUrl;
|
||||
class KService;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class DesktopExecParserPrivate;
|
||||
|
||||
/**
|
||||
* @class KIO::DesktopExecParser desktopexecparser.h <KIO/DesktopExecParser>
|
||||
*
|
||||
* Parses the Exec= line from a .desktop file,
|
||||
* and process all the '\%' placeholders, e.g.\ handling URLs vs local files.
|
||||
*
|
||||
* The processing actually happens when calling resultingArguments(), after
|
||||
* setting everything up.
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
class KIOCORE_EXPORT DesktopExecParser
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a parser for a desktop file Exec line.
|
||||
*
|
||||
* @param service the service to extract information from.
|
||||
* The KService instance must remain alive as long as the parser is alive.
|
||||
* @param urls The urls the service should open.
|
||||
*/
|
||||
DesktopExecParser(const KService &service, const QList<QUrl> &urls);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~DesktopExecParser();
|
||||
|
||||
/**
|
||||
* If @p tempFiles is set to true and the urls given to the constructor are local files,
|
||||
* they will be deleted when the application exits.
|
||||
*/
|
||||
void setUrlsAreTempFiles(bool tempFiles);
|
||||
|
||||
/**
|
||||
* Sets the file name to use in the case of downloading the file to a tempfile
|
||||
* in order to give to a non-url-aware application. Some apps rely on the extension
|
||||
* to determine the MIME type of the file. Usually the file name comes from the URL,
|
||||
* but in the case of the HTTP Content-Disposition header, we need to override the
|
||||
* file name.
|
||||
*/
|
||||
void setSuggestedFileName(const QString &suggestedFileName);
|
||||
|
||||
/**
|
||||
* @return a list of arguments suitable for QProcess.
|
||||
* Returns an empty list on error, check errorMessage() for details.
|
||||
*/
|
||||
QStringList resultingArguments() const;
|
||||
|
||||
/**
|
||||
* @return an error message for when resultingArguments() returns an empty list
|
||||
* @since 5.71
|
||||
*/
|
||||
QString errorMessage() const;
|
||||
|
||||
/**
|
||||
* Returns the list of protocols which the application supports.
|
||||
* This can be a list of actual protocol names, or just "KIO" for KIO-based apps.
|
||||
*/
|
||||
static QStringList supportedProtocols(const KService &service);
|
||||
|
||||
/**
|
||||
* Returns true if @p protocol is in the list of protocols returned by supportedProtocols().
|
||||
* The only reason for this method is the special handling of "KIO".
|
||||
*/
|
||||
static bool isProtocolInSupportedList(const QUrl &url, const QStringList &supportedProtocols);
|
||||
|
||||
/**
|
||||
* Returns true if @p protocol should be opened by a "handler" application, i.e.\ an application
|
||||
* associated to _all_ URLs using this protocol (a.k.a. scheme).
|
||||
*/
|
||||
static bool hasSchemeHandler(const QUrl &url); // KF6 TODO move to OpenUrlJob
|
||||
|
||||
/**
|
||||
* Given a full command line (e.g.\ the Exec= line from a .desktop file),
|
||||
* extract the name of the executable being run (removing the path, if specified).
|
||||
* @param execLine the full command line
|
||||
* @return the name of the executable to run, example: "ls"
|
||||
*/
|
||||
static QString executableName(const QString &execLine);
|
||||
|
||||
/**
|
||||
* Given a full command line (e.g.\ the Exec= line from a .desktop file),
|
||||
* extract the name of the executable being run, including its full path, if specified.
|
||||
* @param execLine the full command line
|
||||
* @return the name of the executable to run, example: "/bin/ls"
|
||||
*/
|
||||
static QString executablePath(const QString &execLine);
|
||||
|
||||
private:
|
||||
QScopedPointer<DesktopExecParserPrivate> d;
|
||||
};
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000, 2006 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "directorysizejob.h"
|
||||
#include "global.h"
|
||||
#include "listjob.h"
|
||||
#include <QDebug>
|
||||
#include <QTimer>
|
||||
#include <kio/jobuidelegatefactory.h>
|
||||
|
||||
#include "job_p.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class DirectorySizeJobPrivate : public KIO::JobPrivate
|
||||
{
|
||||
public:
|
||||
DirectorySizeJobPrivate()
|
||||
: m_totalSize(0L)
|
||||
, m_totalFiles(0L)
|
||||
, m_totalSubdirs(0L)
|
||||
, m_currentItem(0)
|
||||
{
|
||||
}
|
||||
explicit DirectorySizeJobPrivate(const KFileItemList &lstItems)
|
||||
: m_totalSize(0L)
|
||||
, m_totalFiles(0L)
|
||||
, m_totalSubdirs(0L)
|
||||
, m_lstItems(lstItems)
|
||||
, m_currentItem(0)
|
||||
{
|
||||
}
|
||||
KIO::filesize_t m_totalSize;
|
||||
KIO::filesize_t m_totalFiles;
|
||||
KIO::filesize_t m_totalSubdirs;
|
||||
KFileItemList m_lstItems;
|
||||
int m_currentItem;
|
||||
QHash<long, std::set<long>> m_visitedInodes; // device -> set of inodes
|
||||
|
||||
void startNextJob(const QUrl &url);
|
||||
void slotEntries(KIO::Job *, const KIO::UDSEntryList &);
|
||||
void processNextItem();
|
||||
|
||||
Q_DECLARE_PUBLIC(DirectorySizeJob)
|
||||
|
||||
static inline DirectorySizeJob *newJob(const QUrl &directory)
|
||||
{
|
||||
DirectorySizeJobPrivate *d = new DirectorySizeJobPrivate;
|
||||
DirectorySizeJob *job = new DirectorySizeJob(*d);
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
d->startNextJob(directory);
|
||||
return job;
|
||||
}
|
||||
|
||||
static inline DirectorySizeJob *newJob(const KFileItemList &lstItems)
|
||||
{
|
||||
DirectorySizeJobPrivate *d = new DirectorySizeJobPrivate(lstItems);
|
||||
DirectorySizeJob *job = new DirectorySizeJob(*d);
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
QTimer::singleShot(0, job, [d]() {
|
||||
d->processNextItem();
|
||||
});
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
DirectorySizeJob::DirectorySizeJob(DirectorySizeJobPrivate &dd)
|
||||
: KIO::Job(dd)
|
||||
{
|
||||
}
|
||||
|
||||
DirectorySizeJob::~DirectorySizeJob()
|
||||
{
|
||||
}
|
||||
|
||||
KIO::filesize_t DirectorySizeJob::totalSize() const
|
||||
{
|
||||
return d_func()->m_totalSize;
|
||||
}
|
||||
|
||||
KIO::filesize_t DirectorySizeJob::totalFiles() const
|
||||
{
|
||||
return d_func()->m_totalFiles;
|
||||
}
|
||||
|
||||
KIO::filesize_t DirectorySizeJob::totalSubdirs() const
|
||||
{
|
||||
return d_func()->m_totalSubdirs;
|
||||
}
|
||||
|
||||
void DirectorySizeJobPrivate::processNextItem()
|
||||
{
|
||||
Q_Q(DirectorySizeJob);
|
||||
while (m_currentItem < m_lstItems.count()) {
|
||||
const KFileItem item = m_lstItems[m_currentItem++];
|
||||
// qDebug() << item;
|
||||
if (!item.isLink()) {
|
||||
if (item.isDir()) {
|
||||
// qDebug() << "dir -> listing";
|
||||
const auto localPath = item.localPath();
|
||||
if (!localPath.isNull()) {
|
||||
startNextJob(QUrl::fromLocalFile(localPath));
|
||||
} else {
|
||||
startNextJob(item.targetUrl());
|
||||
}
|
||||
return; // we'll come back later, when this one's finished
|
||||
} else {
|
||||
m_totalSize += item.size();
|
||||
m_totalFiles++;
|
||||
// qDebug() << "file -> " << m_totalSize;
|
||||
}
|
||||
} else {
|
||||
m_totalFiles++;
|
||||
}
|
||||
}
|
||||
// qDebug() << "finished";
|
||||
q->emitResult();
|
||||
}
|
||||
|
||||
void DirectorySizeJobPrivate::startNextJob(const QUrl &url)
|
||||
{
|
||||
Q_Q(DirectorySizeJob);
|
||||
// qDebug() << url;
|
||||
KIO::ListJob *listJob = KIO::listRecursive(url, KIO::HideProgressInfo);
|
||||
listJob->addMetaData(QStringLiteral("details"), QString::number(KIO::StatBasic | KIO::StatResolveSymlink | KIO::StatInode));
|
||||
q->connect(listJob, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &list) {
|
||||
slotEntries(job, list);
|
||||
});
|
||||
q->addSubjob(listJob);
|
||||
}
|
||||
|
||||
void DirectorySizeJobPrivate::slotEntries(KIO::Job *, const KIO::UDSEntryList &list)
|
||||
{
|
||||
KIO::UDSEntryList::ConstIterator it = list.begin();
|
||||
const KIO::UDSEntryList::ConstIterator end = list.end();
|
||||
for (; it != end; ++it) {
|
||||
const KIO::UDSEntry &entry = *it;
|
||||
|
||||
const long device = entry.numberValue(KIO::UDSEntry::UDS_DEVICE_ID, 0);
|
||||
if (device && !entry.isLink()) {
|
||||
// Hard-link detection (#67939)
|
||||
const long inode = entry.numberValue(KIO::UDSEntry::UDS_INODE, 0);
|
||||
std::set<long> &visitedInodes = m_visitedInodes[device]; // find or insert
|
||||
const auto [it, isNewInode] = visitedInodes.insert(inode);
|
||||
if (!isNewInode) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
const KIO::filesize_t size = entry.numberValue(KIO::UDSEntry::UDS_SIZE, 0);
|
||||
const QString name = entry.stringValue(KIO::UDSEntry::UDS_NAME);
|
||||
if (name == QLatin1Char('.')) {
|
||||
m_totalSize += size;
|
||||
// qDebug() << "'.': added" << size << "->" << m_totalSize;
|
||||
} else if (name != QLatin1String("..")) {
|
||||
if (!entry.isLink()) {
|
||||
m_totalSize += size;
|
||||
}
|
||||
if (!entry.isDir()) {
|
||||
m_totalFiles++;
|
||||
} else {
|
||||
m_totalSubdirs++;
|
||||
}
|
||||
// qDebug() << name << ":" << size << "->" << m_totalSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DirectorySizeJob::slotResult(KJob *job)
|
||||
{
|
||||
Q_D(DirectorySizeJob);
|
||||
// qDebug() << d->m_totalSize;
|
||||
removeSubjob(job);
|
||||
if (d->m_currentItem < d->m_lstItems.count()) {
|
||||
d->processNextItem();
|
||||
} else {
|
||||
if (job->error()) {
|
||||
setError(job->error());
|
||||
setErrorText(job->errorText());
|
||||
}
|
||||
emitResult();
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
DirectorySizeJob *KIO::directorySize(const QUrl &directory)
|
||||
{
|
||||
return DirectorySizeJobPrivate::newJob(directory); // useless - but consistent with other jobs
|
||||
}
|
||||
|
||||
// static
|
||||
DirectorySizeJob *KIO::directorySize(const KFileItemList &lstItems)
|
||||
{
|
||||
return DirectorySizeJobPrivate::newJob(lstItems);
|
||||
}
|
||||
|
||||
#include "moc_directorysizejob.cpp"
|
||||
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000, 2006 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef DIRECTORYSIZEJOB_H
|
||||
#define DIRECTORYSIZEJOB_H
|
||||
|
||||
#include "job_base.h"
|
||||
#include "kiocore_export.h"
|
||||
#include <kfileitem.h>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class DirectorySizeJobPrivate;
|
||||
/**
|
||||
* @class KIO::DirectorySizeJob directorysizejob.h <KIO/DirectorySizeJob>
|
||||
*
|
||||
* Computes a directory size (similar to "du", but doesn't give the same results
|
||||
* since we simply sum up the dir and file sizes, whereas du speaks disk blocks)
|
||||
*
|
||||
* Usage: see KIO::directorySize.
|
||||
*/
|
||||
class KIOCORE_EXPORT DirectorySizeJob : public KIO::Job
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~DirectorySizeJob() override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @return the size we found
|
||||
*/
|
||||
KIO::filesize_t totalSize() const;
|
||||
|
||||
/**
|
||||
* @return the total number of files (counting symlinks to files, sockets
|
||||
* and character devices as files) in this directory and all sub-directories
|
||||
*/
|
||||
KIO::filesize_t totalFiles() const;
|
||||
|
||||
/**
|
||||
* @return the total number of sub-directories found (not including the
|
||||
* directory the search started from and treating symlinks to directories
|
||||
* as directories)
|
||||
*/
|
||||
KIO::filesize_t totalSubdirs() const;
|
||||
|
||||
protected Q_SLOTS:
|
||||
void slotResult(KJob *job) override;
|
||||
|
||||
protected:
|
||||
KIOCORE_NO_EXPORT explicit DirectorySizeJob(DirectorySizeJobPrivate &dd);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(DirectorySizeJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Computes a directory size (by doing a recursive listing).
|
||||
* Connect to the result signal (this is the preferred solution to avoid blocking the GUI),
|
||||
* or use exec() for a synchronous (blocking) calculation.
|
||||
*
|
||||
* This one lists a single directory.
|
||||
*/
|
||||
KIOCORE_EXPORT DirectorySizeJob *directorySize(const QUrl &directory);
|
||||
|
||||
/**
|
||||
* Computes a directory size (by doing a recursive listing).
|
||||
* Connect to the result signal (this is the preferred solution to avoid blocking the GUI),
|
||||
* or use exec() for a synchronous (blocking) calculation.
|
||||
*
|
||||
* This one lists the items from @p lstItems.
|
||||
* The reason we asks for items instead of just urls, is so that
|
||||
* we directly know if the item is a file or a directory,
|
||||
* and in case of a file, we already have its size.
|
||||
*/
|
||||
KIOCORE_EXPORT DirectorySizeJob *directorySize(const KFileItemList &lstItems);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2014 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "emptytrashjob.h"
|
||||
#include "job.h"
|
||||
#include "job_p.h"
|
||||
// #include <KNotification>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
class KIO::EmptyTrashJobPrivate : public SimpleJobPrivate
|
||||
{
|
||||
public:
|
||||
EmptyTrashJobPrivate(int command, const QByteArray &packedArgs)
|
||||
: SimpleJobPrivate(QUrl(QStringLiteral("trash:/")), command, packedArgs)
|
||||
{
|
||||
}
|
||||
|
||||
Q_DECLARE_PUBLIC(EmptyTrashJob)
|
||||
|
||||
static inline EmptyTrashJob *newJob(int command, const QByteArray &packedArgs)
|
||||
{
|
||||
EmptyTrashJob *job = new EmptyTrashJob(*new EmptyTrashJobPrivate(command, packedArgs));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
EmptyTrashJob::EmptyTrashJob(EmptyTrashJobPrivate &dd)
|
||||
: SimpleJob(dd)
|
||||
{
|
||||
}
|
||||
|
||||
EmptyTrashJob::~EmptyTrashJob()
|
||||
{
|
||||
}
|
||||
|
||||
// TODO KF6: remove this
|
||||
void EmptyTrashJob::slotFinished()
|
||||
{
|
||||
SimpleJob::slotFinished();
|
||||
}
|
||||
|
||||
KIO::EmptyTrashJob *KIO::emptyTrash()
|
||||
{
|
||||
KIO_ARGS << int(1);
|
||||
return EmptyTrashJobPrivate::newJob(CMD_SPECIAL, packedArgs);
|
||||
}
|
||||
|
||||
#include "moc_emptytrashjob.cpp"
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2014 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef EMPTYTRASHJOB_H
|
||||
#define EMPTYTRASHJOB_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include "simplejob.h"
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class EmptyTrashJobPrivate;
|
||||
/**
|
||||
* @class KIO::EmptyTrashJob emptytrashjob.h <KIO/EmptyTrashJob>
|
||||
*
|
||||
* A KIO job for emptying the trash
|
||||
* @see KIO::trash()
|
||||
* @see KIO::restoreFromTrash()
|
||||
* @since 5.2
|
||||
*/
|
||||
class KIOCORE_EXPORT EmptyTrashJob : public SimpleJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~EmptyTrashJob() override;
|
||||
|
||||
protected:
|
||||
void slotFinished() override;
|
||||
|
||||
private:
|
||||
KIOCORE_NO_EXPORT explicit EmptyTrashJob(EmptyTrashJobPrivate &dd);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(EmptyTrashJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Empties the trash.
|
||||
*
|
||||
* @return A pointer to the job handling the operation.
|
||||
* @since 5.2
|
||||
*/
|
||||
KIOCORE_EXPORT EmptyTrashJob *emptyTrash();
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
This file is part of KIO.
|
||||
SPDX-FileCopyrightText: 2001 Malte Starostik <malte@kde.org>
|
||||
SPDX-FileCopyrightText: 2016 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
#include "faviconscache_p.h"
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <QMutex>
|
||||
|
||||
#include <QCache>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QSet>
|
||||
#include <QStandardPaths>
|
||||
#include <QUrl>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
static QString portForUrl(const QUrl &url)
|
||||
{
|
||||
if (url.port() > 0) {
|
||||
return QLatin1Char('_') + QString::number(url.port());
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
static QString simplifyUrl(const QUrl &url)
|
||||
{
|
||||
// splat any = in the URL so it can be safely used as a config key
|
||||
QString result = url.host() + portForUrl(url) + url.path();
|
||||
result.replace(QLatin1Char('='), QLatin1Char('_'));
|
||||
while (result.endsWith(QLatin1Char('/'))) {
|
||||
result.chop(1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString iconNameFromUrl(const QUrl &iconUrl)
|
||||
{
|
||||
if (iconUrl.path() == QLatin1String("/favicon.ico")) {
|
||||
return iconUrl.host() + portForUrl(iconUrl);
|
||||
}
|
||||
|
||||
QString result = simplifyUrl(iconUrl);
|
||||
// splat / so it can be safely used as a file name
|
||||
result.replace(QLatin1Char('/'), QLatin1Char('_'));
|
||||
|
||||
const int dotExtLen = 4;
|
||||
const QStringView ext = QStringView(result).right(dotExtLen);
|
||||
if (ext == QLatin1String(".ico") || ext == QLatin1String(".png") || ext == QLatin1String(".xpm")) {
|
||||
result.chop(dotExtLen);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
////
|
||||
|
||||
class KIO::FavIconsCachePrivate
|
||||
{
|
||||
public:
|
||||
FavIconsCachePrivate()
|
||||
: cacheDir(QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + QStringLiteral("/favicons/"))
|
||||
, config(cacheDir + QStringLiteral("index"))
|
||||
{
|
||||
}
|
||||
|
||||
QString cachedIconUrlForUrl(const QUrl &url);
|
||||
|
||||
const QString cacheDir;
|
||||
QMutex mutex; // protects all the member variables below
|
||||
KConfig config;
|
||||
QCache<QString, QString> faviconsCache;
|
||||
QSet<QUrl> failedDownloads;
|
||||
};
|
||||
|
||||
QString FavIconsCachePrivate::cachedIconUrlForUrl(const QUrl &url)
|
||||
{
|
||||
Q_ASSERT(!mutex.tryLock());
|
||||
const QString simplifiedUrl = simplifyUrl(url);
|
||||
QString *cachedIconUrl = faviconsCache[simplifiedUrl];
|
||||
return (cachedIconUrl ? *cachedIconUrl : config.group(QString()).readEntry(simplifiedUrl, QString()));
|
||||
}
|
||||
|
||||
FavIconsCache *FavIconsCache::instance()
|
||||
{
|
||||
static FavIconsCache s_cache; // remind me why we need Q_GLOBAL_STATIC, again, now that C++11 guarantees thread safety?
|
||||
return &s_cache;
|
||||
}
|
||||
|
||||
FavIconsCache::FavIconsCache()
|
||||
: d(new FavIconsCachePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
FavIconsCache::~FavIconsCache() = default;
|
||||
|
||||
QString FavIconsCache::iconForUrl(const QUrl &url)
|
||||
{
|
||||
if (url.host().isEmpty()) {
|
||||
return QString();
|
||||
}
|
||||
QMutexLocker locker(&d->mutex);
|
||||
const QString cachedIconUrl = d->cachedIconUrlForUrl(url);
|
||||
QString icon = d->cacheDir;
|
||||
if (!cachedIconUrl.isEmpty()) {
|
||||
icon += iconNameFromUrl(QUrl(cachedIconUrl));
|
||||
} else {
|
||||
icon += url.host();
|
||||
}
|
||||
icon += QStringLiteral(".png");
|
||||
if (QFile::exists(icon)) {
|
||||
return icon;
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
QUrl FavIconsCache::iconUrlForUrl(const QUrl &url)
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
const QString cachedIconUrl = d->cachedIconUrlForUrl(url);
|
||||
if (!cachedIconUrl.isEmpty()) {
|
||||
return QUrl(cachedIconUrl);
|
||||
} else {
|
||||
QUrl iconUrl;
|
||||
iconUrl.setScheme(url.scheme());
|
||||
iconUrl.setHost(url.host());
|
||||
iconUrl.setPort(url.port());
|
||||
iconUrl.setPath(QStringLiteral("/favicon.ico"));
|
||||
iconUrl.setUserInfo(url.userInfo());
|
||||
return iconUrl;
|
||||
}
|
||||
}
|
||||
|
||||
void FavIconsCache::setIconForUrl(const QUrl &url, const QUrl &iconUrl)
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
const QString simplifiedUrl = simplifyUrl(url);
|
||||
const QString iconUrlStr = iconUrl.url();
|
||||
d->faviconsCache.insert(simplifiedUrl, new QString(iconUrlStr));
|
||||
d->config.group(QString()).writeEntry(simplifiedUrl, iconUrlStr);
|
||||
d->config.sync();
|
||||
}
|
||||
|
||||
QString FavIconsCache::cachePathForIconUrl(const QUrl &iconUrl) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
const QString iconName = iconNameFromUrl(iconUrl);
|
||||
return d->cacheDir + iconName + QLatin1String(".png");
|
||||
}
|
||||
|
||||
void FavIconsCache::ensureCacheExists()
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
QDir().mkpath(d->cacheDir);
|
||||
}
|
||||
|
||||
void FavIconsCache::addFailedDownload(const QUrl &url)
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
d->failedDownloads.insert(url);
|
||||
}
|
||||
|
||||
void FavIconsCache::removeFailedDownload(const QUrl &url)
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
d->failedDownloads.remove(url);
|
||||
}
|
||||
|
||||
bool FavIconsCache::isFailedDownload(const QUrl &url) const
|
||||
{
|
||||
QMutexLocker locker(&d->mutex);
|
||||
return d->failedDownloads.contains(url);
|
||||
}
|
||||
|
||||
#include "moc_faviconscache_p.cpp"
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
This file is part of KIO.
|
||||
SPDX-FileCopyrightText: 2016 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
|
||||
*/
|
||||
|
||||
#ifndef FAVICONSCACHE_P_H
|
||||
#define FAVICONSCACHE_P_H
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <kiocore_export.h>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class FavIconsCachePrivate;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Singleton handling the cache (memory + disk) for favicons.
|
||||
* Exported for KIOGui's FavIconsManager
|
||||
*/
|
||||
class KIOCORE_EXPORT FavIconsCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static FavIconsCache *instance();
|
||||
|
||||
// Fast cache lookup, used by KIO::favIconForUrl
|
||||
QString iconForUrl(const QUrl &url);
|
||||
|
||||
// Look for a custom icon URL in the cache, otherwise assemble default host icon URL
|
||||
QUrl iconUrlForUrl(const QUrl &url);
|
||||
|
||||
// Remember association to a custom icon URL
|
||||
void setIconForUrl(const QUrl &url, const QUrl &iconUrl);
|
||||
|
||||
QString cachePathForIconUrl(const QUrl &iconUrl) const;
|
||||
|
||||
void ensureCacheExists();
|
||||
|
||||
void addFailedDownload(const QUrl &url);
|
||||
void removeFailedDownload(const QUrl &url);
|
||||
bool isFailedDownload(const QUrl &url) const;
|
||||
|
||||
Q_SIGNALS:
|
||||
|
||||
private:
|
||||
KIOCORE_NO_EXPORT FavIconsCache();
|
||||
KIOCORE_NO_EXPORT ~FavIconsCache() override;
|
||||
std::unique_ptr<FavIconsCachePrivate> const d;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FAVICONSCACHE_H
|
||||
@@ -0,0 +1,649 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "filecopyjob.h"
|
||||
#include "askuseractioninterface.h"
|
||||
#include "job_p.h"
|
||||
#include "kprotocolmanager.h"
|
||||
#include "scheduler.h"
|
||||
#include "worker_p.h"
|
||||
#include <kio/jobuidelegatefactory.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
static inline Worker *jobWorker(SimpleJob *job)
|
||||
{
|
||||
return SimpleJobPrivate::get(job)->m_worker;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
class KIO::FileCopyJobPrivate : public KIO::JobPrivate
|
||||
{
|
||||
public:
|
||||
FileCopyJobPrivate(const QUrl &src, const QUrl &dest, int permissions, bool move, JobFlags flags)
|
||||
: m_sourceSize(filesize_t(-1))
|
||||
, m_src(src)
|
||||
, m_dest(dest)
|
||||
, m_moveJob(nullptr)
|
||||
, m_copyJob(nullptr)
|
||||
, m_delJob(nullptr)
|
||||
, m_chmodJob(nullptr)
|
||||
, m_getJob(nullptr)
|
||||
, m_putJob(nullptr)
|
||||
, m_permissions(permissions)
|
||||
, m_move(move)
|
||||
, m_mustChmod(0)
|
||||
, m_bFileCopyInProgress(false)
|
||||
, m_flags(flags)
|
||||
{
|
||||
}
|
||||
KIO::filesize_t m_sourceSize;
|
||||
QDateTime m_modificationTime;
|
||||
QUrl m_src;
|
||||
QUrl m_dest;
|
||||
QByteArray m_buffer;
|
||||
SimpleJob *m_moveJob;
|
||||
SimpleJob *m_copyJob;
|
||||
SimpleJob *m_delJob;
|
||||
SimpleJob *m_chmodJob;
|
||||
TransferJob *m_getJob;
|
||||
TransferJob *m_putJob;
|
||||
int m_permissions;
|
||||
bool m_move : 1;
|
||||
bool m_canResume : 1;
|
||||
bool m_resumeAnswerSent : 1;
|
||||
bool m_mustChmod : 1;
|
||||
bool m_bFileCopyInProgress : 1;
|
||||
JobFlags m_flags;
|
||||
|
||||
void startBestCopyMethod();
|
||||
void startCopyJob();
|
||||
void startCopyJob(const QUrl &workerUrl);
|
||||
void startRenameJob(const QUrl &workerUrl);
|
||||
void startDataPump();
|
||||
void connectSubjob(SimpleJob *job);
|
||||
|
||||
void slotStart();
|
||||
void slotData(KIO::Job *, const QByteArray &data);
|
||||
void slotDataReq(KIO::Job *, QByteArray &data);
|
||||
void slotMimetype(KIO::Job *, const QString &type);
|
||||
/**
|
||||
* Forward signal from subjob
|
||||
* @param job the job that emitted this signal
|
||||
* @param offset the offset to resume from
|
||||
*/
|
||||
void slotCanResume(KIO::Job *job, KIO::filesize_t offset);
|
||||
void processCanResumeResult(KIO::Job *job, RenameDialog_Result result, KIO::filesize_t offset);
|
||||
|
||||
Q_DECLARE_PUBLIC(FileCopyJob)
|
||||
|
||||
static inline FileCopyJob *newJob(const QUrl &src, const QUrl &dest, int permissions, bool move, JobFlags flags)
|
||||
{
|
||||
// qDebug() << src << "->" << dest;
|
||||
FileCopyJob *job = new FileCopyJob(*new FileCopyJobPrivate(src, dest, permissions, move, flags));
|
||||
job->setProperty("destUrl", dest.toString());
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
if (!(flags & NoPrivilegeExecution)) {
|
||||
job->d_func()->m_privilegeExecutionEnabled = true;
|
||||
job->d_func()->m_operationType = move ? Move : Copy;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
static bool isSrcDestSameWorkerProcess(const QUrl &src, const QUrl &dest)
|
||||
{
|
||||
/* clang-format off */
|
||||
return src.scheme() == dest.scheme()
|
||||
&& src.host() == dest.host()
|
||||
&& src.port() == dest.port()
|
||||
&& src.userName() == dest.userName()
|
||||
&& src.password() == dest.password();
|
||||
/* clang-format on */
|
||||
}
|
||||
|
||||
/*
|
||||
* The FileCopyJob works according to the famous Bavarian
|
||||
* 'Alternating Bitburger Protocol': we either drink a beer or we
|
||||
* we order a beer, but never both at the same time.
|
||||
* Translated to KIO workers: We alternate between receiving a block of data
|
||||
* and sending it away.
|
||||
*/
|
||||
FileCopyJob::FileCopyJob(FileCopyJobPrivate &dd)
|
||||
: Job(dd)
|
||||
{
|
||||
Q_D(FileCopyJob);
|
||||
QTimer::singleShot(0, this, [d]() {
|
||||
d->slotStart();
|
||||
});
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::slotStart()
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
if (!m_move) {
|
||||
JobPrivate::emitCopying(q, m_src, m_dest);
|
||||
} else {
|
||||
JobPrivate::emitMoving(q, m_src, m_dest);
|
||||
}
|
||||
|
||||
if (m_move) {
|
||||
// The if() below must be the same as the one in startBestCopyMethod
|
||||
if (isSrcDestSameWorkerProcess(m_src, m_dest)) {
|
||||
startRenameJob(m_src);
|
||||
return;
|
||||
} else if (m_src.isLocalFile() && KProtocolManager::canRenameFromFile(m_dest)) {
|
||||
startRenameJob(m_dest);
|
||||
return;
|
||||
} else if (m_dest.isLocalFile() && KProtocolManager::canRenameToFile(m_src)) {
|
||||
startRenameJob(m_src);
|
||||
return;
|
||||
}
|
||||
// No fast-move available, use copy + del.
|
||||
}
|
||||
startBestCopyMethod();
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::startBestCopyMethod()
|
||||
{
|
||||
if (isSrcDestSameWorkerProcess(m_src, m_dest)) {
|
||||
startCopyJob();
|
||||
} else if (m_src.isLocalFile() && KProtocolManager::canCopyFromFile(m_dest)) {
|
||||
startCopyJob(m_dest);
|
||||
} else if (m_dest.isLocalFile() && KProtocolManager::canCopyToFile(m_src) && !KIO::Scheduler::isWorkerOnHoldFor(m_src)) {
|
||||
startCopyJob(m_src);
|
||||
} else {
|
||||
startDataPump();
|
||||
}
|
||||
}
|
||||
|
||||
FileCopyJob::~FileCopyJob()
|
||||
{
|
||||
}
|
||||
|
||||
void FileCopyJob::setSourceSize(KIO::filesize_t size)
|
||||
{
|
||||
Q_D(FileCopyJob);
|
||||
d->m_sourceSize = size;
|
||||
if (size != (KIO::filesize_t)-1) {
|
||||
setTotalAmount(KJob::Bytes, size);
|
||||
}
|
||||
}
|
||||
|
||||
void FileCopyJob::setModificationTime(const QDateTime &mtime)
|
||||
{
|
||||
Q_D(FileCopyJob);
|
||||
d->m_modificationTime = mtime;
|
||||
}
|
||||
|
||||
QUrl FileCopyJob::srcUrl() const
|
||||
{
|
||||
return d_func()->m_src;
|
||||
}
|
||||
|
||||
QUrl FileCopyJob::destUrl() const
|
||||
{
|
||||
return d_func()->m_dest;
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::startCopyJob()
|
||||
{
|
||||
startCopyJob(m_src);
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::startCopyJob(const QUrl &workerUrl)
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
// qDebug();
|
||||
KIO_ARGS << m_src << m_dest << m_permissions << (qint8)(m_flags & Overwrite);
|
||||
auto job = new DirectCopyJob(workerUrl, packedArgs);
|
||||
m_copyJob = job;
|
||||
m_copyJob->setParentJob(q);
|
||||
if (m_modificationTime.isValid()) {
|
||||
m_copyJob->addMetaData(QStringLiteral("modified"), m_modificationTime.toString(Qt::ISODate)); // #55804
|
||||
}
|
||||
q->addSubjob(m_copyJob);
|
||||
connectSubjob(m_copyJob);
|
||||
q->connect(job, &DirectCopyJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) {
|
||||
slotCanResume(job, offset);
|
||||
});
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::startRenameJob(const QUrl &workerUrl)
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
m_mustChmod = true; // CMD_RENAME by itself doesn't change permissions
|
||||
KIO_ARGS << m_src << m_dest << (qint8)(m_flags & Overwrite);
|
||||
m_moveJob = SimpleJobPrivate::newJobNoUi(workerUrl, CMD_RENAME, packedArgs);
|
||||
m_moveJob->setParentJob(q);
|
||||
if (m_modificationTime.isValid()) {
|
||||
m_moveJob->addMetaData(QStringLiteral("modified"), m_modificationTime.toString(Qt::ISODate)); // #55804
|
||||
}
|
||||
q->addSubjob(m_moveJob);
|
||||
connectSubjob(m_moveJob);
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::connectSubjob(SimpleJob *job)
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
q->connect(job, &KJob::totalSize, q, [q](KJob *job, qulonglong totalSize) {
|
||||
Q_UNUSED(job);
|
||||
if (totalSize != q->totalAmount(KJob::Bytes)) {
|
||||
q->setTotalAmount(KJob::Bytes, totalSize);
|
||||
}
|
||||
});
|
||||
|
||||
q->connect(job, &KJob::processedSize, q, [q, this](const KJob *job, qulonglong processedSize) {
|
||||
if (job == m_copyJob) {
|
||||
m_bFileCopyInProgress = processedSize > 0;
|
||||
}
|
||||
q->setProcessedAmount(KJob::Bytes, processedSize);
|
||||
});
|
||||
|
||||
q->connect(job, &KJob::percentChanged, q, [q](KJob *, ulong percent) {
|
||||
if (percent > q->percent()) {
|
||||
q->setPercent(percent);
|
||||
}
|
||||
});
|
||||
|
||||
if (q->isSuspended()) {
|
||||
job->suspend();
|
||||
}
|
||||
}
|
||||
|
||||
bool FileCopyJob::doSuspend()
|
||||
{
|
||||
Q_D(FileCopyJob);
|
||||
if (d->m_moveJob) {
|
||||
d->m_moveJob->suspend();
|
||||
}
|
||||
|
||||
if (d->m_copyJob) {
|
||||
d->m_copyJob->suspend();
|
||||
}
|
||||
|
||||
if (d->m_getJob) {
|
||||
d->m_getJob->suspend();
|
||||
}
|
||||
|
||||
if (d->m_putJob) {
|
||||
d->m_putJob->suspend();
|
||||
}
|
||||
|
||||
Job::doSuspend();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileCopyJob::doResume()
|
||||
{
|
||||
Q_D(FileCopyJob);
|
||||
if (d->m_moveJob) {
|
||||
d->m_moveJob->resume();
|
||||
}
|
||||
|
||||
if (d->m_copyJob) {
|
||||
d->m_copyJob->resume();
|
||||
}
|
||||
|
||||
if (d->m_getJob) {
|
||||
d->m_getJob->resume();
|
||||
}
|
||||
|
||||
if (d->m_putJob) {
|
||||
d->m_putJob->resume();
|
||||
}
|
||||
|
||||
Job::doResume();
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::startDataPump()
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
// qDebug();
|
||||
|
||||
m_canResume = false;
|
||||
m_resumeAnswerSent = false;
|
||||
m_getJob = nullptr; // for now
|
||||
m_putJob = put(m_dest, m_permissions, (m_flags | HideProgressInfo) /* no GUI */);
|
||||
m_putJob->setParentJob(q);
|
||||
// qDebug() << "m_putJob=" << m_putJob << "m_dest=" << m_dest;
|
||||
if (m_modificationTime.isValid()) {
|
||||
m_putJob->setModificationTime(m_modificationTime);
|
||||
}
|
||||
|
||||
// The first thing the put job will tell us is whether we can
|
||||
// resume or not (this is always emitted)
|
||||
q->connect(m_putJob, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) {
|
||||
slotCanResume(job, offset);
|
||||
});
|
||||
q->connect(m_putJob, &KIO::TransferJob::dataReq, q, [this](KIO::Job *job, QByteArray &data) {
|
||||
slotDataReq(job, data);
|
||||
});
|
||||
q->addSubjob(m_putJob);
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::slotCanResume(KIO::Job *job, KIO::filesize_t offset)
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
|
||||
if (job == m_getJob) {
|
||||
// Cool, the get job said ok, we can resume
|
||||
m_canResume = true;
|
||||
// qDebug() << "'can resume' from the GET job -> we can resume";
|
||||
|
||||
jobWorker(m_getJob)->setOffset(jobWorker(m_putJob)->offset());
|
||||
return;
|
||||
}
|
||||
|
||||
if (job == m_putJob || job == m_copyJob) {
|
||||
// qDebug() << "'can resume' from PUT job. offset=" << KIO::number(offset);
|
||||
if (offset == 0) {
|
||||
m_resumeAnswerSent = true; // No need for an answer
|
||||
} else {
|
||||
KIO::Job *kioJob = q->parentJob() ? q->parentJob() : q;
|
||||
auto *askUserActionInterface = KIO::delegateExtension<KIO::AskUserActionInterface *>(kioJob);
|
||||
if (!KProtocolManager::autoResume() && !(m_flags & Overwrite) && askUserActionInterface) {
|
||||
auto renameSignal = &AskUserActionInterface::askUserRenameResult;
|
||||
|
||||
q->connect(askUserActionInterface, renameSignal, q, [=, this](KIO::RenameDialog_Result result, const QUrl &, const KJob *askJob) {
|
||||
Q_ASSERT(kioJob == askJob);
|
||||
|
||||
// Only receive askUserRenameResult once per rename dialog
|
||||
QObject::disconnect(askUserActionInterface, renameSignal, q, nullptr);
|
||||
|
||||
processCanResumeResult(job, result, offset);
|
||||
});
|
||||
|
||||
// Ask confirmation about resuming previous transfer
|
||||
askUserActionInterface->askUserRename(kioJob,
|
||||
i18n("File Already Exists"),
|
||||
m_src,
|
||||
m_dest,
|
||||
RenameDialog_Options(RenameDialog_Overwrite | RenameDialog_Resume | RenameDialog_NoRename),
|
||||
m_sourceSize,
|
||||
offset);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
processCanResumeResult(job, //
|
||||
Result_Resume, // The default is to resume
|
||||
offset);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
qCWarning(KIO_CORE) << "unknown job=" << job << "m_getJob=" << m_getJob << "m_putJob=" << m_putJob;
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::processCanResumeResult(KIO::Job *job, RenameDialog_Result result, KIO::filesize_t offset)
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
if (result == Result_Overwrite || (m_flags & Overwrite)) {
|
||||
offset = 0;
|
||||
} else if (result == Result_Cancel) {
|
||||
if (job == m_putJob) {
|
||||
m_putJob->kill(FileCopyJob::Quietly);
|
||||
q->removeSubjob(m_putJob);
|
||||
m_putJob = nullptr;
|
||||
} else {
|
||||
m_copyJob->kill(FileCopyJob::Quietly);
|
||||
q->removeSubjob(m_copyJob);
|
||||
m_copyJob = nullptr;
|
||||
}
|
||||
q->setError(ERR_USER_CANCELED);
|
||||
q->emitResult();
|
||||
return;
|
||||
}
|
||||
|
||||
if (job == m_copyJob) {
|
||||
jobWorker(m_copyJob)->sendResumeAnswer(offset != 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (job == m_putJob) {
|
||||
m_getJob = KIO::get(m_src, NoReload, HideProgressInfo /* no GUI */);
|
||||
m_getJob->setParentJob(q);
|
||||
// qDebug() << "m_getJob=" << m_getJob << m_src;
|
||||
m_getJob->addMetaData(QStringLiteral("AllowCompressedPage"), QStringLiteral("false"));
|
||||
// Set size in subjob. This helps if the worker doesn't emit totalSize.
|
||||
if (m_sourceSize != (KIO::filesize_t)-1) {
|
||||
m_getJob->setTotalAmount(KJob::Bytes, m_sourceSize);
|
||||
}
|
||||
|
||||
if (offset) {
|
||||
// qDebug() << "Setting metadata for resume to" << (unsigned long) offset;
|
||||
m_getJob->addMetaData(QStringLiteral("range-start"), KIO::number(offset));
|
||||
|
||||
// Might or might not get emitted
|
||||
q->connect(m_getJob, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) {
|
||||
slotCanResume(job, offset);
|
||||
});
|
||||
}
|
||||
jobWorker(m_putJob)->setOffset(offset);
|
||||
|
||||
m_putJob->d_func()->internalSuspend();
|
||||
q->addSubjob(m_getJob);
|
||||
connectSubjob(m_getJob); // Progress info depends on get
|
||||
m_getJob->d_func()->internalResume(); // Order a beer
|
||||
|
||||
q->connect(m_getJob, &KIO::TransferJob::data, q, [this](KIO::Job *job, const QByteArray &data) {
|
||||
slotData(job, data);
|
||||
});
|
||||
q->connect(m_getJob, &KIO::TransferJob::mimeTypeFound, q, [this](KIO::Job *job, const QString &type) {
|
||||
slotMimetype(job, type);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::slotData(KIO::Job *, const QByteArray &data)
|
||||
{
|
||||
// qDebug() << "data size:" << data.size();
|
||||
Q_ASSERT(m_putJob);
|
||||
if (!m_putJob) {
|
||||
return; // Don't crash
|
||||
}
|
||||
m_getJob->d_func()->internalSuspend();
|
||||
m_putJob->d_func()->internalResume(); // Drink the beer
|
||||
m_buffer += data;
|
||||
|
||||
// On the first set of data incoming, we tell the "put" worker about our
|
||||
// decision about resuming
|
||||
if (!m_resumeAnswerSent) {
|
||||
m_resumeAnswerSent = true;
|
||||
// qDebug() << "(first time) -> send resume answer " << m_canResume;
|
||||
jobWorker(m_putJob)->sendResumeAnswer(m_canResume);
|
||||
}
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::slotDataReq(KIO::Job *, QByteArray &data)
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
// qDebug();
|
||||
if (!m_resumeAnswerSent && !m_getJob) {
|
||||
// This can't happen
|
||||
q->setError(ERR_INTERNAL);
|
||||
q->setErrorText(QStringLiteral("'Put' job did not send canResume or 'Get' job did not send data!"));
|
||||
m_putJob->kill(FileCopyJob::Quietly);
|
||||
q->removeSubjob(m_putJob);
|
||||
m_putJob = nullptr;
|
||||
q->emitResult();
|
||||
return;
|
||||
}
|
||||
if (m_getJob) {
|
||||
m_getJob->d_func()->internalResume(); // Order more beer
|
||||
m_putJob->d_func()->internalSuspend();
|
||||
}
|
||||
data = m_buffer;
|
||||
m_buffer = QByteArray();
|
||||
}
|
||||
|
||||
void FileCopyJobPrivate::slotMimetype(KIO::Job *, const QString &type)
|
||||
{
|
||||
Q_Q(FileCopyJob);
|
||||
Q_EMIT q->mimeTypeFound(q, type);
|
||||
}
|
||||
|
||||
void FileCopyJob::slotResult(KJob *job)
|
||||
{
|
||||
Q_D(FileCopyJob);
|
||||
// qDebug() << "this=" << this << "job=" << job;
|
||||
removeSubjob(job);
|
||||
|
||||
// If result comes from copyjob then we are not writing anymore.
|
||||
if (job == d->m_copyJob) {
|
||||
d->m_bFileCopyInProgress = false;
|
||||
}
|
||||
|
||||
// Did job have an error ?
|
||||
if (job->error()) {
|
||||
if ((job == d->m_moveJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) {
|
||||
d->m_moveJob = nullptr;
|
||||
d->startBestCopyMethod();
|
||||
return;
|
||||
} else if ((job == d->m_copyJob) && (job->error() == ERR_UNSUPPORTED_ACTION)) {
|
||||
d->m_copyJob = nullptr;
|
||||
d->startDataPump();
|
||||
return;
|
||||
} else if (job == d->m_getJob) {
|
||||
d->m_getJob = nullptr;
|
||||
if (d->m_putJob) {
|
||||
d->m_putJob->kill(Quietly);
|
||||
removeSubjob(d->m_putJob);
|
||||
}
|
||||
} else if (job == d->m_putJob) {
|
||||
d->m_putJob = nullptr;
|
||||
if (d->m_getJob) {
|
||||
d->m_getJob->kill(Quietly);
|
||||
removeSubjob(d->m_getJob);
|
||||
}
|
||||
} else if (job == d->m_chmodJob) {
|
||||
d->m_chmodJob = nullptr;
|
||||
if (d->m_delJob) {
|
||||
d->m_delJob->kill(Quietly);
|
||||
removeSubjob(d->m_delJob);
|
||||
}
|
||||
} else if (job == d->m_delJob) {
|
||||
d->m_delJob = nullptr;
|
||||
if (d->m_chmodJob) {
|
||||
d->m_chmodJob->kill(Quietly);
|
||||
removeSubjob(d->m_chmodJob);
|
||||
}
|
||||
}
|
||||
setError(job->error());
|
||||
setErrorText(job->errorText());
|
||||
emitResult();
|
||||
return;
|
||||
}
|
||||
|
||||
if (d->m_mustChmod) {
|
||||
// If d->m_permissions == -1, keep the default permissions
|
||||
if (d->m_permissions != -1) {
|
||||
d->m_chmodJob = chmod(d->m_dest, d->m_permissions);
|
||||
addSubjob(d->m_chmodJob);
|
||||
}
|
||||
d->m_mustChmod = false;
|
||||
}
|
||||
|
||||
if (job == d->m_moveJob) {
|
||||
d->m_moveJob = nullptr; // Finished
|
||||
}
|
||||
|
||||
if (job == d->m_copyJob) {
|
||||
d->m_copyJob = nullptr;
|
||||
if (d->m_move) {
|
||||
d->m_delJob = file_delete(d->m_src, HideProgressInfo /*no GUI*/); // Delete source
|
||||
addSubjob(d->m_delJob);
|
||||
}
|
||||
}
|
||||
|
||||
if (job == d->m_getJob) {
|
||||
// qDebug() << "m_getJob finished";
|
||||
d->m_getJob = nullptr; // No action required
|
||||
if (d->m_putJob) {
|
||||
d->m_putJob->d_func()->internalResume();
|
||||
}
|
||||
}
|
||||
|
||||
if (job == d->m_putJob) {
|
||||
// qDebug() << "m_putJob finished";
|
||||
d->m_putJob = nullptr;
|
||||
if (d->m_getJob) {
|
||||
// The get job is still running, probably after emitting data(QByteArray())
|
||||
// and before we receive its finished().
|
||||
d->m_getJob->d_func()->internalResume();
|
||||
}
|
||||
if (d->m_move) {
|
||||
d->m_delJob = file_delete(d->m_src, HideProgressInfo /*no GUI*/); // Delete source
|
||||
addSubjob(d->m_delJob);
|
||||
}
|
||||
}
|
||||
|
||||
if (job == d->m_delJob) {
|
||||
d->m_delJob = nullptr; // Finished
|
||||
}
|
||||
|
||||
if (job == d->m_chmodJob) {
|
||||
d->m_chmodJob = nullptr; // Finished
|
||||
}
|
||||
|
||||
if (!hasSubjobs()) {
|
||||
emitResult();
|
||||
}
|
||||
}
|
||||
|
||||
bool FileCopyJob::doKill()
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
// TODO Use SetConsoleCtrlHandler on Windows or similar behaviour.
|
||||
// https://stackoverflow.com/questions/2007516/is-there-a-posix-sigterm-alternative-on-windows-a-gentle-kill-for-console-ap
|
||||
// https://danielkaes.wordpress.com/2009/06/04/how-to-catch-kill-events-with-python/
|
||||
// https://phabricator.kde.org/D25117#566107
|
||||
|
||||
Q_D(FileCopyJob);
|
||||
|
||||
// If we are interrupted in the middle of file copying,
|
||||
// we may end up with corrupted file at the destination.
|
||||
// It is better to clean up this file. If a copy is being
|
||||
// made as part of move operation then delete the dest only if
|
||||
// source file is intact (m_delJob == NULL).
|
||||
if (d->m_bFileCopyInProgress && d->m_copyJob && d->m_dest.isLocalFile()) {
|
||||
if (d->m_flags & Overwrite) {
|
||||
QFile::remove(d->m_dest.toLocalFile() + QStringLiteral(".part"));
|
||||
} else {
|
||||
QFile::remove(d->m_dest.toLocalFile());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return Job::doKill();
|
||||
}
|
||||
|
||||
FileCopyJob *KIO::file_copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags)
|
||||
{
|
||||
return FileCopyJobPrivate::newJob(src, dest, permissions, false, flags);
|
||||
}
|
||||
|
||||
FileCopyJob *KIO::file_move(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags)
|
||||
{
|
||||
FileCopyJob *job = FileCopyJobPrivate::newJob(src, dest, permissions, true, flags);
|
||||
if (job->uiDelegateExtension()) {
|
||||
job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::UpdateContent);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
#include "moc_filecopyjob.cpp"
|
||||
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_FILECOPYJOB_H
|
||||
#define KIO_FILECOPYJOB_H
|
||||
|
||||
#include "job_base.h"
|
||||
#include <kio/global.h> // filesize_t
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class FileCopyJobPrivate;
|
||||
/**
|
||||
* @class KIO::FileCopyJob filecopyjob.h <KIO/FileCopyJob>
|
||||
*
|
||||
* The FileCopyJob copies data from one place to another.
|
||||
* @see KIO::file_copy()
|
||||
* @see KIO::file_move()
|
||||
*/
|
||||
class KIOCORE_EXPORT FileCopyJob : public Job
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~FileCopyJob() override;
|
||||
/**
|
||||
* If you know the size of the source file, call this method
|
||||
* to inform this job. It will be displayed in the "resume" dialog.
|
||||
* @param size the size of the source file
|
||||
*/
|
||||
void setSourceSize(KIO::filesize_t size);
|
||||
|
||||
/**
|
||||
* Sets the modification time of the file
|
||||
*
|
||||
* Note that this is ignored if a direct copy (WorkerBase::copy) can be done,
|
||||
* in which case the mtime of the source is applied to the destination (if the protocol
|
||||
* supports the concept).
|
||||
*/
|
||||
void setModificationTime(const QDateTime &mtime);
|
||||
|
||||
/**
|
||||
* Returns the source URL.
|
||||
* @return the source URL
|
||||
*/
|
||||
QUrl srcUrl() const;
|
||||
|
||||
/**
|
||||
* Returns the destination URL.
|
||||
* @return the destination URL
|
||||
*/
|
||||
QUrl destUrl() const;
|
||||
|
||||
bool doSuspend() override;
|
||||
bool doResume() override;
|
||||
bool doKill() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* MIME type determined during a file copy.
|
||||
* This is never emitted during a move, and might not be emitted during
|
||||
* a file copy, depending on the worker. But when a get and a put are
|
||||
* being used (which is the common case), this signal forwards the
|
||||
* MIME type information from the get job.
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param mimeType the MIME type
|
||||
* @since 5.78
|
||||
*/
|
||||
void mimeTypeFound(KIO::Job *job, const QString &mimeType);
|
||||
|
||||
protected Q_SLOTS:
|
||||
/**
|
||||
* Called whenever a subjob finishes.
|
||||
* @param job the job that emitted this signal
|
||||
*/
|
||||
void slotResult(KJob *job) override;
|
||||
|
||||
protected:
|
||||
KIOCORE_NO_EXPORT explicit FileCopyJob(FileCopyJobPrivate &dd);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(FileCopyJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Copy a single file.
|
||||
*
|
||||
* Uses either WorkerBase::copy() if the worker supports that
|
||||
* or get() and put() otherwise.
|
||||
*
|
||||
* @param src Where to get the file
|
||||
* @param dest Where to put the file
|
||||
* @param permissions the file mode permissions to set on @p dest; if this is -1
|
||||
* (the default) no special permissions will be set on @p dest, i.e. it'll have
|
||||
* the default system permissions for newly created files, and the owner and group
|
||||
* permissions are not preserved.
|
||||
* @param flags Can be @ref JobFlag::HideProgressInfo, Overwrite and Resume here
|
||||
* WARNING: Setting @ref JobFlag::Resume means that the data will be appended to
|
||||
* @p dest if @p dest exists
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT FileCopyJob *file_copy(const QUrl &src, const QUrl &dest, int permissions = -1, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Overload for catching code mistakes. Do NOT call this method (it is not implemented),
|
||||
* insert a value for permissions (-1 by default) before the JobFlags.
|
||||
*/
|
||||
FileCopyJob *file_copy(const QUrl &src, const QUrl &dest, JobFlags flags) Q_DECL_EQ_DELETE; // not implemented - on purpose.
|
||||
|
||||
/**
|
||||
* Move a single file.
|
||||
*
|
||||
* Use either WorkerBase::rename() if the worker supports that,
|
||||
* or copy() and del() otherwise, or eventually get() & put() & del()
|
||||
*
|
||||
* @param src Where to get the file
|
||||
* @param dest Where to put the file
|
||||
* @param permissions the file mode permissions to set on @p dest; if this is -1
|
||||
* (the default), no special permissions are set on @p dest, i.e. it'll have
|
||||
* the default system permissions for newly created files, and the owner and group
|
||||
* permissions are not preserved.
|
||||
* @param flags Can be HideProgressInfo, Overwrite and Resume here
|
||||
* WARNING: Setting @ref JobFlag::Resume means that the data will be appended to
|
||||
* @p dest if @p dest exists
|
||||
* @return the job handling the operation
|
||||
*/
|
||||
KIOCORE_EXPORT FileCopyJob *file_move(const QUrl &src, const QUrl &dest, int permissions = -1, JobFlags flags = DefaultFlags);
|
||||
|
||||
/**
|
||||
* Overload for catching code mistakes. Do NOT call this method (it is not implemented),
|
||||
* insert a value for permissions (-1 by default) before the JobFlags.
|
||||
*/
|
||||
FileCopyJob *file_move(const QUrl &src, const QUrl &dest, JobFlags flags) Q_DECL_EQ_DELETE; // not implemented - on purpose.
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006 Allan Sandfeld Jensen <kde@carewolf.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "filejob.h"
|
||||
|
||||
#include "job_p.h"
|
||||
#include "worker_p.h"
|
||||
|
||||
class KIO::FileJobPrivate : public KIO::SimpleJobPrivate
|
||||
{
|
||||
public:
|
||||
FileJobPrivate(const QUrl &url, const QByteArray &packedArgs)
|
||||
: SimpleJobPrivate(url, CMD_OPEN, packedArgs)
|
||||
, m_open(false)
|
||||
, m_size(0)
|
||||
{
|
||||
}
|
||||
|
||||
bool m_open;
|
||||
QString m_mimetype;
|
||||
KIO::filesize_t m_size;
|
||||
|
||||
void slotRedirection(const QUrl &url);
|
||||
void slotData(const QByteArray &data);
|
||||
void slotMimetype(const QString &mimetype);
|
||||
void slotOpen();
|
||||
void slotWritten(KIO::filesize_t);
|
||||
void slotFinished();
|
||||
void slotPosition(KIO::filesize_t);
|
||||
void slotTruncated(KIO::filesize_t);
|
||||
void slotTotalSize(KIO::filesize_t);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Called by the scheduler when a @p worker gets to
|
||||
* work on this job.
|
||||
* @param worker the worker that starts working on this job
|
||||
*/
|
||||
void start(Worker *worker) override;
|
||||
|
||||
Q_DECLARE_PUBLIC(FileJob)
|
||||
|
||||
static inline FileJob *newJob(const QUrl &url, const QByteArray &packedArgs)
|
||||
{
|
||||
FileJob *job = new FileJob(*new FileJobPrivate(url, packedArgs));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
FileJob::FileJob(FileJobPrivate &dd)
|
||||
: SimpleJob(dd)
|
||||
{
|
||||
}
|
||||
|
||||
FileJob::~FileJob()
|
||||
{
|
||||
}
|
||||
|
||||
void FileJob::read(KIO::filesize_t size)
|
||||
{
|
||||
Q_D(FileJob);
|
||||
if (!d->m_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
KIO_ARGS << size;
|
||||
d->m_worker->send(CMD_READ, packedArgs);
|
||||
}
|
||||
|
||||
void FileJob::write(const QByteArray &_data)
|
||||
{
|
||||
Q_D(FileJob);
|
||||
if (!d->m_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_worker->send(CMD_WRITE, _data);
|
||||
}
|
||||
|
||||
void FileJob::seek(KIO::filesize_t offset)
|
||||
{
|
||||
Q_D(FileJob);
|
||||
if (!d->m_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
KIO_ARGS << KIO::filesize_t(offset);
|
||||
d->m_worker->send(CMD_SEEK, packedArgs);
|
||||
}
|
||||
|
||||
void FileJob::truncate(KIO::filesize_t length)
|
||||
{
|
||||
Q_D(FileJob);
|
||||
if (!d->m_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
KIO_ARGS << KIO::filesize_t(length);
|
||||
d->m_worker->send(CMD_TRUNCATE, packedArgs);
|
||||
}
|
||||
|
||||
void FileJob::close()
|
||||
{
|
||||
Q_D(FileJob);
|
||||
if (!d->m_open) {
|
||||
return;
|
||||
}
|
||||
|
||||
d->m_worker->send(CMD_CLOSE);
|
||||
// ### close?
|
||||
}
|
||||
|
||||
KIO::filesize_t FileJob::size()
|
||||
{
|
||||
Q_D(FileJob);
|
||||
if (!d->m_open) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return d->m_size;
|
||||
}
|
||||
|
||||
// Worker sends data
|
||||
void FileJobPrivate::slotData(const QByteArray &_data)
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
Q_EMIT q_func()->data(q, _data);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotRedirection(const QUrl &url)
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
// qDebug() << url;
|
||||
Q_EMIT q->redirection(q, url);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotMimetype(const QString &type)
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
m_mimetype = type;
|
||||
Q_EMIT q->mimeTypeFound(q, m_mimetype);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotPosition(KIO::filesize_t pos)
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
Q_EMIT q->position(q, pos);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotTruncated(KIO::filesize_t length)
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
Q_EMIT q->truncated(q, length);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotTotalSize(KIO::filesize_t t_size)
|
||||
{
|
||||
m_size = t_size;
|
||||
Q_Q(FileJob);
|
||||
q->setTotalAmount(KJob::Bytes, m_size);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotOpen()
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
m_open = true;
|
||||
Q_EMIT q->open(q);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotWritten(KIO::filesize_t t_written)
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
Q_EMIT q->written(q, t_written);
|
||||
}
|
||||
|
||||
void FileJobPrivate::slotFinished()
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
// qDebug() << this << m_url;
|
||||
m_open = false;
|
||||
|
||||
Q_EMIT q->fileClosed(q);
|
||||
|
||||
// Return worker to the scheduler
|
||||
workerDone();
|
||||
// Scheduler::doJob(this);
|
||||
q->emitResult();
|
||||
}
|
||||
|
||||
void FileJobPrivate::start(Worker *worker)
|
||||
{
|
||||
Q_Q(FileJob);
|
||||
q->connect(worker, &KIO::WorkerInterface::data, q, [this](const QByteArray &ba) {
|
||||
slotData(ba);
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::redirection, q, [this](const QUrl &url) {
|
||||
slotRedirection(url);
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::mimeType, q, [this](const QString &mimeType) {
|
||||
slotMimetype(mimeType);
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::open, q, [this]() {
|
||||
slotOpen();
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::finished, q, [this]() {
|
||||
slotFinished();
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::position, q, [this](KIO::filesize_t pos) {
|
||||
slotPosition(pos);
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::truncated, q, [this](KIO::filesize_t length) {
|
||||
slotTruncated(length);
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::written, q, [this](KIO::filesize_t dataWritten) {
|
||||
slotWritten(dataWritten);
|
||||
});
|
||||
|
||||
q->connect(worker, &KIO::WorkerInterface::totalSize, q, [this](KIO::filesize_t size) {
|
||||
slotTotalSize(size);
|
||||
});
|
||||
|
||||
SimpleJobPrivate::start(worker);
|
||||
}
|
||||
|
||||
FileJob *KIO::open(const QUrl &url, QIODevice::OpenMode mode)
|
||||
{
|
||||
// Send decoded path and encoded query
|
||||
KIO_ARGS << url << mode;
|
||||
return FileJobPrivate::newJob(url, packedArgs);
|
||||
}
|
||||
|
||||
#include "moc_filejob.cpp"
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2006 Allan Sandfeld Jensen <kde@carewolf.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KIO_FILEJOB_H
|
||||
#define KIO_FILEJOB_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include "simplejob.h"
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class FileJobPrivate;
|
||||
/**
|
||||
* @class KIO::FileJob filejob.h <KIO/FileJob>
|
||||
*
|
||||
* The file-job is an asynchronous version of normal file handling.
|
||||
* It allows block-wise reading and writing, and allows seeking and truncation. Results are returned through signals.
|
||||
*
|
||||
* Should always be created using KIO::open(const QUrl&, QIODevice::OpenMode).
|
||||
*/
|
||||
|
||||
class KIOCORE_EXPORT FileJob : public SimpleJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~FileJob() override;
|
||||
|
||||
/**
|
||||
* This function attempts to read up to \p size bytes from the URL passed to
|
||||
* KIO::open() and returns the bytes received via the data() signal.
|
||||
*
|
||||
* The read operation commences at the current file offset, and the file
|
||||
* offset is incremented by the number of bytes read, but this change in the
|
||||
* offset does not result in the position() signal being emitted.
|
||||
*
|
||||
* If the current file offset is at or past the end of file (i.e. EOD), no
|
||||
* bytes are read, and the data() signal returns an empty QByteArray.
|
||||
*
|
||||
* On error the data() signal is not emitted. To catch errors please connect
|
||||
* to the result() signal.
|
||||
*
|
||||
* @param size the requested amount of data to read
|
||||
*
|
||||
*/
|
||||
void read(KIO::filesize_t size);
|
||||
|
||||
/**
|
||||
* This function attempts to write all the bytes in \p data to the URL
|
||||
* passed to KIO::open() and returns the bytes written received via the
|
||||
* written() signal.
|
||||
*
|
||||
* The write operation commences at the current file offset, and the file
|
||||
* offset is incremented by the number of bytes read, but this change in the
|
||||
* offset does not result in the position() being emitted.
|
||||
*
|
||||
* On error the written() signal is not emitted. To catch errors please
|
||||
* connect to the result() signal.
|
||||
*
|
||||
* @param data the data to write
|
||||
*/
|
||||
void write(const QByteArray &data);
|
||||
|
||||
/**
|
||||
* Closes the file KIO worker.
|
||||
*
|
||||
* The worker emits close() and result().
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* Seek
|
||||
*
|
||||
* The worker emits position() on successful seek to the specified \p offset.
|
||||
*
|
||||
* On error the position() signal is not emitted. To catch errors please
|
||||
* connect to the result() signal.
|
||||
*
|
||||
* @param offset the position from start to go to
|
||||
*/
|
||||
void seek(KIO::filesize_t offset);
|
||||
|
||||
/**
|
||||
* Truncate
|
||||
*
|
||||
* The worker emits truncated() on successful truncation to the specified \p length.
|
||||
*
|
||||
* On error the truncated() signal is not emitted. To catch errors please
|
||||
* connect to the result() signal.
|
||||
*
|
||||
* @param length the desired length to truncate to
|
||||
* @since 5.66
|
||||
*/
|
||||
void truncate(KIO::filesize_t length);
|
||||
|
||||
/**
|
||||
* Size
|
||||
*
|
||||
* @return the file size
|
||||
*/
|
||||
KIO::filesize_t size();
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Data from the worker has arrived. Emitted after read().
|
||||
*
|
||||
* Unless a read() request was sent for 0 bytes, End of data (EOD) has been
|
||||
* reached if data.size() == 0
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
* @param data data received from the worker.
|
||||
*
|
||||
*/
|
||||
void data(KIO::Job *job, const QByteArray &data);
|
||||
|
||||
/**
|
||||
* Signals the file is a redirection.
|
||||
* Follow this url manually to reach data
|
||||
* @param job the job that emitted this signal
|
||||
* @param url the new URL
|
||||
*/
|
||||
void redirection(KIO::Job *job, const QUrl &url);
|
||||
|
||||
/**
|
||||
* MIME type determined.
|
||||
* @param job the job that emitted this signal
|
||||
* @param mimeType the MIME type
|
||||
* @since 5.78
|
||||
*/
|
||||
void mimeTypeFound(KIO::Job *job, const QString &mimeType);
|
||||
|
||||
/**
|
||||
* File is open, metadata has been determined and the
|
||||
* file KIO worker is ready to receive commands.
|
||||
* @param job the job that emitted this signal
|
||||
*/
|
||||
void open(KIO::Job *job);
|
||||
|
||||
/**
|
||||
* \p written bytes were written to the file. Emitted after write().
|
||||
* @param job the job that emitted this signal
|
||||
* @param written bytes written.
|
||||
*/
|
||||
void written(KIO::Job *job, KIO::filesize_t written);
|
||||
|
||||
/**
|
||||
* Signals that the file is closed and will accept no more commands.
|
||||
*
|
||||
* @param job the job that emitted this signal
|
||||
*
|
||||
* @since 5.79
|
||||
*/
|
||||
void fileClosed(KIO::Job *job);
|
||||
|
||||
/**
|
||||
* The file has reached this position. Emitted after seek().
|
||||
* @param job the job that emitted this signal
|
||||
* @param offset the new position
|
||||
*/
|
||||
void position(KIO::Job *job, KIO::filesize_t offset);
|
||||
|
||||
/**
|
||||
* The file has been truncated to this point. Emitted after truncate().
|
||||
* @param job the job that emitted this signal
|
||||
* @param length the new length of the file
|
||||
* @since 5.66
|
||||
*/
|
||||
void truncated(KIO::Job *job, KIO::filesize_t length);
|
||||
|
||||
protected:
|
||||
KIOCORE_NO_EXPORT explicit FileJob(FileJobPrivate &dd);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(FileJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Open ( random access I/O )
|
||||
*
|
||||
* The file-job emits open() when opened
|
||||
*
|
||||
* On error the open() signal is not emitted. To catch errors please
|
||||
* connect to the result() signal.
|
||||
*
|
||||
* @param url the URL of the file
|
||||
* @param mode the access privileges: see \ref OpenMode
|
||||
*
|
||||
* @return The file-handling job. It will never return 0. Errors are handled asynchronously
|
||||
* (emitted as signals).
|
||||
*/
|
||||
KIOCORE_EXPORT FileJob *open(const QUrl &url, QIODevice::OpenMode mode);
|
||||
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2014 Mathias Tillman <master.homer@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "filesystemfreespacejob.h"
|
||||
#include "job.h"
|
||||
#include "job_p.h"
|
||||
#include <worker_p.h>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
class KIO::FileSystemFreeSpaceJobPrivate : public SimpleJobPrivate
|
||||
{
|
||||
public:
|
||||
FileSystemFreeSpaceJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs)
|
||||
: SimpleJobPrivate(url, command, packedArgs)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Called by the scheduler when a @p worker gets to
|
||||
* work on this job.
|
||||
* @param worker the worker that starts working on this job
|
||||
*/
|
||||
void start(Worker *worker) override;
|
||||
|
||||
Q_DECLARE_PUBLIC(FileSystemFreeSpaceJob)
|
||||
|
||||
static inline FileSystemFreeSpaceJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs)
|
||||
{
|
||||
FileSystemFreeSpaceJob *job = new FileSystemFreeSpaceJob(*new FileSystemFreeSpaceJobPrivate(url, command, packedArgs));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
return job;
|
||||
}
|
||||
KIO::filesize_t size = -1;
|
||||
KIO::filesize_t availableSize = -1;
|
||||
};
|
||||
|
||||
FileSystemFreeSpaceJob::FileSystemFreeSpaceJob(FileSystemFreeSpaceJobPrivate &dd)
|
||||
: SimpleJob(dd)
|
||||
{
|
||||
}
|
||||
|
||||
FileSystemFreeSpaceJob::~FileSystemFreeSpaceJob()
|
||||
{
|
||||
}
|
||||
KIO::filesize_t FileSystemFreeSpaceJob::size() const
|
||||
{
|
||||
Q_D(const FileSystemFreeSpaceJob);
|
||||
return d->size;
|
||||
}
|
||||
KIO::filesize_t FileSystemFreeSpaceJob::availableSize() const
|
||||
{
|
||||
Q_D(const FileSystemFreeSpaceJob);
|
||||
return d->availableSize;
|
||||
}
|
||||
|
||||
void FileSystemFreeSpaceJobPrivate::start(Worker *worker)
|
||||
{
|
||||
SimpleJobPrivate::start(worker);
|
||||
}
|
||||
|
||||
void FileSystemFreeSpaceJob::slotFinished()
|
||||
{
|
||||
Q_D(FileSystemFreeSpaceJob);
|
||||
const QString totalStr = queryMetaData(QStringLiteral("total"));
|
||||
const QString availableStr = queryMetaData(QStringLiteral("available"));
|
||||
|
||||
if (availableStr.isEmpty()) { // CopyJob only cares for available. "total" is optional
|
||||
setError(KIO::ERR_UNSUPPORTED_ACTION);
|
||||
}
|
||||
d->size = totalStr.toULongLong();
|
||||
d->availableSize = availableStr.toULongLong();
|
||||
|
||||
// Return worker to the scheduler
|
||||
SimpleJob::slotFinished();
|
||||
}
|
||||
|
||||
KIO::FileSystemFreeSpaceJob *KIO::fileSystemFreeSpace(const QUrl &url)
|
||||
{
|
||||
KIO_ARGS << url;
|
||||
return FileSystemFreeSpaceJobPrivate::newJob(url, CMD_FILESYSTEMFREESPACE, packedArgs);
|
||||
}
|
||||
|
||||
#include "moc_filesystemfreespacejob.cpp"
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2014 Mathias Tillman <master.homer@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef FILESYSTEMFREESPACEJOB_H
|
||||
#define FILESYSTEMFREESPACEJOB_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include "simplejob.h"
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class FileSystemFreeSpaceJobPrivate;
|
||||
/**
|
||||
* @class KIO::FileSystemFreeSpaceJob filesystemfreespacejob.h <KIO/FileSystemFreeSpaceJob>
|
||||
*
|
||||
* A KIO job that retrieves the total and available size of a filesystem.
|
||||
* @since 5.3
|
||||
*/
|
||||
class KIOCORE_EXPORT FileSystemFreeSpaceJob : public SimpleJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
~FileSystemFreeSpaceJob() override;
|
||||
|
||||
/**
|
||||
* Get total amount of space
|
||||
* @since 6.0
|
||||
*/
|
||||
KIO::filesize_t size() const;
|
||||
|
||||
/**
|
||||
* Get available amount of space
|
||||
* @since 6.0
|
||||
*/
|
||||
KIO::filesize_t availableSize() const;
|
||||
|
||||
public:
|
||||
KIOCORE_NO_EXPORT explicit FileSystemFreeSpaceJob(FileSystemFreeSpaceJobPrivate &dd);
|
||||
|
||||
private:
|
||||
void slotFinished() override;
|
||||
Q_DECLARE_PRIVATE(FileSystemFreeSpaceJob)
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a filesystem's total and available space.
|
||||
*
|
||||
* @param url Url to the filesystem.
|
||||
* @return the job handling the operation.
|
||||
*/
|
||||
KIOCORE_EXPORT FileSystemFreeSpaceJob *fileSystemFreeSpace(const QUrl &url);
|
||||
|
||||
}
|
||||
|
||||
#endif /* FILESYSTEMFREESPACEJOB_H */
|
||||
@@ -0,0 +1,475 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2004 Kevin Ottens <ervin@ipsquad.net>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "forwardingworkerbase.h"
|
||||
#include "../utils_p.h"
|
||||
|
||||
#include "deletejob.h"
|
||||
#include "filecopyjob.h"
|
||||
#include "kiocoredebug.h"
|
||||
#include "listjob.h"
|
||||
#include "mimetypejob.h"
|
||||
#include "mkdirjob.h"
|
||||
#include "statjob.h"
|
||||
#include "transferjob.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QMimeDatabase>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class ForwardingWorkerBasePrivate
|
||||
{
|
||||
public:
|
||||
ForwardingWorkerBasePrivate(const QByteArray &protocol, QObject *eventLoopParent, ForwardingWorkerBase *qq)
|
||||
: q(qq)
|
||||
, m_protocol(QString::fromUtf8(protocol))
|
||||
, eventLoop(eventLoopParent)
|
||||
{
|
||||
}
|
||||
ForwardingWorkerBase *const q;
|
||||
|
||||
const QString m_protocol;
|
||||
QUrl m_processedURL;
|
||||
QUrl m_requestedURL;
|
||||
|
||||
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) const;
|
||||
void _k_slotInfoMessage(KJob *job, const QString &msg) const;
|
||||
void _k_slotTotalSize(KJob *job, qulonglong size) const;
|
||||
void _k_slotProcessedSize(KJob *job, qulonglong size) const;
|
||||
void _k_slotSpeed(KJob *job, unsigned long bytesPerSecond) const;
|
||||
|
||||
// KIO::SimpleJob subclasses
|
||||
void _k_slotRedirection(KIO::Job *job, const QUrl &url);
|
||||
|
||||
// KIO::ListJob
|
||||
void _k_slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries) const;
|
||||
|
||||
// KIO::TransferJob
|
||||
void _k_slotData(KIO::Job *job, const QByteArray &data) const;
|
||||
void _k_slotDataReq(KIO::Job *job, QByteArray &data) const;
|
||||
void _k_slotMimetype(KIO::Job *job, const QString &type) const;
|
||||
void _k_slotCanResume(KIO::Job *job, KIO::filesize_t offset) const;
|
||||
|
||||
[[nodiscard]] WorkerResult loopResult()
|
||||
{
|
||||
eventLoop.exec();
|
||||
return m_pendingResult;
|
||||
}
|
||||
|
||||
private:
|
||||
// These are intentionally private to force us to go through [[nodiscard]] helper functions, lest we forget to retrieve the result.
|
||||
QEventLoop eventLoop;
|
||||
WorkerResult m_pendingResult = WorkerResult::pass();
|
||||
};
|
||||
|
||||
ForwardingWorkerBase::ForwardingWorkerBase(const QByteArray &protocol, const QByteArray &poolSocket, const QByteArray &appSocket)
|
||||
: WorkerBase(protocol, poolSocket, appSocket)
|
||||
, d(new ForwardingWorkerBasePrivate(protocol, this, this))
|
||||
{
|
||||
}
|
||||
|
||||
ForwardingWorkerBase::~ForwardingWorkerBase() = default;
|
||||
|
||||
bool ForwardingWorkerBasePrivate::internalRewriteUrl(const QUrl &url, QUrl &newURL)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (url.scheme() == m_protocol) {
|
||||
result = q->rewriteUrl(url, newURL);
|
||||
} else {
|
||||
newURL = url;
|
||||
}
|
||||
|
||||
m_processedURL = newURL;
|
||||
m_requestedURL = url;
|
||||
return result;
|
||||
}
|
||||
|
||||
void ForwardingWorkerBase::adjustUDSEntry(KIO::UDSEntry &entry, UDSEntryCreationMode creationMode) const
|
||||
{
|
||||
const bool listing = (creationMode == UDSEntryCreationInListDir);
|
||||
// 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(Utils::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(Utils::concatPaths(new_url.path(), url.fileName()));
|
||||
} else if (listing) {
|
||||
new_url.setPath(Utils::concatPaths(new_url.path(), name));
|
||||
}
|
||||
|
||||
QMimeDatabase db;
|
||||
mimetype = db.mimeTypeForUrl(new_url).name();
|
||||
|
||||
entry.replace(KIO::UDSEntry::UDS_MIME_TYPE, mimetype);
|
||||
|
||||
// qDebug() << "New MIME type = " << mimetype;
|
||||
}
|
||||
|
||||
if (d->m_processedURL.isLocalFile()) {
|
||||
QUrl new_url(d->m_processedURL);
|
||||
if (listing) {
|
||||
new_url.setPath(Utils::concatPaths(new_url.path(), name));
|
||||
}
|
||||
|
||||
entry.replace(KIO::UDSEntry::UDS_LOCAL_PATH, new_url.toLocalFile());
|
||||
}
|
||||
}
|
||||
|
||||
QUrl ForwardingWorkerBase::processedUrl() const
|
||||
{
|
||||
return d->m_processedURL;
|
||||
}
|
||||
|
||||
QUrl ForwardingWorkerBase::requestedUrl() const
|
||||
{
|
||||
return d->m_requestedURL;
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::get(const QUrl &url)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::TransferJob *job = KIO::get(new_url, NoReload, HideProgressInfo);
|
||||
d->connectTransferJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::put(const QUrl &url, int permissions, JobFlags flags)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::TransferJob *job = KIO::put(new_url, permissions, flags | HideProgressInfo);
|
||||
d->connectTransferJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::stat(const QUrl &url)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::SimpleJob *job = KIO::stat(new_url, KIO::HideProgressInfo);
|
||||
d->connectSimpleJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::mimetype(const QUrl &url)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::TransferJob *job = KIO::mimetype(new_url, KIO::HideProgressInfo);
|
||||
d->connectTransferJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::listDir(const QUrl &url)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::ListJob *job = KIO::listDir(new_url, KIO::HideProgressInfo);
|
||||
d->connectListJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::mkdir(const QUrl &url, int permissions)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::SimpleJob *job = KIO::mkdir(new_url, permissions);
|
||||
d->connectSimpleJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_MALFORMED_URL, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::rename(const QUrl &src, const QUrl &dest, JobFlags flags)
|
||||
{
|
||||
qCDebug(KIO_CORE) << "rename" << src << dest;
|
||||
|
||||
QUrl new_src;
|
||||
QUrl new_dest;
|
||||
if (!d->internalRewriteUrl(src, new_src)) {
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString());
|
||||
}
|
||||
if (d->internalRewriteUrl(dest, new_dest)) {
|
||||
KIO::Job *job = KIO::rename(new_src, new_dest, flags);
|
||||
d->connectJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_MALFORMED_URL, dest.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::symlink(const QString &target, const QUrl &dest, JobFlags flags)
|
||||
{
|
||||
qCDebug(KIO_CORE) << "symlink" << target << dest;
|
||||
|
||||
QUrl new_dest;
|
||||
if (d->internalRewriteUrl(dest, new_dest)) {
|
||||
KIO::SimpleJob *job = KIO::symlink(target, new_dest, flags | HideProgressInfo);
|
||||
d->connectSimpleJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_MALFORMED_URL, dest.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::chmod(const QUrl &url, int permissions)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::SimpleJob *job = KIO::chmod(new_url, permissions);
|
||||
d->connectSimpleJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::setModificationTime(const QUrl &url, const QDateTime &mtime)
|
||||
{
|
||||
QUrl new_url;
|
||||
if (d->internalRewriteUrl(url, new_url)) {
|
||||
KIO::SimpleJob *job = KIO::setModificationTime(new_url, mtime);
|
||||
d->connectSimpleJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags)
|
||||
{
|
||||
qCDebug(KIO_CORE) << "copy" << src << dest;
|
||||
|
||||
QUrl new_src;
|
||||
QUrl new_dest;
|
||||
if (!d->internalRewriteUrl(src, new_src)) {
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, src.toDisplayString());
|
||||
}
|
||||
if (d->internalRewriteUrl(dest, new_dest)) {
|
||||
KIO::Job *job = KIO::file_copy(new_src, new_dest, permissions, flags | HideProgressInfo);
|
||||
d->connectJob(job);
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_MALFORMED_URL, dest.toDisplayString());
|
||||
}
|
||||
|
||||
WorkerResult ForwardingWorkerBase::del(const QUrl &url, bool isfile)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
return d->loopResult();
|
||||
}
|
||||
return WorkerResult::fail(KIO::ERR_DOES_NOT_EXIST, url.toDisplayString());
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ForwardingWorkerBasePrivate::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());
|
||||
|
||||
q->connect(job, &KJob::result, q, [this](KJob *job) {
|
||||
_k_slotResult(job);
|
||||
});
|
||||
q->connect(job, &KJob::warning, q, [this](KJob *job, const QString &text) {
|
||||
_k_slotWarning(job, text);
|
||||
});
|
||||
q->connect(job, &KJob::infoMessage, q, [this](KJob *job, const QString &info) {
|
||||
_k_slotInfoMessage(job, info);
|
||||
});
|
||||
q->connect(job, &KJob::totalSize, q, [this](KJob *job, qulonglong size) {
|
||||
_k_slotTotalSize(job, size);
|
||||
});
|
||||
q->connect(job, &KJob::processedSize, q, [this](KJob *job, qulonglong size) {
|
||||
_k_slotProcessedSize(job, size);
|
||||
});
|
||||
q->connect(job, &KJob::speed, q, [this](KJob *job, ulong speed) {
|
||||
_k_slotSpeed(job, speed);
|
||||
});
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::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 ForwardingWorkerBasePrivate::connectListJob(KIO::ListJob *job)
|
||||
{
|
||||
connectSimpleJob(job);
|
||||
q->connect(job, &KIO::ListJob::entries, q, [this](KIO::Job *job, const KIO::UDSEntryList &entries) {
|
||||
_k_slotEntries(job, entries);
|
||||
});
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::connectTransferJob(KIO::TransferJob *job)
|
||||
{
|
||||
connectSimpleJob(job);
|
||||
q->connect(job, &KIO::TransferJob::data, q, [this](KIO::Job *job, const QByteArray &data) {
|
||||
_k_slotData(job, data);
|
||||
});
|
||||
q->connect(job, &KIO::TransferJob::dataReq, q, [this](KIO::Job *job, QByteArray &data) {
|
||||
_k_slotDataReq(job, data);
|
||||
});
|
||||
q->connect(job, &KIO::TransferJob::mimeTypeFound, q, [this](KIO::Job *job, const QString &mimeType) {
|
||||
_k_slotMimetype(job, mimeType);
|
||||
});
|
||||
q->connect(job, &KIO::TransferJob::canResume, q, [this](KIO::Job *job, KIO::filesize_t offset) {
|
||||
_k_slotCanResume(job, offset);
|
||||
});
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotResult(KJob *job)
|
||||
{
|
||||
if (job->error() != 0) {
|
||||
m_pendingResult = WorkerResult::fail(job->error(), job->errorText());
|
||||
} else {
|
||||
if (auto stat_job = qobject_cast<KIO::StatJob *>(job)) {
|
||||
KIO::UDSEntry entry = stat_job->statResult();
|
||||
q->adjustUDSEntry(entry, ForwardingWorkerBase::UDSEntryCreationInStat);
|
||||
q->statEntry(entry);
|
||||
}
|
||||
m_pendingResult = WorkerResult::pass();
|
||||
}
|
||||
|
||||
eventLoop.exit();
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotWarning(KJob * /*job*/, const QString &msg) const
|
||||
{
|
||||
q->warning(msg);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotInfoMessage(KJob * /*job*/, const QString &msg) const
|
||||
{
|
||||
q->infoMessage(msg);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotTotalSize(KJob * /*job*/, qulonglong size) const
|
||||
{
|
||||
q->totalSize(size);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotProcessedSize(KJob * /*job*/, qulonglong size) const
|
||||
{
|
||||
q->processedSize(size);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotSpeed(KJob * /*job*/, unsigned long bytesPerSecond) const
|
||||
{
|
||||
q->speed(bytesPerSecond);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotRedirection(KIO::Job *job, const QUrl &url)
|
||||
{
|
||||
q->redirection(url);
|
||||
|
||||
// We've been redirected stop everything.
|
||||
job->kill(KJob::Quietly);
|
||||
m_pendingResult = WorkerResult::pass();
|
||||
|
||||
eventLoop.exit();
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotEntries(KIO::Job * /*job*/, const KIO::UDSEntryList &entries) const
|
||||
{
|
||||
KIO::UDSEntryList final_entries = entries;
|
||||
|
||||
for (auto &entry : final_entries) {
|
||||
q->adjustUDSEntry(entry, ForwardingWorkerBase::UDSEntryCreationInListDir);
|
||||
}
|
||||
|
||||
q->listEntries(final_entries);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotData(KIO::Job * /*job*/, const QByteArray &_data) const
|
||||
{
|
||||
q->data(_data);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotDataReq(KIO::Job * /*job*/, QByteArray &data) const
|
||||
{
|
||||
q->dataReq();
|
||||
q->readData(data);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotMimetype(KIO::Job * /*job*/, const QString &type) const
|
||||
{
|
||||
q->mimeType(type);
|
||||
}
|
||||
|
||||
void ForwardingWorkerBasePrivate::_k_slotCanResume(KIO::Job * /*job*/, KIO::filesize_t offset) const
|
||||
{
|
||||
q->canResume(offset);
|
||||
}
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#include "moc_forwardingworkerbase.cpp"
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2004 Kevin Ottens <ervin@ipsquad.net>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef _FORWARDING_WORKER_BASE_H_
|
||||
#define _FORWARDING_WORKER_BASE_H_
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <kio/workerbase.h>
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class ForwardingWorkerBasePrivate;
|
||||
|
||||
/**
|
||||
* @class KIO::ForwardingWorkerBase forwardingworkerbase.h <KIO/ForwardingWorkerBase>
|
||||
*
|
||||
* This class should be used as a base for KIO workers acting as a
|
||||
* forwarder to other KIO workers. It has been designed to support only
|
||||
* local filesystem like KIO workers.
|
||||
*
|
||||
* If the resulting KIO worker should be a simple proxy, you only need
|
||||
* to implement the ForwardingWorkerBase::rewriteUrl() method.
|
||||
*
|
||||
* For more advanced behavior, the classic KIO worker methods should
|
||||
* be reimplemented, because their default behavior in this class
|
||||
* is to forward using the ForwardingWorkerBase::rewriteUrl() method.
|
||||
*
|
||||
* A possible code snippet for an advanced stat() behavior would look
|
||||
* like this in the child class:
|
||||
*
|
||||
* \code
|
||||
* WorkerResult ChildProtocol::stat(const QUrl &url)
|
||||
* {
|
||||
* bool is_special = false;
|
||||
*
|
||||
* // Process the URL to see if it should have
|
||||
* // a special treatment
|
||||
*
|
||||
* if (is_special) {
|
||||
* // Handle the URL ourselves
|
||||
* KIO::UDSEntry entry;
|
||||
* // Fill entry with values
|
||||
* statEntry(entry);
|
||||
* return WorkerResult::pass();
|
||||
* }
|
||||
* // Setup the KIO worker internal state if
|
||||
* // required by ChildProtocol::rewriteUrl()
|
||||
* return ForwardingWorkerBase::stat(url);
|
||||
* }
|
||||
* \endcode
|
||||
*
|
||||
* Of course in this case, you surely need to reimplement listDir()
|
||||
* and get() accordingly.
|
||||
*
|
||||
* If you want view on directories to be correctly refreshed when
|
||||
* something changes on a forwarded URL, you'll need a companion kded
|
||||
* module to emit the KDirNotify Files*() D-Bus signals.
|
||||
*
|
||||
* @see ForwardingWorkerBase::rewriteUrl()
|
||||
* @author Kevin Ottens <ervin@ipsquad.net>
|
||||
* @since 5.101
|
||||
*/
|
||||
class KIOCORE_EXPORT ForwardingWorkerBase : public QObject, public WorkerBase
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ForwardingWorkerBase(const QByteArray &protocol, const QByteArray &poolSocket, const QByteArray &appSocket);
|
||||
~ForwardingWorkerBase() override;
|
||||
Q_DISABLE_COPY_MOVE(ForwardingWorkerBase)
|
||||
|
||||
WorkerResult get(const QUrl &url) override;
|
||||
WorkerResult put(const QUrl &url, int permissions, JobFlags flags) override;
|
||||
WorkerResult stat(const QUrl &url) override;
|
||||
WorkerResult mimetype(const QUrl &url) override;
|
||||
WorkerResult listDir(const QUrl &url) override;
|
||||
WorkerResult mkdir(const QUrl &url, int permissions) override;
|
||||
WorkerResult rename(const QUrl &src, const QUrl &dest, JobFlags flags) override;
|
||||
WorkerResult symlink(const QString &target, const QUrl &dest, JobFlags flags) override;
|
||||
WorkerResult chmod(const QUrl &url, int permissions) override;
|
||||
WorkerResult setModificationTime(const QUrl &url, const QDateTime &mtime) override;
|
||||
WorkerResult copy(const QUrl &src, const QUrl &dest, int permissions, JobFlags flags) override;
|
||||
WorkerResult del(const QUrl &url, bool isfile) override;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Rewrite an url to its forwarded counterpart. It should return
|
||||
* true if everything was ok, and false otherwise.
|
||||
*
|
||||
* If a problem is detected it's up to this method to trigger error()
|
||||
* before returning. Returning false silently cancels the current
|
||||
* KIO worker operation.
|
||||
*
|
||||
* @param url The URL as given during the KIO worker call
|
||||
* @param newURL The new URL to forward the KIO worker call to
|
||||
* @return true if the given url could be correctly rewritten
|
||||
*/
|
||||
virtual bool rewriteUrl(const QUrl &url, QUrl &newURL) = 0;
|
||||
|
||||
enum UDSEntryCreationMode {
|
||||
UDSEntryCreationInStat, ///< The entry is created during a stat operation.
|
||||
UDSEntryCreationInListDir, ///< The entry is created during a listDir operation.
|
||||
};
|
||||
|
||||
/**
|
||||
* Adjusts a UDSEntry before it's sent in the reply to the KIO worker endpoint.
|
||||
* This is the default implementation working in most cases, but sometimes
|
||||
* you could make use of more forwarding black magic (for example
|
||||
* dynamically transform any desktop file into a fake directory...)
|
||||
*
|
||||
* @param entry the UDSEntry to adjust
|
||||
* @param creationMode the operation for which this entry is created
|
||||
*/
|
||||
virtual void adjustUDSEntry(KIO::UDSEntry &entry, UDSEntryCreationMode creationMode) const;
|
||||
|
||||
/**
|
||||
* Return the URL being processed by the KIO worker
|
||||
* Only access it inside adjustUDSEntry()
|
||||
*/
|
||||
QUrl processedUrl() const;
|
||||
|
||||
/**
|
||||
* Return the URL asked to the KIO worker
|
||||
* Only access it inside adjustUDSEntry()
|
||||
*/
|
||||
QUrl requestedUrl() const;
|
||||
|
||||
private:
|
||||
Q_PRIVATE_SLOT(d, void _k_slotRedirection(KIO::Job *, QUrl))
|
||||
friend class ForwardingWorkerBasePrivate;
|
||||
std::unique_ptr<ForwardingWorkerBasePrivate> const d;
|
||||
};
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,254 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "global.h"
|
||||
#include "faviconscache_p.h"
|
||||
#include "kioglobal_p.h"
|
||||
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KFileUtils>
|
||||
#include <KFormat>
|
||||
#include <KLocalizedString>
|
||||
#include <KSharedConfig>
|
||||
#include <QMimeDatabase>
|
||||
#include <QUrl>
|
||||
#include <kfileitem.h>
|
||||
#include <kprotocolinfo.h>
|
||||
|
||||
#include "kiocoredebug.h"
|
||||
|
||||
KFormat::BinaryUnitDialect _k_loadBinaryDialect();
|
||||
Q_GLOBAL_STATIC_WITH_ARGS(KFormat::BinaryUnitDialect, _k_defaultBinaryDialect, (_k_loadBinaryDialect()))
|
||||
|
||||
KFormat::BinaryUnitDialect _k_loadBinaryDialect()
|
||||
{
|
||||
KConfigGroup mainGroup(KSharedConfig::openConfig(), QStringLiteral("Locale"));
|
||||
|
||||
KFormat::BinaryUnitDialect dialect(KFormat::BinaryUnitDialect(mainGroup.readEntry("BinaryUnitDialect", int(KFormat::DefaultBinaryDialect))));
|
||||
dialect = static_cast<KFormat::BinaryUnitDialect>(mainGroup.readEntry("BinaryUnitDialect", int(dialect)));
|
||||
|
||||
// Error checking
|
||||
if (dialect <= KFormat::DefaultBinaryDialect || dialect > KFormat::LastBinaryDialect) {
|
||||
dialect = KFormat::IECBinaryDialect;
|
||||
}
|
||||
|
||||
return dialect;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QString KIO::convertSize(KIO::filesize_t fileSize)
|
||||
{
|
||||
const KFormat::BinaryUnitDialect dialect = *_k_defaultBinaryDialect();
|
||||
|
||||
return KFormat().formatByteSize(fileSize, 1, dialect);
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QString KIO::convertSizeFromKiB(KIO::filesize_t kibSize)
|
||||
{
|
||||
return convertSize(kibSize * 1024);
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QString KIO::number(KIO::filesize_t size)
|
||||
{
|
||||
return QString::number(size);
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT unsigned int KIO::calculateRemainingSeconds(KIO::filesize_t totalSize, KIO::filesize_t processedSize, KIO::filesize_t speed)
|
||||
{
|
||||
if ((speed != 0) && (totalSize != 0)) {
|
||||
return (totalSize - processedSize) / speed;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QString KIO::convertSeconds(unsigned int seconds)
|
||||
{
|
||||
unsigned int days = seconds / 86400;
|
||||
unsigned int hours = (seconds - (days * 86400)) / 3600;
|
||||
unsigned int mins = (seconds - (days * 86400) - (hours * 3600)) / 60;
|
||||
seconds = (seconds - (days * 86400) - (hours * 3600) - (mins * 60));
|
||||
|
||||
const QTime time(hours, mins, seconds);
|
||||
const QString timeStr(time.toString(QStringLiteral("hh:mm:ss")));
|
||||
if (days > 0) {
|
||||
return i18np("1 day %2", "%1 days %2", days, timeStr);
|
||||
} else {
|
||||
return timeStr;
|
||||
}
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QString KIO::itemsSummaryString(uint items, uint files, uint dirs, KIO::filesize_t size, bool showSize)
|
||||
{
|
||||
if (files == 0 && dirs == 0 && items == 0) {
|
||||
return i18np("%1 Item", "%1 Items", 0);
|
||||
}
|
||||
|
||||
QString summary;
|
||||
const QString foldersText = i18np("1 Folder", "%1 Folders", dirs);
|
||||
const QString filesText = i18np("1 File", "%1 Files", files);
|
||||
if (files > 0 && dirs > 0) {
|
||||
summary = showSize ? i18nc("folders, files (size)", "%1, %2 (%3)", foldersText, filesText, KIO::convertSize(size))
|
||||
: i18nc("folders, files", "%1, %2", foldersText, filesText);
|
||||
} else if (files > 0) {
|
||||
summary = showSize ? i18nc("files (size)", "%1 (%2)", filesText, KIO::convertSize(size)) : filesText;
|
||||
} else if (dirs > 0) {
|
||||
summary = foldersText;
|
||||
}
|
||||
|
||||
if (items > dirs + files) {
|
||||
const QString itemsText = i18np("%1 Item", "%1 Items", items);
|
||||
summary = summary.isEmpty() ? itemsText : i18nc("items: folders, files (size)", "%1: %2", itemsText, summary);
|
||||
}
|
||||
|
||||
return summary;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QString KIO::encodeFileName(const QString &_str)
|
||||
{
|
||||
QString str(_str);
|
||||
str.replace(QLatin1Char('/'), QChar(0x2044)); // "Fraction slash"
|
||||
return str;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QString KIO::decodeFileName(const QString &_str)
|
||||
{
|
||||
// Nothing to decode. "Fraction slash" is fine in filenames.
|
||||
return _str;
|
||||
}
|
||||
|
||||
/***************************************************************
|
||||
*
|
||||
* Utility functions
|
||||
*
|
||||
***************************************************************/
|
||||
|
||||
#if KIOCORE_BUILD_DEPRECATED_SINCE(6, 6)
|
||||
KIO::CacheControl KIO::parseCacheControl(const QString &cacheControl)
|
||||
{
|
||||
QString tmp = cacheControl.toLower();
|
||||
|
||||
if (tmp == QLatin1String("cacheonly")) {
|
||||
return KIO::CC_CacheOnly;
|
||||
}
|
||||
if (tmp == QLatin1String("cache")) {
|
||||
return KIO::CC_Cache;
|
||||
}
|
||||
if (tmp == QLatin1String("verify")) {
|
||||
return KIO::CC_Verify;
|
||||
}
|
||||
if (tmp == QLatin1String("refresh")) {
|
||||
return KIO::CC_Refresh;
|
||||
}
|
||||
if (tmp == QLatin1String("reload")) {
|
||||
return KIO::CC_Reload;
|
||||
}
|
||||
|
||||
qCDebug(KIO_CORE) << "unrecognized Cache control option:" << cacheControl;
|
||||
return KIO::CC_Verify;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if KIOCORE_BUILD_DEPRECATED_SINCE(6, 6)
|
||||
QString KIO::getCacheControlString(KIO::CacheControl cacheControl)
|
||||
{
|
||||
if (cacheControl == KIO::CC_CacheOnly) {
|
||||
return QStringLiteral("CacheOnly");
|
||||
}
|
||||
if (cacheControl == KIO::CC_Cache) {
|
||||
return QStringLiteral("Cache");
|
||||
}
|
||||
if (cacheControl == KIO::CC_Verify) {
|
||||
return QStringLiteral("Verify");
|
||||
}
|
||||
if (cacheControl == KIO::CC_Refresh) {
|
||||
return QStringLiteral("Refresh");
|
||||
}
|
||||
if (cacheControl == KIO::CC_Reload) {
|
||||
return QStringLiteral("Reload");
|
||||
}
|
||||
qCDebug(KIO_CORE) << "unrecognized Cache control enum value:" << cacheControl;
|
||||
return QString();
|
||||
}
|
||||
#endif
|
||||
|
||||
QString KIO::favIconForUrl(const QUrl &url)
|
||||
{
|
||||
if (url.isLocalFile() || !url.scheme().startsWith(QLatin1String("http"))) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return FavIconsCache::instance()->iconForUrl(url);
|
||||
}
|
||||
|
||||
QString KIO::iconNameForUrl(const QUrl &url)
|
||||
{
|
||||
if (url.scheme().isEmpty()) { // empty URL or relative URL (e.g. '~')
|
||||
return QStringLiteral("unknown");
|
||||
}
|
||||
QMimeDatabase db;
|
||||
const QMimeType mt = db.mimeTypeForUrl(url);
|
||||
QString iconName;
|
||||
|
||||
if (url.isLocalFile()) {
|
||||
// Check to see whether it's an xdg location (e.g. Pictures folder)
|
||||
if (mt.inherits(QStringLiteral("inode/directory"))) {
|
||||
iconName = KIOPrivate::iconForStandardPath(url.toLocalFile());
|
||||
}
|
||||
|
||||
// Let KFileItem::iconName handle things for us
|
||||
if (iconName.isEmpty()) {
|
||||
const KFileItem item(url, mt.name());
|
||||
iconName = item.iconName();
|
||||
}
|
||||
|
||||
} else {
|
||||
// It's non-local and maybe on a slow filesystem
|
||||
|
||||
// Look for a favicon
|
||||
if (url.scheme().startsWith(QLatin1String("http"))) {
|
||||
iconName = favIconForUrl(url);
|
||||
}
|
||||
|
||||
// Then handle the trash
|
||||
else if (url.scheme() == QLatin1String("trash")) {
|
||||
if (url.path().length() <= 1) { // trash:/ itself
|
||||
KConfig trashConfig(QStringLiteral("trashrc"), KConfig::SimpleConfig);
|
||||
iconName =
|
||||
trashConfig.group(QStringLiteral("Status")).readEntry("Empty", true) ? QStringLiteral("user-trash") : QStringLiteral("user-trash-full");
|
||||
} else { // url.path().length() > 1, it's a file/folder under trash:/
|
||||
iconName = mt.iconName();
|
||||
}
|
||||
}
|
||||
|
||||
// and other protocols
|
||||
if (iconName.isEmpty() && (mt.isDefault() || url.path().size() <= 1)) {
|
||||
iconName = KProtocolInfo::icon(url.scheme());
|
||||
}
|
||||
}
|
||||
// if we found nothing, return QMimeType.iconName()
|
||||
// (which fallbacks to "application-octet-stream" when no MIME type could be determined)
|
||||
return !iconName.isEmpty() ? iconName : mt.iconName();
|
||||
}
|
||||
|
||||
QUrl KIO::upUrl(const QUrl &url)
|
||||
{
|
||||
if (!url.isValid() || url.isRelative()) {
|
||||
return QUrl();
|
||||
}
|
||||
|
||||
QUrl u(url);
|
||||
if (url.hasQuery()) {
|
||||
u.setQuery(QString());
|
||||
return u;
|
||||
}
|
||||
if (url.hasFragment()) {
|
||||
u.setFragment(QString());
|
||||
}
|
||||
u = u.adjusted(QUrl::StripTrailingSlash); /// don't combine with the line below
|
||||
return u.adjusted(QUrl::RemoveFilename);
|
||||
}
|
||||
@@ -0,0 +1,359 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2005 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
#ifndef KIO_GLOBAL_H
|
||||
#define KIO_GLOBAL_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QFile> // for QFile::Permissions
|
||||
#include <QString>
|
||||
|
||||
#include <KJob>
|
||||
|
||||
class QUrl;
|
||||
class QTime;
|
||||
|
||||
#if defined(Q_OS_WIN) && defined(Q_CC_MSVC)
|
||||
// on windows ssize_t is not defined, only SSIZE_T exists
|
||||
#include <basetsd.h>
|
||||
typedef SSIZE_T ssize_t;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @short A namespace for KIO globals
|
||||
*
|
||||
*/
|
||||
namespace KIO
|
||||
{
|
||||
/// 64-bit file offset
|
||||
typedef qlonglong fileoffset_t;
|
||||
/// 64-bit file size
|
||||
typedef qulonglong filesize_t;
|
||||
|
||||
/**
|
||||
* Converts @p size from bytes to the string representation.
|
||||
*
|
||||
* @param size size in bytes
|
||||
* @return converted size as a string - e.g. 123.4 KiB , 12.0 MiB
|
||||
*/
|
||||
KIOCORE_EXPORT QString convertSize(KIO::filesize_t size);
|
||||
|
||||
/**
|
||||
* Converts a size to a string representation
|
||||
* Not unlike QString::number(...)
|
||||
*
|
||||
* @param size size in bytes
|
||||
* @return converted size as a string - e.g. 123456789
|
||||
*/
|
||||
KIOCORE_EXPORT QString number(KIO::filesize_t size);
|
||||
|
||||
/**
|
||||
* Converts size from kibi-bytes (2^10) to the string representation.
|
||||
*
|
||||
* @param kibSize size in kibi-bytes (2^10)
|
||||
* @return converted size as a string - e.g. 123.4 KiB , 12.0 MiB
|
||||
*/
|
||||
KIOCORE_EXPORT QString convertSizeFromKiB(KIO::filesize_t kibSize);
|
||||
|
||||
/**
|
||||
* Calculates remaining time in seconds from total size, processed size and speed.
|
||||
*
|
||||
* @param totalSize total size in bytes
|
||||
* @param processedSize processed size in bytes
|
||||
* @param speed speed in bytes per second
|
||||
* @return calculated remaining time in seconds
|
||||
*/
|
||||
KIOCORE_EXPORT unsigned int calculateRemainingSeconds(KIO::filesize_t totalSize, KIO::filesize_t processedSize, KIO::filesize_t speed);
|
||||
|
||||
/**
|
||||
* Convert @p seconds to a string representing number of days, hours, minutes and seconds
|
||||
*
|
||||
* @param seconds number of seconds to convert
|
||||
* @return string representation in a locale depending format
|
||||
*/
|
||||
KIOCORE_EXPORT QString convertSeconds(unsigned int seconds);
|
||||
|
||||
/**
|
||||
* Helper for showing information about a set of files and directories
|
||||
* @param items the number of items (= @p files + @p dirs + number of symlinks :)
|
||||
* @param files the number of files
|
||||
* @param dirs the number of dirs
|
||||
* @param size the sum of the size of the @p files
|
||||
* @param showSize whether to show the size in the result
|
||||
* @return the summary string
|
||||
*/
|
||||
KIOCORE_EXPORT QString itemsSummaryString(uint items, uint files, uint dirs, KIO::filesize_t size, bool showSize);
|
||||
|
||||
/**
|
||||
* Encodes (from the text displayed to the real filename)
|
||||
* This translates '/' into a "unicode fraction slash", QChar(0x2044).
|
||||
* Used by KIO::link, for instance.
|
||||
* @param str the file name to encode
|
||||
* @return the encoded file name
|
||||
*/
|
||||
KIOCORE_EXPORT QString encodeFileName(const QString &str);
|
||||
/**
|
||||
* Decodes (from the filename to the text displayed)
|
||||
* This doesn't do anything anymore, it used to do the opposite of encodeFileName
|
||||
* when encodeFileName was using %2F for '/'.
|
||||
* @param str the file name to decode
|
||||
* @return the decoded file name
|
||||
*/
|
||||
KIOCORE_EXPORT QString decodeFileName(const QString &str);
|
||||
|
||||
/**
|
||||
* Error codes that can be emitted by KIO.
|
||||
*/
|
||||
enum Error {
|
||||
ERR_CANNOT_OPEN_FOR_READING = KJob::UserDefinedError + 1,
|
||||
ERR_CANNOT_OPEN_FOR_WRITING = KJob::UserDefinedError + 2,
|
||||
ERR_CANNOT_LAUNCH_PROCESS = KJob::UserDefinedError + 3,
|
||||
ERR_INTERNAL = KJob::UserDefinedError + 4,
|
||||
ERR_MALFORMED_URL = KJob::UserDefinedError + 5,
|
||||
ERR_UNSUPPORTED_PROTOCOL = KJob::UserDefinedError + 6,
|
||||
ERR_NO_SOURCE_PROTOCOL = KJob::UserDefinedError + 7,
|
||||
ERR_UNSUPPORTED_ACTION = KJob::UserDefinedError + 8,
|
||||
ERR_IS_DIRECTORY = KJob::UserDefinedError + 9, ///< ... where a file was expected
|
||||
ERR_IS_FILE = KJob::UserDefinedError + 10, ///< ... where a directory was expected (e.g.\ listing)
|
||||
ERR_DOES_NOT_EXIST = KJob::UserDefinedError + 11,
|
||||
ERR_FILE_ALREADY_EXIST = KJob::UserDefinedError + 12,
|
||||
ERR_DIR_ALREADY_EXIST = KJob::UserDefinedError + 13,
|
||||
ERR_UNKNOWN_HOST = KJob::UserDefinedError + 14,
|
||||
ERR_ACCESS_DENIED = KJob::UserDefinedError + 15,
|
||||
ERR_WRITE_ACCESS_DENIED = KJob::UserDefinedError + 16,
|
||||
ERR_CANNOT_ENTER_DIRECTORY = KJob::UserDefinedError + 17,
|
||||
ERR_PROTOCOL_IS_NOT_A_FILESYSTEM = KJob::UserDefinedError + 18,
|
||||
ERR_CYCLIC_LINK = KJob::UserDefinedError + 19,
|
||||
ERR_USER_CANCELED = KJob::KilledJobError,
|
||||
ERR_CYCLIC_COPY = KJob::UserDefinedError + 21,
|
||||
ERR_CANNOT_CREATE_SOCKET = KJob::UserDefinedError + 22,
|
||||
ERR_CANNOT_CONNECT = KJob::UserDefinedError + 23,
|
||||
ERR_CONNECTION_BROKEN = KJob::UserDefinedError + 24,
|
||||
ERR_NOT_FILTER_PROTOCOL = KJob::UserDefinedError + 25,
|
||||
ERR_CANNOT_MOUNT = KJob::UserDefinedError + 26,
|
||||
ERR_CANNOT_UNMOUNT = KJob::UserDefinedError + 27,
|
||||
ERR_CANNOT_READ = KJob::UserDefinedError + 28,
|
||||
ERR_CANNOT_WRITE = KJob::UserDefinedError + 29,
|
||||
ERR_CANNOT_BIND = KJob::UserDefinedError + 30,
|
||||
ERR_CANNOT_LISTEN = KJob::UserDefinedError + 31,
|
||||
ERR_CANNOT_ACCEPT = KJob::UserDefinedError + 32,
|
||||
ERR_CANNOT_LOGIN = KJob::UserDefinedError + 33,
|
||||
ERR_CANNOT_STAT = KJob::UserDefinedError + 34,
|
||||
ERR_CANNOT_CLOSEDIR = KJob::UserDefinedError + 35,
|
||||
ERR_CANNOT_MKDIR = KJob::UserDefinedError + 37,
|
||||
ERR_CANNOT_RMDIR = KJob::UserDefinedError + 38,
|
||||
ERR_CANNOT_RESUME = KJob::UserDefinedError + 39,
|
||||
ERR_CANNOT_RENAME = KJob::UserDefinedError + 40,
|
||||
ERR_CANNOT_CHMOD = KJob::UserDefinedError + 41,
|
||||
ERR_CANNOT_DELETE = KJob::UserDefinedError + 42,
|
||||
// The text argument is the protocol that the dead worker supported.
|
||||
// This means for example: file, ftp, http, ...
|
||||
ERR_WORKER_DIED = KJob::UserDefinedError + 43, ///< @since 5.96
|
||||
ERR_OUT_OF_MEMORY = KJob::UserDefinedError + 44,
|
||||
ERR_UNKNOWN_PROXY_HOST = KJob::UserDefinedError + 45,
|
||||
ERR_CANNOT_AUTHENTICATE = KJob::UserDefinedError + 46,
|
||||
ERR_ABORTED = KJob::UserDefinedError + 47, ///< Action got aborted from application side
|
||||
ERR_INTERNAL_SERVER = KJob::UserDefinedError + 48,
|
||||
ERR_SERVER_TIMEOUT = KJob::UserDefinedError + 49,
|
||||
ERR_SERVICE_NOT_AVAILABLE = KJob::UserDefinedError + 50,
|
||||
ERR_UNKNOWN = KJob::UserDefinedError + 51,
|
||||
// (was a warning) ERR_CHECKSUM_MISMATCH = 52,
|
||||
ERR_UNKNOWN_INTERRUPT = KJob::UserDefinedError + 53,
|
||||
ERR_CANNOT_DELETE_ORIGINAL = KJob::UserDefinedError + 54,
|
||||
ERR_CANNOT_DELETE_PARTIAL = KJob::UserDefinedError + 55,
|
||||
ERR_CANNOT_RENAME_ORIGINAL = KJob::UserDefinedError + 56,
|
||||
ERR_CANNOT_RENAME_PARTIAL = KJob::UserDefinedError + 57,
|
||||
ERR_NEED_PASSWD = KJob::UserDefinedError + 58,
|
||||
ERR_CANNOT_SYMLINK = KJob::UserDefinedError + 59,
|
||||
ERR_NO_CONTENT = KJob::UserDefinedError + 60, ///< Action succeeded but no content will follow.
|
||||
ERR_DISK_FULL = KJob::UserDefinedError + 61,
|
||||
ERR_IDENTICAL_FILES = KJob::UserDefinedError + 62, ///< src==dest when moving/copying
|
||||
/**
|
||||
* For worker specified errors that can be
|
||||
* rich text. Email links will be handled
|
||||
* by the standard email app and all hrefs
|
||||
* will be handled by the standard browser.
|
||||
* <a href="exec:/khelpcenter ?" will be
|
||||
* forked.
|
||||
* @since 5.96
|
||||
*/
|
||||
ERR_WORKER_DEFINED = KJob::UserDefinedError + 63,
|
||||
ERR_UPGRADE_REQUIRED = KJob::UserDefinedError + 64, ///< A transport upgrade is required to access this
|
||||
///< object. For instance, TLS is demanded by
|
||||
///< the server in order to continue.
|
||||
ERR_POST_DENIED = KJob::UserDefinedError + 65, ///< Issued when trying to POST data to a certain Ports
|
||||
// see job.cpp
|
||||
ERR_CANNOT_SEEK = KJob::UserDefinedError + 66,
|
||||
ERR_CANNOT_SETTIME = KJob::UserDefinedError + 67, ///< Emitted by setModificationTime
|
||||
ERR_CANNOT_CHOWN = KJob::UserDefinedError + 68,
|
||||
ERR_POST_NO_SIZE = KJob::UserDefinedError + 69,
|
||||
ERR_DROP_ON_ITSELF = KJob::UserDefinedError + 70, ///< from KIO::DropJob, @since 5.6
|
||||
ERR_CANNOT_MOVE_INTO_ITSELF = KJob::UserDefinedError + 71, ///< emitted by KIO::move, @since 5.18
|
||||
ERR_PASSWD_SERVER = KJob::UserDefinedError + 72, ///< returned by WorkerBase::openPasswordDialog, @since 5.24
|
||||
ERR_CANNOT_CREATE_WORKER = KJob::UserDefinedError + 73, ///< used by Worker::createWorker, @since 5.96
|
||||
ERR_FILE_TOO_LARGE_FOR_FAT32 = KJob::UserDefinedError + 74, ///< @since 5.54
|
||||
ERR_OWNER_DIED ///< Value used between kuiserver and views when the job owner disappears unexpectedly. It should not be emitted by workers. @since 5.54
|
||||
= KJob::UserDefinedError + 75,
|
||||
ERR_PRIVILEGE_NOT_REQUIRED = KJob::UserDefinedError + 76, ///< used by file ioworker, @since 5.60
|
||||
ERR_CANNOT_TRUNCATE = KJob::UserDefinedError + 77, // used by FileJob::truncate, @since 5.66
|
||||
/**
|
||||
* Indicates failure to create a symlink due to the underlying filesystem (FAT/ExFAT)
|
||||
* not supporting them. Used by e.g. CopyJob.
|
||||
* @since 5.88
|
||||
*/
|
||||
ERR_SYMLINKS_NOT_SUPPORTED = KJob::UserDefinedError + 78,
|
||||
|
||||
/**
|
||||
* Moving files/dirs to the Trash failed due to size constraints.
|
||||
*
|
||||
* @since 5.100
|
||||
*/
|
||||
ERR_TRASH_FILE_TOO_LARGE = KJob::UserDefinedError + 79,
|
||||
};
|
||||
|
||||
#if KIOCORE_ENABLE_DEPRECATED_SINCE(6, 6)
|
||||
/**
|
||||
* Specifies how to use the cache.
|
||||
* @see parseCacheControl()
|
||||
* @see getCacheControlString()
|
||||
* @deprecated since 6.6, not used
|
||||
*/
|
||||
KIOCORE_DEPRECATED_VERSION(6, 6, "Not used")
|
||||
enum CacheControl {
|
||||
CC_CacheOnly, ///< Fail request if not in cache
|
||||
CC_Cache, ///< Use cached entry if available
|
||||
CC_Verify, ///< Validate cached entry with remote site if expired
|
||||
CC_Refresh, ///< Always validate cached entry with remote site
|
||||
CC_Reload, ///< Always fetch from remote site.
|
||||
};
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Specifies privilege file operation status.
|
||||
* @since 5.43
|
||||
*/
|
||||
enum PrivilegeOperationStatus {
|
||||
OperationAllowed = 1,
|
||||
OperationCanceled,
|
||||
OperationNotAllowed,
|
||||
};
|
||||
|
||||
/**
|
||||
* Describes the fields that a stat command will retrieve
|
||||
* @see UDSEntry
|
||||
* @see StatDetails
|
||||
* @since 5.69
|
||||
*/
|
||||
enum StatDetail {
|
||||
/// No field returned, useful to check if a file exists
|
||||
StatNoDetails = 0x0,
|
||||
/// Filename, access, type, size, linkdest
|
||||
StatBasic = 0x1,
|
||||
/// uid, gid
|
||||
StatUser = 0x2,
|
||||
/// atime, mtime, btime
|
||||
StatTime = 0x4,
|
||||
/// Resolve symlinks
|
||||
StatResolveSymlink = 0x8,
|
||||
/// ACL data
|
||||
StatAcl = 0x10,
|
||||
/// dev, inode
|
||||
StatInode = 0x20,
|
||||
/// Recursive size
|
||||
/// @since 5.70
|
||||
StatRecursiveSize = 0x40,
|
||||
/// MIME type
|
||||
/// @since 5.82
|
||||
StatMimeType = 0x80,
|
||||
|
||||
/// Default StatDetail flag when creating a @c StatJob.
|
||||
/// Equivalent to setting <tt>StatBasic | StatUser | StatTime | StatAcl | StatResolveSymlink</tt>
|
||||
StatDefaultDetails = StatBasic | StatUser | StatTime | StatAcl | StatResolveSymlink,
|
||||
};
|
||||
/**
|
||||
* Stores a combination of #StatDetail values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(StatDetails, StatDetail)
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KIO::StatDetails)
|
||||
|
||||
#if KIOCORE_ENABLE_DEPRECATED_SINCE(6, 6)
|
||||
/**
|
||||
* Parses the string representation of the cache control option.
|
||||
*
|
||||
* @param cacheControl the string representation
|
||||
* @return the cache control value
|
||||
* @see getCacheControlString()
|
||||
* @deprecated since 6.6, not used
|
||||
*/
|
||||
|
||||
KIOCORE_EXPORT KIO::CacheControl parseCacheControl(const QString &cacheControl);
|
||||
#endif
|
||||
|
||||
#if KIOCORE_ENABLE_DEPRECATED_SINCE(6, 6)
|
||||
/**
|
||||
* Returns a string representation of the given cache control method.
|
||||
*
|
||||
* @param cacheControl the cache control method
|
||||
* @return the string representation
|
||||
* @see parseCacheControl()
|
||||
* @deprecated since 6.6, not used
|
||||
*/
|
||||
KIOCORE_DEPRECATED_VERSION(6, 6, "Not used")
|
||||
KIOCORE_EXPORT QString getCacheControlString(KIO::CacheControl cacheControl);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Return the "favicon" (see http://www.favicon.com) for the given @p url,
|
||||
* if available. Does NOT attempt to download the favicon, it only returns
|
||||
* one that is already available.
|
||||
*
|
||||
* If unavailable, returns QString().
|
||||
* Use KIO::FavIconRequestJob instead of this method if you can wait
|
||||
* for the favicon to be downloaded.
|
||||
*
|
||||
* @param url the URL of the favicon
|
||||
* @return the path to the icon (to be passed to QIcon()), or QString()
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
KIOCORE_EXPORT QString favIconForUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Converts KIO file permissions from mode_t to QFile::Permissions format.
|
||||
*
|
||||
* This is a convenience function for converting KIO permissions parameter from
|
||||
* mode_t to QFile::Permissions.
|
||||
*
|
||||
* @param permissions KIO file permissions.
|
||||
*
|
||||
* @return -1 if @p permissions is -1, otherwise its OR'ed QFile::Permission equivalent.
|
||||
*/
|
||||
KIOCORE_EXPORT QFile::Permissions convertPermissions(int permissions);
|
||||
|
||||
/**
|
||||
* Return the icon name for a URL.
|
||||
* Most of the time this returns the MIME type icon,
|
||||
* but also has fallback to favicon and protocol-specific icon.
|
||||
*
|
||||
* Pass this to QIcon::fromTheme().
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
KIOCORE_EXPORT QString iconNameForUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* This function is useful to implement the "Up" button in a file manager for example.
|
||||
*
|
||||
* @return a URL that is a level higher
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
KIOCORE_EXPORT QUrl upUrl(const QUrl &url);
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,381 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Roland Harnau <tau@gmx.eu>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "hostinfo.h"
|
||||
|
||||
#include <QCache>
|
||||
#include <QFutureWatcher>
|
||||
#include <QHash>
|
||||
#include <QHostInfo>
|
||||
#include <QList>
|
||||
#include <QMetaType>
|
||||
#include <QPair>
|
||||
#include <QSemaphore>
|
||||
#include <QThread>
|
||||
#include <QTime>
|
||||
#include <QTimer>
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
#ifdef Q_OS_UNIX
|
||||
#include <QFileInfo>
|
||||
#include <arpa/nameser.h>
|
||||
#include <netinet/in.h>
|
||||
#include <resolv.h> // for _PATH_RESCONF
|
||||
#ifndef _PATH_RESCONF
|
||||
#define _PATH_RESCONF "/etc/resolv.conf"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
static constexpr int TTL = 300;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class HostInfoAgentPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
class Query;
|
||||
|
||||
public:
|
||||
explicit HostInfoAgentPrivate(int cacheSize = 100);
|
||||
~HostInfoAgentPrivate() override
|
||||
{
|
||||
}
|
||||
void lookupHost(const QString &hostName, QObject *receiver, const char *member);
|
||||
QHostInfo lookupCachedHostInfoFor(const QString &hostName);
|
||||
void cacheLookup(const QHostInfo &);
|
||||
private Q_SLOTS:
|
||||
void queryFinished(const QHostInfo &info, Query *sender);
|
||||
|
||||
private:
|
||||
class Result;
|
||||
|
||||
QHash<QString, Query *> openQueries;
|
||||
struct HostCacheInfo {
|
||||
QHostInfo hostInfo;
|
||||
QTime time;
|
||||
};
|
||||
QCache<QString, HostCacheInfo> dnsCache;
|
||||
QDateTime resolvConfMTime;
|
||||
};
|
||||
|
||||
class HostInfoAgentPrivate::Result : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_SIGNALS:
|
||||
void result(const QHostInfo &);
|
||||
|
||||
private:
|
||||
friend class HostInfoAgentPrivate;
|
||||
};
|
||||
|
||||
class HostInfoAgentPrivate::Query : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Query()
|
||||
: m_watcher()
|
||||
, m_hostName()
|
||||
{
|
||||
connect(&m_watcher, &QFutureWatcher<QHostInfo>::finished, this, &Query::relayFinished);
|
||||
}
|
||||
void start(const QString &hostName)
|
||||
{
|
||||
m_hostName = hostName;
|
||||
QFuture<QHostInfo> future = QtConcurrent::run(&QHostInfo::fromName, hostName);
|
||||
m_watcher.setFuture(future);
|
||||
}
|
||||
QString hostName() const
|
||||
{
|
||||
return m_hostName;
|
||||
}
|
||||
Q_SIGNALS:
|
||||
void result(const QHostInfo &);
|
||||
private Q_SLOTS:
|
||||
void relayFinished()
|
||||
{
|
||||
Q_EMIT result(m_watcher.result());
|
||||
}
|
||||
|
||||
private:
|
||||
QFutureWatcher<QHostInfo> m_watcher;
|
||||
QString m_hostName;
|
||||
};
|
||||
|
||||
class NameLookupThreadRequest
|
||||
{
|
||||
public:
|
||||
NameLookupThreadRequest(const QString &hostName)
|
||||
: m_hostName(hostName)
|
||||
{
|
||||
}
|
||||
|
||||
QSemaphore *semaphore()
|
||||
{
|
||||
return &m_semaphore;
|
||||
}
|
||||
|
||||
QHostInfo result() const
|
||||
{
|
||||
return m_hostInfo;
|
||||
}
|
||||
|
||||
void setResult(const QHostInfo &hostInfo)
|
||||
{
|
||||
m_hostInfo = hostInfo;
|
||||
}
|
||||
|
||||
QString hostName() const
|
||||
{
|
||||
return m_hostName;
|
||||
}
|
||||
|
||||
int lookupId() const
|
||||
{
|
||||
return m_lookupId;
|
||||
}
|
||||
|
||||
void setLookupId(int id)
|
||||
{
|
||||
m_lookupId = id;
|
||||
}
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(NameLookupThreadRequest)
|
||||
QString m_hostName;
|
||||
QSemaphore m_semaphore;
|
||||
QHostInfo m_hostInfo;
|
||||
int m_lookupId;
|
||||
};
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(std::shared_ptr<KIO::NameLookupThreadRequest>)
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class NameLookUpThreadWorker : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public Q_SLOTS:
|
||||
void lookupHost(const std::shared_ptr<KIO::NameLookupThreadRequest> &request)
|
||||
{
|
||||
const QString hostName = request->hostName();
|
||||
const int lookupId = QHostInfo::lookupHost(hostName, this, SLOT(lookupFinished(QHostInfo)));
|
||||
request->setLookupId(lookupId);
|
||||
m_lookups.insert(lookupId, request);
|
||||
}
|
||||
|
||||
void abortLookup(const std::shared_ptr<KIO::NameLookupThreadRequest> &request)
|
||||
{
|
||||
QHostInfo::abortHostLookup(request->lookupId());
|
||||
m_lookups.remove(request->lookupId());
|
||||
}
|
||||
|
||||
void lookupFinished(const QHostInfo &hostInfo)
|
||||
{
|
||||
auto it = m_lookups.find(hostInfo.lookupId());
|
||||
if (it != m_lookups.end()) {
|
||||
(*it)->setResult(hostInfo);
|
||||
(*it)->semaphore()->release();
|
||||
m_lookups.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
QMap<int, std::shared_ptr<NameLookupThreadRequest>> m_lookups;
|
||||
};
|
||||
|
||||
class NameLookUpThread : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
NameLookUpThread()
|
||||
: m_worker(nullptr)
|
||||
{
|
||||
qRegisterMetaType<std::shared_ptr<NameLookupThreadRequest>>();
|
||||
start();
|
||||
}
|
||||
|
||||
~NameLookUpThread() override
|
||||
{
|
||||
quit();
|
||||
wait();
|
||||
}
|
||||
|
||||
NameLookUpThreadWorker *worker()
|
||||
{
|
||||
return m_worker;
|
||||
}
|
||||
|
||||
QSemaphore *semaphore()
|
||||
{
|
||||
return &m_semaphore;
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
NameLookUpThreadWorker worker;
|
||||
m_worker = &worker;
|
||||
m_semaphore.release();
|
||||
exec();
|
||||
}
|
||||
|
||||
private:
|
||||
NameLookUpThreadWorker *m_worker;
|
||||
QSemaphore m_semaphore;
|
||||
};
|
||||
}
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
Q_GLOBAL_STATIC(HostInfoAgentPrivate, hostInfoAgentPrivate)
|
||||
Q_GLOBAL_STATIC(NameLookUpThread, nameLookUpThread)
|
||||
|
||||
void HostInfo::lookupHost(const QString &hostName, QObject *receiver, const char *member)
|
||||
{
|
||||
hostInfoAgentPrivate()->lookupHost(hostName, receiver, member);
|
||||
}
|
||||
|
||||
QHostInfo HostInfo::lookupHost(const QString &hostName, unsigned long timeout)
|
||||
{
|
||||
// Do not perform a reverse lookup here...
|
||||
QHostAddress address(hostName);
|
||||
QHostInfo hostInfo;
|
||||
if (!address.isNull()) {
|
||||
QList<QHostAddress> addressList;
|
||||
addressList << address;
|
||||
hostInfo.setAddresses(addressList);
|
||||
return hostInfo;
|
||||
}
|
||||
|
||||
// Look up the name in the KIO DNS cache...
|
||||
hostInfo = HostInfo::lookupCachedHostInfoFor(hostName);
|
||||
if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) {
|
||||
return hostInfo;
|
||||
}
|
||||
|
||||
// Failing all of the above, do the lookup...
|
||||
std::shared_ptr<NameLookupThreadRequest> request = std::make_shared<NameLookupThreadRequest>(hostName);
|
||||
nameLookUpThread()->semaphore()->acquire();
|
||||
nameLookUpThread()->semaphore()->release();
|
||||
NameLookUpThreadWorker *worker = nameLookUpThread()->worker();
|
||||
auto lookupFunc = [worker, request]() {
|
||||
worker->lookupHost(request);
|
||||
};
|
||||
QMetaObject::invokeMethod(worker, lookupFunc, Qt::QueuedConnection);
|
||||
if (request->semaphore()->tryAcquire(1, timeout)) {
|
||||
hostInfo = request->result();
|
||||
if (!hostInfo.hostName().isEmpty() && hostInfo.error() == QHostInfo::NoError) {
|
||||
HostInfo::cacheLookup(hostInfo); // cache the look up...
|
||||
}
|
||||
} else {
|
||||
auto abortFunc = [worker, request]() {
|
||||
worker->abortLookup(request);
|
||||
};
|
||||
QMetaObject::invokeMethod(worker, abortFunc, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// qDebug() << "Name look up succeeded for" << hostName;
|
||||
return hostInfo;
|
||||
}
|
||||
|
||||
QHostInfo HostInfo::lookupCachedHostInfoFor(const QString &hostName)
|
||||
{
|
||||
return hostInfoAgentPrivate()->lookupCachedHostInfoFor(hostName);
|
||||
}
|
||||
|
||||
void HostInfo::cacheLookup(const QHostInfo &info)
|
||||
{
|
||||
hostInfoAgentPrivate()->cacheLookup(info);
|
||||
}
|
||||
|
||||
HostInfoAgentPrivate::HostInfoAgentPrivate(int cacheSize)
|
||||
: openQueries()
|
||||
, dnsCache(cacheSize)
|
||||
{
|
||||
qRegisterMetaType<QHostInfo>();
|
||||
}
|
||||
|
||||
void HostInfoAgentPrivate::lookupHost(const QString &hostName, QObject *receiver, const char *member)
|
||||
{
|
||||
#ifdef _PATH_RESCONF
|
||||
QFileInfo resolvConf(QFile::decodeName(_PATH_RESCONF));
|
||||
QDateTime currentMTime = resolvConf.lastModified();
|
||||
if (resolvConf.exists() && currentMTime != resolvConfMTime) {
|
||||
// /etc/resolv.conf has been modified
|
||||
// clear our cache
|
||||
resolvConfMTime = currentMTime;
|
||||
dnsCache.clear();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (HostCacheInfo *info = dnsCache.object(hostName)) {
|
||||
if (QTime::currentTime() <= info->time.addSecs(TTL)) {
|
||||
Result result;
|
||||
if (receiver) {
|
||||
QObject::connect(&result, SIGNAL(result(QHostInfo)), receiver, member);
|
||||
Q_EMIT result.result(info->hostInfo);
|
||||
}
|
||||
return;
|
||||
}
|
||||
dnsCache.remove(hostName);
|
||||
}
|
||||
|
||||
if (Query *query = openQueries.value(hostName)) {
|
||||
if (receiver) {
|
||||
connect(query, SIGNAL(result(QHostInfo)), receiver, member);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Query *query = new Query();
|
||||
openQueries.insert(hostName, query);
|
||||
connect(query, &Query::result, this, [this, query](const QHostInfo &info) {
|
||||
queryFinished(info, query);
|
||||
});
|
||||
if (receiver) {
|
||||
connect(query, SIGNAL(result(QHostInfo)), receiver, member);
|
||||
}
|
||||
query->start(hostName);
|
||||
}
|
||||
|
||||
QHostInfo HostInfoAgentPrivate::lookupCachedHostInfoFor(const QString &hostName)
|
||||
{
|
||||
HostCacheInfo *info = dnsCache.object(hostName);
|
||||
if (info && info->time.addSecs(TTL) >= QTime::currentTime()) {
|
||||
return info->hostInfo;
|
||||
}
|
||||
|
||||
// not found in dnsCache
|
||||
QHostInfo hostInfo;
|
||||
hostInfo.setError(QHostInfo::HostNotFound);
|
||||
return hostInfo;
|
||||
}
|
||||
|
||||
void HostInfoAgentPrivate::cacheLookup(const QHostInfo &info)
|
||||
{
|
||||
if (info.hostName().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.error() != QHostInfo::NoError) {
|
||||
return;
|
||||
}
|
||||
|
||||
dnsCache.insert(info.hostName(), new HostCacheInfo{info, QTime::currentTime()});
|
||||
}
|
||||
|
||||
void HostInfoAgentPrivate::queryFinished(const QHostInfo &info, Query *sender)
|
||||
{
|
||||
const auto host = sender->hostName();
|
||||
openQueries.remove(host);
|
||||
if (info.error() == QHostInfo::NoError) {
|
||||
dnsCache.insert(host, new HostCacheInfo{info, QTime::currentTime()});
|
||||
}
|
||||
sender->deleteLater();
|
||||
}
|
||||
|
||||
#include "hostinfo.moc"
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2008 Roland Harnau <tau@gmx.eu>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef HOSTINFO_H_
|
||||
#define HOSTINFO_H_
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
class QHostInfo;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* @internal
|
||||
* WARNING: this could disappear at some point in time.
|
||||
* DO NOT USE outside KDE Frameworks
|
||||
*/
|
||||
/*
|
||||
* TODO KF6: This header is intenionally not installed in KF6, and we should look into unexporting
|
||||
* and removing most of these functions; they are only used internally in very few places where
|
||||
* they might even be inlined to.
|
||||
*/
|
||||
namespace HostInfo
|
||||
{
|
||||
/// @internal
|
||||
void lookupHost(const QString &hostName, QObject *receiver, const char *member);
|
||||
/// @internal
|
||||
KIOCORE_EXPORT QHostInfo lookupHost(const QString &hostName, unsigned long timeout);
|
||||
/// @internal
|
||||
KIOCORE_EXPORT QHostInfo lookupCachedHostInfoFor(const QString &hostName);
|
||||
/// @internal
|
||||
KIOCORE_EXPORT void cacheLookup(const QHostInfo &info);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2002 Jan-Pascal van Best <janpascal@vanbest.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIOWORKER_HTTPMETHOD_P_H_
|
||||
#define KIOWORKER_HTTPMETHOD_P_H_
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/** HTTP / DAV method **/
|
||||
enum HTTP_METHOD {
|
||||
HTTP_GET,
|
||||
HTTP_PUT,
|
||||
HTTP_POST,
|
||||
HTTP_HEAD,
|
||||
HTTP_DELETE,
|
||||
HTTP_OPTIONS,
|
||||
DAV_PROPFIND,
|
||||
DAV_PROPPATCH,
|
||||
DAV_MKCOL,
|
||||
DAV_COPY,
|
||||
DAV_MOVE,
|
||||
DAV_LOCK,
|
||||
DAV_UNLOCK,
|
||||
DAV_SEARCH,
|
||||
DAV_SUBSCRIBE,
|
||||
DAV_UNSUBSCRIBE,
|
||||
DAV_POLL,
|
||||
DAV_NOTIFY,
|
||||
DAV_REPORT,
|
||||
HTTP_UNKNOWN = -1,
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef _KIO_IOWORKER_DEFAULTS_H
|
||||
#define _KIO_IOWORKER_DEFAULTS_H
|
||||
|
||||
// TIMEOUT VALUES
|
||||
static constexpr int DEFAULT_RESPONSE_TIMEOUT = 600; // 10 min.
|
||||
static constexpr int DEFAULT_CONNECT_TIMEOUT = 20; // 20 secs.
|
||||
static constexpr int DEFAULT_READ_TIMEOUT = 15; // 15 secs.
|
||||
static constexpr int DEFAULT_PROXY_CONNECT_TIMEOUT = 10; // 10 secs.
|
||||
static constexpr int MIN_TIMEOUT_VALUE = 2; // 2 secs.
|
||||
|
||||
// MINIMUM SIZE FOR ABORTED DOWNLOAD TO BE KEPT
|
||||
static constexpr int DEFAULT_MINIMUM_KEEP_SIZE = 5120; // 5 Kbs
|
||||
|
||||
// NORMAL PORT DEFAULTS
|
||||
static constexpr int DEFAULT_FTP_PORT = 21;
|
||||
static constexpr int DEFAULT_SFTP_PORT = 22;
|
||||
static constexpr int DEFAULT_SMTP_PORT = 25;
|
||||
static constexpr int DEFAULT_HTTP_PORT = 80;
|
||||
static constexpr int DEFAULT_POP3_PORT = 110;
|
||||
static constexpr int DEFAULT_NNTP_PORT = 119;
|
||||
static constexpr int DEFAULT_IMAP_PORT = 143;
|
||||
static constexpr int DEFAULT_IMAP3_PORT = 220;
|
||||
static constexpr int DEFAULT_LDAP_PORT = 389;
|
||||
|
||||
// SECURE PORT DEFAULTS
|
||||
static constexpr int DEFAULT_HTTPS_PORT = 443;
|
||||
static constexpr int DEFAULT_NNTPS_PORT = 563;
|
||||
static constexpr int DEFAULT_LDAPS_PORT = 389;
|
||||
static constexpr int DEFAULT_IMAPS_PORT = 993;
|
||||
static constexpr int DEFAULT_POP3S_PORT = 995;
|
||||
|
||||
// OTHER GENERIC PORT DEFAULTS
|
||||
static constexpr int DEFAULT_PROXY_PORT = 8080;
|
||||
static constexpr int MAX_PORT_VALUE = 65535;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "job.h"
|
||||
#include "job_p.h"
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include <KLocalizedString>
|
||||
#include <KStringHandler>
|
||||
|
||||
#include "worker_p.h"
|
||||
#include <kio/jobuidelegateextension.h>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
Job::Job()
|
||||
: KCompositeJob(nullptr)
|
||||
, d_ptr(new JobPrivate)
|
||||
{
|
||||
d_ptr->q_ptr = this;
|
||||
setCapabilities(KJob::Killable | KJob::Suspendable);
|
||||
}
|
||||
|
||||
Job::Job(JobPrivate &dd)
|
||||
: KCompositeJob(nullptr)
|
||||
, d_ptr(&dd)
|
||||
{
|
||||
d_ptr->q_ptr = this;
|
||||
setCapabilities(KJob::Killable | KJob::Suspendable);
|
||||
}
|
||||
|
||||
Job::~Job()
|
||||
{
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
JobUiDelegateExtension *Job::uiDelegateExtension() const
|
||||
{
|
||||
Q_D(const Job);
|
||||
return d->m_uiDelegateExtension;
|
||||
}
|
||||
|
||||
void Job::setUiDelegateExtension(JobUiDelegateExtension *extension)
|
||||
{
|
||||
Q_D(Job);
|
||||
d->m_uiDelegateExtension = extension;
|
||||
}
|
||||
|
||||
bool Job::addSubjob(KJob *jobBase)
|
||||
{
|
||||
// qDebug() << "addSubjob(" << jobBase << ") this=" << this;
|
||||
|
||||
bool ok = KCompositeJob::addSubjob(jobBase);
|
||||
KIO::Job *job = qobject_cast<KIO::Job *>(jobBase);
|
||||
if (ok && job) {
|
||||
// Copy metadata into subjob (e.g. window-id, user-timestamp etc.)
|
||||
Q_D(Job);
|
||||
job->mergeMetaData(d->m_outgoingMetaData);
|
||||
|
||||
// Forward information from that subjob.
|
||||
connect(job, &KJob::speed, this, [this](KJob *job, ulong speed) {
|
||||
Q_UNUSED(job);
|
||||
emitSpeed(speed);
|
||||
});
|
||||
job->setProperty("widget", property("widget")); // see KJobWidgets
|
||||
job->setProperty("window", property("window")); // see KJobWidgets
|
||||
job->setProperty("userTimestamp", property("userTimestamp")); // see KJobWidgets
|
||||
job->setUiDelegateExtension(d->m_uiDelegateExtension);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool Job::removeSubjob(KJob *jobBase)
|
||||
{
|
||||
// qDebug() << "removeSubjob(" << jobBase << ") this=" << this << "subjobs=" << subjobs().count();
|
||||
return KCompositeJob::removeSubjob(jobBase);
|
||||
}
|
||||
|
||||
static QString url_description_string(const QUrl &url)
|
||||
{
|
||||
return url.scheme() == QLatin1String("data") ? QStringLiteral("data:[...]") : KStringHandler::csqueeze(url.toDisplayString(QUrl::PreferLocalFile), 100);
|
||||
}
|
||||
|
||||
KIO::JobPrivate::~JobPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
void JobPrivate::emitMoving(KIO::Job *job, const QUrl &src, const QUrl &dest)
|
||||
{
|
||||
static const QString s_title = i18nc("@title job", "Moving");
|
||||
static const QString s_source = i18nc("The source of a file operation", "Source");
|
||||
static const QString s_destination = i18nc("The destination of a file operation", "Destination");
|
||||
Q_EMIT job->description(job, s_title, qMakePair(s_source, url_description_string(src)), qMakePair(s_destination, url_description_string(dest)));
|
||||
}
|
||||
|
||||
void JobPrivate::emitRenaming(KIO::Job *job, const QUrl &src, const QUrl &dest)
|
||||
{
|
||||
static const QString s_title = i18nc("@title job", "Renaming");
|
||||
static const QString s_source = i18nc("The source of a file operation", "Source");
|
||||
static const QString s_destination = i18nc("The destination of a file operation", "Destination");
|
||||
Q_EMIT job->description(job, s_title, {s_source, url_description_string(src)}, {s_destination, url_description_string(dest)});
|
||||
}
|
||||
|
||||
void JobPrivate::emitCopying(KIO::Job *job, const QUrl &src, const QUrl &dest)
|
||||
{
|
||||
static const QString s_title = i18nc("@title job", "Copying");
|
||||
static const QString s_source = i18nc("The source of a file operation", "Source");
|
||||
static const QString s_destination = i18nc("The destination of a file operation", "Destination");
|
||||
Q_EMIT job->description(job, s_title, qMakePair(s_source, url_description_string(src)), qMakePair(s_destination, url_description_string(dest)));
|
||||
}
|
||||
|
||||
void JobPrivate::emitCreatingDir(KIO::Job *job, const QUrl &dir)
|
||||
{
|
||||
static const QString s_title = i18nc("@title job", "Creating directory");
|
||||
static const QString s_directory = i18n("Directory");
|
||||
Q_EMIT job->description(job, s_title, qMakePair(s_directory, url_description_string(dir)));
|
||||
}
|
||||
|
||||
void JobPrivate::emitDeleting(KIO::Job *job, const QUrl &url)
|
||||
{
|
||||
static const QString s_title = i18nc("@title job", "Deleting");
|
||||
static const QString s_file = i18n("File");
|
||||
Q_EMIT job->description(job, s_title, qMakePair(s_file, url_description_string(url)));
|
||||
}
|
||||
|
||||
void JobPrivate::emitStating(KIO::Job *job, const QUrl &url)
|
||||
{
|
||||
static const QString s_title = i18nc("@title job", "Examining");
|
||||
static const QString s_file = i18n("File");
|
||||
Q_EMIT job->description(job, s_title, qMakePair(s_file, url_description_string(url)));
|
||||
}
|
||||
|
||||
void JobPrivate::emitTransferring(KIO::Job *job, const QUrl &url)
|
||||
{
|
||||
static const QString s_title = i18nc("@title job", "Transferring");
|
||||
static const QString s_source = i18nc("The source of a file operation", "Source");
|
||||
Q_EMIT job->description(job, s_title, qMakePair(s_source, url_description_string(url)));
|
||||
}
|
||||
|
||||
void JobPrivate::emitMounting(KIO::Job *job, const QString &dev, const QString &point)
|
||||
{
|
||||
Q_EMIT job->description(job, i18nc("@title job", "Mounting"), qMakePair(i18n("Device"), dev), qMakePair(i18n("Mountpoint"), point));
|
||||
}
|
||||
|
||||
void JobPrivate::emitUnmounting(KIO::Job *job, const QString &point)
|
||||
{
|
||||
Q_EMIT job->description(job, i18nc("@title job", "Unmounting"), qMakePair(i18n("Mountpoint"), point));
|
||||
}
|
||||
|
||||
bool Job::doKill()
|
||||
{
|
||||
// kill all subjobs, without triggering their result slot
|
||||
for (KJob *job : subjobs()) {
|
||||
job->kill(KJob::Quietly);
|
||||
}
|
||||
clearSubjobs();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Job::doSuspend()
|
||||
{
|
||||
for (KJob *job : subjobs()) {
|
||||
if (!job->suspend()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Job::doResume()
|
||||
{
|
||||
for (KJob *job : subjobs()) {
|
||||
if (!job->resume()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Job::errorString is implemented in job_error.cpp
|
||||
|
||||
void Job::setParentJob(Job *job)
|
||||
{
|
||||
Q_D(Job);
|
||||
Q_ASSERT(d->m_parentJob == nullptr);
|
||||
Q_ASSERT(job);
|
||||
d->m_parentJob = job;
|
||||
}
|
||||
|
||||
Job *Job::parentJob() const
|
||||
{
|
||||
return d_func()->m_parentJob;
|
||||
}
|
||||
|
||||
MetaData Job::metaData() const
|
||||
{
|
||||
return d_func()->m_incomingMetaData;
|
||||
}
|
||||
|
||||
QString Job::queryMetaData(const QString &key)
|
||||
{
|
||||
return d_func()->m_incomingMetaData.value(key, QString());
|
||||
}
|
||||
|
||||
void Job::setMetaData(const KIO::MetaData &_metaData)
|
||||
{
|
||||
Q_D(Job);
|
||||
d->m_outgoingMetaData = _metaData;
|
||||
}
|
||||
|
||||
void Job::addMetaData(const QString &key, const QString &value)
|
||||
{
|
||||
d_func()->m_outgoingMetaData.insert(key, value);
|
||||
}
|
||||
|
||||
void Job::addMetaData(const QMap<QString, QString> &values)
|
||||
{
|
||||
Q_D(Job);
|
||||
for (auto it = values.cbegin(); it != values.cend(); ++it) {
|
||||
d->m_outgoingMetaData.insert(it.key(), it.value());
|
||||
}
|
||||
}
|
||||
|
||||
void Job::mergeMetaData(const QMap<QString, QString> &values)
|
||||
{
|
||||
Q_D(Job);
|
||||
for (auto it = values.cbegin(); it != values.cend(); ++it) {
|
||||
// there's probably a faster way
|
||||
if (!d->m_outgoingMetaData.contains(it.key())) {
|
||||
d->m_outgoingMetaData.insert(it.key(), it.value());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MetaData Job::outgoingMetaData() const
|
||||
{
|
||||
return d_func()->m_outgoingMetaData;
|
||||
}
|
||||
|
||||
QByteArray JobPrivate::privilegeOperationData()
|
||||
{
|
||||
PrivilegeOperationStatus status = OperationNotAllowed;
|
||||
|
||||
if (m_parentJob) {
|
||||
QByteArray jobData = m_parentJob->d_func()->privilegeOperationData();
|
||||
// Copy meta-data from parent job
|
||||
m_incomingMetaData.insert(QStringLiteral("TestData"), m_parentJob->queryMetaData(QStringLiteral("TestData")));
|
||||
return jobData;
|
||||
} else {
|
||||
if (m_privilegeExecutionEnabled) {
|
||||
status = OperationAllowed;
|
||||
switch (m_operationType) {
|
||||
case ChangeAttr:
|
||||
m_title = i18n("Change Attribute");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to change file attributes. "
|
||||
"Do you want to continue?");
|
||||
break;
|
||||
case Copy:
|
||||
m_title = i18n("Copy Files");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to complete the copy operation. "
|
||||
"Do you want to continue?");
|
||||
break;
|
||||
case Delete:
|
||||
m_title = i18n("Delete Files");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to complete the delete operation. "
|
||||
"However, doing so may damage your system. Do you want to continue?");
|
||||
break;
|
||||
case MkDir:
|
||||
m_title = i18n("Create Folder");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to create this folder. "
|
||||
"Do you want to continue?");
|
||||
break;
|
||||
case Move:
|
||||
m_title = i18n("Move Items");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to complete the move operation. "
|
||||
"Do you want to continue?");
|
||||
break;
|
||||
case Rename:
|
||||
m_title = i18n("Rename");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to complete renaming. "
|
||||
"Do you want to continue?");
|
||||
break;
|
||||
case Symlink:
|
||||
m_title = i18n("Create Symlink");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to create a symlink. "
|
||||
"Do you want to continue?");
|
||||
break;
|
||||
case Transfer:
|
||||
m_title = i18n("Transfer data");
|
||||
m_message = i18n(
|
||||
"Root privileges are required to complete transferring data. "
|
||||
"Do you want to continue?");
|
||||
Q_FALLTHROUGH();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_outgoingMetaData.value(QStringLiteral("UnitTesting")) == QLatin1String("true")) {
|
||||
// Set meta-data for the top-level job
|
||||
m_incomingMetaData.insert(QStringLiteral("TestData"), QStringLiteral("PrivilegeOperationAllowed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QByteArray parentJobData;
|
||||
QDataStream ds(&parentJobData, QIODevice::WriteOnly);
|
||||
ds << status << m_title << m_message;
|
||||
return parentJobData;
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
class KIO::DirectCopyJobPrivate : public KIO::SimpleJobPrivate
|
||||
{
|
||||
public:
|
||||
DirectCopyJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs)
|
||||
: SimpleJobPrivate(url, command, packedArgs)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Called by the scheduler when a @p worker gets to
|
||||
* work on this job.
|
||||
* @param worker the worker that starts working on this job
|
||||
*/
|
||||
void start(Worker *worker) override;
|
||||
|
||||
Q_DECLARE_PUBLIC(DirectCopyJob)
|
||||
};
|
||||
|
||||
DirectCopyJob::DirectCopyJob(const QUrl &url, const QByteArray &packedArgs)
|
||||
: SimpleJob(*new DirectCopyJobPrivate(url, CMD_COPY, packedArgs))
|
||||
{
|
||||
setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
}
|
||||
|
||||
DirectCopyJob::~DirectCopyJob()
|
||||
{
|
||||
}
|
||||
|
||||
void DirectCopyJobPrivate::start(Worker *worker)
|
||||
{
|
||||
Q_Q(DirectCopyJob);
|
||||
q->connect(worker, &WorkerInterface::canResume, q, &DirectCopyJob::slotCanResume);
|
||||
SimpleJobPrivate::start(worker);
|
||||
}
|
||||
|
||||
void DirectCopyJob::slotCanResume(KIO::filesize_t offset)
|
||||
{
|
||||
Q_EMIT canResume(this, offset);
|
||||
}
|
||||
|
||||
//////////////////////////
|
||||
|
||||
SimpleJob *KIO::file_delete(const QUrl &src, JobFlags flags)
|
||||
{
|
||||
KIO_ARGS << src << qint8(true); // isFile
|
||||
SimpleJob *job = SimpleJobPrivate::newJob(src, CMD_DEL, packedArgs, flags);
|
||||
if (job->uiDelegateExtension()) {
|
||||
job->uiDelegateExtension()->createClipboardUpdater(job, JobUiDelegateExtension::RemoveContent);
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
//////////
|
||||
////
|
||||
|
||||
#include "moc_job_base.cpp"
|
||||
#include "moc_job_p.cpp"
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_JOB_H
|
||||
#define KIO_JOB_H
|
||||
|
||||
#include "job_base.h" // IWYU pragma: export
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QUrl>
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* Returns a translated error message for @p errorCode using the
|
||||
* additional error information provided by @p errorText.
|
||||
* @param errorCode the error code
|
||||
* @param errorText the additional error text
|
||||
* @return the created error string
|
||||
*/
|
||||
KIOCORE_EXPORT QString buildErrorString(int errorCode, const QString &errorText);
|
||||
|
||||
/**
|
||||
* Returns translated error details for @p errorCode using the
|
||||
* additional error information provided by @p errorText , @p reqUrl
|
||||
* (the request URL), and the KIO worker @p method .
|
||||
*
|
||||
* @param errorCode the error code
|
||||
* @param errorText the additional error text
|
||||
* @param reqUrl the request URL
|
||||
* @param method the KIO worker method
|
||||
* @return the following data:
|
||||
* @li QString errorName - the name of the error
|
||||
* @li QString techName - if not null, the more technical name of the error
|
||||
* @li QString description - a description of the error
|
||||
* @li QStringList causes - a list of possible causes of the error
|
||||
* @li QStringList solutions - a liso of solutions for the error
|
||||
*/
|
||||
KIOCORE_EXPORT QByteArray rawErrorDetail(int errorCode, const QString &errorText, const QUrl *reqUrl = nullptr, int method = -1);
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,290 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_JOB_BASE_H
|
||||
#define KIO_JOB_BASE_H
|
||||
|
||||
#include <KCompositeJob>
|
||||
#include <kio/metadata.h>
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class JobUiDelegateExtension;
|
||||
|
||||
class JobPrivate;
|
||||
/**
|
||||
* @class KIO::Job job_base.h <KIO/Job>
|
||||
*
|
||||
* The base class for all jobs.
|
||||
* For all jobs created in an application, the code looks like
|
||||
*
|
||||
* \code
|
||||
* KIO::Job* job = KIO::someoperation(some parameters);
|
||||
* connect(job, &KJob::result, this, &MyClass::slotResult);
|
||||
* \endcode
|
||||
* (other connects, specific to the job)
|
||||
*
|
||||
* And slotResult is usually at least:
|
||||
*
|
||||
* \code
|
||||
* void MyClass::slotResult(KJob *job)
|
||||
* {
|
||||
* if (job->error()) {
|
||||
* job->uiDelegate()->showErrorMessage();
|
||||
* }
|
||||
* }
|
||||
* \endcode
|
||||
* @see KIO::Scheduler
|
||||
*/
|
||||
class KIOCORE_EXPORT Job : public KCompositeJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
protected:
|
||||
Job();
|
||||
// used also from KIOGui's PreviewJob, so needs to be exported
|
||||
explicit Job(JobPrivate &dd);
|
||||
|
||||
public:
|
||||
~Job() override;
|
||||
void start() override
|
||||
{
|
||||
} // Since KIO autostarts its jobs
|
||||
|
||||
/**
|
||||
* Retrieves the UI delegate extension used by this job.
|
||||
* @since 5.0
|
||||
*/
|
||||
JobUiDelegateExtension *uiDelegateExtension() const;
|
||||
|
||||
/**
|
||||
* Sets the UI delegate extension to be used by this job.
|
||||
* The default UI delegate extension is KIO::defaultJobUiDelegateExtension()
|
||||
*/
|
||||
void setUiDelegateExtension(JobUiDelegateExtension *extension);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Abort this job.
|
||||
* This kills all subjobs and deletes the job.
|
||||
*
|
||||
*/
|
||||
bool doKill() override;
|
||||
|
||||
/**
|
||||
* Suspend this job
|
||||
* @see resume
|
||||
*/
|
||||
bool doSuspend() override;
|
||||
|
||||
/**
|
||||
* Resume this job
|
||||
* @see suspend
|
||||
*/
|
||||
bool doResume() override;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Converts an error code and a non-i18n error message into an
|
||||
* error message in the current language. The low level (non-i18n)
|
||||
* error message (usually a url) is put into the translated error
|
||||
* message using %1.
|
||||
*
|
||||
* Example for errid == ERR_CANNOT_OPEN_FOR_READING:
|
||||
* \code
|
||||
* i18n("Could not read\n%1", errortext);
|
||||
* \endcode
|
||||
* Use this to display the error yourself, but for a dialog box
|
||||
* use uiDelegate()->showErrorMessage(). Do not call it if error()
|
||||
* is not 0.
|
||||
* @return the error message and if there is no error, a message
|
||||
* telling the user that the app is broken, so check with
|
||||
* error() whether there is an error
|
||||
*/
|
||||
QString errorString() const override;
|
||||
|
||||
/**
|
||||
* Converts an error code and a non-i18n error message into i18n
|
||||
* strings suitable for presentation in a detailed error message box.
|
||||
*
|
||||
* @param reqUrl the request URL that generated this error message
|
||||
* @param method the method that generated this error message
|
||||
* (unimplemented)
|
||||
* @return the following strings: title, error + description,
|
||||
* causes+solutions
|
||||
*/
|
||||
QStringList detailedErrorStrings(const QUrl *reqUrl = nullptr, int method = -1) const;
|
||||
|
||||
/**
|
||||
* Set the parent Job.
|
||||
* One example use of this is when FileCopyJob calls RenameDialog::open,
|
||||
* it must pass the correct progress ID of the parent CopyJob
|
||||
* (to hide the progress dialog).
|
||||
* You can set the parent job only once. By default a job does not
|
||||
* have a parent job.
|
||||
* @param parentJob the new parent job
|
||||
*/
|
||||
void setParentJob(Job *parentJob);
|
||||
|
||||
/**
|
||||
* Returns the parent job, if there is one.
|
||||
* @return the parent job, or @c nullptr if there is none
|
||||
* @see setParentJob
|
||||
*/
|
||||
Job *parentJob() const;
|
||||
|
||||
/**
|
||||
* Set meta data to be sent to the worker, replacing existing
|
||||
* meta data.
|
||||
* @param metaData the meta data to set
|
||||
* @see addMetaData()
|
||||
* @see mergeMetaData()
|
||||
*/
|
||||
void setMetaData(const KIO::MetaData &metaData);
|
||||
|
||||
/**
|
||||
* Add key/value pair to the meta data that is sent to the worker.
|
||||
* @param key the key of the meta data
|
||||
* @param value the value of the meta data
|
||||
* @see setMetaData()
|
||||
* @see mergeMetaData()
|
||||
*/
|
||||
void addMetaData(const QString &key, const QString &value);
|
||||
|
||||
/**
|
||||
* Add key/value pairs to the meta data that is sent to the worker.
|
||||
* If a certain key already existed, it will be overridden.
|
||||
* @param values the meta data to add
|
||||
* @see setMetaData()
|
||||
* @see mergeMetaData()
|
||||
*/
|
||||
void addMetaData(const QMap<QString, QString> &values);
|
||||
|
||||
/**
|
||||
* Add key/value pairs to the meta data that is sent to the worker.
|
||||
* If a certain key already existed, it will remain unchanged.
|
||||
* @param values the meta data to merge
|
||||
* @see setMetaData()
|
||||
* @see addMetaData()
|
||||
*/
|
||||
void mergeMetaData(const QMap<QString, QString> &values);
|
||||
|
||||
/**
|
||||
* @internal. For the scheduler. Do not use.
|
||||
*/
|
||||
MetaData outgoingMetaData() const;
|
||||
|
||||
/**
|
||||
* Get meta data received from the worker.
|
||||
* (Valid when first data is received and/or worker is finished)
|
||||
* @return the job's meta data
|
||||
*/
|
||||
MetaData metaData() const;
|
||||
|
||||
/**
|
||||
* Query meta data received from the worker.
|
||||
* (Valid when first data is received and/or worker is finished)
|
||||
* @param key the key of the meta data to retrieve
|
||||
* @return the value of the meta data, or QString() if the
|
||||
* @p key does not exist
|
||||
*/
|
||||
QString queryMetaData(const QString &key);
|
||||
|
||||
protected:
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the worker successfully connected to the host.
|
||||
* There is no guarantee the worker will send this, and this is
|
||||
* currently unused (in the applications).
|
||||
* @param job the job that emitted this signal
|
||||
*/
|
||||
void connected(KIO::Job *job);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Add a job that has to be finished before a result
|
||||
* is emitted. This has obviously to be called before
|
||||
* the finish signal is emitted by the worker.
|
||||
*
|
||||
* @param job the subjob to add
|
||||
*/
|
||||
bool addSubjob(KJob *job) override;
|
||||
|
||||
/**
|
||||
* Mark a sub job as being done.
|
||||
*
|
||||
* Note that this does not terminate the parent job, even if @p job
|
||||
* is the last subjob. emitResult must be called to indicate that
|
||||
* the job is complete.
|
||||
*
|
||||
* @param job the subjob to remove
|
||||
*/
|
||||
bool removeSubjob(KJob *job) override;
|
||||
|
||||
protected:
|
||||
JobPrivate *const d_ptr;
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(Job)
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags for the job properties.
|
||||
* Not all flags are supported in all cases. Please see documentation of
|
||||
* the calling function!
|
||||
* @see JobFlags
|
||||
*/
|
||||
enum JobFlag {
|
||||
/**
|
||||
* Show the progress info GUI, no Resume and no Overwrite
|
||||
*/
|
||||
DefaultFlags = 0,
|
||||
|
||||
/**
|
||||
* Hide progress information dialog, i.e.\ don't show a GUI.
|
||||
*/
|
||||
HideProgressInfo = 1,
|
||||
|
||||
/**
|
||||
* When set, automatically append to the destination file if it exists already.
|
||||
* WARNING: this is NOT the builtin support for offering the user to resume a previous
|
||||
* partial download. The Resume option is much less used, it allows to append
|
||||
* to an existing file.
|
||||
* This is used by KIO::put(), KIO::file_copy(), KIO::file_move().
|
||||
*/
|
||||
Resume = 2,
|
||||
|
||||
/**
|
||||
* When set, automatically overwrite the destination if it exists already.
|
||||
* This is used by KIO::rename(), KIO::put(), KIO::file_copy(), KIO::file_move(), KIO::symlink().
|
||||
* Otherwise the operation will fail with ERR_FILE_ALREADY_EXIST or ERR_DIR_ALREADY_EXIST.
|
||||
*/
|
||||
Overwrite = 4,
|
||||
|
||||
/**
|
||||
* When set, notifies the worker that application/job does not want privilege execution.
|
||||
* So in case of failure due to insufficient privileges show an error without attempting
|
||||
* to run the operation as root first.
|
||||
*
|
||||
* @since 5.43
|
||||
*/
|
||||
NoPrivilegeExecution = 8,
|
||||
};
|
||||
/**
|
||||
* Stores a combination of #JobFlag values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(JobFlags, JobFlag)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(JobFlags)
|
||||
|
||||
enum LoadType {
|
||||
Reload,
|
||||
NoReload
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,368 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2009 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 Thiago Macieira <thiago@kde.org>
|
||||
SPDX-FileCopyrightText: 2013 Dawit Alemayehu <adawit@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_JOB_P_H
|
||||
#define KIO_JOB_P_H
|
||||
|
||||
#include "commands_p.h"
|
||||
#include "global.h"
|
||||
#include "jobtracker.h"
|
||||
#include "kiocoredebug.h"
|
||||
#include "simplejob.h"
|
||||
#include "transferjob.h"
|
||||
#include "worker_p.h"
|
||||
#include <KJobTrackerInterface>
|
||||
#include <QDataStream>
|
||||
#include <QPointer>
|
||||
#include <QUrl>
|
||||
#include <kio/jobuidelegateextension.h>
|
||||
#include <kio/jobuidelegatefactory.h>
|
||||
|
||||
/* clang-format off */
|
||||
#define KIO_ARGS \
|
||||
QByteArray packedArgs; \
|
||||
QDataStream stream(&packedArgs, QIODevice::WriteOnly); \
|
||||
stream
|
||||
/* clang-format on */
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
static constexpr filesize_t invalidFilesize = static_cast<KIO::filesize_t>(-1);
|
||||
|
||||
// Exported for KIOWidgets jobs
|
||||
class KIOCORE_EXPORT JobPrivate
|
||||
{
|
||||
public:
|
||||
JobPrivate()
|
||||
: m_parentJob(nullptr)
|
||||
, m_extraFlags(0)
|
||||
, m_uiDelegateExtension(KIO::defaultJobUiDelegateExtension())
|
||||
, m_privilegeExecutionEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~JobPrivate();
|
||||
|
||||
/**
|
||||
* Some extra storage space for jobs that don't have their own
|
||||
* private d pointer.
|
||||
*/
|
||||
enum {
|
||||
EF_TransferJobAsync = (1 << 0),
|
||||
EF_TransferJobNeedData = (1 << 1),
|
||||
EF_TransferJobDataSent = (1 << 2),
|
||||
EF_ListJobUnrestricted = (1 << 3),
|
||||
EF_KillCalled = (1 << 4),
|
||||
};
|
||||
|
||||
enum FileOperationType {
|
||||
ChangeAttr, // chmod(), chown(), setModificationTime()
|
||||
Copy,
|
||||
Delete,
|
||||
MkDir,
|
||||
Move,
|
||||
Rename,
|
||||
Symlink,
|
||||
Transfer, // put() and get()
|
||||
Other, // if other file operation set message, title inside the job.
|
||||
};
|
||||
|
||||
// Maybe we could use the QObject parent/child mechanism instead
|
||||
// (requires a new ctor, and moving the ctor code to some init()).
|
||||
Job *m_parentJob;
|
||||
int m_extraFlags;
|
||||
MetaData m_incomingMetaData;
|
||||
MetaData m_internalMetaData;
|
||||
MetaData m_outgoingMetaData;
|
||||
JobUiDelegateExtension *m_uiDelegateExtension;
|
||||
Job *q_ptr;
|
||||
// For privilege operation
|
||||
bool m_privilegeExecutionEnabled;
|
||||
QString m_title, m_message;
|
||||
FileOperationType m_operationType;
|
||||
|
||||
QByteArray privilegeOperationData();
|
||||
void slotSpeed(KJob *job, unsigned long speed);
|
||||
|
||||
static void emitMoving(KIO::Job *, const QUrl &src, const QUrl &dest);
|
||||
static void emitRenaming(KIO::Job *, const QUrl &src, const QUrl &dest);
|
||||
static void emitCopying(KIO::Job *, const QUrl &src, const QUrl &dest);
|
||||
static void emitCreatingDir(KIO::Job *, const QUrl &dir);
|
||||
static void emitDeleting(KIO::Job *, const QUrl &url);
|
||||
static void emitStating(KIO::Job *, const QUrl &url);
|
||||
static void emitTransferring(KIO::Job *, const QUrl &url);
|
||||
static void emitMounting(KIO::Job *, const QString &dev, const QString &point);
|
||||
static void emitUnmounting(KIO::Job *, const QString &point);
|
||||
|
||||
Q_DECLARE_PUBLIC(Job)
|
||||
};
|
||||
|
||||
class SimpleJobPrivate : public JobPrivate
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new simple job.
|
||||
* @param url the url of the job
|
||||
* @param command the command of the job
|
||||
* @param packedArgs the arguments
|
||||
*/
|
||||
SimpleJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs)
|
||||
: m_worker(nullptr)
|
||||
, m_packedArgs(packedArgs)
|
||||
, m_url(url)
|
||||
, m_command(command)
|
||||
, m_schedSerial(0)
|
||||
, m_redirectionHandlingEnabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
QPointer<Worker> m_worker;
|
||||
QByteArray m_packedArgs;
|
||||
QUrl m_url;
|
||||
int m_command;
|
||||
|
||||
// for use in KIO::Scheduler
|
||||
//
|
||||
// There are two kinds of protocol:
|
||||
// (1) The protocol of the url
|
||||
// (2) The actual protocol that the KIO worker uses.
|
||||
//
|
||||
// These two often match, but not necessarily. Most notably, they don't
|
||||
// match when doing ftp via a proxy.
|
||||
// In that case (1) is ftp, but (2) is http.
|
||||
//
|
||||
// JobData::protocol stores (2) while Job::url().protocol() returns (1).
|
||||
// The ProtocolInfoDict is indexed with (2).
|
||||
//
|
||||
// We schedule workers based on (2) but tell the worker about (1) via
|
||||
// Worker::setProtocol().
|
||||
QString m_protocol;
|
||||
int m_schedSerial;
|
||||
bool m_redirectionHandlingEnabled;
|
||||
|
||||
void simpleJobInit();
|
||||
|
||||
/**
|
||||
* Called on a worker's connected signal.
|
||||
* @see connected()
|
||||
*/
|
||||
void slotConnected();
|
||||
/**
|
||||
* Forward signal from the worker.
|
||||
* @param data_size the processed size in bytes
|
||||
* @see processedSize()
|
||||
*/
|
||||
void slotProcessedSize(KIO::filesize_t data_size);
|
||||
/**
|
||||
* Forward signal from the worker.
|
||||
* @param speed the speed in bytes/s
|
||||
* @see speed()
|
||||
*/
|
||||
void slotSpeed(unsigned long speed);
|
||||
/**
|
||||
* Forward signal from the worker.
|
||||
* Can also be called by the parent job, when it knows the size.
|
||||
* @param data_size the total size
|
||||
*/
|
||||
void slotTotalSize(KIO::filesize_t data_size);
|
||||
|
||||
/**
|
||||
* Called on a worker's info message.
|
||||
* @param s the info message
|
||||
* @see infoMessage()
|
||||
*/
|
||||
void _k_slotWorkerInfoMessage(const QString &s);
|
||||
|
||||
/**
|
||||
* Called when privilegeOperationRequested() is emitted by worker.
|
||||
*/
|
||||
void slotPrivilegeOperationRequested();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Called by the scheduler when a worker gets to
|
||||
* work on this job.
|
||||
**/
|
||||
virtual void start(KIO::Worker *worker);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Called to detach a worker from a job.
|
||||
**/
|
||||
void workerDone();
|
||||
|
||||
/**
|
||||
* Called by subclasses to restart the job after a redirection was signalled.
|
||||
* The m_redirectionURL data member can appear in several subclasses, so we have it
|
||||
* passed in. The regular URL will be set to the redirection URL which is then cleared.
|
||||
*/
|
||||
void restartAfterRedirection(QUrl *redirectionUrl);
|
||||
|
||||
Q_DECLARE_PUBLIC(SimpleJob)
|
||||
|
||||
static inline SimpleJobPrivate *get(KIO::SimpleJob *job)
|
||||
{
|
||||
return job->d_func();
|
||||
}
|
||||
static inline SimpleJob *newJobNoUi(const QUrl &url, int command, const QByteArray &packedArgs)
|
||||
{
|
||||
SimpleJob *job = new SimpleJob(*new SimpleJobPrivate(url, command, packedArgs));
|
||||
return job;
|
||||
}
|
||||
static inline SimpleJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, JobFlags flags = HideProgressInfo)
|
||||
{
|
||||
SimpleJob *job = new SimpleJob(*new SimpleJobPrivate(url, command, packedArgs));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
if (!(flags & NoPrivilegeExecution)) {
|
||||
job->d_func()->m_privilegeExecutionEnabled = true;
|
||||
// Only delete, rename and symlink operation accept JobFlags.
|
||||
FileOperationType opType;
|
||||
switch (command) {
|
||||
case CMD_DEL:
|
||||
opType = Delete;
|
||||
break;
|
||||
case CMD_RENAME:
|
||||
opType = Rename;
|
||||
break;
|
||||
case CMD_SYMLINK:
|
||||
opType = Symlink;
|
||||
break;
|
||||
default:
|
||||
return job;
|
||||
}
|
||||
job->d_func()->m_operationType = opType;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
class TransferJobPrivate : public SimpleJobPrivate
|
||||
{
|
||||
public:
|
||||
inline TransferJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs, const QByteArray &_staticData)
|
||||
: SimpleJobPrivate(url, command, packedArgs)
|
||||
, m_internalSuspended(false)
|
||||
, staticData(_staticData)
|
||||
, m_isMimetypeEmitted(false)
|
||||
, m_closedBeforeStart(false)
|
||||
{
|
||||
}
|
||||
|
||||
inline TransferJobPrivate(const QUrl &url, int command, const QByteArray &packedArgs, QIODevice *ioDevice)
|
||||
: SimpleJobPrivate(url, command, packedArgs)
|
||||
, m_internalSuspended(false)
|
||||
, m_isMimetypeEmitted(false)
|
||||
, m_closedBeforeStart(false)
|
||||
, m_outgoingDataSource(QPointer<QIODevice>(ioDevice))
|
||||
{
|
||||
}
|
||||
|
||||
bool m_internalSuspended;
|
||||
QByteArray staticData;
|
||||
QUrl m_redirectionURL;
|
||||
QList<QUrl> m_redirectionList;
|
||||
QString m_mimetype;
|
||||
bool m_isMimetypeEmitted;
|
||||
bool m_closedBeforeStart;
|
||||
QPointer<QIODevice> m_outgoingDataSource;
|
||||
QMetaObject::Connection m_readChannelFinishedConnection;
|
||||
|
||||
/**
|
||||
* Flow control. Suspend data processing from the worker.
|
||||
*/
|
||||
void internalSuspend();
|
||||
/**
|
||||
* Flow control. Resume data processing from the worker.
|
||||
*/
|
||||
void internalResume();
|
||||
/**
|
||||
* @internal
|
||||
* Called by the scheduler when a worker gets to
|
||||
* work on this job.
|
||||
* @param worker the worker that works on the job
|
||||
*/
|
||||
void start(KIO::Worker *worker) override;
|
||||
/**
|
||||
* @internal
|
||||
* Called when the KIO worker needs the data to send the server. This slot
|
||||
* is invoked when the data is to be sent is read from a QIODevice rather
|
||||
* instead of a QByteArray buffer.
|
||||
*/
|
||||
virtual void slotDataReqFromDevice();
|
||||
void slotIODeviceClosed();
|
||||
void slotIODeviceClosedBeforeStart();
|
||||
void slotPostRedirection();
|
||||
|
||||
Q_DECLARE_PUBLIC(TransferJob)
|
||||
static inline TransferJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, const QByteArray &_staticData, JobFlags flags)
|
||||
{
|
||||
TransferJob *job = new TransferJob(*new TransferJobPrivate(url, command, packedArgs, _staticData));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
job->setFinishedNotificationHidden();
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
if (!(flags & NoPrivilegeExecution)) {
|
||||
job->d_func()->m_privilegeExecutionEnabled = true;
|
||||
job->d_func()->m_operationType = Transfer;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
|
||||
static inline TransferJob *newJob(const QUrl &url, int command, const QByteArray &packedArgs, QIODevice *ioDevice, JobFlags flags)
|
||||
{
|
||||
TransferJob *job = new TransferJob(*new TransferJobPrivate(url, command, packedArgs, ioDevice));
|
||||
job->setUiDelegate(KIO::createDefaultJobUiDelegate());
|
||||
if (!(flags & HideProgressInfo)) {
|
||||
job->setFinishedNotificationHidden();
|
||||
KIO::getJobTracker()->registerJob(job);
|
||||
}
|
||||
if (!(flags & NoPrivilegeExecution)) {
|
||||
job->d_func()->m_privilegeExecutionEnabled = true;
|
||||
job->d_func()->m_operationType = Transfer;
|
||||
}
|
||||
return job;
|
||||
}
|
||||
};
|
||||
|
||||
class DirectCopyJobPrivate;
|
||||
/**
|
||||
* @internal
|
||||
* Used for direct copy from or to the local filesystem (i.e.\ WorkerBase::copy())
|
||||
*/
|
||||
class DirectCopyJob : public SimpleJob
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
DirectCopyJob(const QUrl &url, const QByteArray &packedArgs);
|
||||
~DirectCopyJob() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotCanResume(KIO::filesize_t offset);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* @internal
|
||||
* Emitted if the job found an existing partial file
|
||||
* and supports resuming. Used by FileCopyJob.
|
||||
*/
|
||||
void canResume(KIO::Job *job, KIO::filesize_t offset);
|
||||
|
||||
private:
|
||||
Q_DECLARE_PRIVATE(DirectCopyJob)
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "jobtracker.h"
|
||||
#include <KJobTrackerInterface>
|
||||
|
||||
static KJobTrackerInterface *s_tracker = nullptr;
|
||||
Q_GLOBAL_STATIC(KJobTrackerInterface, globalDummyTracker)
|
||||
|
||||
KJobTrackerInterface *KIO::getJobTracker()
|
||||
{
|
||||
if (!s_tracker) {
|
||||
s_tracker = globalDummyTracker(); // don't return nullptr, caller doesn't expect that
|
||||
}
|
||||
return s_tracker;
|
||||
}
|
||||
|
||||
void KIO::setJobTracker(KJobTrackerInterface *tracker)
|
||||
{
|
||||
s_tracker = tracker;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KIO_JOBTRACKER_H
|
||||
#define KIO_JOBTRACKER_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
class KJobTrackerInterface;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* Returns the job tracker to be used by all KIO jobs (in which HideProgressInfo is not set)
|
||||
*/
|
||||
KIOCORE_EXPORT KJobTrackerInterface *getJobTracker();
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Allows the KIO widgets library to register its widget-based job tracker automatically.
|
||||
* @since 5.0
|
||||
*/
|
||||
KIOCORE_EXPORT void setJobTracker(KJobTrackerInterface *tracker);
|
||||
}
|
||||
|
||||
#endif /* KIO_JOBTRACKER_H */
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2013 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <jobuidelegateextension.h>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
JobUiDelegateExtension::JobUiDelegateExtension()
|
||||
: d(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
JobUiDelegateExtension::~JobUiDelegateExtension()
|
||||
{
|
||||
}
|
||||
|
||||
ClipboardUpdater *KIO::JobUiDelegateExtension::createClipboardUpdater(Job *, ClipboardUpdaterMode)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void KIO::JobUiDelegateExtension::updateUrlInClipboard(const QUrl &, const QUrl &)
|
||||
{
|
||||
}
|
||||
|
||||
static JobUiDelegateExtension *s_extension = nullptr;
|
||||
|
||||
JobUiDelegateExtension *KIO::defaultJobUiDelegateExtension()
|
||||
{
|
||||
return s_extension;
|
||||
}
|
||||
|
||||
void KIO::setDefaultJobUiDelegateExtension(JobUiDelegateExtension *extension)
|
||||
{
|
||||
s_extension = extension;
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000 Stephan Kulow <coolo@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2013 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_JOBUIDELEGATEEXTENSION_H
|
||||
#define KIO_JOBUIDELEGATEEXTENSION_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <QDateTime>
|
||||
#include <kio/global.h>
|
||||
|
||||
class KJob;
|
||||
namespace KIO
|
||||
{
|
||||
class Job;
|
||||
class ClipboardUpdater;
|
||||
|
||||
/**
|
||||
* @see RenameDialog_Options
|
||||
* @since 5.0
|
||||
*/
|
||||
enum RenameDialog_Option {
|
||||
RenameDialog_Overwrite = 1, ///< We have an existing destination, show details about it and offer to overwrite it.
|
||||
RenameDialog_OverwriteItself = 2, ///< Warn that the current operation would overwrite a file with itself, which is not allowed.
|
||||
RenameDialog_Skip = 4, ///< Offer a "Skip" button, to skip other files too. Requires RenameDialog_MultipleItems.
|
||||
RenameDialog_MultipleItems =
|
||||
8, ///< Set if the current operation concerns multiple files, so it makes sense to offer buttons that apply the user's choice to all files/folders.
|
||||
RenameDialog_Resume = 16, ///< Offer a "Resume" button (plus "Resume All" if RenameDialog_MultipleItems).
|
||||
RenameDialog_NoRename = 64, ///< Don't offer a "Rename" button.
|
||||
RenameDialog_DestIsDirectory = 128, ///< The destination is a directory, the dialog updates labels and tooltips accordingly. @since 5.78
|
||||
RenameDialog_SourceIsDirectory = 256, ///< The source is a directory, the dialog updates labels and tooltips accordingly. @since 5.78
|
||||
};
|
||||
/**
|
||||
* Stores a combination of #RenameDialog_Option values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(RenameDialog_Options, RenameDialog_Option)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(RenameDialog_Options)
|
||||
|
||||
/**
|
||||
* @see SkipDialog_Options
|
||||
* @since 5.0
|
||||
*/
|
||||
enum SkipDialog_Option {
|
||||
/**
|
||||
* Set if the current operation concerns multiple files, so it makes sense
|
||||
* to offer buttons that apply the user's choice to all files/folders.
|
||||
*/
|
||||
SkipDialog_MultipleItems = 8,
|
||||
/**
|
||||
* Set if the current operation involves copying files/folders with certain
|
||||
* characters in their names that are not supported by the destination
|
||||
* filesystem (e.g.\ VFAT and NTFS disallow "*" in file/folder names).
|
||||
*
|
||||
* This will make the SkipDialog show a "Replace" button that can be used
|
||||
* to instruct the underlying job to replace any problematic character with
|
||||
* an underscore "_".
|
||||
*
|
||||
* @since 5.86
|
||||
*/
|
||||
SkipDialog_Replace_Invalid_Chars = 16,
|
||||
|
||||
/**
|
||||
* Set if the current operation @e cannot be retried.
|
||||
*
|
||||
* For example if there is an issue that involves the destination filesystem
|
||||
* support, e.g. VFAT and ExFat don't support symlinks, then retrying doesn't
|
||||
* make sense.
|
||||
*
|
||||
* @since 5.88
|
||||
*/
|
||||
SkipDialog_Hide_Retry = 32,
|
||||
};
|
||||
/**
|
||||
* Stores a combination of #SkipDialog_Option values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(SkipDialog_Options, SkipDialog_Option)
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(SkipDialog_Options)
|
||||
|
||||
/**
|
||||
* The result of a rename or skip dialog
|
||||
*/
|
||||
enum RenameDialog_Result {
|
||||
Result_Cancel = 0,
|
||||
Result_Rename = 1,
|
||||
Result_Skip = 2,
|
||||
Result_AutoSkip = 3,
|
||||
Result_Overwrite = 4,
|
||||
Result_OverwriteAll = 5,
|
||||
Result_Resume = 6,
|
||||
Result_ResumeAll = 7,
|
||||
Result_AutoRename = 8,
|
||||
Result_Retry = 9,
|
||||
/**
|
||||
* Can be returned only when multiple files are passed, Option overwrite is passed
|
||||
* And files modification times are valid
|
||||
* @since 5.77
|
||||
*/
|
||||
Result_OverwriteWhenOlder = 10,
|
||||
/**
|
||||
* Can be returned if the user selects to replace any character disallowed
|
||||
* by the destination filesystem with an underscore "_".
|
||||
*
|
||||
* See @ref SkipDialog_Option::SkipDialog_Replace_Invalid_Chars
|
||||
*
|
||||
* @since 5.86
|
||||
*/
|
||||
Result_ReplaceInvalidChars = 11,
|
||||
/**
|
||||
* The same as @c Result_ReplaceInvalidChars, but the user selected to
|
||||
* automatically replace any invalid character, without being asked about
|
||||
* every file/folder.
|
||||
*
|
||||
* @since 5.86
|
||||
*/
|
||||
Result_ReplaceAllInvalidChars = 12,
|
||||
};
|
||||
typedef RenameDialog_Result SkipDialog_Result;
|
||||
|
||||
/**
|
||||
* @class KIO::JobUiDelegateExtension jobuidelegateextension.h <KIO/JobUiDelegateExtension>
|
||||
*
|
||||
* An abstract class defining interaction with users from KIO jobs:
|
||||
* \li asking for confirmation before deleting files or directories
|
||||
* @since 5.0
|
||||
*/
|
||||
class KIOCORE_EXPORT JobUiDelegateExtension
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
JobUiDelegateExtension();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~JobUiDelegateExtension();
|
||||
|
||||
public:
|
||||
/**
|
||||
* The type of deletion: real deletion, moving the files to the trash
|
||||
* or emptying the trash
|
||||
* Used by askDeleteConfirmation.
|
||||
*/
|
||||
enum DeletionType {
|
||||
Delete,
|
||||
Trash,
|
||||
EmptyTrash
|
||||
};
|
||||
/**
|
||||
* ForceConfirmation: always ask the user for confirmation
|
||||
* DefaultConfirmation: don't ask the user if he/she said "don't ask again".
|
||||
*
|
||||
* Used by askDeleteConfirmation.
|
||||
*/
|
||||
enum ConfirmationType {
|
||||
DefaultConfirmation,
|
||||
ForceConfirmation
|
||||
};
|
||||
/**
|
||||
* Ask for confirmation before deleting/trashing @p urls.
|
||||
*
|
||||
* Note that this method is not called automatically by KIO jobs. It's the application's
|
||||
* responsibility to ask the user for confirmation before calling KIO::del() or KIO::trash().
|
||||
*
|
||||
* @param urls the urls about to be deleted/trashed
|
||||
* @param deletionType the type of deletion (Delete for real deletion, Trash otherwise)
|
||||
* @param confirmationType see ConfirmationType. Normally set to DefaultConfirmation.
|
||||
* Note: the window passed to setWindow is used as the parent for the message box.
|
||||
* @return true if confirmed
|
||||
*/
|
||||
virtual bool askDeleteConfirmation(const QList<QUrl> &urls, DeletionType deletionType, ConfirmationType confirmationType) = 0;
|
||||
|
||||
enum ClipboardUpdaterMode {
|
||||
UpdateContent,
|
||||
OverwriteContent,
|
||||
RemoveContent,
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a clipboard updater as a child of the given job.
|
||||
*/
|
||||
virtual ClipboardUpdater *createClipboardUpdater(Job *job, ClipboardUpdaterMode mode);
|
||||
/**
|
||||
* Update URL in clipboard, if present
|
||||
*/
|
||||
virtual void updateUrlInClipboard(const QUrl &src, const QUrl &dest);
|
||||
|
||||
// TODO KF6: add virtual_hook
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the default job UI delegate extension to be used by all KIO jobs (in which HideProgressInfo is not set)
|
||||
* Can return nullptr, if no kio GUI library is loaded.
|
||||
* @since 5.0
|
||||
*/
|
||||
KIOCORE_EXPORT JobUiDelegateExtension *defaultJobUiDelegateExtension();
|
||||
|
||||
/**
|
||||
* Internal. Allows the KIO widgets library to register its widget-based job UI delegate extension
|
||||
* automatically.
|
||||
* @since 5.0
|
||||
*/
|
||||
KIOCORE_EXPORT void setDefaultJobUiDelegateExtension(JobUiDelegateExtension *extension);
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2013 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <jobuidelegatefactory.h>
|
||||
|
||||
using namespace KIO;
|
||||
|
||||
JobUiDelegateFactory::JobUiDelegateFactory()
|
||||
: d(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
JobUiDelegateFactory::~JobUiDelegateFactory() = default;
|
||||
|
||||
static JobUiDelegateFactory *s_factory = nullptr;
|
||||
|
||||
KJobUiDelegate *KIO::createDefaultJobUiDelegate()
|
||||
{
|
||||
return s_factory ? s_factory->createDelegate() : nullptr;
|
||||
}
|
||||
|
||||
KJobUiDelegate *KIO::createDefaultJobUiDelegate(KJobUiDelegate::Flags flags, QWidget *window)
|
||||
{
|
||||
return s_factory ? s_factory->createDelegate(flags, window) : nullptr;
|
||||
}
|
||||
|
||||
JobUiDelegateFactory *KIO::defaultJobUiDelegateFactory()
|
||||
{
|
||||
return s_factory;
|
||||
}
|
||||
|
||||
void KIO::setDefaultJobUiDelegateFactory(JobUiDelegateFactory *factory)
|
||||
{
|
||||
s_factory = factory;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2013 David Faure <faure+bluesystems@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KIO_JOBUIDELEGATEFACTORY_H
|
||||
#define KIO_JOBUIDELEGATEFACTORY_H
|
||||
|
||||
#include "job_base.h"
|
||||
#include "kiocore_export.h"
|
||||
#include <KJobUiDelegate>
|
||||
#include <QDateTime>
|
||||
#include <kio/global.h>
|
||||
|
||||
#include <KCompositeJob>
|
||||
|
||||
class QWidget;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
/**
|
||||
* @class KIO::JobUiDelegateFactory jobuidelegatefactory.h <KIO/JobUiDelegateFactory>
|
||||
*
|
||||
* A factory for creating job ui delegates.
|
||||
* Every KIO job will get a delegate from this factory.
|
||||
* \since 5.0
|
||||
*/
|
||||
class KIOCORE_EXPORT JobUiDelegateFactory
|
||||
{
|
||||
protected:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
JobUiDelegateFactory();
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
virtual ~JobUiDelegateFactory();
|
||||
|
||||
public:
|
||||
virtual KJobUiDelegate *createDelegate() const = 0;
|
||||
|
||||
/**
|
||||
* @since 6.0
|
||||
*/
|
||||
virtual KJobUiDelegate *createDelegate(KJobUiDelegate::Flags flags, QWidget *window) const = 0;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
Private *const d;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convenience method: use default factory, if there's one, to create a delegate and return it.
|
||||
*/
|
||||
KIOCORE_EXPORT KJobUiDelegate *createDefaultJobUiDelegate();
|
||||
|
||||
/**
|
||||
* Convenience method: use default factory, if there's one, to create a delegate and return it.
|
||||
*
|
||||
* @since 5.98
|
||||
*/
|
||||
KIOCORE_EXPORT KJobUiDelegate *createDefaultJobUiDelegate(KJobUiDelegate::Flags flags, QWidget *window);
|
||||
|
||||
/**
|
||||
* Returns the default job UI delegate factory to be used by all KIO jobs (in which HideProgressInfo is not set)
|
||||
* Can return nullptr, if no kio GUI library is loaded.
|
||||
* @since 6.0
|
||||
*/
|
||||
KIOCORE_EXPORT JobUiDelegateFactory *defaultJobUiDelegateFactory();
|
||||
|
||||
/**
|
||||
* Internal. Allows the KIO widgets library to register its widget-based job UI delegate factory
|
||||
* automatically.
|
||||
* @since 6.0
|
||||
*/
|
||||
KIOCORE_EXPORT void setDefaultJobUiDelegateFactory(JobUiDelegateFactory *factory);
|
||||
|
||||
/**
|
||||
* Returns the child of the job's uiDelegate() that implements the given extension,
|
||||
* or nullptr if none was found (or if the job had no uiDelegate).
|
||||
* @since 5.78
|
||||
*/
|
||||
template<typename T>
|
||||
inline T delegateExtension(KJob *job)
|
||||
{
|
||||
KJobUiDelegate *ui = job->uiDelegate();
|
||||
|
||||
// If setParentJob() was used, try the uiDelegate of parentJob first
|
||||
if (!ui) {
|
||||
if (KIO::Job *kiojob = qobject_cast<KIO::Job *>(job)) {
|
||||
if (KJob *parentJob = kiojob->parentJob()) {
|
||||
ui = parentJob->uiDelegate();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Still nothing? if compositeJob->addSubjob(job) was used, try the ui delegate
|
||||
// of compositeJob
|
||||
while (!ui) {
|
||||
job = qobject_cast<KCompositeJob *>(job->parent());
|
||||
if (job) {
|
||||
ui = job->uiDelegate();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ui ? ui->findChild<T>(QString(), Qt::FindDirectChildrenOnly) : nullptr;
|
||||
}
|
||||
|
||||
} // namespace KIO
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,691 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2005-2007 Till Adam <adam@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
// $Id: kacl.cpp 424977 2005-06-13 15:13:22Z tilladam $
|
||||
|
||||
#include <config-kiocore.h>
|
||||
|
||||
#include "kacl.h"
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
#include "../aclhelpers_p.h"
|
||||
#endif
|
||||
|
||||
#include <QDataStream>
|
||||
#include <QHash>
|
||||
#include <QString>
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
using namespace KIO;
|
||||
#endif
|
||||
|
||||
class Q_DECL_HIDDEN KACL::KACLPrivate
|
||||
{
|
||||
public:
|
||||
KACLPrivate()
|
||||
#if HAVE_POSIX_ACL
|
||||
: m_acl(nullptr)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
#if HAVE_POSIX_ACL
|
||||
explicit KACLPrivate(acl_t acl)
|
||||
: m_acl(acl)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
#if HAVE_POSIX_ACL
|
||||
~KACLPrivate()
|
||||
{
|
||||
if (m_acl) {
|
||||
acl_free(m_acl);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// helpers
|
||||
#if HAVE_POSIX_ACL
|
||||
bool setMaskPermissions(unsigned short v);
|
||||
QString getUserName(uid_t uid) const;
|
||||
QString getGroupName(gid_t gid) const;
|
||||
bool setAllUsersOrGroups(const QList<QPair<QString, unsigned short>> &list, acl_tag_t type);
|
||||
bool setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type);
|
||||
|
||||
acl_t m_acl;
|
||||
mutable QHash<uid_t, QString> m_usercache;
|
||||
mutable QHash<gid_t, QString> m_groupcache;
|
||||
#endif
|
||||
};
|
||||
|
||||
KACL::KACL(const QString &aclString)
|
||||
: d(new KACLPrivate)
|
||||
{
|
||||
setACL(aclString);
|
||||
}
|
||||
|
||||
KACL::KACL(mode_t basePermissions)
|
||||
#if HAVE_POSIX_ACL
|
||||
: d(new KACLPrivate(ACLPortability::acl_from_mode(basePermissions)))
|
||||
#else
|
||||
: d(new KACLPrivate)
|
||||
#endif
|
||||
{
|
||||
#if !HAVE_POSIX_ACL
|
||||
Q_UNUSED(basePermissions);
|
||||
#endif
|
||||
}
|
||||
|
||||
KACL::KACL()
|
||||
: d(new KACLPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KACL::KACL(const KACL &rhs)
|
||||
: d(new KACLPrivate)
|
||||
{
|
||||
setACL(rhs.asString());
|
||||
}
|
||||
|
||||
KACL::~KACL() = default;
|
||||
|
||||
KACL &KACL::operator=(const KACL &rhs)
|
||||
{
|
||||
if (this != &rhs) {
|
||||
setACL(rhs.asString());
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool KACL::operator==(const KACL &rhs) const
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return (ACLPortability::acl_cmp(d->m_acl, rhs.d->m_acl) == 0);
|
||||
#else
|
||||
Q_UNUSED(rhs);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KACL::operator!=(const KACL &rhs) const
|
||||
{
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
bool KACL::isValid() const
|
||||
{
|
||||
bool valid = false;
|
||||
#if HAVE_POSIX_ACL
|
||||
if (d->m_acl) {
|
||||
valid = (acl_valid(d->m_acl) == 0);
|
||||
}
|
||||
#endif
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool KACL::isExtended() const
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return (ACLPortability::acl_equiv_mode(d->m_acl, nullptr) != 0);
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
static acl_entry_t entryForTag(acl_t acl, acl_tag_t tag)
|
||||
{
|
||||
acl_entry_t entry;
|
||||
int ret = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry);
|
||||
while (ret == 1) {
|
||||
acl_tag_t currentTag;
|
||||
acl_get_tag_type(entry, ¤tTag);
|
||||
if (currentTag == tag) {
|
||||
return entry;
|
||||
}
|
||||
ret = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static unsigned short entryToPermissions(acl_entry_t entry)
|
||||
{
|
||||
if (entry == nullptr) {
|
||||
return 0;
|
||||
}
|
||||
acl_permset_t permset;
|
||||
if (acl_get_permset(entry, &permset) != 0) {
|
||||
return 0;
|
||||
}
|
||||
return (ACLPortability::acl_get_perm(permset, ACL_READ) << 2 | ACLPortability::acl_get_perm(permset, ACL_WRITE) << 1
|
||||
| ACLPortability::acl_get_perm(permset, ACL_EXECUTE));
|
||||
}
|
||||
|
||||
static void permissionsToEntry(acl_entry_t entry, unsigned short v)
|
||||
{
|
||||
if (entry == nullptr) {
|
||||
return;
|
||||
}
|
||||
acl_permset_t permset;
|
||||
if (acl_get_permset(entry, &permset) != 0) {
|
||||
return;
|
||||
}
|
||||
acl_clear_perms(permset);
|
||||
if (v & 4) {
|
||||
acl_add_perm(permset, ACL_READ);
|
||||
}
|
||||
if (v & 2) {
|
||||
acl_add_perm(permset, ACL_WRITE);
|
||||
}
|
||||
if (v & 1) {
|
||||
acl_add_perm(permset, ACL_EXECUTE);
|
||||
}
|
||||
}
|
||||
|
||||
static int getUidForName(const QString &name)
|
||||
{
|
||||
struct passwd *user = getpwnam(name.toLocal8Bit().constData());
|
||||
if (user) {
|
||||
return user->pw_uid;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int getGidForName(const QString &name)
|
||||
{
|
||||
struct group *group = getgrnam(name.toLocal8Bit().constData());
|
||||
if (group) {
|
||||
return group->gr_gid;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
// ------------------ begin API implementation ------------
|
||||
|
||||
unsigned short KACL::ownerPermissions() const
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return entryToPermissions(entryForTag(d->m_acl, ACL_USER_OBJ));
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KACL::setOwnerPermissions(unsigned short v)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
permissionsToEntry(entryForTag(d->m_acl, ACL_USER_OBJ), v);
|
||||
#else
|
||||
Q_UNUSED(v);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned short KACL::owningGroupPermissions() const
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return entryToPermissions(entryForTag(d->m_acl, ACL_GROUP_OBJ));
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KACL::setOwningGroupPermissions(unsigned short v)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
permissionsToEntry(entryForTag(d->m_acl, ACL_GROUP_OBJ), v);
|
||||
#else
|
||||
Q_UNUSED(v);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned short KACL::othersPermissions() const
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return entryToPermissions(entryForTag(d->m_acl, ACL_OTHER));
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool KACL::setOthersPermissions(unsigned short v)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
permissionsToEntry(entryForTag(d->m_acl, ACL_OTHER), v);
|
||||
#else
|
||||
Q_UNUSED(v);
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
mode_t KACL::basePermissions() const
|
||||
{
|
||||
mode_t perms(0);
|
||||
#if HAVE_POSIX_ACL
|
||||
if (ownerPermissions() & ACL_READ) {
|
||||
perms |= S_IRUSR;
|
||||
}
|
||||
if (ownerPermissions() & ACL_WRITE) {
|
||||
perms |= S_IWUSR;
|
||||
}
|
||||
if (ownerPermissions() & ACL_EXECUTE) {
|
||||
perms |= S_IXUSR;
|
||||
}
|
||||
if (owningGroupPermissions() & ACL_READ) {
|
||||
perms |= S_IRGRP;
|
||||
}
|
||||
if (owningGroupPermissions() & ACL_WRITE) {
|
||||
perms |= S_IWGRP;
|
||||
}
|
||||
if (owningGroupPermissions() & ACL_EXECUTE) {
|
||||
perms |= S_IXGRP;
|
||||
}
|
||||
if (othersPermissions() & ACL_READ) {
|
||||
perms |= S_IROTH;
|
||||
}
|
||||
if (othersPermissions() & ACL_WRITE) {
|
||||
perms |= S_IWOTH;
|
||||
}
|
||||
if (othersPermissions() & ACL_EXECUTE) {
|
||||
perms |= S_IXOTH;
|
||||
}
|
||||
#endif
|
||||
return perms;
|
||||
}
|
||||
|
||||
unsigned short KACL::maskPermissions(bool &exists) const
|
||||
{
|
||||
exists = true;
|
||||
#if HAVE_POSIX_ACL
|
||||
acl_entry_t entry = entryForTag(d->m_acl, ACL_MASK);
|
||||
if (entry == nullptr) {
|
||||
exists = false;
|
||||
return 0;
|
||||
}
|
||||
return entryToPermissions(entry);
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
bool KACL::KACLPrivate::setMaskPermissions(unsigned short v)
|
||||
{
|
||||
acl_entry_t entry = entryForTag(m_acl, ACL_MASK);
|
||||
if (entry == nullptr) {
|
||||
acl_create_entry(&m_acl, &entry);
|
||||
acl_set_tag_type(entry, ACL_MASK);
|
||||
}
|
||||
permissionsToEntry(entry, v);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool KACL::setMaskPermissions(unsigned short v)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return d->setMaskPermissions(v);
|
||||
#else
|
||||
Q_UNUSED(v);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
using unique_ptr_acl_free = std::unique_ptr<void, int (*)(void *)>;
|
||||
#endif
|
||||
|
||||
/**************************
|
||||
* Deal with named users *
|
||||
**************************/
|
||||
unsigned short KACL::namedUserPermissions(const QString &name, bool *exists) const
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
acl_entry_t entry;
|
||||
*exists = false;
|
||||
int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry);
|
||||
while (ret == 1) {
|
||||
acl_tag_t currentTag;
|
||||
acl_get_tag_type(entry, ¤tTag);
|
||||
if (currentTag == ACL_USER) {
|
||||
const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free);
|
||||
const uid_t id = *(static_cast<uid_t *>(idptr.get()));
|
||||
if (d->getUserName(id) == name) {
|
||||
*exists = true;
|
||||
return entryToPermissions(entry);
|
||||
}
|
||||
}
|
||||
ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(name);
|
||||
Q_UNUSED(exists);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
bool KACL::KACLPrivate::setNamedUserOrGroupPermissions(const QString &name, unsigned short permissions, acl_tag_t type)
|
||||
{
|
||||
bool allIsWell = true;
|
||||
acl_t newACL = acl_dup(m_acl);
|
||||
acl_entry_t entry;
|
||||
bool createdNewEntry = false;
|
||||
bool found = false;
|
||||
int ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry);
|
||||
while (ret == 1) {
|
||||
acl_tag_t currentTag;
|
||||
acl_get_tag_type(entry, ¤tTag);
|
||||
if (currentTag == type) {
|
||||
const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free);
|
||||
const int id = *(static_cast<int *>(idptr.get())); // We assume that sizeof(uid_t) == sizeof(gid_t)
|
||||
const QString entryName = type == ACL_USER ? getUserName(id) : getGroupName(id);
|
||||
if (entryName == name) {
|
||||
// found him, update
|
||||
permissionsToEntry(entry, permissions);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret = acl_get_entry(newACL, ACL_NEXT_ENTRY, &entry);
|
||||
}
|
||||
if (!found) {
|
||||
acl_create_entry(&newACL, &entry);
|
||||
acl_set_tag_type(entry, type);
|
||||
int id = type == ACL_USER ? getUidForName(name) : getGidForName(name);
|
||||
if (id == -1 || acl_set_qualifier(entry, &id) != 0) {
|
||||
acl_delete_entry(newACL, entry);
|
||||
allIsWell = false;
|
||||
} else {
|
||||
permissionsToEntry(entry, permissions);
|
||||
createdNewEntry = true;
|
||||
}
|
||||
}
|
||||
if (allIsWell && createdNewEntry) {
|
||||
// 23.1.1 of 1003.1e states that as soon as there is a named user or
|
||||
// named group entry, there needs to be a mask entry as well, so add
|
||||
// one, if the user hasn't explicitly set one.
|
||||
if (entryForTag(newACL, ACL_MASK) == nullptr) {
|
||||
acl_calc_mask(&newACL);
|
||||
}
|
||||
}
|
||||
|
||||
if (!allIsWell || acl_valid(newACL) != 0) {
|
||||
acl_free(newACL);
|
||||
allIsWell = false;
|
||||
} else {
|
||||
acl_free(m_acl);
|
||||
m_acl = newACL;
|
||||
}
|
||||
return allIsWell;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool KACL::setNamedUserPermissions(const QString &name, unsigned short permissions)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return d->setNamedUserOrGroupPermissions(name, permissions, ACL_USER);
|
||||
#else
|
||||
Q_UNUSED(name);
|
||||
Q_UNUSED(permissions);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
ACLUserPermissionsList KACL::allUserPermissions() const
|
||||
{
|
||||
ACLUserPermissionsList list;
|
||||
#if HAVE_POSIX_ACL
|
||||
acl_entry_t entry;
|
||||
int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry);
|
||||
while (ret == 1) {
|
||||
acl_tag_t currentTag;
|
||||
acl_get_tag_type(entry, ¤tTag);
|
||||
if (currentTag == ACL_USER) {
|
||||
const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free);
|
||||
const uid_t id = *(static_cast<uid_t *>(idptr.get()));
|
||||
QString name = d->getUserName(id);
|
||||
unsigned short permissions = entryToPermissions(entry);
|
||||
ACLUserPermissions pair = qMakePair(name, permissions);
|
||||
list.append(pair);
|
||||
}
|
||||
ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry);
|
||||
}
|
||||
#endif
|
||||
return list;
|
||||
}
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
bool KACL::KACLPrivate::setAllUsersOrGroups(const QList<QPair<QString, unsigned short>> &list, acl_tag_t type)
|
||||
{
|
||||
bool allIsWell = true;
|
||||
bool atLeastOneUserOrGroup = false;
|
||||
|
||||
// make working copy, in case something goes wrong
|
||||
acl_t newACL = acl_dup(m_acl);
|
||||
acl_entry_t entry;
|
||||
|
||||
// clear user entries
|
||||
int ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry);
|
||||
while (ret == 1) {
|
||||
acl_tag_t currentTag;
|
||||
acl_get_tag_type(entry, ¤tTag);
|
||||
if (currentTag == type) {
|
||||
acl_delete_entry(newACL, entry);
|
||||
// we have to start from the beginning, the iterator is
|
||||
// invalidated, on deletion
|
||||
ret = acl_get_entry(newACL, ACL_FIRST_ENTRY, &entry);
|
||||
} else {
|
||||
ret = acl_get_entry(newACL, ACL_NEXT_ENTRY, &entry);
|
||||
}
|
||||
}
|
||||
|
||||
// now add the entries from the list
|
||||
for (const auto &[name, userId] : list) {
|
||||
acl_create_entry(&newACL, &entry);
|
||||
acl_set_tag_type(entry, type);
|
||||
int id = type == ACL_USER ? getUidForName(name) : getGidForName(name);
|
||||
if (id == -1 || acl_set_qualifier(entry, &id) != 0) {
|
||||
// user or group doesn't exist => error
|
||||
acl_delete_entry(newACL, entry);
|
||||
allIsWell = false;
|
||||
break;
|
||||
} else {
|
||||
permissionsToEntry(entry, userId);
|
||||
atLeastOneUserOrGroup = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (allIsWell && atLeastOneUserOrGroup) {
|
||||
// 23.1.1 of 1003.1e states that as soon as there is a named user or
|
||||
// named group entry, there needs to be a mask entry as well, so add
|
||||
// one, if the user hasn't explicitly set one.
|
||||
if (entryForTag(newACL, ACL_MASK) == nullptr) {
|
||||
acl_calc_mask(&newACL);
|
||||
}
|
||||
}
|
||||
if (allIsWell && (acl_valid(newACL) == 0)) {
|
||||
acl_free(m_acl);
|
||||
m_acl = newACL;
|
||||
} else {
|
||||
acl_free(newACL);
|
||||
}
|
||||
return allIsWell;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool KACL::setAllUserPermissions(const ACLUserPermissionsList &users)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return d->setAllUsersOrGroups(users, ACL_USER);
|
||||
#else
|
||||
Q_UNUSED(users);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**************************
|
||||
* Deal with named groups *
|
||||
**************************/
|
||||
|
||||
unsigned short KACL::namedGroupPermissions(const QString &name, bool *exists) const
|
||||
{
|
||||
*exists = false;
|
||||
#if HAVE_POSIX_ACL
|
||||
acl_entry_t entry;
|
||||
int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry);
|
||||
while (ret == 1) {
|
||||
acl_tag_t currentTag;
|
||||
acl_get_tag_type(entry, ¤tTag);
|
||||
if (currentTag == ACL_GROUP) {
|
||||
const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free);
|
||||
const gid_t id = *(static_cast<gid_t *>(idptr.get()));
|
||||
if (d->getGroupName(id) == name) {
|
||||
*exists = true;
|
||||
return entryToPermissions(entry);
|
||||
}
|
||||
}
|
||||
ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry);
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(name);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool KACL::setNamedGroupPermissions(const QString &name, unsigned short permissions)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return d->setNamedUserOrGroupPermissions(name, permissions, ACL_GROUP);
|
||||
#else
|
||||
Q_UNUSED(name);
|
||||
Q_UNUSED(permissions);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
ACLGroupPermissionsList KACL::allGroupPermissions() const
|
||||
{
|
||||
ACLGroupPermissionsList list;
|
||||
#if HAVE_POSIX_ACL
|
||||
acl_entry_t entry;
|
||||
int ret = acl_get_entry(d->m_acl, ACL_FIRST_ENTRY, &entry);
|
||||
while (ret == 1) {
|
||||
acl_tag_t currentTag;
|
||||
acl_get_tag_type(entry, ¤tTag);
|
||||
if (currentTag == ACL_GROUP) {
|
||||
const unique_ptr_acl_free idptr(acl_get_qualifier(entry), acl_free);
|
||||
const gid_t id = *(static_cast<gid_t *>(idptr.get()));
|
||||
QString name = d->getGroupName(id);
|
||||
unsigned short permissions = entryToPermissions(entry);
|
||||
ACLGroupPermissions pair = qMakePair(name, permissions);
|
||||
list.append(pair);
|
||||
}
|
||||
ret = acl_get_entry(d->m_acl, ACL_NEXT_ENTRY, &entry);
|
||||
}
|
||||
#endif
|
||||
return list;
|
||||
}
|
||||
|
||||
bool KACL::setAllGroupPermissions(const ACLGroupPermissionsList &groups)
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
return d->setAllUsersOrGroups(groups, ACL_GROUP);
|
||||
#else
|
||||
Q_UNUSED(groups);
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**************************
|
||||
* from and to string *
|
||||
**************************/
|
||||
|
||||
bool KACL::setACL(const QString &aclStr)
|
||||
{
|
||||
bool ret = false;
|
||||
#if HAVE_POSIX_ACL
|
||||
acl_t temp = acl_from_text(aclStr.toLatin1().constData());
|
||||
if (acl_valid(temp) != 0) {
|
||||
// TODO errno is set, what to do with it here?
|
||||
acl_free(temp);
|
||||
} else {
|
||||
if (d->m_acl) {
|
||||
acl_free(d->m_acl);
|
||||
}
|
||||
d->m_acl = temp;
|
||||
ret = true;
|
||||
}
|
||||
#else
|
||||
Q_UNUSED(aclStr);
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
QString KACL::asString() const
|
||||
{
|
||||
#if HAVE_POSIX_ACL
|
||||
ssize_t size = 0;
|
||||
char *txt = acl_to_text(d->m_acl, &size);
|
||||
const QString ret = QString::fromLatin1(txt, size);
|
||||
acl_free(txt);
|
||||
return ret;
|
||||
#else
|
||||
return QString();
|
||||
#endif
|
||||
}
|
||||
|
||||
// helpers
|
||||
|
||||
#if HAVE_POSIX_ACL
|
||||
QString KACL::KACLPrivate::getUserName(uid_t uid) const
|
||||
{
|
||||
auto it = m_usercache.find(uid);
|
||||
if (it == m_usercache.end()) {
|
||||
struct passwd *user = getpwuid(uid);
|
||||
if (user) {
|
||||
it = m_usercache.insert(uid, QString::fromLatin1(user->pw_name));
|
||||
} else {
|
||||
return QString::number(uid);
|
||||
}
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
|
||||
QString KACL::KACLPrivate::getGroupName(gid_t gid) const
|
||||
{
|
||||
auto it = m_groupcache.find(gid);
|
||||
if (it == m_groupcache.end()) {
|
||||
struct group *grp = getgrgid(gid);
|
||||
if (grp) {
|
||||
it = m_groupcache.insert(gid, QString::fromLatin1(grp->gr_name));
|
||||
} else {
|
||||
return QString::number(gid);
|
||||
}
|
||||
}
|
||||
return *it;
|
||||
}
|
||||
#endif
|
||||
|
||||
void KACL::virtual_hook(int, void *)
|
||||
{
|
||||
/*BASE::virtual_hook( id, data );*/
|
||||
}
|
||||
|
||||
QDataStream &operator<<(QDataStream &s, const KACL &a)
|
||||
{
|
||||
s << a.asString();
|
||||
return s;
|
||||
}
|
||||
|
||||
QDataStream &operator>>(QDataStream &s, KACL &a)
|
||||
{
|
||||
QString str;
|
||||
s >> str;
|
||||
a.setACL(str);
|
||||
return s;
|
||||
}
|
||||
@@ -0,0 +1,196 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2005-2007 Till Adam <adam@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KACL_H
|
||||
#define KACL_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
|
||||
#include <memory>
|
||||
|
||||
typedef QPair<QString, unsigned short> ACLUserPermissions;
|
||||
typedef QList<ACLUserPermissions> ACLUserPermissionsList;
|
||||
typedef QList<ACLUserPermissions>::iterator ACLUserPermissionsIterator;
|
||||
typedef QList<ACLUserPermissions>::const_iterator ACLUserPermissionsConstIterator;
|
||||
|
||||
typedef QPair<QString, unsigned short> ACLGroupPermissions;
|
||||
typedef QList<ACLGroupPermissions> ACLGroupPermissionsList;
|
||||
typedef QList<ACLGroupPermissions>::iterator ACLGroupPermissionsIterator;
|
||||
typedef QList<ACLGroupPermissions>::const_iterator ACLGroupPermissionsConstIterator;
|
||||
|
||||
/**
|
||||
* @class KACL kacl.h <KACL>
|
||||
*
|
||||
* The KACL class encapsulates a POSIX Access Control List. It follows the
|
||||
* little standard that couldn't, 1003.1e/1003.2c, which died in draft status.
|
||||
* @short a POSIX ACL encapsulation
|
||||
* @author Till Adam <adam@kde.org>
|
||||
*/
|
||||
class KIOCORE_EXPORT KACL
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a new KACL from @p aclString. If the string is a valid acl
|
||||
* string, isValid() will afterwards return true.
|
||||
*/
|
||||
KACL(const QString &aclString);
|
||||
|
||||
/** Copy ctor */
|
||||
KACL(const KACL &rhs);
|
||||
|
||||
/**
|
||||
* Creates a new KACL from the basic permissions passed in @p basicPermissions.
|
||||
* isValid() will return true, afterwards.
|
||||
*/
|
||||
KACL(mode_t basicPermissions);
|
||||
|
||||
/**
|
||||
* Creates an empty KACL. Until a valid acl string is set via setACL,
|
||||
* isValid() will return false.
|
||||
*/
|
||||
KACL();
|
||||
|
||||
virtual ~KACL();
|
||||
|
||||
KACL &operator=(const KACL &rhs);
|
||||
|
||||
bool operator==(const KACL &rhs) const;
|
||||
|
||||
bool operator!=(const KACL &rhs) const;
|
||||
|
||||
/**
|
||||
* Returns whether the KACL object represents a valid acl.
|
||||
* @return whether the KACL object represents a valid acl.
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/** The standard (non-extended) part of an ACL. These map directly to
|
||||
* standard unix file permissions. Setting them will never make a valid
|
||||
* ACL invalid. */
|
||||
|
||||
/** @return the owner's permissions entry */
|
||||
unsigned short ownerPermissions() const;
|
||||
|
||||
/** Set the owner's permissions entry.
|
||||
* @return success or failure */
|
||||
bool setOwnerPermissions(unsigned short);
|
||||
|
||||
/** @return the owning group's permissions entry */
|
||||
unsigned short owningGroupPermissions() const;
|
||||
|
||||
/** Set the owning group's permissions entry.
|
||||
* @return success or failure */
|
||||
bool setOwningGroupPermissions(unsigned short);
|
||||
|
||||
/** @return the permissions entry for others */
|
||||
unsigned short othersPermissions() const;
|
||||
|
||||
/** Set the permissions entry for others.
|
||||
* @return success or failure */
|
||||
bool setOthersPermissions(unsigned short);
|
||||
|
||||
/** @return the basic (owner/group/others) part of the ACL as a mode_t */
|
||||
mode_t basePermissions() const;
|
||||
|
||||
/** The interface to the extended ACL. This is a mask, permissions for
|
||||
* n named users and permissions for m named groups. */
|
||||
|
||||
/**
|
||||
* Return whether the ACL contains extended entries or can be expressed
|
||||
* using only basic file permissions.
|
||||
* @return whether the ACL contains extended entries */
|
||||
bool isExtended() const;
|
||||
|
||||
/**
|
||||
* Return the entry for the permissions mask if there is one and sets
|
||||
* @p exists to true. If there is no such entry, @p exists is set to false.
|
||||
* @return the permissions mask entry */
|
||||
unsigned short maskPermissions(bool &exists) const;
|
||||
|
||||
/** Set the permissions mask for the ACL. Permissions set for individual
|
||||
* entries will be masked with this, such that their effective permissions
|
||||
* are the result of the logical and of their entry and the mask.
|
||||
* @return success or failure */
|
||||
bool setMaskPermissions(unsigned short);
|
||||
|
||||
/**
|
||||
* Access to the permissions entry for a named user, if such an entry
|
||||
* exists. If @p exists is non-null, the boolean variable it points to
|
||||
* is set to true if a matching entry exists and to false otherwise.
|
||||
* @return the permissions for a user entry with the name in @p name */
|
||||
unsigned short namedUserPermissions(const QString &name, bool *exists) const;
|
||||
|
||||
/** Set the permissions for a user with the name @p name. Will fail
|
||||
* if the user doesn't exist, in which case the ACL will be unchanged.
|
||||
* @return success or failure. */
|
||||
bool setNamedUserPermissions(const QString &name, unsigned short);
|
||||
|
||||
/** Returns the list of all group permission entries. Each entry consists
|
||||
* of a name/permissions pair. This is a QPair, therefore access is provided
|
||||
* via the .first and .next members.
|
||||
* @return the list of all group permission entries. */
|
||||
ACLUserPermissionsList allUserPermissions() const;
|
||||
|
||||
/** Replace the list of all user permissions with @p list. If one
|
||||
* of the entries in the list does not exists, or setting of the ACL
|
||||
* entry fails for any reason, the ACL will be left unchanged.
|
||||
* @return success or failure */
|
||||
bool setAllUserPermissions(const ACLUserPermissionsList &list);
|
||||
|
||||
/**
|
||||
* Access to the permissions entry for a named group, if such an entry
|
||||
* exists. If @p exists is non-null, the boolean variable it points to is
|
||||
* set to true if a matching entry exists and to false otherwise.
|
||||
* @return the permissions for a group with the name in @p name */
|
||||
unsigned short namedGroupPermissions(const QString &name, bool *exists) const;
|
||||
|
||||
/** Set the permissions for a group with the name @p name. Will fail
|
||||
* if the group doesn't exist, in which case the ACL be unchanged.
|
||||
* @return success or failure. */
|
||||
bool setNamedGroupPermissions(const QString &name, unsigned short);
|
||||
|
||||
/** Returns the list of all group permission entries. Each entry consists
|
||||
* of a name/permissions pair. This is a QPair, therefore access is provided
|
||||
* via the .first and .next members.
|
||||
* @return the list of all group permission entries. */
|
||||
|
||||
ACLGroupPermissionsList allGroupPermissions() const;
|
||||
/** Replace the list of all user permissions with @p list. If one
|
||||
* of the entries in the list does not exists, or setting of the ACL
|
||||
* entry fails for any reason, the ACL will be left unchanged.
|
||||
* @return success or failure */
|
||||
bool setAllGroupPermissions(const ACLGroupPermissionsList &);
|
||||
|
||||
/** Sets the whole list from a string. If the string in @p aclStr represents
|
||||
* a valid ACL, it will be set, otherwise the ACL remains unchanged.
|
||||
* @return whether setting the ACL was successful. */
|
||||
bool setACL(const QString &aclStr);
|
||||
|
||||
/** Return a string representation of the ACL.
|
||||
* @return a string version of the ACL in the format compatible with libacl and
|
||||
* POSIX 1003.1e. Implementations conforming to that standard should be able
|
||||
* to take such strings as input. */
|
||||
QString asString() const;
|
||||
|
||||
protected:
|
||||
virtual void virtual_hook(int id, void *data);
|
||||
|
||||
private:
|
||||
class KACLPrivate;
|
||||
std::unique_ptr<KACLPrivate> const d;
|
||||
KIOCORE_EXPORT friend QDataStream &operator<<(QDataStream &s, const KACL &a);
|
||||
KIOCORE_EXPORT friend QDataStream &operator>>(QDataStream &s, KACL &a);
|
||||
};
|
||||
|
||||
KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KACL &a);
|
||||
KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, KACL &a);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,635 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2001, 2002, 2004-2006 Michael Brade <brade@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOREDIRLISTER_H
|
||||
#define KCOREDIRLISTER_H
|
||||
|
||||
#include "kfileitem.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QUrl>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class KJob;
|
||||
namespace KIO
|
||||
{
|
||||
class Job;
|
||||
class ListJob;
|
||||
}
|
||||
|
||||
class KCoreDirListerPrivate;
|
||||
|
||||
/**
|
||||
* @class KCoreDirLister kcoredirlister.h <KCoreDirLister>
|
||||
*
|
||||
* @short Helper class for the kiojob used to list and update a directory.
|
||||
*
|
||||
* The dir lister deals with the kiojob used to list and update a directory
|
||||
* and has signals for the user of this class (e.g. konqueror view or
|
||||
* kdesktop) to create/destroy its items when asked.
|
||||
*
|
||||
* This class is independent from the graphical representation of the dir
|
||||
* (icon container, tree view, ...) and it stores the items (as KFileItems).
|
||||
*
|
||||
* Typical usage :
|
||||
* @li Create an instance.
|
||||
* @li Connect to at least update, clear, itemsAdded, and itemsDeleted.
|
||||
* @li Call openUrl - the signals will be called.
|
||||
* @li Reuse the instance when opening a new url (openUrl).
|
||||
* @li Destroy the instance when not needed anymore (usually destructor).
|
||||
*
|
||||
* Advanced usage : call openUrl with OpenUrlFlag::Keep to list directories
|
||||
* without forgetting the ones previously read (e.g. for a tree view)
|
||||
*
|
||||
* @author Michael Brade <brade@kde.org>
|
||||
*/
|
||||
class KIOCORE_EXPORT KCoreDirLister : public QObject
|
||||
{
|
||||
friend class KCoreDirListerCache;
|
||||
friend struct KCoreDirListerCacheDirectoryData;
|
||||
|
||||
Q_OBJECT
|
||||
Q_PROPERTY(bool autoUpdate READ autoUpdate WRITE setAutoUpdate)
|
||||
Q_PROPERTY(bool showHiddenFiles READ showHiddenFiles WRITE setShowHiddenFiles)
|
||||
Q_PROPERTY(bool dirOnlyMode READ dirOnlyMode WRITE setDirOnlyMode)
|
||||
Q_PROPERTY(bool delayedMimeTypes READ delayedMimeTypes WRITE setDelayedMimeTypes)
|
||||
Q_PROPERTY(bool requestMimeTypeWhileListing READ requestMimeTypeWhileListing WRITE setRequestMimeTypeWhileListing)
|
||||
Q_PROPERTY(QString nameFilter READ nameFilter WRITE setNameFilter)
|
||||
Q_PROPERTY(QStringList mimeFilter READ mimeFilters WRITE setMimeFilter RESET clearMimeFilter)
|
||||
Q_PROPERTY(bool autoErrorHandlingEnabled READ autoErrorHandlingEnabled WRITE setAutoErrorHandlingEnabled)
|
||||
|
||||
public:
|
||||
/**
|
||||
* @see OpenUrlFlags
|
||||
*/
|
||||
enum OpenUrlFlag {
|
||||
NoFlags = 0x0, ///< No additional flags specified.
|
||||
|
||||
Keep = 0x1, ///< Previous directories aren't forgotten
|
||||
///< (they are still watched by kdirwatch and their items
|
||||
///< are kept for this KCoreDirLister). This is useful for e.g.
|
||||
///< a treeview.
|
||||
|
||||
Reload = 0x2, ///< Indicates whether to use the cache or to reread
|
||||
///< the directory from the disk.
|
||||
///< Use only when opening a dir not yet listed by this lister
|
||||
///< without using the cache. Otherwise use updateDirectory.
|
||||
};
|
||||
|
||||
/**
|
||||
* Stores a combination of #OpenUrlFlag values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(OpenUrlFlags, OpenUrlFlag)
|
||||
|
||||
/**
|
||||
* Create a directory lister.
|
||||
*/
|
||||
KCoreDirLister(QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destroy the directory lister.
|
||||
*/
|
||||
~KCoreDirLister() override;
|
||||
|
||||
/**
|
||||
* Run the directory lister on the given url.
|
||||
*
|
||||
* This method causes KCoreDirLister to emit @em all the items of @p dirUrl, in any case.
|
||||
* Depending on @p flags, either clear() or clearDir(const QUrl &) will be emitted first.
|
||||
*
|
||||
* The newItems() signal may be emitted more than once to supply you with KFileItems, up
|
||||
* until the signal completed() is emitted (and isFinished() returns @c true).
|
||||
*
|
||||
* @param dirUrl the directory URL.
|
||||
* @param flags whether to keep previous directories, and whether to reload, see OpenUrlFlags
|
||||
* @return @c true if successful, @c false otherwise (e.g. if @p dirUrl is invalid)
|
||||
*/
|
||||
bool openUrl(const QUrl &dirUrl, OpenUrlFlags flags = NoFlags); // TODO KF6: change bool to void
|
||||
|
||||
/**
|
||||
* Stop listing all directories currently being listed.
|
||||
*
|
||||
* Emits canceled() if there was at least one job running.
|
||||
* Emits listingDirCanceled(const QUrl &) for each stopped job if there is more than one
|
||||
* directory being watched by this KCoreDirLister.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Stop listing the given directory.
|
||||
*
|
||||
* Emits canceled() if the killed job was the last running one.
|
||||
* Emits listingDirCanceled(const QUrl &) for the killed job if there is more than one
|
||||
* directory being watched by this KCoreDirLister.
|
||||
*
|
||||
* No signal is emitted if there was no job running for @p dirUrl.
|
||||
*
|
||||
* @param dirUrl the directory URL
|
||||
*/
|
||||
void stop(const QUrl &dirUrl);
|
||||
|
||||
/**
|
||||
* Stop listening for further changes in the given directory.
|
||||
* When a new directory is opened with OpenUrlFlag::Keep the caller will keep being notified of file changes for all directories that were kept open.
|
||||
* This call selectively removes a directory from sending future notifications to this KCoreDirLister.
|
||||
*
|
||||
* @param dirUrl the directory URL.
|
||||
* @since 5.91
|
||||
*/
|
||||
void forgetDirs(const QUrl &dirUrl);
|
||||
|
||||
/**
|
||||
* @return @c true if the "delayed MIME types" feature was enabled
|
||||
* @see setDelayedMimeTypes
|
||||
*/
|
||||
bool delayedMimeTypes() const;
|
||||
|
||||
/**
|
||||
* Delayed MIME types feature:
|
||||
* If enabled, MIME types will be fetched on demand, which leads to a
|
||||
* faster initial directory listing, where icons get progressively replaced
|
||||
* with the correct one while KMimeTypeResolver is going through the items
|
||||
* with unknown or imprecise MIME type (e.g. files with no extension or an
|
||||
* unknown extension).
|
||||
*/
|
||||
void setDelayedMimeTypes(bool delayedMimeTypes);
|
||||
|
||||
/**
|
||||
* Checks whether KDirWatch will automatically update directories. This is
|
||||
* enabled by default.
|
||||
*
|
||||
* @return @c true if KDirWatch is used to automatically update directories
|
||||
*/
|
||||
bool autoUpdate() const;
|
||||
|
||||
/**
|
||||
* Toggle automatic directory updating, when a directory changes (using KDirWatch).
|
||||
*
|
||||
* @param enable set to @c true to enable or @c false to disable
|
||||
*/
|
||||
void setAutoUpdate(bool enable);
|
||||
|
||||
/**
|
||||
* Checks whether hidden files (e.g. files whose name start with '.' on Unix) will be shown.
|
||||
* By default this option is disabled (hidden files are not shown).
|
||||
*
|
||||
* @return @c true if hidden files are shown, @c false otherwise
|
||||
*
|
||||
* @see setShowHiddenFiles()
|
||||
* @since 5.100
|
||||
*/
|
||||
bool showHiddenFiles() const;
|
||||
|
||||
/**
|
||||
* Toggles whether hidden files (e.g. files whose name start with '.' on Unix) are shown/
|
||||
* By default hidden files are not shown.
|
||||
*
|
||||
* You need to call emitChanges() afterwards.
|
||||
*
|
||||
* @param showHiddenFiles set to @c true/false to show/hide hidden files respectively
|
||||
*
|
||||
* @see showHiddenFiles()
|
||||
* @since 5.100
|
||||
*/
|
||||
void setShowHiddenFiles(bool showHiddenFiles);
|
||||
|
||||
/**
|
||||
* Checks whether this KCoreDirLister only lists directories or all items (directories and
|
||||
* files), by default all items are listed.
|
||||
*
|
||||
* @return @c true if only directories are listed, @c false otherwise
|
||||
*
|
||||
* @see setDirOnlyMode(bool)
|
||||
*/
|
||||
bool dirOnlyMode() const;
|
||||
|
||||
/**
|
||||
* Call this to list only directories (by default all items (directories and files)
|
||||
* are listed).
|
||||
*
|
||||
* You need to call emitChanges() afterwards.
|
||||
*
|
||||
* @param dirsOnly set to @c true to list only directories
|
||||
*/
|
||||
void setDirOnlyMode(bool dirsOnly);
|
||||
|
||||
/**
|
||||
* Checks whether this KCoreDirLister requests the MIME type of files from the worker.
|
||||
*
|
||||
* Enabling this will tell the worker used for listing that it should try to
|
||||
* determine the mime type of entries while listing them. This potentially
|
||||
* reduces the speed at which entries are listed but ensures mime types are
|
||||
* immediately available when an entry is added, greatly speeding up things
|
||||
* like mime type filtering.
|
||||
*
|
||||
* By default this is disabled.
|
||||
*
|
||||
* @return @c true if the worker is asked for MIME types, @c false otherwise.
|
||||
*
|
||||
* @see setRequestMimeTypeWhileListing(bool)
|
||||
*
|
||||
* @since 5.82
|
||||
*/
|
||||
bool requestMimeTypeWhileListing() const;
|
||||
|
||||
/**
|
||||
* Toggles whether to request MIME types from the worker or in-process.
|
||||
*
|
||||
* @param request set to @c true to request MIME types from the worker.
|
||||
*
|
||||
* @note If this is changed while the lister is already listing a directory,
|
||||
* it will only have an effect the next time openUrl() is called.
|
||||
*
|
||||
* @see requestMimeTypeWhileListing()
|
||||
*
|
||||
* @since 5.82
|
||||
*/
|
||||
void setRequestMimeTypeWhileListing(bool request);
|
||||
|
||||
/**
|
||||
* Returns the top level URL that is listed by this KCoreDirLister.
|
||||
*
|
||||
* It might be different from the one given with openUrl() if there was a
|
||||
* redirection. If you called openUrl() with OpenUrlFlag::Keep this is the
|
||||
* first url opened (e.g. in a treeview this is the root).
|
||||
*
|
||||
* @return the url used by this instance to list the files
|
||||
*/
|
||||
QUrl url() const;
|
||||
|
||||
/**
|
||||
* Returns all URLs that are listed by this KCoreDirLister. This is only
|
||||
* useful if you called openUrl() with OpenUrlFlag::Keep, as it happens in a
|
||||
* treeview, for example. (Note that the base url is included in the list
|
||||
* as well, of course.)
|
||||
*
|
||||
* @return a list of all listed URLs
|
||||
*/
|
||||
QList<QUrl> directories() const;
|
||||
|
||||
/**
|
||||
* Actually emit the changes made with setShowHiddenFiles, setDirOnlyMode,
|
||||
* setNameFilter and setMimeFilter.
|
||||
*/
|
||||
void emitChanges();
|
||||
|
||||
/**
|
||||
* Update the directory @p dirUrl. This method causes KCoreDirLister to @em only emit
|
||||
* the items of @p dirUrl that actually changed compared to the current state in the
|
||||
* cache, and updates the cache.
|
||||
*
|
||||
* The current implementation calls updateDirectory automatically for local files, using
|
||||
* KDirWatch (if autoUpdate() is @c true), but it might be useful to force an update manually.
|
||||
*
|
||||
* @param dirUrl the directory URL
|
||||
*/
|
||||
void updateDirectory(const QUrl &dirUrl);
|
||||
|
||||
/**
|
||||
* Returns @c true if no I/O operation is currently in progress.
|
||||
*
|
||||
* @return @c true if finished, @c false otherwise
|
||||
*/
|
||||
bool isFinished() const;
|
||||
|
||||
/**
|
||||
* Returns the file item of the URL.
|
||||
*
|
||||
* Can return an empty KFileItem.
|
||||
* @return the file item for url() itself (".")
|
||||
*/
|
||||
KFileItem rootItem() const;
|
||||
|
||||
/**
|
||||
* Find an item by its URL.
|
||||
* @param url the item URL
|
||||
* @return the KFileItem
|
||||
*/
|
||||
KFileItem findByUrl(const QUrl &url) const;
|
||||
|
||||
/**
|
||||
* Find an item by its name.
|
||||
* @param name the item name
|
||||
* @return the KFileItem
|
||||
*/
|
||||
KFileItem findByName(const QString &name) const;
|
||||
|
||||
/**
|
||||
* Set a name filter to only list items matching this name, e.g.\ "*.cpp".
|
||||
*
|
||||
* You can set more than one filter by separating them with whitespace, e.g
|
||||
* "*.cpp *.h".
|
||||
* Note: the directory is not automatically reloaded.
|
||||
* You need to call emitChanges() afterwards.
|
||||
*
|
||||
* @param filter the new filter, QString() to disable filtering
|
||||
* @see matchesFilter
|
||||
*/
|
||||
void setNameFilter(const QString &filter);
|
||||
|
||||
/**
|
||||
* Returns the current name filter, as set via setNameFilter()
|
||||
* @return the current name filter, can be QString() if filtering
|
||||
* is turned off
|
||||
*/
|
||||
QString nameFilter() const;
|
||||
|
||||
/**
|
||||
* Set MIME type based filter to only list items matching the given MIME types.
|
||||
*
|
||||
* NOTE: setting the filter does not automatically reload directory.
|
||||
* Also calling this function will not affect any named filter already set.
|
||||
*
|
||||
* You need to call emitChanges() afterwards.
|
||||
*
|
||||
* @param mimeList a list of MIME types
|
||||
*
|
||||
* @see clearMimeFilter
|
||||
* @see matchesMimeFilter
|
||||
*/
|
||||
void setMimeFilter(const QStringList &mimeList);
|
||||
|
||||
/**
|
||||
* Filtering should be done with KFileFilter. This will be implemented in a later
|
||||
* revision of KCoreDirLister. This method may be removed then.
|
||||
*
|
||||
* Set MIME type based exclude filter to only list items not matching the given MIME types
|
||||
*
|
||||
* NOTE: setting the filter does not automatically reload directory.
|
||||
* Also calling this function will not affect any named filter already set.
|
||||
*
|
||||
* @param mimeList a list of MIME types
|
||||
* @see clearMimeFilter
|
||||
* @see matchesMimeFilter
|
||||
*/
|
||||
void setMimeExcludeFilter(const QStringList &mimeList);
|
||||
|
||||
/**
|
||||
* Clears the MIME type based filter.
|
||||
*
|
||||
* You need to call emitChanges() afterwards.
|
||||
*
|
||||
* @see setMimeFilter
|
||||
*/
|
||||
void clearMimeFilter();
|
||||
|
||||
/**
|
||||
* Returns the list of MIME type based filters, as set via setMimeFilter().
|
||||
* @return the list of MIME type based filters. Empty, when no MIME type filter is set.
|
||||
*/
|
||||
QStringList mimeFilters() const;
|
||||
|
||||
/**
|
||||
* Used by items() and itemsForDir() to specify whether you want
|
||||
* all items for a directory or just the filtered ones.
|
||||
*/
|
||||
enum WhichItems {
|
||||
AllItems = 0,
|
||||
FilteredItems = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the items listed for the current url().
|
||||
*
|
||||
* This method will @em not start listing a directory, you should only call
|
||||
* this in a slot connected to the finished() signal.
|
||||
*
|
||||
* The items in the KFileItemList are copies of the items used by KCoreDirLister.
|
||||
*
|
||||
* @param which specifies whether the returned list will contain all entries
|
||||
* or only the ones that passed the nameFilter(), mimeFilter(),
|
||||
* etc. Note that the latter causes iteration over all the
|
||||
* items, filtering them. If this is too slow for you, use the
|
||||
* newItems() signal, sending out filtered items in chunks
|
||||
* @return the items listed for the current url()
|
||||
*/
|
||||
KFileItemList items(WhichItems which = FilteredItems) const;
|
||||
|
||||
/**
|
||||
* Returns the items listed for the given @p dirUrl.
|
||||
* This method will @em not start listing @p dirUrl, you should only call
|
||||
* this in a slot connected to the finished() signal.
|
||||
*
|
||||
* The items in the KFileItemList are copies of the items used by KCoreDirLister.
|
||||
*
|
||||
* @param dirUrl specifies the url for which the items should be returned. This
|
||||
* is only useful if you use KCoreDirLister with multiple URLs
|
||||
* i.e. using bool OpenUrlFlag::Keep in openUrl()
|
||||
* @param which specifies whether the returned list will contain all entries
|
||||
* or only the ones that passed the nameFilter, mimeFilter, etc.
|
||||
* Note that the latter causes iteration over all the items,
|
||||
* filtering them. If this is too slow for you, use the
|
||||
* newItems() signal, sending out filtered items in chunks
|
||||
*
|
||||
* @return the items listed for @p dirUrl
|
||||
*/
|
||||
KFileItemList itemsForDir(const QUrl &dirUrl, WhichItems which = FilteredItems) const;
|
||||
|
||||
/**
|
||||
* Return the KFileItem for the given URL, if it was listed recently and it's
|
||||
* still in the cache, which is always the case if a directory view is currently
|
||||
* showing this item. If not, then it might be in the cache; if not in the cache a
|
||||
* a null KFileItem will be returned.
|
||||
*
|
||||
* If you really need a KFileItem for this URL in all cases, then use KIO::stat() instead.
|
||||
*
|
||||
*/
|
||||
static KFileItem cachedItemForUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Checks whether auto error handling is enabled.
|
||||
* If enabled, it will show an error dialog to the user when an
|
||||
* error occurs (assuming the application links to KIOWidgets).
|
||||
* It is turned on by default.
|
||||
* @return @c true if auto error handling is enabled, @c false otherwise
|
||||
* @see setAutoErrorHandlingEnabled()
|
||||
* @since 5.82
|
||||
*/
|
||||
bool autoErrorHandlingEnabled() const;
|
||||
|
||||
/**
|
||||
* Enable or disable auto error handling.
|
||||
* If enabled, it will show an error dialog to the user when an
|
||||
* error occurs. It is turned on by default.
|
||||
* @param enable true to enable auto error handling, false to disable
|
||||
* @param parent the parent widget for the error dialogs, can be @c nullptr for
|
||||
* top-level
|
||||
* @see autoErrorHandlingEnabled()
|
||||
* @since 5.82
|
||||
*/
|
||||
void setAutoErrorHandlingEnabled(bool enable);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Tell the view that this KCoreDirLister has started to list @p dirUrl. Note that this
|
||||
* does @em not imply that there is really a job running! I.e. KCoreDirLister::jobs()
|
||||
* may return an empty list, in which case the items are taken from the cache.
|
||||
*
|
||||
* The view knows that openUrl should start it, so this might seem useless, but the view
|
||||
* also needs to know when an automatic update happens.
|
||||
* @param dirUrl the URL to list
|
||||
*/
|
||||
void started(const QUrl &dirUrl);
|
||||
|
||||
/**
|
||||
* Tell the view that listing is finished. There are no jobs running anymore.
|
||||
*/
|
||||
void completed();
|
||||
|
||||
/**
|
||||
* Tell the view that the listing of the directory @p dirUrl is finished.
|
||||
* There might be other running jobs left.
|
||||
*
|
||||
* @param dirUrl the directory URL
|
||||
*
|
||||
* @since 5.79
|
||||
*/
|
||||
void listingDirCompleted(const QUrl &dirUrl);
|
||||
|
||||
/**
|
||||
* Tell the view that the user canceled the listing. No running jobs are left.
|
||||
*/
|
||||
void canceled();
|
||||
|
||||
/**
|
||||
* Tell the view that the listing of the directory @p dirUrl was canceled.
|
||||
* There might be other running jobs left.
|
||||
*
|
||||
* @param dirUrl the directory URL
|
||||
*
|
||||
* @since 5.79
|
||||
*/
|
||||
void listingDirCanceled(const QUrl &dirUrl);
|
||||
|
||||
/**
|
||||
* Signals a redirection.
|
||||
*
|
||||
* @param oldUrl the original URL
|
||||
* @param newUrl the new URL
|
||||
*/
|
||||
void redirection(const QUrl &oldUrl, const QUrl &newUrl);
|
||||
|
||||
/**
|
||||
* Signals to the view to remove all items (when e.g.\ going from dirA to dirB).
|
||||
* Make sure to connect to this signal to avoid having duplicate items in the view.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Signals to the view to clear all items from directory @p dirUrl.
|
||||
*
|
||||
* This is only emitted if the lister is holding more than one directory.
|
||||
*
|
||||
* @param dirUrl the directory that the view should clear all items from
|
||||
*
|
||||
* @since 5.79
|
||||
*/
|
||||
void clearDir(const QUrl &dirUrl);
|
||||
|
||||
/**
|
||||
* Signal new items.
|
||||
*
|
||||
* @param items a list of new items
|
||||
*/
|
||||
void newItems(const KFileItemList &items);
|
||||
|
||||
/**
|
||||
* Signal that new items were found during directory listing.
|
||||
* Alternative signal emitted at the same time as newItems(),
|
||||
* but itemsAdded also passes the url of the parent directory.
|
||||
*
|
||||
* @param items a list of new items
|
||||
*/
|
||||
void itemsAdded(const QUrl &directoryUrl, const KFileItemList &items);
|
||||
|
||||
/**
|
||||
* Send a list of items filtered-out by MIME type.
|
||||
* @param items the list of filtered items
|
||||
*
|
||||
*/
|
||||
void itemsFilteredByMime(const KFileItemList &items);
|
||||
|
||||
/**
|
||||
* Signal that items have been deleted
|
||||
*
|
||||
* @since 4.1.2
|
||||
* @param items the list of deleted items
|
||||
*/
|
||||
void itemsDeleted(const KFileItemList &items);
|
||||
|
||||
/**
|
||||
* Signal an item to refresh (its MIME-type/icon/name has changed).
|
||||
* Note: KFileItem::refresh has already been called on those items.
|
||||
* @param items the items to refresh. This is a list of pairs, where
|
||||
* the first item in the pair is the OLD item, and the second item is the
|
||||
* NEW item. This allows to track which item has changed, especially after
|
||||
* a renaming.
|
||||
*/
|
||||
void refreshItems(const QList<QPair<KFileItem, KFileItem>> &items);
|
||||
|
||||
/**
|
||||
* Emitted to display information about running jobs.
|
||||
* Examples of message are "Resolving host", "Connecting to host...", etc.
|
||||
* @param msg the info message
|
||||
*/
|
||||
void infoMessage(const QString &msg);
|
||||
|
||||
/**
|
||||
* Progress signal showing the overall progress of the KCoreDirLister.
|
||||
* This allows using a progress bar very easily. (see QProgressBar)
|
||||
* @param percent the progress in percent
|
||||
*/
|
||||
void percent(int percent);
|
||||
|
||||
/**
|
||||
* Emitted when we know the size of the jobs.
|
||||
* @param size the total size in bytes
|
||||
*/
|
||||
void totalSize(KIO::filesize_t size);
|
||||
|
||||
/**
|
||||
* Regularly emitted to show the progress of this KCoreDirLister.
|
||||
* @param size the processed size in bytes
|
||||
*/
|
||||
void processedSize(KIO::filesize_t size);
|
||||
|
||||
/**
|
||||
* Emitted to display information about the speed of the jobs.
|
||||
* @param bytes_per_second the speed in bytes/s
|
||||
*/
|
||||
void speed(int bytes_per_second);
|
||||
|
||||
/**
|
||||
* Emitted if listing a directory fails with an error.
|
||||
* A typical implementation in a widgets-based application
|
||||
* would show a message box by calling this in a slot connected to this signal:
|
||||
* <tt>job->uiDelegate()->showErrorMessage()</tt>
|
||||
* Many applications might prefer to embed the error message though
|
||||
* (e.g. by using the KMessageWidget class, from the KWidgetsAddons Framework).
|
||||
* @param the job with an error
|
||||
* @since 5.82
|
||||
*/
|
||||
void jobError(KIO::Job *job);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Reimplemented by KDirLister to associate windows with jobs
|
||||
* @since 5.0
|
||||
*/
|
||||
virtual void jobStarted(KIO::ListJob *);
|
||||
|
||||
private:
|
||||
friend class KCoreDirListerPrivate;
|
||||
std::unique_ptr<KCoreDirListerPrivate> d;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KCoreDirLister::OpenUrlFlags)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,605 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2002-2006 Michael Brade <brade@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KCOREDIRLISTER_P_H
|
||||
#define KCOREDIRLISTER_P_H
|
||||
|
||||
#include "kfileitem.h"
|
||||
#include "kmountpoint.h"
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
#include "kdirnotify.h"
|
||||
#endif
|
||||
|
||||
#include <QCache>
|
||||
#include <QCoreApplication>
|
||||
#include <QFileInfo>
|
||||
#include <QHash>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QTimer>
|
||||
#include <QUrl>
|
||||
|
||||
#include <KDirWatch>
|
||||
#include <kio/global.h>
|
||||
|
||||
#include <set>
|
||||
|
||||
class QRegularExpression;
|
||||
class KCoreDirLister;
|
||||
namespace KIO
|
||||
{
|
||||
class Job;
|
||||
class ListJob;
|
||||
}
|
||||
class OrgKdeKDirNotifyInterface;
|
||||
struct KCoreDirListerCacheDirectoryData;
|
||||
|
||||
class KCoreDirListerPrivate
|
||||
{
|
||||
public:
|
||||
explicit KCoreDirListerPrivate(KCoreDirLister *qq);
|
||||
|
||||
void emitCachedItems(const QUrl &, bool, bool);
|
||||
void slotInfoMessage(KJob *, const QString &);
|
||||
void slotPercent(KJob *, unsigned long);
|
||||
void slotTotalSize(KJob *, qulonglong);
|
||||
void slotProcessedSize(KJob *, qulonglong);
|
||||
void slotSpeed(KJob *, unsigned long);
|
||||
|
||||
/*
|
||||
* Called by the public matchesMimeFilter() to do the
|
||||
* actual filtering. Those methods may be reimplemented to customize
|
||||
* filtering.
|
||||
* @param mimeType the MIME type to filter
|
||||
* @param filters the list of MIME types to filter
|
||||
*/
|
||||
bool doMimeFilter(const QString &mimeType, const QStringList &filters) const;
|
||||
bool doMimeExcludeFilter(const QString &mimeExclude, const QStringList &filters) const;
|
||||
void connectJob(KIO::ListJob *);
|
||||
void jobDone(KIO::ListJob *);
|
||||
uint numJobs();
|
||||
void addNewItem(const QUrl &directoryUrl, const KFileItem &item);
|
||||
void addNewItems(const QUrl &directoryUrl, const QList<KFileItem> &items);
|
||||
void addRefreshItem(const QUrl &directoryUrl, const KFileItem &oldItem, const KFileItem &item);
|
||||
void emitItems();
|
||||
void emitItemsDeleted(const KFileItemList &items);
|
||||
|
||||
/*
|
||||
* Called for every new item before emitting newItems().
|
||||
* You may reimplement this method in a subclass to implement your own
|
||||
* filtering.
|
||||
* The default implementation filters out ".." and everything not matching
|
||||
* the name filter(s)
|
||||
* @return @c true if the item is "ok".
|
||||
* @c false if the item shall not be shown in a view, e.g.
|
||||
* files not matching a pattern *.cpp ( KFileItem::isHidden())
|
||||
* @see matchesFilter
|
||||
* @see setNameFilter
|
||||
*/
|
||||
bool matchesFilter(const KFileItem &) const;
|
||||
|
||||
/*
|
||||
* Called for every new item before emitting newItems().
|
||||
* You may reimplement this method in a subclass to implement your own
|
||||
* filtering.
|
||||
* The default implementation filters out everything not matching
|
||||
* the mime filter(s)
|
||||
* @return @c true if the item is "ok".
|
||||
* @c false if the item shall not be shown in a view, e.g.
|
||||
* files not matching the mime filter
|
||||
* @see matchesMimeFilter
|
||||
* @see setMimeFilter
|
||||
*/
|
||||
bool matchesMimeFilter(const KFileItem &) const;
|
||||
|
||||
/**
|
||||
* Redirect this dirlister from oldUrl to newUrl.
|
||||
* @param keepItems if true, keep the fileitems (e.g. when renaming an existing dir);
|
||||
* if false, clear out everything (e.g. when redirecting during listing).
|
||||
*/
|
||||
void redirect(const QUrl &oldUrl, const QUrl &newUrl, bool keepItems);
|
||||
|
||||
/**
|
||||
* Should this item be visible according to the current filter settings?
|
||||
*/
|
||||
bool isItemVisible(const KFileItem &item) const;
|
||||
|
||||
void prepareForSettingsChange()
|
||||
{
|
||||
if (!hasPendingChanges) {
|
||||
hasPendingChanges = true;
|
||||
oldSettings = settings;
|
||||
}
|
||||
}
|
||||
|
||||
void emitChanges();
|
||||
|
||||
class CachedItemsJob;
|
||||
CachedItemsJob *cachedItemsJobForUrl(const QUrl &url) const;
|
||||
|
||||
KCoreDirLister *const q;
|
||||
|
||||
/**
|
||||
* List of dirs handled by this dirlister. The first entry is the base URL.
|
||||
* For a tree view, it contains all the dirs shown.
|
||||
*/
|
||||
QList<QUrl> lstDirs;
|
||||
|
||||
// toplevel URL
|
||||
QUrl url;
|
||||
|
||||
bool complete = false;
|
||||
bool autoUpdate = false;
|
||||
bool delayedMimeTypes = false;
|
||||
bool hasPendingChanges = false; // i.e. settings != oldSettings
|
||||
bool m_autoErrorHandling = true;
|
||||
bool requestMimeTypeWhileListing = false;
|
||||
|
||||
struct JobData {
|
||||
long unsigned int percent, speed;
|
||||
KIO::filesize_t processedSize, totalSize;
|
||||
};
|
||||
|
||||
QMap<KIO::ListJob *, JobData> jobData;
|
||||
|
||||
// file item for the root itself (".")
|
||||
KFileItem rootFileItem;
|
||||
|
||||
typedef QHash<QUrl, KFileItemList> NewItemsHash;
|
||||
NewItemsHash lstNewItems;
|
||||
QList<QPair<KFileItem, KFileItem>> lstRefreshItems;
|
||||
KFileItemList lstMimeFilteredItems, lstRemoveItems;
|
||||
|
||||
QList<CachedItemsJob *> m_cachedItemsJobs;
|
||||
|
||||
QString nameFilter; // parsed into lstFilters
|
||||
|
||||
struct FilterSettings {
|
||||
FilterSettings()
|
||||
: isShowingDotFiles(false)
|
||||
, dirOnlyMode(false)
|
||||
{
|
||||
}
|
||||
bool isShowingDotFiles;
|
||||
bool dirOnlyMode;
|
||||
QList<QRegularExpression> lstFilters;
|
||||
QStringList mimeFilter;
|
||||
QStringList mimeExcludeFilter;
|
||||
};
|
||||
FilterSettings settings;
|
||||
FilterSettings oldSettings;
|
||||
|
||||
friend class KCoreDirListerCache;
|
||||
};
|
||||
|
||||
/**
|
||||
* Design of the cache:
|
||||
* There is a single KCoreDirListerCache for the whole process.
|
||||
* It holds all the items used by the dir listers (itemsInUse)
|
||||
* as well as a cache of the recently used items (itemsCached).
|
||||
* Those items are grouped by directory (a DirItem represents a whole directory).
|
||||
*
|
||||
* KCoreDirListerCache also runs all the jobs for listing directories, whether they are for
|
||||
* normal listing or for updates.
|
||||
* For faster lookups, it also stores a hash table, which gives for a directory URL:
|
||||
* - the dirlisters holding that URL (listersCurrentlyHolding)
|
||||
* - the dirlisters currently listing that URL (listersCurrentlyListing)
|
||||
*/
|
||||
class KCoreDirListerCache : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
KCoreDirListerCache(); // only called by QThreadStorage internally
|
||||
~KCoreDirListerCache() override;
|
||||
|
||||
void updateDirectory(const QUrl &dir);
|
||||
|
||||
KFileItem itemForUrl(const QUrl &url) const;
|
||||
QList<KFileItem> *itemsForDir(const QUrl &dir) const;
|
||||
|
||||
bool listDir(KCoreDirLister *lister, const QUrl &_url, bool _keep, bool _reload);
|
||||
|
||||
// stop all running jobs for lister
|
||||
void stop(KCoreDirLister *lister, bool silent = false);
|
||||
// stop just the job listing url for lister
|
||||
void stopListingUrl(KCoreDirLister *lister, const QUrl &_url, bool silent = false);
|
||||
|
||||
void setAutoUpdate(KCoreDirLister *lister, bool enable);
|
||||
|
||||
void forgetDirs(KCoreDirLister *lister);
|
||||
void forgetDirs(KCoreDirLister *lister, const QUrl &_url, bool notify, const KMountPoint::List &possibleMountPoints);
|
||||
|
||||
KFileItem findByName(const KCoreDirLister *lister, const QString &_name) const;
|
||||
// findByUrl returns a pointer so that it's possible to modify the item.
|
||||
// See itemForUrl for the version that returns a readonly kfileitem.
|
||||
// @param lister can be 0. If set, it is checked that the url is held by the lister
|
||||
KFileItem findByUrl(const KCoreDirLister *lister, const QUrl &url) const;
|
||||
|
||||
// Called by CachedItemsJob:
|
||||
// Emits the cached items, for this lister and this url
|
||||
void emitItemsFromCache(KCoreDirListerPrivate::CachedItemsJob *job, KCoreDirLister *lister, const QUrl &_url, bool _reload, bool _emitCompleted);
|
||||
// Called by CachedItemsJob:
|
||||
void forgetCachedItemsJob(KCoreDirListerPrivate::CachedItemsJob *job, KCoreDirLister *lister, const QUrl &url);
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Notify that files have been added in @p directory
|
||||
* The receiver will list that directory again to find
|
||||
* the new items (since it needs more than just the names anyway).
|
||||
* Connected to the DBus signal from the KDirNotify interface.
|
||||
*/
|
||||
void slotFilesAdded(const QString &urlDirectory);
|
||||
|
||||
/**
|
||||
* Notify that files have been deleted.
|
||||
* This call passes the exact urls of the deleted files
|
||||
* so that any view showing them can simply remove them
|
||||
* or be closed (if its current dir was deleted)
|
||||
* Connected to the DBus signal from the KDirNotify interface.
|
||||
*/
|
||||
void slotFilesRemoved(const QStringList &fileList);
|
||||
|
||||
/**
|
||||
* Notify that files have been changed.
|
||||
* At the moment, this is only used for new icon, but it could be
|
||||
* used for size etc. as well.
|
||||
* Connected to the DBus signal from the KDirNotify interface.
|
||||
*/
|
||||
void slotFilesChanged(const QStringList &fileList);
|
||||
void slotFileRenamed(const QString &srcUrl, const QString &dstUrl, const QString &dstPath);
|
||||
|
||||
private Q_SLOTS:
|
||||
void slotFileDirty(const QString &_file);
|
||||
void slotFileCreated(const QString &_file);
|
||||
void slotFileDeleted(const QString &_file);
|
||||
|
||||
void slotEntries(KIO::Job *job, const KIO::UDSEntryList &entries);
|
||||
void slotResult(KJob *j);
|
||||
void slotRedirection(KIO::Job *job, const QUrl &url);
|
||||
|
||||
void slotUpdateEntries(KIO::Job *job, const KIO::UDSEntryList &entries);
|
||||
void slotUpdateResult(KJob *job);
|
||||
void processPendingUpdates();
|
||||
|
||||
private:
|
||||
void itemsAddedInDirectory(const QUrl &url);
|
||||
|
||||
class DirItem;
|
||||
DirItem *dirItemForUrl(const QUrl &dir) const;
|
||||
|
||||
void stopListJob(const QUrl &url, bool silent);
|
||||
|
||||
KIO::ListJob *jobForUrl(const QUrl &url, KIO::ListJob *not_job = nullptr);
|
||||
const QUrl &joburl(KIO::ListJob *job);
|
||||
|
||||
void killJob(KIO::ListJob *job);
|
||||
|
||||
// Called when something tells us that the directory @p url has changed.
|
||||
// Returns true if @p url is held by some lister (meaning: do the update now)
|
||||
// otherwise mark the cached item as not-up-to-date for later and return false
|
||||
bool checkUpdate(const QUrl &url);
|
||||
|
||||
// Helper method for slotFileDirty
|
||||
void handleFileDirty(const QUrl &url);
|
||||
void handleDirDirty(const QUrl &url);
|
||||
|
||||
// when there were items deleted from the filesystem all the listers holding
|
||||
// the parent directory need to be notified, the items have to be deleted
|
||||
// and removed from the cache including all the children.
|
||||
void deleteUnmarkedItems(const QList<KCoreDirLister *> &, QList<KFileItem> &lstItems, const QHash<QString, KFileItem> &itemsToDelete);
|
||||
|
||||
// Helper method called when we know that a list of items was deleted
|
||||
void itemsDeleted(const QList<KCoreDirLister *> &listers, const KFileItemList &deletedItems);
|
||||
void slotFilesRemoved(const QList<QUrl> &urls);
|
||||
// common for slotRedirection and slotFileRenamed
|
||||
void renameDir(const QUrl &oldUrl, const QUrl &url);
|
||||
// common for deleteUnmarkedItems and slotFilesRemoved
|
||||
void deleteDir(const QUrl &dirUrl);
|
||||
// remove directory from cache (itemsCached), including all child dirs
|
||||
void removeDirFromCache(const QUrl &dir);
|
||||
// helper for renameDir
|
||||
void emitRedirections(const QUrl &oldUrl, const QUrl &url);
|
||||
|
||||
/**
|
||||
* Emits refreshItem() in the directories that cared for oldItem.
|
||||
* The caller has to remember to call emitItems in the set of dirlisters returned
|
||||
* (but this allows to buffer change notifications)
|
||||
*/
|
||||
std::set<KCoreDirLister *> emitRefreshItem(const KFileItem &oldItem, const KFileItem &fileitem);
|
||||
|
||||
/**
|
||||
* Remove the item from the sorted by url list matching @p oldUrl,
|
||||
* that is in the wrong place (because its url has changed) and insert @p item in the right place.
|
||||
* @param oldUrl the previous url of the @p item
|
||||
* @param item the modified item to be inserted
|
||||
*/
|
||||
void reinsert(const KFileItem &item, const QUrl &oldUrl)
|
||||
{
|
||||
const QUrl parentDir = oldUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
|
||||
DirItem *dirItem = dirItemForUrl(parentDir);
|
||||
if (dirItem) {
|
||||
auto it = std::lower_bound(dirItem->lstItems.begin(), dirItem->lstItems.end(), oldUrl);
|
||||
if (it != dirItem->lstItems.end()) {
|
||||
dirItem->lstItems.erase(it);
|
||||
dirItem->insert(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void remove(const QUrl &oldUrl)
|
||||
{
|
||||
const QUrl parentDir = oldUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash);
|
||||
DirItem *dirItem = dirItemForUrl(parentDir);
|
||||
if (dirItem) {
|
||||
auto it = std::lower_bound(dirItem->lstItems.begin(), dirItem->lstItems.end(), oldUrl);
|
||||
if (it != dirItem->lstItems.end()) {
|
||||
dirItem->lstItems.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* When KDirWatch tells us that something changed in "dir", we need to
|
||||
* also notify the dirlisters that are listing a symlink to "dir" (#213799)
|
||||
*/
|
||||
QList<QUrl> directoriesForCanonicalPath(const QUrl &dir) const;
|
||||
|
||||
// Definition of the cache of ".hidden" files
|
||||
struct CacheHiddenFile {
|
||||
CacheHiddenFile(const QDateTime &mtime, std::set<QString> &&listedFilesParam)
|
||||
: mtime(mtime)
|
||||
, listedFiles(std::move(listedFilesParam))
|
||||
{
|
||||
}
|
||||
QDateTime mtime;
|
||||
std::set<QString> listedFiles;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the names listed in dir's ".hidden" file, if it exists.
|
||||
* If a file named ".hidden" exists in the @p dir directory, this method
|
||||
* returns all the file names listed in that file. If it doesn't exist, an
|
||||
* empty set is returned.
|
||||
* @param dir path to the target directory.
|
||||
* @return names listed in the directory's ".hidden" file (empty if it doesn't exist).
|
||||
*/
|
||||
CacheHiddenFile *cachedDotHiddenForDir(const QString &dir);
|
||||
|
||||
#ifndef NDEBUG
|
||||
void printDebug();
|
||||
#endif
|
||||
|
||||
class DirItem
|
||||
{
|
||||
public:
|
||||
DirItem(const QUrl &dir, const QString &canonicalPath)
|
||||
: url(dir)
|
||||
, m_canonicalPath(canonicalPath)
|
||||
{
|
||||
autoUpdates = 0;
|
||||
complete = false;
|
||||
watchedWhileInCache = false;
|
||||
}
|
||||
|
||||
~DirItem()
|
||||
{
|
||||
if (autoUpdates) {
|
||||
if (KDirWatch::exists() && url.isLocalFile()) {
|
||||
KDirWatch::self()->removeDir(m_canonicalPath);
|
||||
}
|
||||
// Since sendSignal goes through D-Bus, QCoreApplication has to be available
|
||||
// which might not be the case anymore from a global static dtor like the
|
||||
// lister cache
|
||||
if (QCoreApplication::instance()) {
|
||||
sendSignal(false, url);
|
||||
}
|
||||
}
|
||||
lstItems.clear();
|
||||
}
|
||||
|
||||
DirItem(const DirItem &) = delete;
|
||||
DirItem &operator=(const DirItem &) = delete;
|
||||
|
||||
void sendSignal(bool entering, const QUrl &url)
|
||||
{
|
||||
// Note that "entering" means "start watching", and "leaving" means "stop watching"
|
||||
// (i.e. it's not when the user leaves the directory, it's when the directory is removed from the cache)
|
||||
#ifdef WITH_QTDBUS
|
||||
if (entering) {
|
||||
org::kde::KDirNotify::emitEnteredDirectory(url);
|
||||
} else {
|
||||
org::kde::KDirNotify::emitLeftDirectory(url);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void redirect(const QUrl &newUrl)
|
||||
{
|
||||
if (autoUpdates) {
|
||||
if (url.isLocalFile()) {
|
||||
KDirWatch::self()->removeDir(m_canonicalPath);
|
||||
}
|
||||
sendSignal(false, url);
|
||||
|
||||
if (newUrl.isLocalFile()) {
|
||||
m_canonicalPath = QFileInfo(newUrl.toLocalFile()).canonicalFilePath();
|
||||
KDirWatch::self()->addDir(m_canonicalPath);
|
||||
}
|
||||
sendSignal(true, newUrl);
|
||||
}
|
||||
|
||||
url = newUrl;
|
||||
|
||||
if (!rootItem.isNull()) {
|
||||
rootItem.setUrl(newUrl);
|
||||
}
|
||||
}
|
||||
|
||||
void incAutoUpdate()
|
||||
{
|
||||
if (autoUpdates++ == 0) {
|
||||
if (url.isLocalFile()) {
|
||||
KDirWatch::self()->addDir(m_canonicalPath);
|
||||
}
|
||||
sendSignal(true, url);
|
||||
}
|
||||
}
|
||||
|
||||
void decAutoUpdate()
|
||||
{
|
||||
if (--autoUpdates == 0) {
|
||||
if (url.isLocalFile()) {
|
||||
KDirWatch::self()->removeDir(m_canonicalPath);
|
||||
}
|
||||
sendSignal(false, url);
|
||||
}
|
||||
|
||||
else if (autoUpdates < 0) {
|
||||
autoUpdates = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Insert the item in the sorted list
|
||||
void insert(const KFileItem &item)
|
||||
{
|
||||
auto it = std::lower_bound(lstItems.begin(), lstItems.end(), item.url());
|
||||
lstItems.insert(it, item);
|
||||
}
|
||||
|
||||
// Insert the already sorted items in the sorted list
|
||||
void insertSortedItems(const KFileItemList &items)
|
||||
{
|
||||
if (items.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
lstItems.reserve(lstItems.size() + items.size());
|
||||
auto it = lstItems.begin();
|
||||
for (const auto &item : items) {
|
||||
it = std::lower_bound(it, lstItems.end(), item.url());
|
||||
it = lstItems.insert(it, item);
|
||||
}
|
||||
}
|
||||
|
||||
// number of KCoreDirListers using autoUpdate for this dir
|
||||
short autoUpdates;
|
||||
|
||||
// this directory is up-to-date
|
||||
bool complete;
|
||||
|
||||
// the directory is watched while being in the cache (useful for proper incAutoUpdate/decAutoUpdate count)
|
||||
bool watchedWhileInCache;
|
||||
|
||||
// the complete url of this directory
|
||||
QUrl url;
|
||||
|
||||
// the local path, with symlinks resolved, so that KDirWatch works
|
||||
QString m_canonicalPath;
|
||||
|
||||
// KFileItem representing the root of this directory.
|
||||
// Remember that this is optional. FTP sites don't return '.' in
|
||||
// the list, so they give no root item
|
||||
KFileItem rootItem;
|
||||
// The fileitems contained in the directory. Empty when directory is not readable.
|
||||
QList<KFileItem> lstItems;
|
||||
};
|
||||
|
||||
QMap<KIO::ListJob *, KIO::UDSEntryList> runningListJobs;
|
||||
|
||||
// an item is a complete directory
|
||||
QHash<QUrl, DirItem *> itemsInUse;
|
||||
QCache<QUrl, DirItem> itemsCached;
|
||||
|
||||
// cache of ".hidden" files
|
||||
QCache<QString /*dot hidden file*/, CacheHiddenFile> m_cacheHiddenFiles;
|
||||
|
||||
typedef QHash<QUrl, KCoreDirListerCacheDirectoryData> DirectoryDataHash;
|
||||
DirectoryDataHash directoryData;
|
||||
|
||||
// Symlink-to-directories are registered here so that we can
|
||||
// find the url that changed, when kdirwatch tells us about
|
||||
// changes in the canonical url. (#213799)
|
||||
QHash<QUrl /*canonical path*/, QList<QUrl> /*dirlister urls*/> canonicalUrls;
|
||||
|
||||
// Set of local files that we have changed recently (according to KDirWatch)
|
||||
// We temporize the notifications by keeping them 500ms in this list.
|
||||
std::set<QString /*path*/> pendingUpdates;
|
||||
std::set<QString /*path*/> pendingDirectoryUpdates;
|
||||
// The timer for doing the delayed updates
|
||||
QTimer pendingUpdateTimer;
|
||||
|
||||
// Set of remote files that have changed recently -- but we can't emit those
|
||||
// changes yet, we need to wait for the "update" directory listing.
|
||||
// The cmp() call can't differ MIME types since they are determined on demand,
|
||||
// this is why we need to remember those files here.
|
||||
std::set<KFileItem> pendingRemoteUpdates;
|
||||
|
||||
#ifdef WITH_QTDBUS
|
||||
// the KDirNotify signals
|
||||
OrgKdeKDirNotifyInterface *kdirnotify;
|
||||
#endif
|
||||
|
||||
struct ItemInUseChange;
|
||||
};
|
||||
|
||||
// Data associated with a directory url
|
||||
// This could be in DirItem but only in the itemsInUse dict...
|
||||
struct KCoreDirListerCacheDirectoryData {
|
||||
// A lister can be EITHER in listersCurrentlyListing OR listersCurrentlyHolding
|
||||
// but NOT in both at the same time.
|
||||
// But both lists can have different listers at the same time; this
|
||||
// happens if more listers are requesting url at the same time and
|
||||
// one lister was stopped during the listing of files.
|
||||
|
||||
// Listers that are currently listing this url
|
||||
QList<KCoreDirLister *> listersCurrentlyListing;
|
||||
// Listers that are currently holding this url
|
||||
QList<KCoreDirLister *> listersCurrentlyHolding;
|
||||
|
||||
void moveListersWithoutCachedItemsJob(const QUrl &url);
|
||||
};
|
||||
|
||||
// This job tells KCoreDirListerCache to emit cached items asynchronously from listDir()
|
||||
// to give the KCoreDirLister user enough time for connecting to its signals, and so
|
||||
// that KCoreDirListerCache behaves just like when a real KIO::Job is used: nothing
|
||||
// is emitted during the openUrl call itself.
|
||||
class KCoreDirListerPrivate::CachedItemsJob : public KJob
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
CachedItemsJob(KCoreDirLister *lister, const QUrl &url, bool reload);
|
||||
|
||||
/*reimp*/ void start() override
|
||||
{
|
||||
QMetaObject::invokeMethod(this, &KCoreDirListerPrivate::CachedItemsJob::done, Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
// For updateDirectory() to cancel m_emitCompleted;
|
||||
void setEmitCompleted(bool b)
|
||||
{
|
||||
m_emitCompleted = b;
|
||||
}
|
||||
|
||||
QUrl url() const
|
||||
{
|
||||
return m_url;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool doKill() override;
|
||||
|
||||
public Q_SLOTS:
|
||||
void done();
|
||||
|
||||
private:
|
||||
KCoreDirLister *m_lister;
|
||||
QUrl m_url;
|
||||
bool m_reload;
|
||||
bool m_emitCompleted;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2012 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Thiago Macieira <thiago@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kdirnotify.h"
|
||||
#include <QUrl>
|
||||
|
||||
/*
|
||||
* Implementation of interface class OrgKdeKDirNotifyInterface
|
||||
*/
|
||||
|
||||
OrgKdeKDirNotifyInterface::OrgKdeKDirNotifyInterface(const QString &service, const QString &path, const QDBusConnection &connection, QObject *parent)
|
||||
: QDBusAbstractInterface(service, path, staticInterfaceName(), connection, parent)
|
||||
{
|
||||
}
|
||||
|
||||
OrgKdeKDirNotifyInterface::~OrgKdeKDirNotifyInterface()
|
||||
{
|
||||
}
|
||||
|
||||
static void emitSignal(const QString &signalName, const QVariantList &args)
|
||||
{
|
||||
QDBusMessage message = QDBusMessage::createSignal(QStringLiteral("/"), QLatin1String(org::kde::KDirNotify::staticInterfaceName()), signalName);
|
||||
message.setArguments(args);
|
||||
QDBusConnection::sessionBus().send(message);
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitFileRenamed(const QUrl &src, const QUrl &dst)
|
||||
{
|
||||
emitSignal(QStringLiteral("FileRenamed"), QVariantList{src.toString(), dst.toString()});
|
||||
emitSignal(QStringLiteral("FileRenamedWithLocalPath"), QVariantList{src.toString(), dst.toString(), QString()});
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitFileRenamedWithLocalPath(const QUrl &src, const QUrl &dst, const QString &dstPath)
|
||||
{
|
||||
emitSignal(QStringLiteral("FileRenamed"), QVariantList{src.toString(), dst.toString()});
|
||||
emitSignal(QStringLiteral("FileRenamedWithLocalPath"), QVariantList{src.toString(), dst.toString(), dstPath});
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitFileMoved(const QUrl &src, const QUrl &dst)
|
||||
{
|
||||
emitSignal(QStringLiteral("FileMoved"), QVariantList{src.toString(), dst.toString()});
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitFilesAdded(const QUrl &directory)
|
||||
{
|
||||
emitSignal(QStringLiteral("FilesAdded"), QVariantList{QVariant(directory.toString())});
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitFilesChanged(const QList<QUrl> &fileList)
|
||||
{
|
||||
emitSignal(QStringLiteral("FilesChanged"), QVariantList{QVariant(QUrl::toStringList(fileList))});
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitFilesRemoved(const QList<QUrl> &fileList)
|
||||
{
|
||||
emitSignal(QStringLiteral("FilesRemoved"), QVariantList{QVariant(QUrl::toStringList(fileList))});
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitEnteredDirectory(const QUrl &url)
|
||||
{
|
||||
emitSignal(QStringLiteral("enteredDirectory"), QVariantList{QVariant(url.toString())});
|
||||
}
|
||||
|
||||
void OrgKdeKDirNotifyInterface::emitLeftDirectory(const QUrl &url)
|
||||
{
|
||||
emitSignal(QStringLiteral("leftDirectory"), QVariantList{QVariant(url.toString())});
|
||||
}
|
||||
|
||||
#include "moc_kdirnotify.cpp"
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2000-2012 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2006 Thiago Macieira <thiago@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KDIRNOTIFY_H
|
||||
#define KDIRNOTIFY_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QByteArray>
|
||||
#ifdef QT_DBUS_LIB
|
||||
#include <QDBusAbstractInterface>
|
||||
#include <QList>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
#include <QVariant>
|
||||
|
||||
class QDBusConnection;
|
||||
|
||||
/**
|
||||
* \class OrgKdeKDirNotifyInterface kdirnotify.h KDirNotify
|
||||
*
|
||||
* \brief Proxy class for interface org.kde.KDirNotify.
|
||||
*
|
||||
* KDirNotify can be used to inform KIO about changes in real or virtual file systems.
|
||||
* Classes like KDirModel connect to the signals as in the following example to
|
||||
* be able to keep caches up-to-date.
|
||||
*
|
||||
* \code
|
||||
* kdirnotify = new org::kde::KDirNotify(QString(), QString(), QDBusConnection::sessionBus(), this);
|
||||
* connect(kdirnotify, &KDirNotify::FileRenamedWithLocalPath,
|
||||
* this, [this](const QString &src, const QString &dst, const QString &dstPath) {
|
||||
* slotFileRenamed(src, dst, dstPath);
|
||||
* });
|
||||
*
|
||||
* connect(kdirnotify, &KDirNotify::FilesAdded,
|
||||
* this, [this](const QString &directory) { slotFilesAdded(directory); });
|
||||
*
|
||||
* connect(kdirnotify, &KDirNotify::FilesChanged,
|
||||
* this, [this](const QStringList &fileList) { slotFilesChanged(fileList); });
|
||||
*
|
||||
* connect(kdirnotify, &KDirNotify::FilesRemoved,
|
||||
* this, [this](const QStringList &fileList) { slotFilesRemoved(fileList); });
|
||||
* \endcode
|
||||
*
|
||||
* Especially noteworthy are the empty strings for both \p service and \p path. That
|
||||
* way the client will connect to signals emitted by any application.
|
||||
*
|
||||
* The second usage is to actually emit the signals. For that emitFileRenamed() and friends are
|
||||
* to be used.
|
||||
*/
|
||||
class KIOCORE_EXPORT OrgKdeKDirNotifyInterface : public QDBusAbstractInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
static inline const char *staticInterfaceName()
|
||||
{
|
||||
return "org.kde.KDirNotify";
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* Create a new KDirNotify interface.
|
||||
*
|
||||
* \param service The service whose signals one wants to listed to. Use an empty
|
||||
* string to connect to all services/applications.
|
||||
* \param path The path to the D-Bus object whose signals one wants to listed to.
|
||||
* Use an empty string to connect to signals from all objects.
|
||||
* \param connection Typically QDBusConnection::sessionBus().
|
||||
* \param parent The parent QObject.
|
||||
*/
|
||||
OrgKdeKDirNotifyInterface(const QString &service,
|
||||
const QString &path,
|
||||
const QDBusConnection &connection = QDBusConnection::sessionBus(),
|
||||
QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~OrgKdeKDirNotifyInterface() override;
|
||||
|
||||
public Q_SLOTS: // METHODS
|
||||
Q_SIGNALS: // SIGNALS
|
||||
void FileRenamed(const QString &src, const QString &dst);
|
||||
void FileRenamedWithLocalPath(const QString &src, const QString &dst, const QString &dstPath);
|
||||
void FileMoved(const QString &src, const QString &dst);
|
||||
void FilesAdded(const QString &directory);
|
||||
void FilesChanged(const QStringList &fileList);
|
||||
void FilesRemoved(const QStringList &fileList);
|
||||
void enteredDirectory(const QString &url);
|
||||
void leftDirectory(const QString &url);
|
||||
|
||||
public:
|
||||
static void emitFileRenamed(const QUrl &src, const QUrl &dst);
|
||||
/**
|
||||
* \param src The old URL of the file that has been renamed.
|
||||
* \param dst The new URL of the file after it was renamed.
|
||||
* \param dstPath The local path of the file after it was renamed. This may be empty
|
||||
* and should otherwise be used to update UDS_LOCAL_PATH.
|
||||
* @since 5.20
|
||||
*/
|
||||
static void emitFileRenamedWithLocalPath(const QUrl &src, const QUrl &dst, const QString &dstPath);
|
||||
static void emitFileMoved(const QUrl &src, const QUrl &dst);
|
||||
static void emitFilesAdded(const QUrl &directory);
|
||||
static void emitFilesChanged(const QList<QUrl> &fileList);
|
||||
static void emitFilesRemoved(const QList<QUrl> &fileList);
|
||||
static void emitEnteredDirectory(const QUrl &url);
|
||||
static void emitLeftDirectory(const QUrl &url);
|
||||
};
|
||||
|
||||
namespace org
|
||||
{
|
||||
namespace kde
|
||||
{
|
||||
typedef ::OrgKdeKDirNotifyInterface KDirNotify;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -0,0 +1,224 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kfilefilter.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QMetaType>
|
||||
#include <QMimeDatabase>
|
||||
#include <algorithm>
|
||||
#include <qchar.h>
|
||||
|
||||
#include "kiocoredebug.h"
|
||||
|
||||
class KFileFilterPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
KFileFilterPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
KFileFilterPrivate(const KFileFilterPrivate &other)
|
||||
: QSharedData(other)
|
||||
, m_label(other.m_label)
|
||||
, m_filePatterns(other.m_filePatterns)
|
||||
, m_mimePatterns(other.m_mimePatterns)
|
||||
{
|
||||
}
|
||||
|
||||
QString m_label;
|
||||
QStringList m_filePatterns;
|
||||
QStringList m_mimePatterns;
|
||||
bool m_isValid = true;
|
||||
};
|
||||
|
||||
QList<KFileFilter> KFileFilter::fromFilterString(const QString &filterString)
|
||||
{
|
||||
int pos = filterString.indexOf(QLatin1Char('/'));
|
||||
|
||||
// Check for an un-escaped '/', if found
|
||||
// interpret as a MIME filter.
|
||||
|
||||
if (pos > 0 && filterString[pos - 1] != QLatin1Char('\\')) {
|
||||
const QStringList filters = filterString.split(QLatin1Char(' '), Qt::SkipEmptyParts);
|
||||
|
||||
QList<KFileFilter> result;
|
||||
result.reserve(filters.size());
|
||||
|
||||
std::transform(filters.begin(), filters.end(), std::back_inserter(result), [](const QString &mimeType) {
|
||||
return KFileFilter::fromMimeType(mimeType);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Strip the escape characters from
|
||||
// escaped '/' characters.
|
||||
|
||||
QString escapeRemoved(filterString);
|
||||
for (pos = 0; (pos = escapeRemoved.indexOf(QLatin1String("\\/"), pos)) != -1; ++pos) {
|
||||
escapeRemoved.remove(pos, 1);
|
||||
}
|
||||
|
||||
const QStringList filters = escapeRemoved.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
|
||||
|
||||
QList<KFileFilter> result;
|
||||
|
||||
for (const QString &filter : filters) {
|
||||
int separatorPos = filter.indexOf(QLatin1Char('|'));
|
||||
|
||||
QString label;
|
||||
QStringList patterns;
|
||||
|
||||
if (separatorPos != -1) {
|
||||
label = filter.mid(separatorPos + 1);
|
||||
patterns = filter.left(separatorPos).split(QLatin1Char(' '));
|
||||
} else {
|
||||
patterns = filter.split(QLatin1Char(' '));
|
||||
label = patterns.join(QLatin1Char(' '));
|
||||
}
|
||||
|
||||
result << KFileFilter(label, patterns, {});
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
KFileFilter::KFileFilter()
|
||||
: d(new KFileFilterPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
|
||||
: d(new KFileFilterPrivate)
|
||||
{
|
||||
d->m_filePatterns = filePatterns;
|
||||
d->m_mimePatterns = mimePatterns;
|
||||
d->m_label = label;
|
||||
}
|
||||
|
||||
KFileFilter::~KFileFilter() = default;
|
||||
|
||||
KFileFilter::KFileFilter(const KFileFilter &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
KFileFilter &KFileFilter::operator=(const KFileFilter &other)
|
||||
{
|
||||
if (this != &other) {
|
||||
d = other.d;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
QString KFileFilter::label() const
|
||||
{
|
||||
return d->m_label;
|
||||
}
|
||||
|
||||
QStringList KFileFilter::filePatterns() const
|
||||
{
|
||||
return d->m_filePatterns;
|
||||
}
|
||||
|
||||
QStringList KFileFilter::mimePatterns() const
|
||||
{
|
||||
return d->m_mimePatterns;
|
||||
}
|
||||
|
||||
bool KFileFilter::operator==(const KFileFilter &other) const
|
||||
{
|
||||
return d->m_filePatterns == other.d->m_filePatterns && d->m_mimePatterns == other.d->m_mimePatterns;
|
||||
}
|
||||
|
||||
bool KFileFilter::isEmpty() const
|
||||
{
|
||||
return d->m_filePatterns.isEmpty() && d->m_mimePatterns.isEmpty();
|
||||
}
|
||||
|
||||
bool KFileFilter::isValid() const
|
||||
{
|
||||
return d->m_isValid;
|
||||
}
|
||||
|
||||
QString KFileFilter::toFilterString() const
|
||||
{
|
||||
if (!d->m_filePatterns.isEmpty() && !d->m_mimePatterns.isEmpty()) {
|
||||
qCWarning(KIO_CORE) << "KFileFilters with both mime and file patterns cannot be converted to filter strings";
|
||||
return QString();
|
||||
}
|
||||
|
||||
if (!d->m_mimePatterns.isEmpty()) {
|
||||
return d->m_mimePatterns.join(QLatin1Char(' '));
|
||||
}
|
||||
|
||||
if (!d->m_label.isEmpty()) {
|
||||
const QString patterns = d->m_filePatterns.join(QLatin1Char(' '));
|
||||
const QString escapedLabel = QString(d->m_label).replace(QLatin1String("/"), QLatin1String("\\/"));
|
||||
|
||||
if (patterns != d->m_label) {
|
||||
return patterns + QLatin1Char('|') + escapedLabel;
|
||||
} else {
|
||||
return patterns;
|
||||
}
|
||||
} else {
|
||||
return d->m_filePatterns.join(QLatin1Char(' '));
|
||||
}
|
||||
}
|
||||
|
||||
KFileFilter KFileFilter::fromMimeType(const QString &mimeType)
|
||||
{
|
||||
if (mimeType.isEmpty()) {
|
||||
qCWarning(KIO_CORE) << "KFileFilter::fromMimeType() called with empty input";
|
||||
|
||||
KFileFilter filter;
|
||||
filter.d->m_isValid = false;
|
||||
return filter;
|
||||
}
|
||||
|
||||
static QMimeDatabase db;
|
||||
const QMimeType type = db.mimeTypeForName(mimeType);
|
||||
|
||||
if (type.isValid() || mimeType.endsWith(QLatin1String("/*"))) {
|
||||
KFileFilter filter(type.comment(), {}, {mimeType});
|
||||
return filter;
|
||||
} else {
|
||||
qCWarning(KIO_CORE) << "KFileFilter::fromMimeType() called with unknown MIME type" << mimeType;
|
||||
|
||||
KFileFilter filter;
|
||||
filter.d->m_isValid = false;
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
QList<KFileFilter> KFileFilter::fromMimeTypes(const QStringList &mimeTypes)
|
||||
{
|
||||
QList<KFileFilter> ret;
|
||||
ret.reserve(mimeTypes.size());
|
||||
for (const QString &type : mimeTypes) {
|
||||
ret << KFileFilter::fromMimeType(type);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
QDebug operator<<(QDebug dbg, const KFileFilter &filter)
|
||||
{
|
||||
dbg.nospace() << "KFileFilter(";
|
||||
|
||||
dbg.nospace() << "MIME patterns: " << filter.mimePatterns();
|
||||
dbg.nospace() << " ";
|
||||
dbg.nospace() << "File patterns: " << filter.filePatterns();
|
||||
dbg.nospace() << " ";
|
||||
dbg.nospace() << "label: " << filter.label();
|
||||
|
||||
dbg.nospace() << ")";
|
||||
return dbg;
|
||||
}
|
||||
|
||||
Q_DECLARE_METATYPE(KFileFilter);
|
||||
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFILEFILTER_H
|
||||
#define KFILEFILTER_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QSharedDataPointer>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class KFileFilterPrivate;
|
||||
|
||||
/**
|
||||
* @class KFileFilter kfilefilter.h <KFileFilter>
|
||||
*
|
||||
* Encapsulates rules to filter a list of files.
|
||||
* Files can be filtered based on name patterns (e.g. *.cpp), MIME types, or both.
|
||||
* Filters also optionally have a user-facing label.
|
||||
*
|
||||
* @since 5.101
|
||||
*/
|
||||
class KIOCORE_EXPORT KFileFilter
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates an empty filter.
|
||||
*/
|
||||
explicit KFileFilter();
|
||||
|
||||
/**
|
||||
* Creates a filter with a given label, name patterns, and MIME types.
|
||||
*
|
||||
* @param label The user-facing label for this filter.
|
||||
* @param filePatterns A list of file name patterns that should be included, e.g. ("*.cpp", "*.cxx").
|
||||
* @param mimePatterns A list of MIME types that should be included, e.g. ("text/plain", "image/png").
|
||||
*
|
||||
*/
|
||||
explicit KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns);
|
||||
|
||||
KFileFilter(const KFileFilter &other);
|
||||
KFileFilter &operator=(const KFileFilter &other);
|
||||
~KFileFilter();
|
||||
|
||||
/**
|
||||
* Checks whether two filters are equal.
|
||||
*
|
||||
* Filters are considered equal if their file and name patters match.
|
||||
* The label is ignored here.
|
||||
*/
|
||||
bool operator==(const KFileFilter &other) const;
|
||||
|
||||
/**
|
||||
* The user-facing label for this filter.
|
||||
*
|
||||
* If no label is passed on creation one is created based on the patterns.
|
||||
*/
|
||||
QString label() const;
|
||||
|
||||
/**
|
||||
* List of file name patterns that are included by this filter.
|
||||
*/
|
||||
QStringList filePatterns() const;
|
||||
|
||||
/**
|
||||
* List of MIME types that are included by this filter;
|
||||
*/
|
||||
QStringList mimePatterns() const;
|
||||
|
||||
/**
|
||||
* Converts this filter to a string representation
|
||||
*/
|
||||
QString toFilterString() const;
|
||||
|
||||
/**
|
||||
* Whether the filer is empty, i.e.\ matches all files.
|
||||
*/
|
||||
bool isEmpty() const;
|
||||
|
||||
/**
|
||||
* Whether the filter is valid.
|
||||
*
|
||||
* Creating a filter from an invalid/unkown MIME type will result in an invalid filter.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
bool isValid() const;
|
||||
|
||||
/*
|
||||
* Creates a filter for one MIME type.
|
||||
* The user-facing label is automatically determined from the MIME type.
|
||||
*/
|
||||
static KFileFilter fromMimeType(const QString &mimeType);
|
||||
|
||||
/**
|
||||
* Creates filters from a list of MIME types.
|
||||
* The user-facing label is automatically determined from the MIME type.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
static QList<KFileFilter> fromMimeTypes(const QStringList &mimeTypes);
|
||||
|
||||
private:
|
||||
/**
|
||||
* Convert a filter string understood by KFileWidget to a list of KFileFilters.
|
||||
*/
|
||||
static QList<KFileFilter> fromFilterString(const QString &filterString);
|
||||
friend class KFileFilterCombo;
|
||||
friend class KFileFilterTest;
|
||||
friend class KFileFilterComboPrivate;
|
||||
friend class KFileWidgetTest;
|
||||
friend class KFileFilterComboTest;
|
||||
friend class KDEPlatformFileDialog;
|
||||
friend class KDEPlatformFileDialogHelper;
|
||||
friend class KEncodingFileDialog;
|
||||
|
||||
QSharedDataPointer<KFileFilterPrivate> d;
|
||||
};
|
||||
|
||||
KIOCORE_EXPORT QDebug operator<<(QDebug dbg, const KFileFilter &filter);
|
||||
|
||||
#endif
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,677 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 1999-2006 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KFILEITEM_H
|
||||
#define KFILEITEM_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <QDateTime>
|
||||
#include <QFile>
|
||||
#include <QUrl>
|
||||
#include <kacl.h>
|
||||
#include <kio/global.h>
|
||||
#include <kio/udsentry.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QMimeType>
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
class KFileItemPrivate;
|
||||
|
||||
/**
|
||||
* @class KFileItem kfileitem.h <KFileItem>
|
||||
*
|
||||
* A KFileItem is a generic class to handle a file, local or remote.
|
||||
* In particular, it makes it easier to handle the result of KIO::listDir
|
||||
* (UDSEntry isn't very friendly to use).
|
||||
* It includes many file attributes such as MIME type, icon, text, mode, link...
|
||||
*
|
||||
* KFileItem is implicitly shared, i.e. it can be used as a value and copied around at almost no cost.
|
||||
*/
|
||||
class KIOCORE_EXPORT KFileItem
|
||||
{
|
||||
Q_GADGET
|
||||
|
||||
Q_PROPERTY(QUrl url READ url WRITE setUrl)
|
||||
Q_PROPERTY(QString user READ user)
|
||||
Q_PROPERTY(QString group READ group)
|
||||
Q_PROPERTY(bool isLink READ isLink)
|
||||
Q_PROPERTY(bool isDir READ isDir)
|
||||
Q_PROPERTY(bool isFile READ isFile)
|
||||
Q_PROPERTY(bool isReadable READ isReadable)
|
||||
Q_PROPERTY(bool isWritable READ isWritable)
|
||||
Q_PROPERTY(bool isHidden READ isHidden)
|
||||
Q_PROPERTY(bool isSlow READ isSlow)
|
||||
Q_PROPERTY(bool isDesktopFile READ isDesktopFile)
|
||||
Q_PROPERTY(QString linkDest READ linkDest)
|
||||
Q_PROPERTY(QUrl targetUrl READ targetUrl)
|
||||
Q_PROPERTY(QString localPath READ localPath WRITE setLocalPath)
|
||||
Q_PROPERTY(bool isLocalFile READ isLocalFile)
|
||||
Q_PROPERTY(QString text READ text)
|
||||
Q_PROPERTY(QString name READ name WRITE setName)
|
||||
Q_PROPERTY(QString mimetype READ mimetype)
|
||||
Q_PROPERTY(QMimeType determineMimeType READ determineMimeType)
|
||||
Q_PROPERTY(QMimeType currentMimeType READ currentMimeType)
|
||||
Q_PROPERTY(bool isFinalIconKnown READ isFinalIconKnown)
|
||||
Q_PROPERTY(bool isMimeTypeKnown READ isMimeTypeKnown)
|
||||
Q_PROPERTY(QString mimeComment READ mimeComment)
|
||||
Q_PROPERTY(QString iconName READ iconName)
|
||||
Q_PROPERTY(QStringList overlays READ overlays)
|
||||
Q_PROPERTY(QString comment READ comment)
|
||||
Q_PROPERTY(QString getStatusBarInfo READ getStatusBarInfo)
|
||||
Q_PROPERTY(bool isRegularFile READ isRegularFile)
|
||||
|
||||
public:
|
||||
enum {
|
||||
Unknown = static_cast<mode_t>(-1)
|
||||
};
|
||||
|
||||
/**
|
||||
* The timestamps associated with a file.
|
||||
* - ModificationTime: the time the file's contents were last modified
|
||||
* - AccessTime: the time the file was last accessed (last read or written to)
|
||||
* - CreationTime: the time the file was created
|
||||
*/
|
||||
enum FileTimes {
|
||||
// warning: don't change without looking at the Private class
|
||||
ModificationTime = 0,
|
||||
AccessTime = 1,
|
||||
CreationTime = 2,
|
||||
// ChangeTime
|
||||
};
|
||||
Q_ENUM(FileTimes)
|
||||
|
||||
enum MimeTypeDetermination {
|
||||
NormalMimeTypeDetermination = 0,
|
||||
SkipMimeTypeFromContent,
|
||||
};
|
||||
Q_ENUM(MimeTypeDetermination)
|
||||
|
||||
/**
|
||||
* Null KFileItem. Doesn't represent any file, only exists for convenience.
|
||||
*/
|
||||
KFileItem();
|
||||
|
||||
/**
|
||||
* Creates an item representing a file, from a UDSEntry.
|
||||
* This is the preferred constructor when using KIO::listDir().
|
||||
*
|
||||
* @param entry the KIO entry used to get the file, contains info about it
|
||||
* @param itemOrDirUrl the URL of the item or of the directory containing this item (see urlIsDirectory).
|
||||
* @param delayedMimeTypes specifies if the MIME type of the given
|
||||
* URL should be determined immediately or on demand.
|
||||
* See the bool delayedMimeTypes in the KDirLister constructor.
|
||||
* @param urlIsDirectory specifies if the url is just the directory of the
|
||||
* fileitem and the filename from the UDSEntry should be used.
|
||||
*
|
||||
* When creating KFileItems out of the UDSEntry emitted by a KIO list job,
|
||||
* use KFileItem(entry, listjob->url(), delayedMimeTypes, true);
|
||||
*/
|
||||
KFileItem(const KIO::UDSEntry &entry, const QUrl &itemOrDirUrl, bool delayedMimeTypes = false, bool urlIsDirectory = false);
|
||||
|
||||
/**
|
||||
* Creates an item representing a file, for which the MIME type is already known.
|
||||
* @param url the file url
|
||||
* @param mimeType the name of the file's MIME type
|
||||
* @param mode the mode (S_IFDIR...)
|
||||
*/
|
||||
explicit KFileItem(const QUrl &url, const QString &mimeType = QString(), mode_t mode = KFileItem::Unknown);
|
||||
|
||||
/**
|
||||
* Creates an item representing a file, with the option of skipping MIME type determination.
|
||||
* @param url the file url
|
||||
* @param mimeTypeDetermination the mode of determining the MIME type:
|
||||
* NormalMimeTypeDetermination by content if local file, i.e. access the file,
|
||||
* open and read part of it;
|
||||
* by QMimeDatabase::MatchMode::MatchExtension if not local.
|
||||
* SkipMimeTypeFromContent always by QMimeDatabase::MatchMode::MatchExtension,
|
||||
* i.e. won't access the file by stat() or opening it;
|
||||
* only suitable for files, directories won't be recognized.
|
||||
* @since 5.57
|
||||
*/
|
||||
KFileItem(const QUrl &url, KFileItem::MimeTypeDetermination mimeTypeDetermination);
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*/
|
||||
KFileItem(const KFileItem &);
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~KFileItem();
|
||||
|
||||
/**
|
||||
* Move constructor
|
||||
* @since 5.43
|
||||
*/
|
||||
KFileItem(KFileItem &&);
|
||||
|
||||
/**
|
||||
* Copy assignment
|
||||
*/
|
||||
KFileItem &operator=(const KFileItem &);
|
||||
|
||||
/**
|
||||
* Move assignment
|
||||
* @since 5.43
|
||||
*/
|
||||
KFileItem &operator=(KFileItem &&);
|
||||
|
||||
/**
|
||||
* Throw away and re-read (for local files) all information about the file.
|
||||
* This is called when the _file_ changes.
|
||||
*/
|
||||
void refresh();
|
||||
|
||||
/**
|
||||
* Re-reads MIME type information.
|
||||
* This is called when the MIME type database changes.
|
||||
*/
|
||||
void refreshMimeType();
|
||||
|
||||
/**
|
||||
* Sets MIME type determination to be immediate or on demand.
|
||||
* Call this after the constructor, and before using any MIME-type-related method.
|
||||
* @since 5.0
|
||||
*/
|
||||
void setDelayedMimeTypes(bool b);
|
||||
|
||||
/**
|
||||
* Returns the url of the file.
|
||||
* @return the url of the file
|
||||
*/
|
||||
QUrl url() const;
|
||||
|
||||
/**
|
||||
* Sets the item's URL. Do not call unless you know what you are doing!
|
||||
* (used for example when an item got renamed).
|
||||
* @param url the item's URL
|
||||
*/
|
||||
void setUrl(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Sets the item's local path (UDS_LOCAL_PATH). Do not call unless you know what you are doing!
|
||||
* This won't change the item's name or URL.
|
||||
* (used for example when an item got renamed).
|
||||
* @param path the item's local path
|
||||
* @since 5.20
|
||||
*/
|
||||
void setLocalPath(const QString &path);
|
||||
|
||||
/**
|
||||
* Sets the item's name (i.e.\ the filename).
|
||||
* This is automatically done by setUrl, to set the name from the URL's fileName().
|
||||
* This method is provided for some special cases like relative paths as names (KFindPart)
|
||||
* @param name the item's name
|
||||
*/
|
||||
void setName(const QString &name);
|
||||
|
||||
/**
|
||||
* Returns the permissions of the file (stat.st_mode containing only permissions).
|
||||
* @return the permissions of the file
|
||||
*/
|
||||
mode_t permissions() const;
|
||||
|
||||
/**
|
||||
* Returns the access permissions for the file as a string.
|
||||
* @return the access permission as string
|
||||
*/
|
||||
QString permissionsString() const;
|
||||
|
||||
/**
|
||||
* Tells if the file has extended access level information ( Posix ACL )
|
||||
* @return true if the file has extend ACL information or false if it hasn't
|
||||
*/
|
||||
bool hasExtendedACL() const;
|
||||
|
||||
/**
|
||||
* Returns the access control list for the file.
|
||||
* @return the access control list as a KACL
|
||||
*/
|
||||
KACL ACL() const;
|
||||
|
||||
/**
|
||||
* Returns the default access control list for the directory.
|
||||
* @return the default access control list as a KACL
|
||||
*/
|
||||
KACL defaultACL() const;
|
||||
|
||||
/**
|
||||
* Returns the file type (stat.st_mode containing only S_IFDIR, S_IFLNK, ...).
|
||||
* @return the file type
|
||||
*/
|
||||
mode_t mode() const;
|
||||
|
||||
/**
|
||||
* Returns the file's owner's user id.
|
||||
* Available only on supported protocols.
|
||||
* @since 6.0
|
||||
*/
|
||||
int userId() const;
|
||||
/**
|
||||
* Returns the file's owner's group id.
|
||||
* Available only on supported protocols.
|
||||
* @since 6.0
|
||||
*/
|
||||
int groupId() const;
|
||||
|
||||
/**
|
||||
* Returns the owner of the file.
|
||||
* @return the file's owner
|
||||
*/
|
||||
QString user() const;
|
||||
|
||||
/**
|
||||
* Returns the group of the file.
|
||||
* @return the file's group
|
||||
*/
|
||||
QString group() const;
|
||||
|
||||
/**
|
||||
* Returns true if this item represents a link in the UNIX sense of
|
||||
* a link.
|
||||
* @return true if the file is a link
|
||||
*/
|
||||
bool isLink() const;
|
||||
|
||||
/**
|
||||
* Returns true if this item represents a directory.
|
||||
* @return true if the item is a directory
|
||||
*/
|
||||
bool isDir() const;
|
||||
|
||||
/**
|
||||
* Returns true if this item represents a file (and not a directory)
|
||||
* @return true if the item is a file
|
||||
*/
|
||||
bool isFile() const;
|
||||
|
||||
/**
|
||||
* Checks whether the file or directory is readable. In some cases
|
||||
* (remote files), we may return true even though it can't be read.
|
||||
* @return true if the file can be read - more precisely,
|
||||
* false if we know for sure it can't
|
||||
*/
|
||||
bool isReadable() const;
|
||||
|
||||
/**
|
||||
* Checks whether the file or directory is writable. In some cases
|
||||
* (remote files), we may return true even though it can't be written to.
|
||||
* @return true if the file or directory can be written to - more precisely,
|
||||
* false if we know for sure it can't
|
||||
*/
|
||||
bool isWritable() const;
|
||||
|
||||
/**
|
||||
* Checks whether the file is hidden.
|
||||
* @return true if the file is hidden.
|
||||
*/
|
||||
bool isHidden() const;
|
||||
|
||||
/**
|
||||
* @return true if the file is a remote URL, or a local file on a network mount.
|
||||
* It will return false only for really-local file systems.
|
||||
* @since 4.7.4
|
||||
*/
|
||||
bool isSlow() const;
|
||||
|
||||
/**
|
||||
* Checks whether the file is a readable local .desktop file,
|
||||
* i.e.\ a file whose path can be given to KDesktopFile
|
||||
* @return true if the file is a desktop file.
|
||||
*/
|
||||
bool isDesktopFile() const;
|
||||
|
||||
/**
|
||||
* Returns the link destination if isLink() == true.
|
||||
* @return the link destination. QString() if the item is not a link
|
||||
*/
|
||||
QString linkDest() const;
|
||||
|
||||
/**
|
||||
* Returns the target url of the file, which is the same as url()
|
||||
* in cases where the worker doesn't specify UDS_TARGET_URL
|
||||
* @return the target url.
|
||||
*/
|
||||
QUrl targetUrl() const;
|
||||
|
||||
/**
|
||||
* Returns the local path if isLocalFile() == true or the KIO item has
|
||||
* a UDS_LOCAL_PATH atom.
|
||||
*
|
||||
* Treat it as a readonly path to open/list contents, use original url to move/delete files.
|
||||
*
|
||||
* @return the item local path, or QString() if not known
|
||||
*/
|
||||
QString localPath() const;
|
||||
|
||||
/**
|
||||
* Returns the size of the file, if known.
|
||||
* @return the file size, or 0 if not known
|
||||
*/
|
||||
KIO::filesize_t size() const;
|
||||
|
||||
/**
|
||||
* @brief For folders, its recursive size:
|
||||
* the size of its files plus the recursiveSize of its folder
|
||||
*
|
||||
* Initially only implemented for trash:/
|
||||
*
|
||||
* @since 5.70
|
||||
* @return The recursive size
|
||||
*/
|
||||
KIO::filesize_t recursiveSize() const;
|
||||
|
||||
/**
|
||||
* Requests the modification, access or creation time, depending on @p which.
|
||||
* @param which the timestamp
|
||||
* @return the time asked for, QDateTime() if not available
|
||||
* @see timeString()
|
||||
*/
|
||||
Q_INVOKABLE QDateTime time(KFileItem::FileTimes which) const;
|
||||
|
||||
/**
|
||||
* Requests the modification, access or creation time as a string, depending
|
||||
* on @p which.
|
||||
* @param which the timestamp
|
||||
* @returns a formatted string of the requested time.
|
||||
* @see time
|
||||
*/
|
||||
Q_INVOKABLE QString timeString(KFileItem::FileTimes which = ModificationTime) const;
|
||||
|
||||
/**
|
||||
* Returns true if the file is a local file.
|
||||
* @return true if the file is local, false otherwise
|
||||
*/
|
||||
bool isLocalFile() const;
|
||||
|
||||
/**
|
||||
* Returns the text of the file item.
|
||||
* It's not exactly the filename since some decoding happens ('%2F'->'/').
|
||||
* @return the text of the file item
|
||||
*/
|
||||
QString text() const;
|
||||
|
||||
/**
|
||||
* Return the name of the file item (without a path).
|
||||
* Similar to text(), but unencoded, i.e. the original name.
|
||||
* @param lowerCase if true, the name will be returned in lower case,
|
||||
* which is useful to speed up sorting by name, case insensitively.
|
||||
* @return the file's name
|
||||
*/
|
||||
QString name(bool lowerCase = false) const;
|
||||
|
||||
/**
|
||||
* Returns the MIME type of the file item.
|
||||
* If @p delayedMimeTypes was used in the constructor, this will determine
|
||||
* the MIME type first. Equivalent to determineMimeType()->name()
|
||||
* @return the MIME type of the file
|
||||
*/
|
||||
QString mimetype() const;
|
||||
|
||||
/**
|
||||
* Returns the MIME type of the file item.
|
||||
* If delayedMimeTypes was used in the constructor, this will determine
|
||||
* the MIME type first.
|
||||
* @return the MIME type
|
||||
*/
|
||||
QMimeType determineMimeType() const;
|
||||
|
||||
/**
|
||||
* Returns the currently known MIME type of the file item.
|
||||
* This will not try to determine the MIME type if unknown.
|
||||
* @return the known MIME type
|
||||
*/
|
||||
QMimeType currentMimeType() const;
|
||||
|
||||
/**
|
||||
* @return true if we have determined the final icon of this file already.
|
||||
* @since 4.10.2
|
||||
*/
|
||||
bool isFinalIconKnown() const;
|
||||
|
||||
/**
|
||||
* @return true if we have determined the MIME type of this file already,
|
||||
* i.e. if determineMimeType() will be fast. Otherwise it will have to
|
||||
* find what the MIME type is, which is a possibly slow operation; usually
|
||||
* this is delayed until necessary.
|
||||
*/
|
||||
bool isMimeTypeKnown() const;
|
||||
|
||||
/**
|
||||
* Returns the user-readable string representing the type of this file,
|
||||
* like "OpenDocument Text File".
|
||||
* @return the type of this KFileItem
|
||||
*/
|
||||
QString mimeComment() const;
|
||||
|
||||
/**
|
||||
* Returns the full path name to the icon that represents
|
||||
* this MIME type.
|
||||
* @return iconName the name of the file's icon
|
||||
*/
|
||||
QString iconName() const;
|
||||
|
||||
/**
|
||||
* Returns the overlays (bitfield of KIconLoader::*Overlay flags) that are used
|
||||
* for this item's pixmap. Overlays are used to show for example, whether
|
||||
* a file can be modified.
|
||||
* @return the overlays of the pixmap
|
||||
*/
|
||||
QStringList overlays() const;
|
||||
|
||||
/**
|
||||
* A comment which can contain anything - even rich text. It will
|
||||
* simply be displayed to the user as is.
|
||||
*
|
||||
*/
|
||||
QString comment() const;
|
||||
|
||||
/**
|
||||
* Returns the string to be displayed in the statusbar,
|
||||
* e.g.\ when the mouse is over this item.
|
||||
* @return the status bar information
|
||||
*/
|
||||
QString getStatusBarInfo() const;
|
||||
|
||||
/**
|
||||
* Returns the UDS entry. Used by the tree view to access all details
|
||||
* by position.
|
||||
* @return the UDS entry
|
||||
*/
|
||||
KIO::UDSEntry entry() const;
|
||||
|
||||
/**
|
||||
* Return true if this item is a regular file,
|
||||
* false otherwise (directory, link, character/block device, fifo, socket)
|
||||
*/
|
||||
bool isRegularFile() const;
|
||||
|
||||
/**
|
||||
* Returns the file extension
|
||||
* Similar to QFileInfo::suffix except it takes into account UDS_DISPLAY_NAME and saves a stat call
|
||||
* @since 6.0
|
||||
*/
|
||||
QString suffix() const;
|
||||
|
||||
/**
|
||||
* Somewhat like a comparison operator, but more explicit,
|
||||
* and it can detect that two fileitems differ if any property of the file item
|
||||
* has changed (file size, modification date, etc.). Two items are equal if
|
||||
* all properties are equal. In contrast, operator== only compares URLs.
|
||||
* @param item the item to compare
|
||||
* @return true if all values are equal
|
||||
*/
|
||||
bool cmp(const KFileItem &item) const;
|
||||
|
||||
/**
|
||||
* Returns true if both items share the same URL.
|
||||
*/
|
||||
bool operator==(const KFileItem &other) const;
|
||||
|
||||
/**
|
||||
* Returns true if both items do not share the same URL.
|
||||
*/
|
||||
bool operator!=(const KFileItem &other) const;
|
||||
|
||||
/**
|
||||
* Returns true if this item's URL is lexically less than other's URL; otherwise returns false
|
||||
* @since 5.48
|
||||
*/
|
||||
bool operator<(const KFileItem &other) const;
|
||||
|
||||
/**
|
||||
* Returns true if this item's URL is lexically less than url other; otherwise returns false
|
||||
* @since 5.48
|
||||
*/
|
||||
bool operator<(const QUrl &other) const;
|
||||
|
||||
/**
|
||||
* Converts this KFileItem to a QVariant, this allows to use KFileItem
|
||||
* in QVariant() constructor
|
||||
*/
|
||||
operator QVariant() const;
|
||||
|
||||
/**
|
||||
* Tries to return a local URL for this file item if possible.
|
||||
* If @p local is not null, it will be set to @c true if the returned url is local,
|
||||
* @c false otherwise.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* bool isLocal = false;
|
||||
* KFileItem item;
|
||||
* const QUrl url = item.mostLocalUrl(&isLocal);
|
||||
* if (isLocal) {
|
||||
* // Use url
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
*/
|
||||
QUrl mostLocalUrl(bool *local = nullptr) const;
|
||||
|
||||
struct MostLocalUrlResult {
|
||||
QUrl url;
|
||||
bool local;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a MostLocalUrlResult, with the local Url for this item if possible
|
||||
* (otherwise an empty Url), and a bool that is set to @c true if this Url
|
||||
* does represent a local file otherwise @c false.
|
||||
*
|
||||
* Basically this is an alternative to mostLocalUrl(bool*), that does not use an
|
||||
* output parameter.
|
||||
*
|
||||
* Example:
|
||||
* @code
|
||||
* KFileItem item;
|
||||
* const MostLocalUrlResult result = item.isMostLocalUrl();
|
||||
* if (result.local) { // A local file
|
||||
* // Use result.url
|
||||
* }
|
||||
* @endcode
|
||||
* @since 5.84
|
||||
*/
|
||||
MostLocalUrlResult isMostLocalUrl() const;
|
||||
|
||||
/**
|
||||
* Return true if default-constructed
|
||||
*/
|
||||
bool isNull() const;
|
||||
|
||||
/**
|
||||
* returns whether the KFileItem exists on-disk
|
||||
* Call only after initialization (i.e `KIO::stat` or `refresh()` for local files)
|
||||
* @since 6.0
|
||||
*/
|
||||
bool exists() const;
|
||||
|
||||
/**
|
||||
* Return true if the file has executable permission
|
||||
* @since 6.0
|
||||
*/
|
||||
bool isExecutable() const;
|
||||
|
||||
private:
|
||||
QSharedDataPointer<KFileItemPrivate> d;
|
||||
|
||||
/**
|
||||
* Hides the file.
|
||||
*/
|
||||
KIOCORE_NO_EXPORT void setHidden();
|
||||
|
||||
private:
|
||||
KIOCORE_EXPORT friend QDataStream &operator<<(QDataStream &s, const KFileItem &a);
|
||||
KIOCORE_EXPORT friend QDataStream &operator>>(QDataStream &s, KFileItem &a);
|
||||
|
||||
friend class KFileItemTest;
|
||||
friend class KCoreDirListerCache;
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(KFileItem)
|
||||
Q_DECLARE_TYPEINFO(KFileItem, Q_RELOCATABLE_TYPE);
|
||||
|
||||
inline size_t qHash(const KFileItem &item, size_t seed = 0)
|
||||
{
|
||||
return qHash(item.url(), seed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @class KFileItemList kfileitem.h <KFileItem>
|
||||
*
|
||||
* List of KFileItems, which adds a few helper
|
||||
* methods to QList<KFileItem>.
|
||||
*/
|
||||
class KIOCORE_EXPORT KFileItemList : public QList<KFileItem>
|
||||
{
|
||||
public:
|
||||
/// Creates an empty list of file items.
|
||||
KFileItemList();
|
||||
|
||||
/// Creates a new KFileItemList from a QList of file @p items.
|
||||
KFileItemList(const QList<KFileItem> &items);
|
||||
|
||||
/// Creates a new KFileItemList from an initializer_list of file @p items.
|
||||
/// @since 5.76
|
||||
KFileItemList(std::initializer_list<KFileItem> items);
|
||||
|
||||
/**
|
||||
* Find a KFileItem by name and return it.
|
||||
* @return the item with the given name, or a null-item if none was found
|
||||
* (see KFileItem::isNull())
|
||||
*/
|
||||
KFileItem findByName(const QString &fileName) const;
|
||||
|
||||
/**
|
||||
* Find a KFileItem by URL and return it.
|
||||
* @return the item with the given URL, or a null-item if none was found
|
||||
* (see KFileItem::isNull())
|
||||
*/
|
||||
KFileItem findByUrl(const QUrl &url) const;
|
||||
|
||||
/// @return the list of URLs that those items represent
|
||||
QList<QUrl> urlList() const;
|
||||
|
||||
/// @return the list of target URLs that those items represent
|
||||
/// @since 4.2
|
||||
QList<QUrl> targetUrlList() const;
|
||||
|
||||
// TODO KDE-5 add d pointer here so that we can merge KFileItemListProperties into KFileItemList
|
||||
};
|
||||
|
||||
KIOCORE_EXPORT QDataStream &operator<<(QDataStream &s, const KFileItem &a);
|
||||
KIOCORE_EXPORT QDataStream &operator>>(QDataStream &s, KFileItem &a);
|
||||
|
||||
/**
|
||||
* Support for qDebug() << aFileItem
|
||||
* \since 4.4
|
||||
*/
|
||||
KIOCORE_EXPORT QDebug operator<<(QDebug stream, const KFileItem &item);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2008 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2008 George Goldberg <grundleborg@googlemail.com>
|
||||
SPDX-FileCopyrightText: 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kfileitemlistproperties.h"
|
||||
|
||||
#include <kfileitem.h>
|
||||
#include <kprotocolmanager.h>
|
||||
|
||||
#include <QFileInfo>
|
||||
|
||||
class KFileItemListPropertiesPrivate : public QSharedData
|
||||
{
|
||||
public:
|
||||
KFileItemListPropertiesPrivate()
|
||||
: m_isDirectory(false)
|
||||
, m_isFile(false)
|
||||
, m_supportsReading(false)
|
||||
, m_supportsDeleting(false)
|
||||
, m_supportsWriting(false)
|
||||
, m_supportsMoving(false)
|
||||
, m_isLocal(true)
|
||||
{
|
||||
}
|
||||
void setItems(const KFileItemList &items);
|
||||
|
||||
void determineMimeTypeAndGroup() const;
|
||||
|
||||
KFileItemList m_items;
|
||||
mutable QString m_mimeType;
|
||||
mutable QString m_mimeGroup;
|
||||
bool m_isDirectory : 1;
|
||||
bool m_isFile : 1;
|
||||
bool m_supportsReading : 1;
|
||||
bool m_supportsDeleting : 1;
|
||||
bool m_supportsWriting : 1;
|
||||
bool m_supportsMoving : 1;
|
||||
bool m_isLocal : 1;
|
||||
};
|
||||
|
||||
KFileItemListProperties::KFileItemListProperties()
|
||||
: d(new KFileItemListPropertiesPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KFileItemListProperties::KFileItemListProperties(const KFileItemList &items)
|
||||
: d(new KFileItemListPropertiesPrivate)
|
||||
{
|
||||
setItems(items);
|
||||
}
|
||||
|
||||
void KFileItemListProperties::setItems(const KFileItemList &items)
|
||||
{
|
||||
d->setItems(items);
|
||||
}
|
||||
|
||||
void KFileItemListPropertiesPrivate::setItems(const KFileItemList &items)
|
||||
{
|
||||
const bool initialValue = !items.isEmpty();
|
||||
m_items = items;
|
||||
m_supportsReading = initialValue;
|
||||
m_supportsDeleting = initialValue;
|
||||
m_supportsWriting = initialValue;
|
||||
m_supportsMoving = initialValue;
|
||||
m_isDirectory = initialValue;
|
||||
m_isFile = initialValue;
|
||||
m_isLocal = true;
|
||||
m_mimeType.clear();
|
||||
m_mimeGroup.clear();
|
||||
|
||||
QFileInfo parentDirInfo;
|
||||
for (const KFileItem &item : items) {
|
||||
const QUrl url = item.url();
|
||||
const auto [localUrl, isLocal] = item.isMostLocalUrl();
|
||||
m_isLocal = m_isLocal && isLocal;
|
||||
m_supportsReading = m_supportsReading && KProtocolManager::supportsReading(url);
|
||||
m_supportsDeleting = m_supportsDeleting && KProtocolManager::supportsDeleting(url);
|
||||
m_supportsWriting = m_supportsWriting && KProtocolManager::supportsWriting(url) && item.isWritable();
|
||||
m_supportsMoving = m_supportsMoving && KProtocolManager::supportsMoving(url);
|
||||
|
||||
// For local files we can do better: check if we have write permission in parent directory
|
||||
// TODO: if we knew about the parent KFileItem, we could even do that for remote protocols too
|
||||
#ifndef Q_OS_WIN
|
||||
if (m_isLocal && (m_supportsDeleting || m_supportsMoving)) {
|
||||
const QString directory = localUrl.adjusted(QUrl::RemoveFilename | QUrl::StripTrailingSlash).toLocalFile();
|
||||
if (parentDirInfo.filePath() != directory) {
|
||||
parentDirInfo.setFile(directory);
|
||||
}
|
||||
if (!parentDirInfo.isWritable()) {
|
||||
m_supportsDeleting = false;
|
||||
m_supportsMoving = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (m_isLocal && m_supportsDeleting) {
|
||||
if (!QFileInfo(url.toLocalFile()).isWritable())
|
||||
m_supportsDeleting = false;
|
||||
}
|
||||
#endif
|
||||
if (m_isDirectory && !item.isDir()) {
|
||||
m_isDirectory = false;
|
||||
}
|
||||
|
||||
if (m_isFile && !item.isFile()) {
|
||||
m_isFile = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KFileItemListProperties::KFileItemListProperties(const KFileItemListProperties &other)
|
||||
: d(other.d)
|
||||
{
|
||||
}
|
||||
|
||||
KFileItemListProperties &KFileItemListProperties::operator=(const KFileItemListProperties &other)
|
||||
{
|
||||
d = other.d;
|
||||
return *this;
|
||||
}
|
||||
|
||||
KFileItemListProperties::~KFileItemListProperties()
|
||||
{
|
||||
}
|
||||
|
||||
bool KFileItemListProperties::supportsReading() const
|
||||
{
|
||||
return d->m_supportsReading;
|
||||
}
|
||||
|
||||
bool KFileItemListProperties::supportsDeleting() const
|
||||
{
|
||||
return d->m_supportsDeleting;
|
||||
}
|
||||
|
||||
bool KFileItemListProperties::supportsWriting() const
|
||||
{
|
||||
return d->m_supportsWriting;
|
||||
}
|
||||
|
||||
bool KFileItemListProperties::supportsMoving() const
|
||||
{
|
||||
return d->m_supportsMoving && d->m_supportsDeleting;
|
||||
}
|
||||
|
||||
bool KFileItemListProperties::isLocal() const
|
||||
{
|
||||
return d->m_isLocal;
|
||||
}
|
||||
|
||||
KFileItemList KFileItemListProperties::items() const
|
||||
{
|
||||
return d->m_items;
|
||||
}
|
||||
|
||||
QList<QUrl> KFileItemListProperties::urlList() const
|
||||
{
|
||||
return d->m_items.targetUrlList();
|
||||
}
|
||||
|
||||
bool KFileItemListProperties::isDirectory() const
|
||||
{
|
||||
return d->m_isDirectory;
|
||||
}
|
||||
|
||||
bool KFileItemListProperties::isFile() const
|
||||
{
|
||||
return d->m_isFile;
|
||||
}
|
||||
|
||||
QString KFileItemListProperties::mimeType() const
|
||||
{
|
||||
if (d->m_mimeType.isEmpty()) {
|
||||
d->determineMimeTypeAndGroup();
|
||||
}
|
||||
return d->m_mimeType;
|
||||
}
|
||||
|
||||
QString KFileItemListProperties::mimeGroup() const
|
||||
{
|
||||
if (d->m_mimeType.isEmpty()) {
|
||||
d->determineMimeTypeAndGroup();
|
||||
}
|
||||
return d->m_mimeGroup;
|
||||
}
|
||||
|
||||
void KFileItemListPropertiesPrivate::determineMimeTypeAndGroup() const
|
||||
{
|
||||
if (!m_items.isEmpty()) {
|
||||
m_mimeType = m_items.first().mimetype();
|
||||
m_mimeGroup = m_mimeType.left(m_mimeType.indexOf(QLatin1Char('/')));
|
||||
}
|
||||
for (const KFileItem &item : std::as_const(m_items)) {
|
||||
const QString itemMimeType = item.mimetype();
|
||||
// Determine if common MIME type among all items
|
||||
if (m_mimeType != itemMimeType) {
|
||||
m_mimeType.clear();
|
||||
const auto type = QStringView(itemMimeType).left(itemMimeType.indexOf(QLatin1Char('/')));
|
||||
if (m_mimeGroup != type) {
|
||||
m_mimeGroup.clear(); // MIME type groups are different as well!
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2008 Peter Penz <peter.penz@gmx.at>
|
||||
SPDX-FileCopyrightText: 2008 George Goldberg <grundleborg@googlemail.com>
|
||||
SPDX-FileCopyrightText: 2009 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KFILEITEMLISTPROPERTIES_H
|
||||
#define KFILEITEMLISTPROPERTIES_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QList>
|
||||
#include <QSharedDataPointer>
|
||||
#include <QUrl>
|
||||
|
||||
class KFileItemListPropertiesPrivate;
|
||||
class KFileItemList;
|
||||
|
||||
/**
|
||||
* @class KFileItemListProperties kfileitemlistproperties.h <KFileItemListProperties>
|
||||
*
|
||||
* @brief Provides information about the common properties of a group of
|
||||
* KFileItem objects.
|
||||
*
|
||||
* Given a list of KFileItems, this class can determine (and cache) the common
|
||||
* MIME type for all items, whether all items are directories, whether all items
|
||||
* are readable, writable, etc.
|
||||
* As soon as one file item does not support a specific capability (read, write etc.),
|
||||
* it is marked as unsupported for all items.
|
||||
*
|
||||
* This class is implicitly shared, which means it can be used as a value and
|
||||
* copied around at almost no cost.
|
||||
*
|
||||
*/
|
||||
class KIOCORE_EXPORT KFileItemListProperties
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Default constructor. Use setItems to specify the items.
|
||||
*/
|
||||
KFileItemListProperties();
|
||||
/**
|
||||
* @brief Constructor that takes a KFileItemList and sets the capabilities
|
||||
* supported by all the FileItems as true.
|
||||
* @param items The list of items that are to have their supported
|
||||
* capabilities checked.
|
||||
*/
|
||||
KFileItemListProperties(const KFileItemList &items);
|
||||
/**
|
||||
* @brief Copy constructor
|
||||
*/
|
||||
KFileItemListProperties(const KFileItemListProperties &);
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
virtual ~KFileItemListProperties();
|
||||
/**
|
||||
* @brief Assignment operator
|
||||
*/
|
||||
KFileItemListProperties &operator=(const KFileItemListProperties &other);
|
||||
/**
|
||||
* Sets the items that are to have their supported capabilities checked.
|
||||
*/
|
||||
void setItems(const KFileItemList &items);
|
||||
|
||||
/**
|
||||
* @brief Check if reading capability is supported
|
||||
* @return true if all the FileItems can be read, otherwise false.
|
||||
*/
|
||||
bool supportsReading() const;
|
||||
/**
|
||||
* @brief Check if deleting capability is supported
|
||||
* @return true if all the FileItems can be deleted, otherwise false.
|
||||
*/
|
||||
bool supportsDeleting() const;
|
||||
/**
|
||||
* @brief Check if writing capability is supported
|
||||
* (file managers use this mostly for directories)
|
||||
* @return true if all the FileItems can be written to, otherwise false.
|
||||
*/
|
||||
bool supportsWriting() const;
|
||||
/**
|
||||
* @brief Check if moving capability is supported
|
||||
* @return true if all the FileItems can be moved, otherwise false.
|
||||
*/
|
||||
bool supportsMoving() const;
|
||||
/**
|
||||
* @brief Check if files are local
|
||||
* @return true if all the FileItems are local, otherwise there is one or more
|
||||
* remote file, so false.
|
||||
*/
|
||||
bool isLocal() const;
|
||||
|
||||
/**
|
||||
* List of fileitems passed to the constructor or to setItems().
|
||||
*/
|
||||
KFileItemList items() const;
|
||||
|
||||
/**
|
||||
* List of urls, gathered from the fileitems
|
||||
*/
|
||||
QList<QUrl> urlList() const;
|
||||
|
||||
/**
|
||||
* @return true if all items are directories
|
||||
*/
|
||||
bool isDirectory() const;
|
||||
|
||||
/**
|
||||
* @return Whether all items are files, as reported by KFileItem::isFile().
|
||||
* @since 5.47
|
||||
*/
|
||||
bool isFile() const;
|
||||
|
||||
/**
|
||||
* @return the MIME type of all items, if they all have the same, otherwise an empty string
|
||||
*/
|
||||
QString mimeType() const;
|
||||
|
||||
/**
|
||||
* @return the MIME type group (e.g. "text") of all items, if they all have the same, otherwise an empty string
|
||||
*/
|
||||
QString mimeGroup() const;
|
||||
|
||||
private:
|
||||
/** @brief d-pointer */
|
||||
QSharedDataPointer<KFileItemListPropertiesPrivate> d;
|
||||
};
|
||||
|
||||
#endif /* KFILEITEMLISTPROPERTIES_H */
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2015 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kioglobal_p.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
|
||||
using LocationMap = QMap<QString, QString>;
|
||||
|
||||
static QMap<QString, QString> standardLocationsMap()
|
||||
{
|
||||
struct LocationInfo {
|
||||
QStandardPaths::StandardLocation location;
|
||||
const char *iconName;
|
||||
};
|
||||
static const LocationInfo mapping[] = {
|
||||
{QStandardPaths::TemplatesLocation, "folder-templates"},
|
||||
{QStandardPaths::PublicShareLocation, "folder-public"},
|
||||
{QStandardPaths::MusicLocation, "folder-music"},
|
||||
{QStandardPaths::MoviesLocation, "folder-videos"},
|
||||
{QStandardPaths::PicturesLocation, "folder-pictures"},
|
||||
{QStandardPaths::TempLocation, "folder-temp"},
|
||||
{QStandardPaths::DownloadLocation, "folder-download"},
|
||||
// Order matters here as paths can be reused for multiple purposes
|
||||
// We essentially want more generic choices to trump more specific
|
||||
// ones.
|
||||
// home > desktop > documents > *.
|
||||
{QStandardPaths::DocumentsLocation, "folder-documents"},
|
||||
{QStandardPaths::DesktopLocation, "user-desktop"},
|
||||
{QStandardPaths::HomeLocation, "user-home"},
|
||||
};
|
||||
|
||||
LocationMap map;
|
||||
|
||||
for (const auto &row : mapping) {
|
||||
const QStringList locations = QStandardPaths::standardLocations(row.location);
|
||||
for (const QString &location : locations) {
|
||||
map.insert(location, QLatin1String(row.iconName));
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
QString KIOPrivate::iconForStandardPath(const QString &localDirectory)
|
||||
{
|
||||
static auto map = standardLocationsMap();
|
||||
QString path = localDirectory;
|
||||
if (path.endsWith(QLatin1Char('/'))) {
|
||||
path.chop(1);
|
||||
}
|
||||
return map.value(path, QString());
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KIO_KIOGLOBAL_P_H
|
||||
#define KIO_KIOGLOBAL_P_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#include <KUser>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
// windows just sets the mode_t access rights bits to the same value for user+group+other.
|
||||
// This means using the Linux values here is fine.
|
||||
#ifndef S_IRUSR
|
||||
#define S_IRUSR 0400
|
||||
#endif
|
||||
#ifndef S_IRGRP
|
||||
#define S_IRGRP 0040
|
||||
#endif
|
||||
#ifndef S_IROTH
|
||||
#define S_IROTH 0004
|
||||
#endif
|
||||
|
||||
#ifndef S_IWUSR
|
||||
#define S_IWUSR 0200
|
||||
#endif
|
||||
#ifndef S_IWGRP
|
||||
#define S_IWGRP 0020
|
||||
#endif
|
||||
#ifndef S_IWOTH
|
||||
#define S_IWOTH 0002
|
||||
#endif
|
||||
|
||||
#ifndef S_IXUSR
|
||||
#define S_IXUSR 0100
|
||||
#endif
|
||||
#ifndef S_IXGRP
|
||||
#define S_IXGRP 0010
|
||||
#endif
|
||||
#ifndef S_IXOTH
|
||||
#define S_IXOTH 0001
|
||||
#endif
|
||||
|
||||
#ifndef S_IRWXU
|
||||
#define S_IRWXU S_IRUSR | S_IWUSR | S_IXUSR
|
||||
#endif
|
||||
#ifndef S_IRWXG
|
||||
#define S_IRWXG S_IRGRP | S_IWGRP | S_IXGRP
|
||||
#endif
|
||||
#ifndef S_IRWXO
|
||||
#define S_IRWXO S_IROTH | S_IWOTH | S_IXOTH
|
||||
#endif
|
||||
Q_STATIC_ASSERT(S_IRUSR == _S_IREAD && S_IWUSR == _S_IWRITE && S_IXUSR == _S_IEXEC);
|
||||
|
||||
// these three will never be set in st_mode
|
||||
#ifndef S_ISUID
|
||||
#define S_ISUID 04000 // SUID bit does not exist on windows
|
||||
#endif
|
||||
#ifndef S_ISGID
|
||||
#define S_ISGID 02000 // SGID bit does not exist on windows
|
||||
#endif
|
||||
#ifndef S_ISVTX
|
||||
#define S_ISVTX 01000 // sticky bit does not exist on windows
|
||||
#endif
|
||||
|
||||
// Windows does not have S_IFBLK and S_IFSOCK, just use the Linux values, they won't conflict
|
||||
#ifndef S_IFBLK
|
||||
#define S_IFBLK 0060000
|
||||
#endif
|
||||
#ifndef S_IFSOCK
|
||||
#define S_IFSOCK 0140000
|
||||
#endif
|
||||
/** performs a QT_STAT and add QT_STAT_LNK to st_mode if the path is a symlink */
|
||||
KIOCORE_EXPORT int kio_windows_lstat(const char *path, QT_STATBUF *buffer);
|
||||
|
||||
#ifndef QT_LSTAT
|
||||
#define QT_LSTAT kio_windows_lstat
|
||||
#endif
|
||||
|
||||
#ifndef QT_STAT_LNK
|
||||
#define QT_STAT_LNK 0120000
|
||||
#endif // QT_STAT_LNK
|
||||
|
||||
#endif // Q_OS_WIN
|
||||
|
||||
namespace KIOPrivate
|
||||
{
|
||||
/** @return true if the process with given PID is currently running */
|
||||
KIOCORE_EXPORT bool isProcessAlive(qint64 pid);
|
||||
/** Send a terminate signal (SIGTERM on UNIX) to the process with given PID. */
|
||||
KIOCORE_EXPORT void sendTerminateSignal(qint64 pid);
|
||||
|
||||
enum SymlinkType {
|
||||
GuessSymlinkType,
|
||||
FileSymlink,
|
||||
DirectorySymlink,
|
||||
};
|
||||
|
||||
/** Creates a symbolic link at @p destination pointing to @p source
|
||||
* Unlike UNIX, Windows needs to know whether the symlink points to a file or a directory
|
||||
* when creating the link. This information can be passed in @p type. If @p type is not given
|
||||
* the windows code will guess the type based on the source file.
|
||||
* @note On Windows this requires the current user to have the SeCreateSymbolicLink privilege which
|
||||
* is usually only given to administrators.
|
||||
* @return true on success, false on error
|
||||
*/
|
||||
KIOCORE_EXPORT bool createSymlink(const QString &source, const QString &destination, SymlinkType type = GuessSymlinkType);
|
||||
|
||||
/** Changes the ownership of @p file (like chown()) */
|
||||
KIOCORE_EXPORT bool changeOwnership(const QString &file, KUserId newOwner, KGroupId newGroup);
|
||||
|
||||
/** Returns an icon name for a standard path,
|
||||
* e.g. folder-pictures for any path in QStandardPaths::PicturesLocation */
|
||||
QString iconForStandardPath(const QString &localDirectory);
|
||||
}
|
||||
|
||||
#endif // KIO_KIOGLOBAL_P_H
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
#include "kioglobal_p.h"
|
||||
|
||||
#include <QFile>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
KIOCORE_EXPORT bool KIOPrivate::isProcessAlive(qint64 pid)
|
||||
{
|
||||
return ::kill(pid, 0) == 0;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT void KIOPrivate::sendTerminateSignal(qint64 pid)
|
||||
{
|
||||
::kill(pid, SIGTERM);
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT bool KIOPrivate::createSymlink(const QString &source, const QString &destination, SymlinkType type)
|
||||
{
|
||||
Q_UNUSED(type)
|
||||
return ::symlink(QFile::encodeName(source).constData(), QFile::encodeName(destination).constData()) == 0;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT bool KIOPrivate::changeOwnership(const QString &file, KUserId newOwner, KGroupId newGroup)
|
||||
{
|
||||
return ::chown(QFile::encodeName(file).constData(), newOwner.nativeId(), newGroup.nativeId()) == 0;
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2014 Alex Richardson <arichardson.kde@gmail.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
#include "kioglobal_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
|
||||
#include <qt_windows.h>
|
||||
|
||||
KIOCORE_EXPORT bool KIOPrivate::isProcessAlive(qint64 pid)
|
||||
{
|
||||
HANDLE procHandle = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, pid);
|
||||
bool alive = false;
|
||||
if (procHandle != INVALID_HANDLE_VALUE) {
|
||||
DWORD exitCode;
|
||||
if (GetExitCodeProcess(procHandle, &exitCode)) {
|
||||
alive = exitCode == STILL_ACTIVE;
|
||||
}
|
||||
CloseHandle(procHandle);
|
||||
}
|
||||
return alive;
|
||||
}
|
||||
|
||||
// A callback to shutdown cleanly (no forced kill)
|
||||
BOOL CALLBACK closeProcessCallback(HWND hwnd, LPARAM lParam)
|
||||
{
|
||||
DWORD id;
|
||||
GetWindowThreadProcessId(hwnd, &id);
|
||||
if (id == (DWORD)lParam) {
|
||||
PostMessage(hwnd, WM_CLOSE, 0, 0);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT void KIOPrivate::sendTerminateSignal(qint64 pid)
|
||||
{
|
||||
// no error checking whether kill succeeded, Linux code also just sends a SIGTERM without checking
|
||||
HANDLE procHandle = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE, pid);
|
||||
if (procHandle != INVALID_HANDLE_VALUE) {
|
||||
EnumWindows(&closeProcessCallback, (LPARAM)pid);
|
||||
CloseHandle(procHandle);
|
||||
}
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT bool KIOPrivate::createSymlink(const QString &source, const QString &destination, KIOPrivate::SymlinkType type)
|
||||
{
|
||||
#if _WIN32_WINNT >= 0x600
|
||||
DWORD flag;
|
||||
if (type == KIOPrivate::DirectorySymlink) {
|
||||
flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
|
||||
} else if (type == KIOPrivate::FileSymlink) {
|
||||
flag = 0;
|
||||
} else {
|
||||
// Guess the type of the symlink based on the source path
|
||||
// If the source is a directory we set the flag SYMBOLIC_LINK_FLAG_DIRECTORY, for files
|
||||
// and non-existent paths we create a symlink to a file
|
||||
DWORD sourceAttrs = GetFileAttributesW((LPCWSTR)source.utf16());
|
||||
if (sourceAttrs != INVALID_FILE_ATTRIBUTES && (sourceAttrs & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
flag = SYMBOLIC_LINK_FLAG_DIRECTORY;
|
||||
} else {
|
||||
flag = 0;
|
||||
}
|
||||
}
|
||||
bool ok = CreateSymbolicLinkW((LPCWSTR)destination.utf16(), (LPCWSTR)source.utf16(), flag);
|
||||
if (!ok) {
|
||||
// create a .lnk file
|
||||
ok = QFile::link(source, destination);
|
||||
}
|
||||
return ok;
|
||||
#else
|
||||
qWarning("KIOPrivate::createSymlink: not implemented");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT int kio_windows_lstat(const char *path, QT_STATBUF *buffer)
|
||||
{
|
||||
int result = QT_STAT(path, buffer);
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
const QString pathStr = QFile::decodeName(path);
|
||||
// QFileInfo currently only checks for .lnk file in isSymlink() -> also check native win32 symlinks
|
||||
const DWORD fileAttrs = GetFileAttributesW((LPCWSTR)pathStr.utf16());
|
||||
if (fileAttrs != INVALID_FILE_ATTRIBUTES && (fileAttrs & FILE_ATTRIBUTE_REPARSE_POINT) || QFileInfo(pathStr).isSymLink()) {
|
||||
buffer->st_mode |= QT_STAT_LNK;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT bool KIOPrivate::changeOwnership(const QString &file, KUserId newOwner, KGroupId newGroup)
|
||||
{
|
||||
#pragma message("TODO")
|
||||
qWarning("KIOPrivate::changeOwnership: not implemented yet");
|
||||
return false;
|
||||
}
|
||||
@@ -0,0 +1,534 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kmountpoint.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../utils_p.h"
|
||||
#include <config-kmountpoint.h>
|
||||
#include <kioglobal_p.h> // Defines QT_LSTAT on windows to kio_windows_lstat
|
||||
|
||||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QTextStream>
|
||||
|
||||
#include <qplatformdefs.h>
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <qt_windows.h>
|
||||
static const Qt::CaseSensitivity cs = Qt::CaseInsensitive;
|
||||
#else
|
||||
static const Qt::CaseSensitivity cs = Qt::CaseSensitive;
|
||||
#endif
|
||||
|
||||
// This is the *BSD branch
|
||||
#if HAVE_SYS_MOUNT_H
|
||||
#if HAVE_SYS_PARAM_H
|
||||
#include <sys/param.h>
|
||||
#endif
|
||||
// FreeBSD has a table of names of mount-options in mount.h, which is only
|
||||
// defined (as MNTOPT_NAMES) if _WANT_MNTOPTNAMES is defined.
|
||||
#define _WANT_MNTOPTNAMES
|
||||
#include <sys/mount.h>
|
||||
#undef _WANT_MNTOPTNAMES
|
||||
#endif
|
||||
|
||||
#if HAVE_FSTAB_H
|
||||
#include <fstab.h>
|
||||
#endif
|
||||
|
||||
// Linux
|
||||
#if HAVE_LIB_MOUNT
|
||||
#include <libmount/libmount.h>
|
||||
#endif
|
||||
|
||||
static bool isNetfs(const QString &mountType)
|
||||
{
|
||||
// List copied from util-linux/libmount/src/utils.c
|
||||
static const std::vector<QLatin1String> netfsList{
|
||||
QLatin1String("cifs"),
|
||||
QLatin1String("smb3"),
|
||||
QLatin1String("smbfs"),
|
||||
QLatin1String("nfs"),
|
||||
QLatin1String("nfs3"),
|
||||
QLatin1String("nfs4"),
|
||||
QLatin1String("afs"),
|
||||
QLatin1String("ncpfs"),
|
||||
QLatin1String("fuse.curlftpfs"),
|
||||
QLatin1String("fuse.sshfs"),
|
||||
QLatin1String("9p"),
|
||||
};
|
||||
|
||||
return std::any_of(netfsList.cbegin(), netfsList.cend(), [mountType](const QLatin1String netfs) {
|
||||
return mountType == netfs;
|
||||
});
|
||||
}
|
||||
|
||||
class KMountPointPrivate
|
||||
{
|
||||
public:
|
||||
void resolveGvfsMountPoints(KMountPoint::List &result);
|
||||
void finalizePossibleMountPoint(KMountPoint::DetailsNeededFlags infoNeeded);
|
||||
void finalizeCurrentMountPoint(KMountPoint::DetailsNeededFlags infoNeeded);
|
||||
|
||||
QString m_mountedFrom;
|
||||
QString m_device; // Only available when the NeedRealDeviceName flag was set.
|
||||
QString m_mountPoint;
|
||||
QString m_mountType;
|
||||
QStringList m_mountOptions;
|
||||
dev_t m_deviceId = 0;
|
||||
bool m_isNetFs = false;
|
||||
};
|
||||
|
||||
KMountPoint::KMountPoint()
|
||||
: d(new KMountPointPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KMountPoint::~KMountPoint() = default;
|
||||
|
||||
#if HAVE_GETMNTINFO
|
||||
|
||||
#ifdef MNTOPT_NAMES
|
||||
static struct mntoptnames bsdOptionNames[] = {MNTOPT_NAMES};
|
||||
|
||||
/** @brief Get mount options from @p flags and puts human-readable version in @p list
|
||||
*
|
||||
* Appends all positive options found in @p flags to the @p list
|
||||
* This is roughly paraphrased from FreeBSD's mount.c, prmount().
|
||||
*/
|
||||
static void translateMountOptions(QStringList &list, uint64_t flags)
|
||||
{
|
||||
const struct mntoptnames *optionInfo = bsdOptionNames;
|
||||
|
||||
// Not all 64 bits are useful option names
|
||||
flags = flags & MNT_VISFLAGMASK;
|
||||
// Chew up options as long as we're in the table and there
|
||||
// are any flags left.
|
||||
for (; flags != 0 && optionInfo->o_opt != 0; ++optionInfo) {
|
||||
if (flags & optionInfo->o_opt) {
|
||||
list.append(QString::fromLatin1(optionInfo->o_name));
|
||||
flags &= ~optionInfo->o_opt;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
/** @brief Get mount options from @p flags and puts human-readable version in @p list
|
||||
*
|
||||
* This default version just puts the hex representation of @p flags
|
||||
* in the list, because there is no human-readable version.
|
||||
*/
|
||||
static void translateMountOptions(QStringList &list, uint64_t flags)
|
||||
{
|
||||
list.append(QStringLiteral("0x%1").arg(QString::number(flags, 16)));
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // HAVE_GETMNTINFO
|
||||
|
||||
void KMountPointPrivate::finalizePossibleMountPoint(KMountPoint::DetailsNeededFlags infoNeeded)
|
||||
{
|
||||
QString potentialDevice;
|
||||
if (const auto tag = QLatin1String("UUID="); m_mountedFrom.startsWith(tag)) {
|
||||
potentialDevice = QFile::symLinkTarget(QLatin1String("/dev/disk/by-uuid/") + QStringView(m_mountedFrom).mid(tag.size()));
|
||||
} else if (const auto tag = QLatin1String("LABEL="); m_mountedFrom.startsWith(tag)) {
|
||||
potentialDevice = QFile::symLinkTarget(QLatin1String("/dev/disk/by-label/") + QStringView(m_mountedFrom).mid(tag.size()));
|
||||
}
|
||||
|
||||
if (QFile::exists(potentialDevice)) {
|
||||
m_mountedFrom = potentialDevice;
|
||||
}
|
||||
|
||||
if (infoNeeded & KMountPoint::NeedRealDeviceName) {
|
||||
if (m_mountedFrom.startsWith(QLatin1Char('/'))) {
|
||||
m_device = QFileInfo(m_mountedFrom).canonicalFilePath();
|
||||
}
|
||||
}
|
||||
|
||||
// Chop trailing slash
|
||||
Utils::removeTrailingSlash(m_mountedFrom);
|
||||
}
|
||||
|
||||
void KMountPointPrivate::finalizeCurrentMountPoint(KMountPoint::DetailsNeededFlags infoNeeded)
|
||||
{
|
||||
if (infoNeeded & KMountPoint::NeedRealDeviceName) {
|
||||
if (m_mountedFrom.startsWith(QLatin1Char('/'))) {
|
||||
m_device = QFileInfo(m_mountedFrom).canonicalFilePath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KMountPoint::List KMountPoint::possibleMountPoints(DetailsNeededFlags infoNeeded)
|
||||
{
|
||||
KMountPoint::List result;
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
result = KMountPoint::currentMountPoints(infoNeeded);
|
||||
|
||||
#elif HAVE_LIB_MOUNT
|
||||
if (struct libmnt_table *table = mnt_new_table()) {
|
||||
// By default parses "/etc/fstab"
|
||||
if (mnt_table_parse_fstab(table, nullptr) == 0) {
|
||||
struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
|
||||
struct libmnt_fs *fs;
|
||||
|
||||
while (mnt_table_next_fs(table, itr, &fs) == 0) {
|
||||
const char *fsType = mnt_fs_get_fstype(fs);
|
||||
if (qstrcmp(fsType, "swap") == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Ptr mp(new KMountPoint);
|
||||
mp->d->m_mountType = QFile::decodeName(fsType);
|
||||
const char *target = mnt_fs_get_target(fs);
|
||||
mp->d->m_mountPoint = QFile::decodeName(target);
|
||||
|
||||
if (QT_STATBUF buff; QT_LSTAT(target, &buff) == 0) {
|
||||
mp->d->m_deviceId = buff.st_dev;
|
||||
}
|
||||
|
||||
// First field in /etc/fstab, e.g. /dev/sdXY, LABEL=, UUID=, /some/bind/mount/dir
|
||||
// or some network mount
|
||||
if (const char *source = mnt_fs_get_source(fs)) {
|
||||
mp->d->m_mountedFrom = QFile::decodeName(source);
|
||||
}
|
||||
|
||||
if (infoNeeded & NeedMountOptions) {
|
||||
mp->d->m_mountOptions = QFile::decodeName(mnt_fs_get_options(fs)).split(QLatin1Char(','));
|
||||
}
|
||||
|
||||
mp->d->finalizePossibleMountPoint(infoNeeded);
|
||||
result.append(mp);
|
||||
}
|
||||
mnt_free_iter(itr);
|
||||
}
|
||||
|
||||
mnt_free_table(table);
|
||||
}
|
||||
#elif HAVE_FSTAB_H
|
||||
|
||||
QFile f{QLatin1String(FSTAB)};
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
return result;
|
||||
}
|
||||
|
||||
QTextStream t(&f);
|
||||
QString s;
|
||||
|
||||
while (!t.atEnd()) {
|
||||
s = t.readLine().simplified();
|
||||
if (s.isEmpty() || (s[0] == QLatin1Char('#'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// not empty or commented out by '#'
|
||||
const QStringList item = s.split(QLatin1Char(' '));
|
||||
|
||||
if (item.count() < 4) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Ptr mp(new KMountPoint);
|
||||
|
||||
int i = 0;
|
||||
mp->d->m_mountedFrom = item[i++];
|
||||
mp->d->m_mountPoint = item[i++];
|
||||
mp->d->m_mountType = item[i++];
|
||||
if (mp->d->m_mountType == QLatin1String("swap")) {
|
||||
continue;
|
||||
}
|
||||
QString options = item[i++];
|
||||
|
||||
if (infoNeeded & NeedMountOptions) {
|
||||
mp->d->m_mountOptions = options.split(QLatin1Char(','));
|
||||
}
|
||||
|
||||
mp->d->finalizePossibleMountPoint(infoNeeded);
|
||||
|
||||
result.append(mp);
|
||||
} // while
|
||||
|
||||
f.close();
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void KMountPointPrivate::resolveGvfsMountPoints(KMountPoint::List &result)
|
||||
{
|
||||
if (m_mountedFrom == QLatin1String("gvfsd-fuse")) {
|
||||
const QDir gvfsDir(m_mountPoint);
|
||||
const QStringList mountDirs = gvfsDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
|
||||
for (const QString &mountDir : mountDirs) {
|
||||
const QString type = mountDir.section(QLatin1Char(':'), 0, 0);
|
||||
if (type.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
KMountPoint::Ptr gvfsmp(new KMountPoint);
|
||||
gvfsmp->d->m_mountedFrom = m_mountedFrom;
|
||||
gvfsmp->d->m_mountPoint = m_mountPoint + QLatin1Char('/') + mountDir;
|
||||
gvfsmp->d->m_mountType = type;
|
||||
result.append(gvfsmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KMountPoint::List KMountPoint::currentMountPoints(DetailsNeededFlags infoNeeded)
|
||||
{
|
||||
KMountPoint::List result;
|
||||
|
||||
#if HAVE_GETMNTINFO
|
||||
|
||||
#if GETMNTINFO_USES_STATVFS
|
||||
struct statvfs *mounted;
|
||||
#else
|
||||
struct statfs *mounted;
|
||||
#endif
|
||||
|
||||
int num_fs = getmntinfo(&mounted, MNT_NOWAIT);
|
||||
|
||||
result.reserve(num_fs);
|
||||
|
||||
for (int i = 0; i < num_fs; i++) {
|
||||
Ptr mp(new KMountPoint);
|
||||
mp->d->m_mountedFrom = QFile::decodeName(mounted[i].f_mntfromname);
|
||||
mp->d->m_mountPoint = QFile::decodeName(mounted[i].f_mntonname);
|
||||
mp->d->m_mountType = QFile::decodeName(mounted[i].f_fstypename);
|
||||
|
||||
if (QT_STATBUF buff; QT_LSTAT(mounted[i].f_mntonname, &buff) == 0) {
|
||||
mp->d->m_deviceId = buff.st_dev;
|
||||
}
|
||||
|
||||
if (infoNeeded & NeedMountOptions) {
|
||||
struct fstab *ft = getfsfile(mounted[i].f_mntonname);
|
||||
if (ft != nullptr) {
|
||||
QString options = QFile::decodeName(ft->fs_mntops);
|
||||
mp->d->m_mountOptions = options.split(QLatin1Char(','));
|
||||
} else {
|
||||
translateMountOptions(mp->d->m_mountOptions, mounted[i].f_flags);
|
||||
}
|
||||
}
|
||||
|
||||
mp->d->finalizeCurrentMountPoint(infoNeeded);
|
||||
// TODO: Strip trailing '/' ?
|
||||
result.append(mp);
|
||||
}
|
||||
|
||||
#elif defined(Q_OS_WIN)
|
||||
// nothing fancy with infoNeeded but it gets the job done
|
||||
DWORD bits = GetLogicalDrives();
|
||||
if (!bits) {
|
||||
return result;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 26; i++) {
|
||||
if (bits & (1 << i)) {
|
||||
Ptr mp(new KMountPoint);
|
||||
mp->d->m_mountPoint = QString(QLatin1Char('A' + i) + QLatin1String(":/"));
|
||||
result.append(mp);
|
||||
}
|
||||
}
|
||||
|
||||
#elif HAVE_LIB_MOUNT
|
||||
if (struct libmnt_table *table = mnt_new_table()) {
|
||||
// if "/etc/mtab" is a regular file,
|
||||
// "/etc/mtab" is used by default instead of "/proc/self/mountinfo" file.
|
||||
// This leads to NTFS mountpoints being hidden.
|
||||
if (
|
||||
#if QT_VERSION_CHECK(LIBMOUNT_MAJOR_VERSION, LIBMOUNT_MINOR_VERSION, LIBMOUNT_PATCH_VERSION) >= QT_VERSION_CHECK(2, 39, 0)
|
||||
mnt_table_parse_mtab(table, nullptr)
|
||||
#else // backwards compat, the documentation advises to use nullptr so lets do that whenever possible
|
||||
mnt_table_parse_mtab(table, "/proc/self/mountinfo")
|
||||
#endif
|
||||
== 0) {
|
||||
struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_FORWARD);
|
||||
struct libmnt_fs *fs;
|
||||
|
||||
while (mnt_table_next_fs(table, itr, &fs) == 0) {
|
||||
Ptr mp(new KMountPoint);
|
||||
mp->d->m_mountedFrom = QFile::decodeName(mnt_fs_get_source(fs));
|
||||
mp->d->m_mountPoint = QFile::decodeName(mnt_fs_get_target(fs));
|
||||
mp->d->m_mountType = QFile::decodeName(mnt_fs_get_fstype(fs));
|
||||
mp->d->m_isNetFs = mnt_fs_is_netfs(fs) == 1;
|
||||
mp->d->m_deviceId = mnt_fs_get_devno(fs);
|
||||
|
||||
if (infoNeeded & NeedMountOptions) {
|
||||
mp->d->m_mountOptions = QFile::decodeName(mnt_fs_get_options(fs)).split(QLatin1Char(','));
|
||||
}
|
||||
|
||||
if (infoNeeded & NeedRealDeviceName) {
|
||||
if (mp->d->m_mountedFrom.startsWith(QLatin1Char('/'))) {
|
||||
mp->d->m_device = mp->d->m_mountedFrom;
|
||||
}
|
||||
}
|
||||
|
||||
mp->d->resolveGvfsMountPoints(result);
|
||||
|
||||
mp->d->finalizeCurrentMountPoint(infoNeeded);
|
||||
result.push_back(mp);
|
||||
}
|
||||
|
||||
mnt_free_iter(itr);
|
||||
}
|
||||
|
||||
mnt_free_table(table);
|
||||
}
|
||||
#endif
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString KMountPoint::mountedFrom() const
|
||||
{
|
||||
return d->m_mountedFrom;
|
||||
}
|
||||
|
||||
dev_t KMountPoint::deviceId() const
|
||||
{
|
||||
return d->m_deviceId;
|
||||
}
|
||||
|
||||
bool KMountPoint::isOnNetwork() const
|
||||
{
|
||||
return d->m_isNetFs || isNetfs(d->m_mountType);
|
||||
}
|
||||
|
||||
QString KMountPoint::realDeviceName() const
|
||||
{
|
||||
return d->m_device;
|
||||
}
|
||||
|
||||
QString KMountPoint::mountPoint() const
|
||||
{
|
||||
return d->m_mountPoint;
|
||||
}
|
||||
|
||||
QString KMountPoint::mountType() const
|
||||
{
|
||||
return d->m_mountType;
|
||||
}
|
||||
|
||||
QStringList KMountPoint::mountOptions() const
|
||||
{
|
||||
return d->m_mountOptions;
|
||||
}
|
||||
|
||||
KMountPoint::List::List()
|
||||
: QList<Ptr>()
|
||||
{
|
||||
}
|
||||
|
||||
KMountPoint::Ptr KMountPoint::List::findByPath(const QString &path) const
|
||||
{
|
||||
#ifdef Q_OS_WIN
|
||||
const QString realPath = QDir::fromNativeSeparators(QDir(path).absolutePath());
|
||||
#else
|
||||
/* If the path contains symlinks, get the real name */
|
||||
QFileInfo fileinfo(path);
|
||||
// canonicalFilePath won't work unless file exists
|
||||
const QString realPath = fileinfo.exists() ? fileinfo.canonicalFilePath() : fileinfo.absolutePath();
|
||||
#endif
|
||||
|
||||
KMountPoint::Ptr result;
|
||||
|
||||
if (QT_STATBUF buff; QT_LSTAT(QFile::encodeName(realPath).constData(), &buff) == 0) {
|
||||
auto it = std::find_if(this->cbegin(), this->cend(), [&buff, &realPath](const KMountPoint::Ptr &mountPtr) {
|
||||
// For a bind mount, the deviceId() is that of the base mount point, e.g. /mnt/foo,
|
||||
// however the path we're looking for, e.g. /home/user/bar, doesn't start with the
|
||||
// mount point of the base device, so we go on searching
|
||||
return mountPtr->deviceId() == buff.st_dev && realPath.startsWith(mountPtr->mountPoint());
|
||||
});
|
||||
|
||||
if (it != this->cend()) {
|
||||
result = *it;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
KMountPoint::Ptr KMountPoint::List::findByDevice(const QString &device) const
|
||||
{
|
||||
const QString realDevice = QFileInfo(device).canonicalFilePath();
|
||||
if (realDevice.isEmpty()) { // d->m_device can be empty in the loop below, don't match empty with it
|
||||
return Ptr();
|
||||
}
|
||||
for (const KMountPoint::Ptr &mountPoint : *this) {
|
||||
if (realDevice.compare(mountPoint->d->m_device, cs) == 0 || realDevice.compare(mountPoint->d->m_mountedFrom, cs) == 0) {
|
||||
return mountPoint;
|
||||
}
|
||||
}
|
||||
return Ptr();
|
||||
}
|
||||
|
||||
bool KMountPoint::probablySlow() const
|
||||
{
|
||||
/* clang-format off */
|
||||
return isOnNetwork()
|
||||
|| d->m_mountType == QLatin1String("autofs")
|
||||
|| d->m_mountType == QLatin1String("subfs")
|
||||
// Technically KIOFUSe mounts local workers as well,
|
||||
// such as recents:/, but better safe than sorry...
|
||||
|| d->m_mountType == QLatin1String("fuse.kio-fuse");
|
||||
/* clang-format on */
|
||||
}
|
||||
|
||||
bool KMountPoint::testFileSystemFlag(FileSystemFlag flag) const
|
||||
{
|
||||
/* clang-format off */
|
||||
const bool isMsDos = d->m_mountType == QLatin1String("msdos")
|
||||
|| d->m_mountType == QLatin1String("fat")
|
||||
|| d->m_mountType == QLatin1String("vfat");
|
||||
|
||||
const bool isNtfs = d->m_mountType.contains(QLatin1String("fuse.ntfs"))
|
||||
|| d->m_mountType.contains(QLatin1String("fuseblk.ntfs"))
|
||||
// fuseblk could really be anything. But its most common use is for NTFS mounts, these days.
|
||||
|| d->m_mountType == QLatin1String("fuseblk");
|
||||
|
||||
const bool isSmb = d->m_mountType == QLatin1String("cifs")
|
||||
|| d->m_mountType == QLatin1String("smb3")
|
||||
|| d->m_mountType == QLatin1String("smbfs")
|
||||
// gvfs-fuse mounted SMB share
|
||||
|| d->m_mountType == QLatin1String("smb-share");
|
||||
/* clang-format on */
|
||||
|
||||
switch (flag) {
|
||||
case SupportsChmod:
|
||||
case SupportsChown:
|
||||
case SupportsUTime:
|
||||
case SupportsSymlinks:
|
||||
return !isMsDos && !isNtfs && !isSmb; // it's amazing the number of things Microsoft filesystems don't support :)
|
||||
case CaseInsensitive:
|
||||
return isMsDos;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
KIOCORE_EXPORT QDebug operator<<(QDebug debug, const KMountPoint::Ptr &mp)
|
||||
{
|
||||
QDebugStateSaver saver(debug);
|
||||
if (!mp) {
|
||||
debug << "QDebug operator<< called on a null KMountPoint::Ptr";
|
||||
return debug;
|
||||
}
|
||||
|
||||
// clang-format off
|
||||
debug.nospace() << "KMountPoint ["
|
||||
<< "Mounted from: " << mp->d->m_mountedFrom
|
||||
<< ", device name: " << mp->d->m_device
|
||||
<< ", mount point: " << mp->d->m_mountPoint
|
||||
<< ", mount type: " << mp->d->m_mountType
|
||||
<<']';
|
||||
|
||||
// clang-format on
|
||||
return debug;
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2007 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KMOUNTPOINT_H
|
||||
#define KMOUNTPOINT_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <QExplicitlySharedDataPointer>
|
||||
#include <QStringList>
|
||||
|
||||
#include <memory>
|
||||
#include <sys/types.h> // dev_t among other definitions
|
||||
|
||||
class KMountPointPrivate;
|
||||
|
||||
/**
|
||||
* @class KMountPoint kmountpoint.h <KMountPoint>
|
||||
*
|
||||
* The KMountPoint class provides information about mounted and unmounted disks.
|
||||
* It provides a system independent interface to fstab.
|
||||
*
|
||||
* @author Waldo Bastian <bastian@kde.org>
|
||||
*/
|
||||
class KIOCORE_EXPORT KMountPoint : public QSharedData
|
||||
{
|
||||
public:
|
||||
using Ptr = QExplicitlySharedDataPointer<KMountPoint>;
|
||||
|
||||
/**
|
||||
* List of mount points.
|
||||
*/
|
||||
class KIOCORE_EXPORT List : public QList<Ptr>
|
||||
{
|
||||
public:
|
||||
List();
|
||||
/**
|
||||
* Find the mountpoint on which resides @p path
|
||||
* For instance if /home is a separate partition, findByPath("/home/user/blah")
|
||||
* will return /home
|
||||
* @param path the path to check
|
||||
* @return the mount point of the given file
|
||||
*/
|
||||
Ptr findByPath(const QString &path) const;
|
||||
|
||||
/**
|
||||
* Returns the mount point associated with @p device,
|
||||
* i.e. the one where mountedFrom() == @p device
|
||||
* (after symlink resolution).
|
||||
* @return the mountpoint, or @c nullptr if this device doesn't exist or isn't mounted
|
||||
*/
|
||||
Ptr findByDevice(const QString &device) const;
|
||||
};
|
||||
|
||||
public:
|
||||
/**
|
||||
* Flags that specify which additional details should be fetched for each mountpoint.
|
||||
* @see DetailsNeededFlags
|
||||
*/
|
||||
enum DetailsNeededFlag {
|
||||
/**
|
||||
* Only the basic details: mountedFrom, mountPoint, mountType.
|
||||
*/
|
||||
BasicInfoNeeded = 0,
|
||||
/**
|
||||
* Also fetch the options used when mounting, see KMountPoint::mountOptions().
|
||||
*/
|
||||
NeedMountOptions = 1,
|
||||
/**
|
||||
* Also fetch the device name (with symlinks resolved), see KMountPoint::realDeviceName().
|
||||
*/
|
||||
NeedRealDeviceName = 2,
|
||||
};
|
||||
/**
|
||||
* Stores a combination of #DetailsNeededFlag values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(DetailsNeededFlags, DetailsNeededFlag)
|
||||
|
||||
/**
|
||||
* This function gives a list of all possible mountpoints. (fstab)
|
||||
* @param infoNeeded Flags that specify which additional information
|
||||
* should be fetched.
|
||||
*/
|
||||
static List possibleMountPoints(DetailsNeededFlags infoNeeded = BasicInfoNeeded);
|
||||
|
||||
/**
|
||||
* Returns a list of all current mountpoints.
|
||||
*
|
||||
* @param infoNeeded Flags that specify which additional information
|
||||
* should be fetched.
|
||||
*
|
||||
* @note This method will return an empty list on @c Android
|
||||
*/
|
||||
static List currentMountPoints(DetailsNeededFlags infoNeeded = BasicInfoNeeded);
|
||||
|
||||
/**
|
||||
* Where this filesystem gets mounted from.
|
||||
* This can refer to a device, a remote server or something else.
|
||||
*/
|
||||
QString mountedFrom() const;
|
||||
|
||||
/**
|
||||
* Returns @c true if this mount point represents a network filesystem (e.g.\ NFS,
|
||||
* CIFS, etc.), otherwise returns @c false.
|
||||
*
|
||||
* @since 5.86
|
||||
*/
|
||||
bool isOnNetwork() const;
|
||||
|
||||
/**
|
||||
* Returns the device ID (dev_t, major, minor) of this mount point. This
|
||||
* ID is unique per device (including network mounts).
|
||||
*
|
||||
* @since 5.86
|
||||
*/
|
||||
dev_t deviceId() const;
|
||||
|
||||
/**
|
||||
* Canonical name of the device where the filesystem got mounted from.
|
||||
* (Or empty, if not a device)
|
||||
* Only available when the NeedRealDeviceName flag was set.
|
||||
*/
|
||||
QString realDeviceName() const;
|
||||
|
||||
/**
|
||||
* Path where the filesystem is mounted (if you used @ref currentMountPoints()),
|
||||
* or can be mounted (if you used @ref possibleMountPoints()).
|
||||
*/
|
||||
QString mountPoint() const;
|
||||
|
||||
/**
|
||||
* Type of filesystem
|
||||
*/
|
||||
QString mountType() const;
|
||||
|
||||
/**
|
||||
* Options used to mount the filesystem.
|
||||
* Only available if the @ref NeedMountOptions flag was set.
|
||||
*/
|
||||
QStringList mountOptions() const;
|
||||
|
||||
/**
|
||||
* Returns @c true if the filesystem is "probably" slow, e.g.\ a network mount,
|
||||
* @c false otherwise.
|
||||
*/
|
||||
bool probablySlow() const;
|
||||
|
||||
enum FileSystemFlag {
|
||||
SupportsChmod,
|
||||
SupportsChown,
|
||||
SupportsUTime,
|
||||
SupportsSymlinks,
|
||||
CaseInsensitive,
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks the capabilities of the filesystem.
|
||||
* @param flag the flag to check
|
||||
* @return true if the filesystem has that flag, false if not
|
||||
*
|
||||
* The available flags are:
|
||||
* @li SupportsChmod: returns true if the filesystem supports chmod
|
||||
* (e.g. msdos filesystems return false)
|
||||
* @li SupportsChown: returns true if the filesystem supports chown
|
||||
* (e.g. msdos filesystems return false)
|
||||
* @li SupportsUtime: returns true if the filesystems supports utime
|
||||
* (e.g. msdos filesystems return false)
|
||||
* @li SupportsSymlinks: returns true if the filesystems supports symlinks
|
||||
* (e.g. msdos filesystems return false)
|
||||
* @li CaseInsensitive: returns true if the filesystem treats
|
||||
* "foo" and "FOO" as being the same file (true for msdos filesystems)
|
||||
*
|
||||
*/
|
||||
bool testFileSystemFlag(FileSystemFlag flag) const;
|
||||
|
||||
/**
|
||||
* Destructor
|
||||
*/
|
||||
~KMountPoint();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
KIOCORE_NO_EXPORT KMountPoint();
|
||||
|
||||
friend KIOCORE_EXPORT QDebug operator<<(QDebug debug, const Ptr &mp);
|
||||
|
||||
friend KMountPointPrivate;
|
||||
std::unique_ptr<KMountPointPrivate> d;
|
||||
};
|
||||
|
||||
KIOCORE_EXPORT QDebug operator<<(QDebug debug, const KMountPoint::Ptr &mp);
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KMountPoint::DetailsNeededFlags)
|
||||
|
||||
#endif // KMOUNTPOINT_H
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "knfsshare.h"
|
||||
|
||||
#include "../utils_p.h"
|
||||
|
||||
#include <QDebug>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QSet>
|
||||
#include <QTextStream>
|
||||
|
||||
#include "kiocoredebug.h"
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KDirWatch>
|
||||
|
||||
class Q_DECL_HIDDEN KNFSShare::KNFSSharePrivate
|
||||
{
|
||||
public:
|
||||
explicit KNFSSharePrivate(KNFSShare *parent);
|
||||
|
||||
void slotFileChange(const QString &);
|
||||
|
||||
bool readExportsFile();
|
||||
bool findExportsFile();
|
||||
|
||||
KNFSShare *const q;
|
||||
QSet<QString> sharedPaths;
|
||||
QString exportsFile;
|
||||
};
|
||||
|
||||
KNFSShare::KNFSSharePrivate::KNFSSharePrivate(KNFSShare *parent)
|
||||
: q(parent)
|
||||
{
|
||||
if (findExportsFile()) {
|
||||
readExportsFile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find the nfs config file path
|
||||
* First tries the kconfig, then checks
|
||||
* several well-known paths
|
||||
* @return whether an 'exports' file was found.
|
||||
**/
|
||||
bool KNFSShare::KNFSSharePrivate::findExportsFile()
|
||||
{
|
||||
KConfig knfsshare(QStringLiteral("knfsshare"));
|
||||
KConfigGroup config(&knfsshare, QStringLiteral("General"));
|
||||
exportsFile = config.readPathEntry("exportsFile", QString());
|
||||
|
||||
if (!exportsFile.isEmpty() && QFileInfo::exists(exportsFile)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (QFile::exists(QStringLiteral("/etc/exports"))) {
|
||||
exportsFile = QStringLiteral("/etc/exports");
|
||||
} else {
|
||||
// qDebug() << "Could not find exports file! /etc/exports doesn't exist. Configure it in share/config/knfsshare, [General], exportsFile=....";
|
||||
return false;
|
||||
}
|
||||
|
||||
config.writeEntry("exportsFile", exportsFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads all paths from the exports file
|
||||
* and fills the sharedPaths dict with the values
|
||||
*/
|
||||
bool KNFSShare::KNFSSharePrivate::readExportsFile()
|
||||
{
|
||||
QFile f(exportsFile);
|
||||
|
||||
// qDebug() << exportsFile;
|
||||
|
||||
if (!f.open(QIODevice::ReadOnly)) {
|
||||
qCWarning(KIO_CORE) << "KNFSShare: Could not open" << exportsFile;
|
||||
return false;
|
||||
}
|
||||
|
||||
sharedPaths.clear();
|
||||
|
||||
QTextStream s(&f);
|
||||
|
||||
bool continuedLine = false; // is true if the line before ended with a backslash
|
||||
QString completeLine;
|
||||
|
||||
while (!s.atEnd()) {
|
||||
QString currentLine = s.readLine().trimmed();
|
||||
|
||||
if (continuedLine) {
|
||||
completeLine += currentLine;
|
||||
continuedLine = false;
|
||||
} else {
|
||||
completeLine = currentLine;
|
||||
}
|
||||
|
||||
// is the line continued in the next line ?
|
||||
if (completeLine.endsWith(QLatin1Char('\\'))) {
|
||||
continuedLine = true;
|
||||
// remove the ending backslash
|
||||
completeLine.chop(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
// comments or empty lines
|
||||
if (completeLine.startsWith(QLatin1Char('#')) || completeLine.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
QString path;
|
||||
|
||||
// Handle quotation marks
|
||||
if (completeLine[0] == QLatin1Char('\"')) {
|
||||
int i = completeLine.indexOf(QLatin1Char('"'), 1);
|
||||
if (i == -1) {
|
||||
qCWarning(KIO_CORE) << "KNFSShare: Parse error: Missing quotation mark:" << completeLine;
|
||||
continue;
|
||||
}
|
||||
path = completeLine.mid(1, i - 1);
|
||||
|
||||
} else { // no quotation marks
|
||||
int i = completeLine.indexOf(QLatin1Char(' '));
|
||||
if (i == -1) {
|
||||
i = completeLine.indexOf(QLatin1Char('\t'));
|
||||
}
|
||||
|
||||
if (i == -1) {
|
||||
path = completeLine;
|
||||
} else {
|
||||
path = completeLine.left(i);
|
||||
}
|
||||
}
|
||||
|
||||
// qDebug() << "KNFSShare: Found path: " << path;
|
||||
|
||||
if (!path.isEmpty()) {
|
||||
// Append a '/' to normalize path
|
||||
Utils::appendSlash(path);
|
||||
sharedPaths.insert(path);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
KNFSShare::KNFSShare()
|
||||
: d(new KNFSSharePrivate(this))
|
||||
{
|
||||
if (!d->exportsFile.isEmpty() && QFileInfo::exists(d->exportsFile)) {
|
||||
KDirWatch::self()->addFile(d->exportsFile);
|
||||
connect(KDirWatch::self(), &KDirWatch::dirty, this, [this](const QString &path) {
|
||||
d->slotFileChange(path);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
KNFSShare::~KNFSShare() = default;
|
||||
|
||||
bool KNFSShare::isDirectoryShared(const QString &path) const
|
||||
{
|
||||
if (path.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return d->sharedPaths.contains(Utils::slashAppended(path));
|
||||
}
|
||||
|
||||
QStringList KNFSShare::sharedDirectories() const
|
||||
{
|
||||
return d->sharedPaths.values();
|
||||
}
|
||||
|
||||
QString KNFSShare::exportsPath() const
|
||||
{
|
||||
return d->exportsFile;
|
||||
}
|
||||
|
||||
void KNFSShare::KNFSSharePrivate::slotFileChange(const QString &path)
|
||||
{
|
||||
if (path == exportsFile) {
|
||||
readExportsFile();
|
||||
}
|
||||
|
||||
Q_EMIT q->changed();
|
||||
}
|
||||
|
||||
class KNFSShareSingleton
|
||||
{
|
||||
public:
|
||||
KNFSShare instance;
|
||||
};
|
||||
|
||||
Q_GLOBAL_STATIC(KNFSShareSingleton, _instance)
|
||||
|
||||
KNFSShare *KNFSShare::instance()
|
||||
{
|
||||
return &_instance()->instance;
|
||||
}
|
||||
|
||||
#include "moc_knfsshare.cpp"
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2004 Jan Schaefer <j_schaef@informatik.uni-kl.de>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef knfsshare_h
|
||||
#define knfsshare_h
|
||||
|
||||
#include <QObject>
|
||||
|
||||
#include "kiocore_export.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
/**
|
||||
* @class KNFSShare knfsshare.h <KNFSShare>
|
||||
*
|
||||
* Similar functionality like KFileShare,
|
||||
* but works only for NFS and do not need
|
||||
* any suid script.
|
||||
* It parses the /etc/exports file to get its information.
|
||||
* Singleton class, call instance() to get an instance.
|
||||
*/
|
||||
class KNFSShare : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* Returns the one and only instance of KNFSShare
|
||||
*/
|
||||
static KNFSShare *instance();
|
||||
|
||||
/**
|
||||
* Whether or not the given path is shared by NFS.
|
||||
* @param path the path to check if it is shared by NFS.
|
||||
* @return whether the given path is shared by NFS.
|
||||
*/
|
||||
bool isDirectoryShared(const QString &path) const;
|
||||
|
||||
/**
|
||||
* Returns a list of all directories shared by NFS.
|
||||
* The resulting list is not sorted.
|
||||
* @return a list of all directories shared by NFS.
|
||||
*/
|
||||
QStringList sharedDirectories() const;
|
||||
|
||||
/**
|
||||
* KNFSShare destructor.
|
||||
* Do not call!
|
||||
* The instance is destroyed automatically!
|
||||
*/
|
||||
~KNFSShare() override;
|
||||
|
||||
/**
|
||||
* Returns the path to the used exports file,
|
||||
* or null if no exports file was found
|
||||
*/
|
||||
QString exportsPath() const;
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when the /etc/exports file has changed
|
||||
*/
|
||||
void changed();
|
||||
|
||||
private:
|
||||
KIOCORE_NO_EXPORT KNFSShare();
|
||||
class KNFSSharePrivate;
|
||||
std::unique_ptr<KNFSSharePrivate> const d;
|
||||
|
||||
friend class KNFSShareSingleton;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 Olivier Goffart <ogoffart@woboq.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "koverlayiconplugin.h"
|
||||
|
||||
KOverlayIconPlugin::KOverlayIconPlugin(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
|
||||
KOverlayIconPlugin::~KOverlayIconPlugin()
|
||||
{
|
||||
}
|
||||
|
||||
#include "moc_koverlayiconplugin.cpp"
|
||||
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
This file is part of the KDE project
|
||||
SPDX-FileCopyrightText: 2015 Olivier Goffart <ogoffart@woboq.com>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KOVERLAYICONPLUGIN_H
|
||||
#define KOVERLAYICONPLUGIN_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <QObject>
|
||||
|
||||
class QUrl;
|
||||
|
||||
/**
|
||||
* @class KOverlayIconPlugin koverlayiconplugin.h <KOverlayIconPlugin>
|
||||
*
|
||||
* @brief Base class for overlay icon plugins.
|
||||
* Enables file managers to show custom overlay icons on files.
|
||||
*
|
||||
* This plugin can be created and installed through kcoreaddons_add_plugin
|
||||
* @code
|
||||
* kcoreaddons_add_plugin(myoverlayplugin SOURCES myoverlayplugin.cpp INSTALL_NAMESPACE "kf6/overlayicon")
|
||||
* target_link_libraries(myoverlayplugin KF6::KIOCore)
|
||||
* @endcode
|
||||
* The C++ file should look like this:
|
||||
* @code
|
||||
#include <KOverlayIconPlugin>
|
||||
|
||||
class MyOverlayPlugin : public KOverlayIconPlugin
|
||||
{
|
||||
Q_PLUGIN_METADATA(IID "org.kde.overlayicon.myplugin")
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MyOverlayPlugin() {
|
||||
}
|
||||
|
||||
QStringList getOverlays(const QUrl &url) override {
|
||||
// Implement your logic
|
||||
}
|
||||
};
|
||||
|
||||
#include "myoverlayplugin.moc"
|
||||
* @endcode
|
||||
* @since 5.16
|
||||
*/
|
||||
class KIOCORE_EXPORT KOverlayIconPlugin : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit KOverlayIconPlugin(QObject *parent = nullptr);
|
||||
~KOverlayIconPlugin() override;
|
||||
|
||||
/**
|
||||
* Returns a list of overlay icons to add to a file
|
||||
* This can be a path to an icon, or the icon name
|
||||
*
|
||||
* This function is called from the main thread and must not block.
|
||||
* It is recommended to have a cache. And if the item is not in cache
|
||||
* just return an empty list and call the overlaysChanged when the
|
||||
* information is available.
|
||||
*/
|
||||
virtual QStringList getOverlays(const QUrl &item) = 0;
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emit this signal when the list of overlay icons changed for a given URL
|
||||
*/
|
||||
void overlaysChanged(const QUrl &url, const QStringList &overlays);
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kpasswdserverclient.h"
|
||||
#include "kiocoredebug.h"
|
||||
|
||||
#include <kio/authinfo.h>
|
||||
#include <kio/global.h>
|
||||
|
||||
#include "kpasswdserver_interface.h"
|
||||
#include "kpasswdserverloop_p.h"
|
||||
|
||||
class KPasswdServerClientPrivate
|
||||
{
|
||||
public:
|
||||
KPasswdServerClientPrivate()
|
||||
: seqNr(0)
|
||||
{
|
||||
}
|
||||
|
||||
qlonglong seqNr;
|
||||
QString lastHost;
|
||||
};
|
||||
|
||||
KPasswdServerClient::KPasswdServerClient()
|
||||
: m_interface(
|
||||
new OrgKdeKPasswdServerInterface(QStringLiteral("org.kde.kpasswdserver6"), QStringLiteral("/modules/kpasswdserver"), QDBusConnection::sessionBus()))
|
||||
, d(new KPasswdServerClientPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KPasswdServerClient::~KPasswdServerClient()
|
||||
{
|
||||
delete m_interface;
|
||||
}
|
||||
|
||||
bool KPasswdServerClient::checkAuthInfo(KIO::AuthInfo *info, qlonglong windowId, qlonglong usertime)
|
||||
{
|
||||
// qDebug() << "window-id=" << windowId << "url=" << info.url;
|
||||
|
||||
if (!QCoreApplication::instance()) {
|
||||
qCWarning(KIO_CORE) << "KIO worker is not a QCoreApplication! This is required for checkAuthInfo.";
|
||||
return false;
|
||||
}
|
||||
|
||||
// create the loop for waiting for a result before sending the request
|
||||
KPasswdServerLoop loop;
|
||||
QObject::connect(m_interface, &OrgKdeKPasswdServerInterface::checkAuthInfoAsyncResult, &loop, &KPasswdServerLoop::slotQueryResult);
|
||||
|
||||
QDBusReply<qlonglong> reply = m_interface->checkAuthInfoAsync(*info, windowId, usertime);
|
||||
if (!reply.isValid()) {
|
||||
qCWarning(KIO_CORE) << "Can't communicate with kiod_kpasswdserver (for checkAuthInfo)!" << reply.error().message();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!loop.waitForResult(reply.value())) {
|
||||
qCWarning(KIO_CORE) << "kiod_kpasswdserver died while waiting for reply!";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (loop.authInfo().isModified()) {
|
||||
// qDebug() << "username=" << info.username << "password=[hidden]";
|
||||
*info = loop.authInfo();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int KPasswdServerClient::queryAuthInfo(KIO::AuthInfo *info, const QString &errorMsg, qlonglong windowId, qlonglong usertime)
|
||||
{
|
||||
if (info->url.host() != d->lastHost) { // see kpasswdserver/DESIGN
|
||||
d->lastHost = info->url.host();
|
||||
d->seqNr = 0;
|
||||
}
|
||||
|
||||
// qDebug() << "window-id=" << windowId;
|
||||
|
||||
if (!QCoreApplication::instance()) {
|
||||
qCWarning(KIO_CORE) << "KIO worker is not a QCoreApplication! This is required for queryAuthInfo.";
|
||||
return KIO::ERR_PASSWD_SERVER;
|
||||
}
|
||||
|
||||
// create the loop for waiting for a result before sending the request
|
||||
KPasswdServerLoop loop;
|
||||
QObject::connect(m_interface, &OrgKdeKPasswdServerInterface::queryAuthInfoAsyncResult, &loop, &KPasswdServerLoop::slotQueryResult);
|
||||
|
||||
QDBusReply<qlonglong> reply = m_interface->queryAuthInfoAsync(*info, errorMsg, windowId, d->seqNr, usertime);
|
||||
if (!reply.isValid()) {
|
||||
qCWarning(KIO_CORE) << "Can't communicate with kiod_kpasswdserver (for queryAuthInfo)!";
|
||||
// qDebug() << reply.error().name() << reply.error().message();
|
||||
return KIO::ERR_PASSWD_SERVER;
|
||||
}
|
||||
|
||||
if (!loop.waitForResult(reply.value())) {
|
||||
qCWarning(KIO_CORE) << "kiod_kpasswdserver died while waiting for reply!";
|
||||
return KIO::ERR_PASSWD_SERVER;
|
||||
}
|
||||
|
||||
*info = loop.authInfo();
|
||||
|
||||
// qDebug() << "username=" << info->username << "password=[hidden]";
|
||||
|
||||
const qlonglong newSeqNr = loop.seqNr();
|
||||
|
||||
if (newSeqNr > 0) {
|
||||
d->seqNr = newSeqNr;
|
||||
if (info->isModified()) {
|
||||
return KJob::NoError;
|
||||
}
|
||||
}
|
||||
|
||||
return KIO::ERR_USER_CANCELED;
|
||||
}
|
||||
|
||||
void KPasswdServerClient::addAuthInfo(const KIO::AuthInfo &info, qlonglong windowId)
|
||||
{
|
||||
m_interface->addAuthInfo(info, windowId);
|
||||
}
|
||||
|
||||
void KPasswdServerClient::removeAuthInfo(const QString &host, const QString &protocol, const QString &user)
|
||||
{
|
||||
m_interface->removeAuthInfo(host, protocol, user);
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KPASSWDSERVERCLIENT_H
|
||||
#define KPASSWDSERVERCLIENT_H
|
||||
|
||||
#include <kiocore_export.h>
|
||||
#include <qglobal.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QString;
|
||||
class OrgKdeKPasswdServerInterface;
|
||||
|
||||
namespace KIO
|
||||
{
|
||||
class AuthInfo;
|
||||
}
|
||||
|
||||
class KPasswdServerClientPrivate;
|
||||
|
||||
/**
|
||||
* @class KPasswdServerClient kpasswdserverclient.h <KPasswdServerClient>
|
||||
*
|
||||
* Interface class for kpasswdserver.
|
||||
* KIO workers should not use this directly but via the WorkerBase API.
|
||||
* @since 5.30
|
||||
*/
|
||||
class KIOCORE_EXPORT KPasswdServerClient
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* Creates a client instance for kpasswdserver.
|
||||
* The instance should be kept for the lifetime of the process, not created for each request.
|
||||
*/
|
||||
KPasswdServerClient();
|
||||
/**
|
||||
* Destructor.
|
||||
*/
|
||||
~KPasswdServerClient();
|
||||
|
||||
KPasswdServerClient(const KPasswdServerClient &) = delete;
|
||||
KPasswdServerClient &operator=(const KPasswdServerClient &) = delete;
|
||||
|
||||
/**
|
||||
* Check if kpasswdserver has cached authentication information regarding
|
||||
* an AuthInfo object.
|
||||
* @param info information to check cache for
|
||||
* @param windowId used as parent for dialogs, comes from QWidget::winId() on the toplevel widget
|
||||
* @param usertime the X11 user time from the calling application, so that any dialog
|
||||
* (e.g. wallet password) respects focus-prevention rules.
|
||||
* Use KUserTimestamp::userTimestamp in the GUI application from which the request originates.
|
||||
* @return true if kpasswdserver provided cached information, false if not
|
||||
* @remarks info will contain the results of the check. To see if
|
||||
* information was retrieved, check info.isModified().
|
||||
*/
|
||||
bool checkAuthInfo(KIO::AuthInfo *info, qlonglong windowId, qlonglong usertime);
|
||||
|
||||
/**
|
||||
* Let kpasswdserver ask the user for authentication information.
|
||||
* @param info information to query the user for
|
||||
* @param errorMsg error message that will be displayed to the user
|
||||
* @param windowId used as parent for dialogs, comes from QWidget::winId() on the toplevel widget
|
||||
* @param usertime the X11 user time from the calling application, so that the dialog
|
||||
* (e.g. wallet password) respects focus-prevention rules.
|
||||
* Use KUserTimestamp::userTimestamp in the GUI application from which the request originates.
|
||||
* @return a KIO error code: KJob::NoError (0) on success, otherwise ERR_USER_CANCELED if the user canceled,
|
||||
* or ERR_PASSWD_SERVER if we couldn't communicate with kpasswdserver.
|
||||
* @remarks If NoError is returned, then @p info will contain the authentication information that was retrieved.
|
||||
*/
|
||||
int queryAuthInfo(KIO::AuthInfo *info, const QString &errorMsg, qlonglong windowId, qlonglong usertime);
|
||||
|
||||
/**
|
||||
* Manually add authentication information to kpasswdserver's cache.
|
||||
* @param info information to add
|
||||
* @param windowId used as parent window for dialogs, comes from QWidget::winId() on the toplevel widget
|
||||
*/
|
||||
void addAuthInfo(const KIO::AuthInfo &info, qlonglong windowId);
|
||||
|
||||
/**
|
||||
* Manually remove authentication information from kpasswdserver's cache.
|
||||
* @param host hostname of the information to remove
|
||||
* @param protocol protocol to remove information for
|
||||
* @param user username to remove information for
|
||||
*/
|
||||
void removeAuthInfo(const QString &host, const QString &protocol, const QString &user);
|
||||
|
||||
private:
|
||||
OrgKdeKPasswdServerInterface *m_interface;
|
||||
std::unique_ptr<KPasswdServerClientPrivate> d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#include "kpasswdserverloop_p.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusServiceWatcher>
|
||||
|
||||
KPasswdServerLoop::KPasswdServerLoop()
|
||||
: m_seqNr(-1)
|
||||
{
|
||||
QDBusServiceWatcher *watcher =
|
||||
new QDBusServiceWatcher(QStringLiteral("org.kde.kpasswdserver6"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForUnregistration, this);
|
||||
connect(watcher, &QDBusServiceWatcher::serviceUnregistered, this, &KPasswdServerLoop::kdedServiceUnregistered);
|
||||
}
|
||||
|
||||
KPasswdServerLoop::~KPasswdServerLoop()
|
||||
{
|
||||
}
|
||||
|
||||
bool KPasswdServerLoop::waitForResult(qlonglong requestId)
|
||||
{
|
||||
m_requestId = requestId;
|
||||
m_seqNr = -1;
|
||||
m_authInfo = KIO::AuthInfo();
|
||||
return (exec() == 0);
|
||||
}
|
||||
|
||||
qlonglong KPasswdServerLoop::seqNr() const
|
||||
{
|
||||
return m_seqNr;
|
||||
}
|
||||
|
||||
const KIO::AuthInfo &KPasswdServerLoop::authInfo() const
|
||||
{
|
||||
return m_authInfo;
|
||||
}
|
||||
|
||||
void KPasswdServerLoop::slotQueryResult(qlonglong requestId, qlonglong seqNr, const KIO::AuthInfo &authInfo)
|
||||
{
|
||||
if (m_requestId == requestId) {
|
||||
m_seqNr = seqNr;
|
||||
m_authInfo = authInfo;
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
void KPasswdServerLoop::kdedServiceUnregistered()
|
||||
{
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
#include "moc_kpasswdserverloop_p.cpp"
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 2009 Michael Leupold <lemma@confuego.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KPASSWDSERVERLOOP_P_H
|
||||
#define KPASSWDSERVERLOOP_P_H
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <kio/authinfo.h>
|
||||
|
||||
// Wait for the result of an asynchronous D-Bus request to KPasswdServer.
|
||||
// Objects of this class are one-way ie. as soon as they have received
|
||||
// a result you can't call waitForResult() again.
|
||||
class KPasswdServerLoop : public QEventLoop
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
KPasswdServerLoop();
|
||||
~KPasswdServerLoop() override;
|
||||
bool waitForResult(qlonglong requestId);
|
||||
|
||||
qlonglong seqNr() const;
|
||||
const KIO::AuthInfo &authInfo() const;
|
||||
|
||||
public Q_SLOTS:
|
||||
void slotQueryResult(qlonglong requestId, qlonglong seqNr, const KIO::AuthInfo &authInfo);
|
||||
|
||||
private Q_SLOTS:
|
||||
void kdedServiceUnregistered();
|
||||
|
||||
private:
|
||||
qlonglong m_requestId;
|
||||
qlonglong m_seqNr;
|
||||
KIO::AuthInfo m_authInfo;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kprotocolinfo.h"
|
||||
#include "kprotocolinfo_p.h"
|
||||
#include "kprotocolinfofactory_p.h"
|
||||
|
||||
#include "kiocoredebug.h"
|
||||
|
||||
#include <KApplicationTrader>
|
||||
#include <KConfig>
|
||||
#include <KConfigGroup>
|
||||
#include <KJsonUtils>
|
||||
#include <KPluginMetaData>
|
||||
#include <KSharedConfig>
|
||||
#include <QUrl>
|
||||
|
||||
KProtocolInfoPrivate::KProtocolInfoPrivate(const QString &name, const QString &exec, const QJsonObject &json)
|
||||
: m_name(name)
|
||||
, m_exec(exec)
|
||||
{
|
||||
// source has fallback true if not set
|
||||
m_isSourceProtocol = json.value(QStringLiteral("source")).toBool(true);
|
||||
// true if not set for backwards compatibility
|
||||
m_supportsPermissions = json.value(QStringLiteral("permissions")).toBool(true);
|
||||
|
||||
// other bools are fine with default false by toBool
|
||||
m_isHelperProtocol = json.value(QStringLiteral("helper")).toBool();
|
||||
m_supportsReading = json.value(QStringLiteral("reading")).toBool();
|
||||
m_supportsWriting = json.value(QStringLiteral("writing")).toBool();
|
||||
m_supportsMakeDir = json.value(QStringLiteral("makedir")).toBool();
|
||||
m_supportsDeleting = json.value(QStringLiteral("deleting")).toBool();
|
||||
m_supportsLinking = json.value(QStringLiteral("linking")).toBool();
|
||||
m_supportsMoving = json.value(QStringLiteral("moving")).toBool();
|
||||
m_supportsOpening = json.value(QStringLiteral("opening")).toBool();
|
||||
m_supportsTruncating = json.value(QStringLiteral("truncating")).toBool();
|
||||
m_canCopyFromFile = json.value(QStringLiteral("copyFromFile")).toBool();
|
||||
m_canCopyToFile = json.value(QStringLiteral("copyToFile")).toBool();
|
||||
m_canRenameFromFile = json.value(QStringLiteral("renameFromFile")).toBool();
|
||||
m_canRenameToFile = json.value(QStringLiteral("renameToFile")).toBool();
|
||||
m_canDeleteRecursive = json.value(QStringLiteral("deleteRecursive")).toBool();
|
||||
|
||||
// default is "FromURL"
|
||||
const QString fnu = json.value(QStringLiteral("fileNameUsedForCopying")).toString();
|
||||
m_fileNameUsedForCopying = KProtocolInfo::FromUrl;
|
||||
if (fnu == QLatin1String("Name")) {
|
||||
m_fileNameUsedForCopying = KProtocolInfo::Name;
|
||||
} else if (fnu == QLatin1String("DisplayName")) {
|
||||
m_fileNameUsedForCopying = KProtocolInfo::DisplayName;
|
||||
}
|
||||
|
||||
m_listing = json.value(QStringLiteral("listing")).toVariant().toStringList();
|
||||
// Many .protocol files say "Listing=false" when they really mean "Listing=" (i.e. unsupported)
|
||||
if (m_listing.count() == 1 && m_listing.first() == QLatin1String("false")) {
|
||||
m_listing.clear();
|
||||
}
|
||||
m_supportsListing = (m_listing.count() > 0);
|
||||
|
||||
m_defaultMimetype = json.value(QStringLiteral("defaultMimetype")).toString();
|
||||
|
||||
// determineMimetypeFromExtension has fallback true if not set
|
||||
m_determineMimetypeFromExtension = json.value(QStringLiteral("determineMimetypeFromExtension")).toBool(true);
|
||||
|
||||
m_archiveMimeTypes = json.value(QStringLiteral("archiveMimetype")).toVariant().toStringList();
|
||||
|
||||
m_icon = json.value(QStringLiteral("Icon")).toString();
|
||||
|
||||
// config has fallback to name if not set
|
||||
m_config = json.value(QStringLiteral("config")).toString(m_name);
|
||||
|
||||
// max workers has fallback to 1 if not set
|
||||
m_maxWorkers = json.value(QStringLiteral("maxInstances")).toInt(1);
|
||||
|
||||
m_maxWorkersPerHost = json.value(QStringLiteral("maxInstancesPerHost")).toInt();
|
||||
|
||||
QString tmp = json.value(QStringLiteral("input")).toString();
|
||||
if (tmp == QLatin1String("filesystem")) {
|
||||
m_inputType = KProtocolInfo::T_FILESYSTEM;
|
||||
} else if (tmp == QLatin1String("stream")) {
|
||||
m_inputType = KProtocolInfo::T_STREAM;
|
||||
} else {
|
||||
m_inputType = KProtocolInfo::T_NONE;
|
||||
}
|
||||
|
||||
tmp = json.value(QStringLiteral("output")).toString();
|
||||
if (tmp == QLatin1String("filesystem")) {
|
||||
m_outputType = KProtocolInfo::T_FILESYSTEM;
|
||||
} else if (tmp == QLatin1String("stream")) {
|
||||
m_outputType = KProtocolInfo::T_STREAM;
|
||||
} else {
|
||||
m_outputType = KProtocolInfo::T_NONE;
|
||||
}
|
||||
|
||||
m_docPath = json.value(QStringLiteral("X-DocPath")).toString();
|
||||
if (m_docPath.isEmpty()) {
|
||||
m_docPath = json.value(QStringLiteral("DocPath")).toString();
|
||||
}
|
||||
|
||||
m_protClass = json.value(QStringLiteral("Class")).toString().toLower();
|
||||
if (!m_protClass.startsWith(QLatin1Char(':'))) {
|
||||
m_protClass.prepend(QLatin1Char(':'));
|
||||
}
|
||||
|
||||
// ExtraNames is a translated value, use the KCoreAddons helper to read it
|
||||
const QStringList extraNames = KJsonUtils::readTranslatedValue(json, QStringLiteral("ExtraNames")).toVariant().toStringList();
|
||||
const QStringList extraTypes = json.value(QStringLiteral("ExtraTypes")).toVariant().toStringList();
|
||||
if (extraNames.size() == extraTypes.size()) {
|
||||
auto func = [](const QString &name, const QString &type) {
|
||||
const int metaType = QMetaType::fromName(type.toLatin1().constData()).id();
|
||||
// currently QMetaType::Type and ExtraField::Type use the same subset of values, so we can just cast.
|
||||
return KProtocolInfo::ExtraField(name, static_cast<KProtocolInfo::ExtraField::Type>(metaType));
|
||||
};
|
||||
|
||||
std::transform(extraNames.cbegin(), extraNames.cend(), extraTypes.cbegin(), std::back_inserter(m_extraFields), func);
|
||||
} else {
|
||||
qCWarning(KIO_CORE) << "Malformed JSON protocol file for protocol:" << name
|
||||
<< ", number of the ExtraNames fields should match the number of ExtraTypes fields";
|
||||
}
|
||||
|
||||
// fallback based on class
|
||||
m_showPreviews = json.value(QStringLiteral("ShowPreviews")).toBool(m_protClass == QLatin1String(":local"));
|
||||
|
||||
m_capabilities = json.value(QStringLiteral("Capabilities")).toVariant().toStringList();
|
||||
|
||||
m_proxyProtocol = json.value(QStringLiteral("ProxiedBy")).toString();
|
||||
}
|
||||
|
||||
//
|
||||
// Static functions:
|
||||
//
|
||||
|
||||
QStringList KProtocolInfo::protocols()
|
||||
{
|
||||
return KProtocolInfoFactory::self()->protocols();
|
||||
}
|
||||
|
||||
bool KProtocolInfo::isFilterProtocol(const QString &_protocol)
|
||||
{
|
||||
// We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !prot->m_isSourceProtocol;
|
||||
}
|
||||
|
||||
QString KProtocolInfo::icon(const QString &_protocol)
|
||||
{
|
||||
// We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
if (auto service = KApplicationTrader::preferredService(QLatin1String("x-scheme-handler/") + _protocol)) {
|
||||
return service->icon();
|
||||
} else {
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
return prot->m_icon;
|
||||
}
|
||||
|
||||
QString KProtocolInfo::config(const QString &_protocol)
|
||||
{
|
||||
// We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return QStringLiteral("kio_%1rc").arg(prot->m_config);
|
||||
}
|
||||
|
||||
int KProtocolInfo::maxWorkers(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return prot->m_maxWorkers;
|
||||
}
|
||||
|
||||
int KProtocolInfo::maxWorkersPerHost(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return prot->m_maxWorkersPerHost;
|
||||
}
|
||||
|
||||
bool KProtocolInfo::determineMimetypeFromExtension(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return prot->m_determineMimetypeFromExtension;
|
||||
}
|
||||
|
||||
QString KProtocolInfo::exec(const QString &protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
|
||||
if (!prot) {
|
||||
return QString();
|
||||
}
|
||||
return prot->m_exec;
|
||||
}
|
||||
|
||||
KProtocolInfo::ExtraFieldList KProtocolInfo::extraFields(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(url.scheme());
|
||||
if (!prot) {
|
||||
return ExtraFieldList();
|
||||
}
|
||||
|
||||
return prot->m_extraFields;
|
||||
}
|
||||
|
||||
QString KProtocolInfo::defaultMimetype(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return prot->m_defaultMimetype;
|
||||
}
|
||||
|
||||
QString KProtocolInfo::docPath(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return prot->m_docPath;
|
||||
}
|
||||
|
||||
QString KProtocolInfo::protocolClass(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return prot->m_protClass;
|
||||
}
|
||||
|
||||
bool KProtocolInfo::showFilePreview(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
const bool defaultSetting = prot ? prot->m_showPreviews : false;
|
||||
|
||||
KConfigGroup group(KSharedConfig::openConfig(), QStringLiteral("PreviewSettings"));
|
||||
return group.readEntry(_protocol, defaultSetting);
|
||||
}
|
||||
|
||||
QStringList KProtocolInfo::capabilities(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
return prot->m_capabilities;
|
||||
}
|
||||
|
||||
QStringList KProtocolInfo::archiveMimetypes(const QString &protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
|
||||
if (!prot) {
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
return prot->m_archiveMimeTypes;
|
||||
}
|
||||
|
||||
#if KIOCORE_BUILD_DEPRECATED_SINCE(6, 4)
|
||||
QString KProtocolInfo::proxiedBy(const QString &_protocol)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(_protocol);
|
||||
if (!prot) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return prot->m_proxyProtocol;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool KProtocolInfo::isFilterProtocol(const QUrl &url)
|
||||
{
|
||||
return isFilterProtocol(url.scheme());
|
||||
}
|
||||
|
||||
bool KProtocolInfo::isHelperProtocol(const QUrl &url)
|
||||
{
|
||||
return isHelperProtocol(url.scheme());
|
||||
}
|
||||
|
||||
bool KProtocolInfo::isHelperProtocol(const QString &protocol)
|
||||
{
|
||||
// We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings.
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol);
|
||||
if (prot) {
|
||||
return prot->m_isHelperProtocol;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool KProtocolInfo::isKnownProtocol(const QUrl &url)
|
||||
{
|
||||
return isKnownProtocol(url.scheme());
|
||||
}
|
||||
|
||||
bool KProtocolInfo::isKnownProtocol(const QString &protocol, bool updateCacheIfNotfound)
|
||||
{
|
||||
// We call the findProtocol (const QString&) to bypass any proxy settings.
|
||||
KProtocolInfoPrivate *prot = KProtocolInfoFactory::self()->findProtocol(protocol, updateCacheIfNotfound);
|
||||
return prot;
|
||||
}
|
||||
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2001 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KPROTOCOLINFO_H
|
||||
#define KPROTOCOLINFO_H
|
||||
|
||||
#include "kiocore_export.h"
|
||||
#include <QMetaType>
|
||||
#include <QStringList>
|
||||
|
||||
/**
|
||||
* \class KProtocolInfo kprotocolinfo.h <KProtocolInfo>
|
||||
*
|
||||
* Information about I/O (Internet, etc.) protocols supported by KDE.
|
||||
|
||||
* KProtocolInfo is useful if you want to know which protocols
|
||||
* KDE supports. In addition you can find out lots of information
|
||||
* about a certain protocol. All of the functionality is provided by the static
|
||||
* methods.
|
||||
* The implementation scans the *.protocol files of all installed KIO workers to get
|
||||
* this information and stores the result into an internal cache.
|
||||
*
|
||||
* *.protocol files are installed in the "services" resource.
|
||||
*
|
||||
* The KProtocolInfo methods are reentrant (i.e. can be called from multiple threads simultaneously).
|
||||
*/
|
||||
class KIOCORE_EXPORT KProtocolInfo
|
||||
{
|
||||
public:
|
||||
//
|
||||
// Static functions:
|
||||
//
|
||||
|
||||
/**
|
||||
* Returns list of all known protocols.
|
||||
* @return a list of all known protocols
|
||||
*/
|
||||
static QStringList protocols();
|
||||
|
||||
/**
|
||||
* Returns whether a protocol is installed that is able to handle @p url.
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol is known
|
||||
* @see name()
|
||||
*/
|
||||
static bool isKnownProtocol(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Same as above except you can supply just the protocol instead of
|
||||
* the whole URL.
|
||||
*/
|
||||
static bool isKnownProtocol(const QString &protocol, bool updateCacheIfNotfound = true);
|
||||
|
||||
/**
|
||||
* Returns the library / executable to open for the protocol @p protocol
|
||||
* Example : "kio_ftp", meaning either the executable "kio_ftp" or
|
||||
* the library "kio_ftp.la" (recommended), whichever is available.
|
||||
*
|
||||
* This corresponds to the "exec=" field in the protocol description file.
|
||||
* @param protocol the protocol to check
|
||||
* @return the executable of library to open, or QString() for
|
||||
* unsupported protocols
|
||||
* @see KUrl::protocol()
|
||||
*/
|
||||
static QString exec(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Describes the type of a protocol.
|
||||
* For instance ftp:// appears as a filesystem with folders and files,
|
||||
* while bzip2:// appears as a single file (a stream of data),
|
||||
* and telnet:// doesn't output anything.
|
||||
* @see outputType
|
||||
*/
|
||||
enum Type {
|
||||
T_STREAM, ///< stream of data (e.g.\ single file)
|
||||
T_FILESYSTEM, ///< structured directory
|
||||
T_NONE, ///< no information about the type available
|
||||
T_ERROR, ///< used to signal an error
|
||||
};
|
||||
|
||||
/**
|
||||
* Definition of an extra field in the UDS entries, returned by a listDir operation.
|
||||
*
|
||||
* The name is the name of the column, translated.
|
||||
*
|
||||
* The type name comes from QMetaType::name()
|
||||
* Currently supported types: "QString", "QDateTime" (ISO-8601 format)
|
||||
*/
|
||||
struct ExtraField {
|
||||
enum Type {
|
||||
String = QMetaType::QString,
|
||||
DateTime = QMetaType::QDateTime,
|
||||
Invalid = QMetaType::UnknownType
|
||||
};
|
||||
|
||||
ExtraField()
|
||||
: type(Invalid)
|
||||
{
|
||||
}
|
||||
ExtraField(const QString &_name, Type _type)
|
||||
: name(_name)
|
||||
, type(_type)
|
||||
{
|
||||
}
|
||||
QString name;
|
||||
Type type;
|
||||
};
|
||||
typedef QList<ExtraField> ExtraFieldList;
|
||||
/**
|
||||
* Definition of extra fields in the UDS entries, returned by a listDir operation.
|
||||
*
|
||||
* This corresponds to the "ExtraNames=" and "ExtraTypes=" fields in the protocol description file.
|
||||
* Those two lists should be separated with ',' in the protocol description file.
|
||||
* See ExtraField for details about names and types
|
||||
*/
|
||||
static ExtraFieldList extraFields(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can act as a helper protocol.
|
||||
* A helper protocol invokes an external application and does not return
|
||||
* a file or stream.
|
||||
*
|
||||
* This corresponds to the "helper=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol is a helper protocol (e.g. vnc), false
|
||||
* if not (e.g. http)
|
||||
*/
|
||||
static bool isHelperProtocol(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Same as above except you can supply just the protocol instead of
|
||||
* the whole URL.
|
||||
*/
|
||||
static bool isHelperProtocol(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can act as a filter protocol.
|
||||
*
|
||||
* A filter protocol can operate on data that is passed to it
|
||||
* but does not retrieve/store data itself, like gzip.
|
||||
* A filter protocol is the opposite of a source protocol.
|
||||
*
|
||||
* The "source=" field in the protocol description file determines
|
||||
* whether a protocol is a source protocol or a filter protocol.
|
||||
* Valid values for this field are "true" (default) for source protocol or
|
||||
* "false" for filter protocol.
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol is a filter (e.g. gzip), false if the
|
||||
* protocol is a helper or source
|
||||
*/
|
||||
static bool isFilterProtocol(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Same as above except you can supply just the protocol instead of
|
||||
* the whole URL.
|
||||
*/
|
||||
static bool isFilterProtocol(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the name of the icon, associated with the specified protocol.
|
||||
*
|
||||
* This corresponds to the "Icon=" field in the protocol description file.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the icon of the protocol, or an empty string if unknown
|
||||
*/
|
||||
static QString icon(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the name of the config file associated with the
|
||||
* specified protocol. This is useful if two similar protocols
|
||||
* need to share a single config file, e.g. http and https.
|
||||
*
|
||||
* This corresponds to the "config=" field in the protocol description file.
|
||||
* The default is the protocol name, see name()
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the config file, or an empty string if unknown
|
||||
*/
|
||||
static QString config(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the soft limit on the number of KIO workers for this protocol.
|
||||
* This limits the number of workers used for a single operation, note
|
||||
* that multiple operations may result in a number of instances that
|
||||
* exceeds this soft limit.
|
||||
*
|
||||
* This corresponds to the "maxInstances=" field in the protocol's worker metadata.
|
||||
* The default is 1.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the maximum number of workers, or 1 if unknown
|
||||
*
|
||||
* @since 5.101
|
||||
*/
|
||||
static int maxWorkers(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the limit on the number of KIO workers for this protocol per host.
|
||||
*
|
||||
* This corresponds to the "maxInstancesPerHost=" field in the protocol's worker metadata.
|
||||
* The default is 0 which means there is no per host limit.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the maximum number of workers, or 1 if unknown
|
||||
*
|
||||
* @since 5.101
|
||||
*/
|
||||
static int maxWorkersPerHost(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns whether MIME types can be determined based on extension for this
|
||||
* protocol. For some protocols, e.g. http, the filename extension in the URL
|
||||
* can not be trusted to truly reflect the file type.
|
||||
*
|
||||
* This corresponds to the "determineMimetypeFromExtension=" field in the protocol description file.
|
||||
* Valid values for this field are "true" (default) or "false".
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return true if the MIME types can be determined by extension
|
||||
*/
|
||||
static bool determineMimetypeFromExtension(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the default MIME type for the specified protocol, if one exists.
|
||||
*
|
||||
* This corresponds to the "defaultMimetype=" field in the protocol description file.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the default MIME type of the protocol, or an empty string if none set or protocol unknown
|
||||
* @since 5.60
|
||||
*/
|
||||
static QString defaultMimetype(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the documentation path for the specified protocol.
|
||||
*
|
||||
* This corresponds to the "X-DocPath=" or "DocPath=" field in the protocol description file.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the docpath of the protocol, or an empty string if unknown
|
||||
*/
|
||||
static QString docPath(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the protocol class for the specified protocol.
|
||||
*
|
||||
* This corresponds to the "Class=" field in the protocol description file.
|
||||
*
|
||||
* The following classes are defined:
|
||||
* @li ":internet" for common internet protocols
|
||||
* @li ":local" for protocols that access local resources
|
||||
*
|
||||
* Protocol classes always start with a ':' so that they can not be confused with
|
||||
* the protocols themselves.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the class of the protocol, or an empty string if unknown
|
||||
*/
|
||||
static QString protocolClass(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns whether file previews should be shown for the specified protocol.
|
||||
*
|
||||
* This corresponds to the "ShowPreviews=" field in the protocol description file.
|
||||
*
|
||||
* By default previews are shown if protocolClass is :local.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return true if previews should be shown by default, false otherwise
|
||||
*/
|
||||
static bool showFilePreview(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the list of capabilities provided by the KIO worker implementing
|
||||
* this protocol.
|
||||
*
|
||||
* This corresponds to the "Capabilities=" field in the protocol description file.
|
||||
*
|
||||
* The capability names are not defined globally, they are up to each
|
||||
* worker implementation. For example when adding support for a new
|
||||
* special command for mounting, one would add the string "Mount" to the
|
||||
* capabilities list, and applications could check for that string
|
||||
* before sending a special() command that would otherwise do nothing
|
||||
* on older KIO worker implementations.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the list of capabilities.
|
||||
*/
|
||||
static QStringList capabilities(const QString &protocol);
|
||||
|
||||
/**
|
||||
* Returns the list of archive MIME types handled by the KIO worker implementing
|
||||
* this protocol.
|
||||
*
|
||||
* This corresponds to the "archiveMimetype=" field in the protocol description file.
|
||||
*
|
||||
* @param protocol the protocol to check
|
||||
* @return the list of archive MIME types (e.g. application/x-zip) handled.
|
||||
* @since 5.23
|
||||
*/
|
||||
static QStringList archiveMimetypes(const QString &protocol);
|
||||
|
||||
#if KIOCORE_ENABLE_DEPRECATED_SINCE(6, 4)
|
||||
/**
|
||||
* Returns the name of the protocol through which the request
|
||||
* will be routed if proxy support is enabled.
|
||||
*
|
||||
* A good example of this is the ftp protocol for which proxy
|
||||
* support is commonly handled by the http protocol.
|
||||
*
|
||||
* This corresponds to the "ProxiedBy=" in the protocol description file.
|
||||
*
|
||||
* @deprecated since 6.4, not used.
|
||||
*/
|
||||
KIOCORE_DEPRECATED_VERSION(6, 4, "Not used")
|
||||
static QString proxiedBy(const QString &protocol);
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
Name,
|
||||
FromUrl,
|
||||
DisplayName
|
||||
} FileNameUsedForCopying;
|
||||
|
||||
private:
|
||||
Q_DISABLE_COPY(KProtocolInfo)
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2000-2001 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KPROTOCOLINFOPRIVATE_H
|
||||
#define KPROTOCOLINFOPRIVATE_H
|
||||
|
||||
#include "kprotocolinfo.h"
|
||||
|
||||
#include <QJsonObject>
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class KProtocolInfoPrivate
|
||||
{
|
||||
public:
|
||||
KProtocolInfoPrivate(const QString &name, const QString &exec, const QJsonObject &json);
|
||||
|
||||
QString m_name;
|
||||
QString m_exec;
|
||||
KProtocolInfo::Type m_inputType;
|
||||
KProtocolInfo::Type m_outputType;
|
||||
QStringList m_listing;
|
||||
bool m_isSourceProtocol : 1;
|
||||
bool m_isHelperProtocol : 1;
|
||||
bool m_supportsListing : 1;
|
||||
bool m_supportsReading : 1;
|
||||
bool m_supportsWriting : 1;
|
||||
bool m_supportsMakeDir : 1;
|
||||
bool m_supportsDeleting : 1;
|
||||
bool m_supportsLinking : 1;
|
||||
bool m_supportsMoving : 1;
|
||||
bool m_supportsOpening : 1;
|
||||
bool m_supportsTruncating : 1;
|
||||
bool m_determineMimetypeFromExtension : 1;
|
||||
bool m_canCopyFromFile : 1;
|
||||
bool m_canCopyToFile : 1;
|
||||
bool m_showPreviews : 1;
|
||||
bool m_canRenameFromFile : 1;
|
||||
bool m_canRenameToFile : 1;
|
||||
bool m_canDeleteRecursive : 1;
|
||||
bool m_supportsPermissions : 1;
|
||||
QString m_defaultMimetype;
|
||||
QString m_icon;
|
||||
QString m_config;
|
||||
int m_maxWorkers;
|
||||
|
||||
QString m_docPath;
|
||||
QString m_protClass;
|
||||
QStringList m_archiveMimeTypes;
|
||||
KProtocolInfo::ExtraFieldList m_extraFields;
|
||||
KProtocolInfo::FileNameUsedForCopying m_fileNameUsedForCopying;
|
||||
QStringList m_capabilities;
|
||||
QString m_proxyProtocol;
|
||||
int m_maxWorkersPerHost;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kprotocolinfo_p.h"
|
||||
#include "kprotocolinfofactory_p.h"
|
||||
|
||||
#include <KPluginMetaData>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDirIterator>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "kiocoredebug.h"
|
||||
|
||||
Q_GLOBAL_STATIC(KProtocolInfoFactory, kProtocolInfoFactoryInstance)
|
||||
|
||||
KProtocolInfoFactory *KProtocolInfoFactory::self()
|
||||
{
|
||||
return kProtocolInfoFactoryInstance();
|
||||
}
|
||||
|
||||
KProtocolInfoFactory::KProtocolInfoFactory()
|
||||
: m_cacheDirty(true)
|
||||
{
|
||||
}
|
||||
|
||||
KProtocolInfoFactory::~KProtocolInfoFactory()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
qDeleteAll(m_cache);
|
||||
m_cache.clear();
|
||||
}
|
||||
|
||||
QStringList KProtocolInfoFactory::protocols()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
// fill cache, if not already done and use it
|
||||
fillCache();
|
||||
return m_cache.keys();
|
||||
}
|
||||
|
||||
QList<KProtocolInfoPrivate *> KProtocolInfoFactory::allProtocols()
|
||||
{
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
// fill cache, if not already done and use it
|
||||
fillCache();
|
||||
return m_cache.values();
|
||||
}
|
||||
|
||||
KProtocolInfoPrivate *KProtocolInfoFactory::findProtocol(const QString &protocol, bool updateCacheIfNotfound)
|
||||
{
|
||||
Q_ASSERT(!protocol.isEmpty());
|
||||
Q_ASSERT(!protocol.contains(QLatin1Char(':')));
|
||||
|
||||
QMutexLocker locker(&m_mutex);
|
||||
|
||||
const bool filled = fillCache();
|
||||
|
||||
KProtocolInfoPrivate *info = m_cache.value(protocol);
|
||||
if (!info && !filled && updateCacheIfNotfound) {
|
||||
// Unknown protocol! Maybe it just got installed and our cache is out of date?
|
||||
qCDebug(KIO_CORE) << "Refilling KProtocolInfoFactory cache in the hope to find" << protocol;
|
||||
m_cacheDirty = true;
|
||||
fillCache();
|
||||
info = m_cache.value(protocol);
|
||||
}
|
||||
return info;
|
||||
}
|
||||
|
||||
bool KProtocolInfoFactory::fillCache()
|
||||
{
|
||||
// mutex MUST be locked from the outside!
|
||||
Q_ASSERT(!m_mutex.tryLock());
|
||||
|
||||
// no work if filled
|
||||
if (!m_cacheDirty) {
|
||||
return false;
|
||||
}
|
||||
|
||||
qDeleteAll(m_cache);
|
||||
m_cache.clear();
|
||||
|
||||
// first: search for meta data protocol info, that might be bundled with applications
|
||||
// we search in all library paths inside kf5/kio
|
||||
const QList<KPluginMetaData> plugins = KPluginMetaData::findPlugins(QStringLiteral("kf6/kio"));
|
||||
for (const KPluginMetaData &md : plugins) {
|
||||
// get worker name & protocols it supports, if any
|
||||
const QString workerPath = md.fileName();
|
||||
const QJsonObject protocols(md.rawData().value(QStringLiteral("KDE-KIO-Protocols")).toObject());
|
||||
qCDebug(KIO_CORE) << workerPath << "supports protocols" << protocols.keys();
|
||||
|
||||
// add all protocols, does nothing if object invalid
|
||||
for (auto it = protocols.begin(); it != protocols.end(); ++it) {
|
||||
// skip empty objects
|
||||
const QJsonObject protocol(it.value().toObject());
|
||||
if (protocol.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// add to cache, skip double entries
|
||||
if (!m_cache.contains(it.key())) {
|
||||
m_cache.insert(it.key(), new KProtocolInfoPrivate(it.key(), workerPath, protocol));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// all done, don't do it again
|
||||
m_cacheDirty = false;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2000, 2003 Waldo Bastian <bastian@kde.org>
|
||||
SPDX-FileCopyrightText: 2012 David Faure <faure@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
#ifndef kprotocolinfofactory_h
|
||||
#define kprotocolinfofactory_h
|
||||
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
#include <QStringList>
|
||||
|
||||
class KProtocolInfoPrivate;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* KProtocolInfoFactory is a factory for getting
|
||||
* KProtocolInfo. The factory is a singleton
|
||||
* (only one instance can exist).
|
||||
*/
|
||||
class KProtocolInfoFactory
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @return the instance of KProtocolInfoFactory (singleton).
|
||||
*/
|
||||
static KProtocolInfoFactory *self();
|
||||
|
||||
KProtocolInfoFactory();
|
||||
~KProtocolInfoFactory();
|
||||
|
||||
/**
|
||||
* Returns protocol info for @p protocol.
|
||||
*
|
||||
* Does not take proxy settings into account.
|
||||
* @param protocol the protocol to search for
|
||||
* @param updateCacheIfNotfound Flag for revalidating the cache. This will cause all plugins to be reloaded
|
||||
* @return the pointer to the KProtocolInfo, or @c nullptr if not found
|
||||
*/
|
||||
KProtocolInfoPrivate *findProtocol(const QString &protocol, bool updateCacheIfNotfound = true);
|
||||
|
||||
/**
|
||||
* Loads all protocols. Slow, obviously, but fills the cache once and for all.
|
||||
*/
|
||||
QList<KProtocolInfoPrivate *> allProtocols();
|
||||
|
||||
/**
|
||||
* Returns list of all known protocols.
|
||||
* @return a list of all protocols
|
||||
*/
|
||||
QStringList protocols();
|
||||
|
||||
private:
|
||||
/**
|
||||
* Fill the internal cache.
|
||||
*/
|
||||
bool fillCache();
|
||||
|
||||
typedef QHash<QString, KProtocolInfoPrivate *> ProtocolCache;
|
||||
ProtocolCache m_cache;
|
||||
bool m_cacheDirty;
|
||||
mutable QMutex m_mutex; // protects m_cache and m_allProtocolsLoaded
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,392 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Waldo Bastain <bastain@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#include "kprotocolmanager.h"
|
||||
#include "kprotocolinfo_p.h"
|
||||
#include "kprotocolmanager_p.h"
|
||||
|
||||
#include <config-kiocore.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QUrl>
|
||||
|
||||
#include <KConfigGroup>
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include <kprotocolinfofactory_p.h>
|
||||
|
||||
#include "ioworker_defaults.h"
|
||||
#include "workerconfig.h"
|
||||
|
||||
Q_GLOBAL_STATIC(KProtocolManagerPrivate, kProtocolManagerPrivate)
|
||||
|
||||
static void syncOnExit()
|
||||
{
|
||||
if (kProtocolManagerPrivate.exists()) {
|
||||
kProtocolManagerPrivate()->sync();
|
||||
}
|
||||
}
|
||||
|
||||
KProtocolManagerPrivate::KProtocolManagerPrivate()
|
||||
{
|
||||
// post routine since KConfig::sync() breaks if called too late
|
||||
qAddPostRoutine(syncOnExit);
|
||||
}
|
||||
|
||||
KProtocolManagerPrivate::~KProtocolManagerPrivate()
|
||||
{
|
||||
}
|
||||
|
||||
void KProtocolManagerPrivate::sync()
|
||||
{
|
||||
QMutexLocker lock(&mutex);
|
||||
if (configPtr) {
|
||||
configPtr->sync();
|
||||
}
|
||||
}
|
||||
|
||||
void KProtocolManager::reparseConfiguration()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
if (d->configPtr) {
|
||||
d->configPtr->reparseConfiguration();
|
||||
}
|
||||
lock.unlock();
|
||||
|
||||
// Force the slave config to re-read its config...
|
||||
KIO::WorkerConfig::self()->reset();
|
||||
}
|
||||
|
||||
static KSharedConfig::Ptr config()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
Q_ASSERT(!d->mutex.tryLock()); // the caller must have locked the mutex
|
||||
if (!d->configPtr) {
|
||||
d->configPtr = KSharedConfig::openConfig(QStringLiteral("kioslaverc"), KConfig::NoGlobals);
|
||||
}
|
||||
return d->configPtr;
|
||||
}
|
||||
|
||||
QMap<QString, QString> KProtocolManager::entryMap(const QString &group)
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
return config()->entryMap(group);
|
||||
}
|
||||
|
||||
/*=============================== TIMEOUT SETTINGS ==========================*/
|
||||
|
||||
int KProtocolManager::readTimeout()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
KConfigGroup cg(config(), QString());
|
||||
int val = cg.readEntry("ReadTimeout", DEFAULT_READ_TIMEOUT);
|
||||
return qMax(MIN_TIMEOUT_VALUE, val);
|
||||
}
|
||||
|
||||
int KProtocolManager::connectTimeout()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
KConfigGroup cg(config(), QString());
|
||||
int val = cg.readEntry("ConnectTimeout", DEFAULT_CONNECT_TIMEOUT);
|
||||
return qMax(MIN_TIMEOUT_VALUE, val);
|
||||
}
|
||||
|
||||
int KProtocolManager::proxyConnectTimeout()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
KConfigGroup cg(config(), QString());
|
||||
int val = cg.readEntry("ProxyConnectTimeout", DEFAULT_PROXY_CONNECT_TIMEOUT);
|
||||
return qMax(MIN_TIMEOUT_VALUE, val);
|
||||
}
|
||||
|
||||
int KProtocolManager::responseTimeout()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
KConfigGroup cg(config(), QString());
|
||||
int val = cg.readEntry("ResponseTimeout", DEFAULT_RESPONSE_TIMEOUT);
|
||||
return qMax(MIN_TIMEOUT_VALUE, val);
|
||||
}
|
||||
|
||||
/*==================================== OTHERS ===============================*/
|
||||
|
||||
bool KProtocolManager::markPartial()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
return config()->group(QString()).readEntry("MarkPartial", true);
|
||||
}
|
||||
|
||||
int KProtocolManager::minimumKeepSize()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
return config()->group(QString()).readEntry("MinimumKeepSize",
|
||||
DEFAULT_MINIMUM_KEEP_SIZE); // 5000 byte
|
||||
}
|
||||
|
||||
bool KProtocolManager::autoResume()
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
return config()->group(QString()).readEntry("AutoResume", false);
|
||||
}
|
||||
|
||||
/* =========================== PROTOCOL CAPABILITIES ============== */
|
||||
|
||||
static KProtocolInfoPrivate *findProtocol(const QUrl &url)
|
||||
{
|
||||
if (!url.isValid()) {
|
||||
return nullptr;
|
||||
}
|
||||
QString protocol = url.scheme();
|
||||
return KProtocolInfoFactory::self()->findProtocol(protocol);
|
||||
}
|
||||
|
||||
KProtocolInfo::Type KProtocolManager::inputType(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return KProtocolInfo::T_NONE;
|
||||
}
|
||||
|
||||
return prot->m_inputType;
|
||||
}
|
||||
|
||||
KProtocolInfo::Type KProtocolManager::outputType(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return KProtocolInfo::T_NONE;
|
||||
}
|
||||
|
||||
return prot->m_outputType;
|
||||
}
|
||||
|
||||
bool KProtocolManager::isSourceProtocol(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_isSourceProtocol;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsListing(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsListing;
|
||||
}
|
||||
|
||||
QStringList KProtocolManager::listing(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return QStringList();
|
||||
}
|
||||
|
||||
return prot->m_listing;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsReading(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsReading;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsWriting(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsWriting;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsMakeDir(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsMakeDir;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsDeleting(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsDeleting;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsLinking(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsLinking;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsMoving(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsMoving;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsOpening(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsOpening;
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsTruncating(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_supportsTruncating;
|
||||
}
|
||||
|
||||
bool KProtocolManager::canCopyFromFile(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_canCopyFromFile;
|
||||
}
|
||||
|
||||
bool KProtocolManager::canCopyToFile(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_canCopyToFile;
|
||||
}
|
||||
|
||||
bool KProtocolManager::canRenameFromFile(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_canRenameFromFile;
|
||||
}
|
||||
|
||||
bool KProtocolManager::canRenameToFile(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_canRenameToFile;
|
||||
}
|
||||
|
||||
bool KProtocolManager::canDeleteRecursive(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return prot->m_canDeleteRecursive;
|
||||
}
|
||||
|
||||
KProtocolInfo::FileNameUsedForCopying KProtocolManager::fileNameUsedForCopying(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return KProtocolInfo::FromUrl;
|
||||
}
|
||||
|
||||
return prot->m_fileNameUsedForCopying;
|
||||
}
|
||||
|
||||
QString KProtocolManager::defaultMimetype(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
return prot->m_defaultMimetype;
|
||||
}
|
||||
|
||||
QString KProtocolManager::protocolForArchiveMimetype(const QString &mimeType)
|
||||
{
|
||||
KProtocolManagerPrivate *d = kProtocolManagerPrivate();
|
||||
QMutexLocker lock(&d->mutex);
|
||||
if (d->protocolForArchiveMimetypes.isEmpty()) {
|
||||
const QList<KProtocolInfoPrivate *> allProtocols = KProtocolInfoFactory::self()->allProtocols();
|
||||
for (KProtocolInfoPrivate *allProtocol : allProtocols) {
|
||||
const QStringList archiveMimetypes = allProtocol->m_archiveMimeTypes;
|
||||
for (const QString &mime : archiveMimetypes) {
|
||||
d->protocolForArchiveMimetypes.insert(mime, allProtocol->m_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
return d->protocolForArchiveMimetypes.value(mimeType);
|
||||
}
|
||||
|
||||
QString KProtocolManager::charsetFor(const QUrl &url)
|
||||
{
|
||||
return KIO::WorkerConfig::self()->configData(url.scheme(), url.host(), QStringLiteral("Charset"));
|
||||
}
|
||||
|
||||
bool KProtocolManager::supportsPermissions(const QUrl &url)
|
||||
{
|
||||
KProtocolInfoPrivate *prot = findProtocol(url);
|
||||
if (!prot) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return prot->m_supportsPermissions;
|
||||
}
|
||||
|
||||
#undef PRIVATE_DATA
|
||||
|
||||
#include "moc_kprotocolmanager_p.cpp"
|
||||
@@ -0,0 +1,423 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Waldo Bastain <bastain@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KPROTOCOLMANAGER_H
|
||||
#define KPROTOCOLMANAGER_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
#include "kio/global.h" // KIO::CacheControl
|
||||
#include "kiocore_export.h"
|
||||
#include "kprotocolinfo.h"
|
||||
|
||||
class KSharedConfig;
|
||||
template<class T>
|
||||
class QExplicitlySharedDataPointer;
|
||||
typedef QExplicitlySharedDataPointer<KSharedConfig> KSharedConfigPtr;
|
||||
namespace KIO
|
||||
{
|
||||
class WorkerConfigPrivate;
|
||||
} // namespace KIO
|
||||
|
||||
/**
|
||||
* @class KProtocolManager kprotocolmanager.h <KProtocolManager>
|
||||
*
|
||||
* Provides information about I/O (Internet, etc.) settings chosen/set
|
||||
* by the end user.
|
||||
*
|
||||
* KProtocolManager has a heap of static functions that allows only read
|
||||
* access to KDE's IO related settings. These include proxy, cache, file
|
||||
* transfer resumption, timeout and user-agent related settings.
|
||||
*
|
||||
* The information provided by this class is generic enough to be applicable
|
||||
* to any application that makes use of KDE's IO sub-system. Note that this
|
||||
* mean the proxy, timeout etc. settings are saved in a separate user-specific
|
||||
* config file and not in the config file of the application.
|
||||
*
|
||||
* Original author:
|
||||
* @author Torben Weis <weis@kde.org>
|
||||
*
|
||||
* Revised by:
|
||||
* @author Waldo Bastain <bastain@kde.org>
|
||||
* @author Dawit Alemayehu <adawit@kde.org>
|
||||
* @see KPAC
|
||||
*/
|
||||
class KIOCORE_EXPORT KProtocolManager
|
||||
{
|
||||
public:
|
||||
/*=========================== TIMEOUT CONFIG ================================*/
|
||||
|
||||
/**
|
||||
* Returns the preferred timeout value for reading from
|
||||
* remote connections in seconds.
|
||||
*
|
||||
* @return timeout value for remote connection in secs.
|
||||
*/
|
||||
static int readTimeout();
|
||||
|
||||
/**
|
||||
* Returns the preferred timeout value for remote connections
|
||||
* in seconds.
|
||||
*
|
||||
* @return timeout value for remote connection in secs.
|
||||
*/
|
||||
static int connectTimeout();
|
||||
|
||||
/**
|
||||
* Returns the preferred timeout value for proxy connections
|
||||
* in seconds.
|
||||
*
|
||||
* @return timeout value for proxy connection in secs.
|
||||
*/
|
||||
static int proxyConnectTimeout();
|
||||
|
||||
/**
|
||||
* Returns the preferred response timeout value for
|
||||
* remote connecting in seconds.
|
||||
*
|
||||
* @return timeout value for remote connection in seconds.
|
||||
*/
|
||||
static int responseTimeout();
|
||||
|
||||
/*============================ DOWNLOAD CONFIG ==============================*/
|
||||
|
||||
/**
|
||||
* Returns true if partial downloads should be
|
||||
* automatically resumed.
|
||||
* @return true to resume partial downloads
|
||||
*/
|
||||
static bool autoResume();
|
||||
|
||||
/**
|
||||
* Returns true if partial downloads should be marked
|
||||
* with a ".part" extension.
|
||||
* @return true if partial downloads should get an ".part" extension
|
||||
*/
|
||||
static bool markPartial();
|
||||
|
||||
/**
|
||||
* Returns the minimum file size for keeping aborted
|
||||
* downloads.
|
||||
*
|
||||
* Any data downloaded that does not meet this minimum
|
||||
* requirement will simply be discarded. The default size
|
||||
* is 5 KB.
|
||||
*
|
||||
* @return the minimum keep size for aborted downloads in bytes
|
||||
*/
|
||||
static int minimumKeepSize();
|
||||
|
||||
/*===================== PROTOCOL CAPABILITIES ===============================*/
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can list files/objects.
|
||||
* If a protocol supports listing it can be browsed in e.g. file-dialogs
|
||||
* and konqueror.
|
||||
*
|
||||
* Whether a protocol supports listing is determined by the "listing="
|
||||
* field in the protocol description file.
|
||||
* If the protocol support listing it should list the fields it provides in
|
||||
* this field. If the protocol does not support listing this field should
|
||||
* remain empty (default.)
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol support listing
|
||||
* @see listing()
|
||||
*/
|
||||
static bool supportsListing(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can retrieve data from URLs.
|
||||
*
|
||||
* This corresponds to the "reading=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if it is possible to read from the URL
|
||||
*/
|
||||
static bool supportsReading(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can store data to URLs.
|
||||
*
|
||||
* This corresponds to the "writing=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol supports writing
|
||||
*/
|
||||
static bool supportsWriting(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can create directories/folders.
|
||||
*
|
||||
* This corresponds to the "makedir=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol can create directories
|
||||
*/
|
||||
static bool supportsMakeDir(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can delete files/objects.
|
||||
*
|
||||
* This corresponds to the "deleting=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol supports deleting
|
||||
*/
|
||||
static bool supportsDeleting(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can create links between files/objects.
|
||||
*
|
||||
* This corresponds to the "linking=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol supports linking
|
||||
*/
|
||||
static bool supportsLinking(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can move files/objects between different
|
||||
* locations.
|
||||
*
|
||||
* This corresponds to the "moving=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol supports moving
|
||||
*/
|
||||
static bool supportsMoving(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can be opened using KIO::open(const QUrl&).
|
||||
*
|
||||
* This corresponds to the "opening=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol supports opening
|
||||
*/
|
||||
static bool supportsOpening(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can be truncated with FileJob::truncate(KIO::filesize_t length).
|
||||
*
|
||||
* This corresponds to the "truncating=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol supports truncating
|
||||
* @since 5.66
|
||||
*/
|
||||
static bool supportsTruncating(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can copy files/objects directly from the
|
||||
* filesystem itself. If not, the application will read files from the
|
||||
* filesystem using the file-protocol and pass the data on to the destination
|
||||
* protocol.
|
||||
*
|
||||
* This corresponds to the "copyFromFile=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol can copy files from the local file system
|
||||
*/
|
||||
static bool canCopyFromFile(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can copy files/objects directly to the
|
||||
* filesystem itself. If not, the application will receive the data from
|
||||
* the source protocol and store it in the filesystem using the
|
||||
* file-protocol.
|
||||
*
|
||||
* This corresponds to the "copyToFile=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol can copy files to the local file system
|
||||
*/
|
||||
static bool canCopyToFile(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can rename (i.e. move fast) files/objects
|
||||
* directly from the filesystem itself. If not, the application will read
|
||||
* files from the filesystem using the file-protocol and pass the data on
|
||||
* to the destination protocol.
|
||||
*
|
||||
* This corresponds to the "renameFromFile=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol can rename/move files from the local file system
|
||||
*/
|
||||
static bool canRenameFromFile(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can rename (i.e. move fast) files/objects
|
||||
* directly to the filesystem itself. If not, the application will receive
|
||||
* the data from the source protocol and store it in the filesystem using the
|
||||
* file-protocol.
|
||||
*
|
||||
* This corresponds to the "renameToFile=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol can rename files to the local file system
|
||||
*/
|
||||
static bool canRenameToFile(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can recursively delete directories by itself.
|
||||
* If not (the usual case) then KIO will list the directory and delete files
|
||||
* and empty directories one by one.
|
||||
*
|
||||
* This corresponds to the "deleteRecursive=" field in the protocol description file.
|
||||
* Valid values for this field are "true" or "false" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return true if the protocol can delete non-empty directories by itself.
|
||||
*/
|
||||
static bool canDeleteRecursive(const QUrl &url);
|
||||
|
||||
/**
|
||||
* This setting defines the strategy to use for generating a filename, when
|
||||
* copying a file or directory to another directory. By default the destination
|
||||
* filename is made out of the filename in the source URL. However if the
|
||||
* KIO worker displays names that are different from the filename of the URL
|
||||
* (e.g. kio_fonts shows Arial for arial.ttf, or kio_trash shows foo.txt and
|
||||
* uses some internal URL), using Name means that the display name (UDS_NAME)
|
||||
* will be used to as the filename in the destination directory.
|
||||
*
|
||||
* This corresponds to the "fileNameUsedForCopying=" field in the protocol description file.
|
||||
* Valid values for this field are "Name" or "FromURL" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return how to generate the filename in the destination directory when copying/moving
|
||||
*/
|
||||
static KProtocolInfo::FileNameUsedForCopying fileNameUsedForCopying(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns default MIME type for this URL based on the protocol.
|
||||
*
|
||||
* This corresponds to the "defaultMimetype=" field in the protocol description file.
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return the default MIME type of the protocol, or an empty string if unknown
|
||||
*/
|
||||
static QString defaultMimetype(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol should be treated as a filesystem
|
||||
* or as a stream when reading from it.
|
||||
*
|
||||
* This corresponds to the "input=" field in the protocol description file.
|
||||
* Valid values for this field are "filesystem", "stream" or "none" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return the input type of the given @p url
|
||||
*/
|
||||
static KProtocolInfo::Type inputType(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol should be treated as a filesystem
|
||||
* or as a stream when writing to it.
|
||||
*
|
||||
* This corresponds to the "output=" field in the protocol description file.
|
||||
* Valid values for this field are "filesystem", "stream" or "none" (default).
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return the output type of the given @p url
|
||||
*/
|
||||
static KProtocolInfo::Type outputType(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns the list of fields this protocol returns when listing
|
||||
* The current possibilities are
|
||||
* Name, Type, Size, Date, AccessDate, Access, Owner, Group, Link, URL, MimeType
|
||||
* as well as Extra1, Extra2 etc. for extra fields (see extraFields).
|
||||
*
|
||||
* This corresponds to the "listing=" field in the protocol description file.
|
||||
* The supported fields should be separated with ',' in the protocol description file.
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return a list of field names
|
||||
*/
|
||||
static QStringList listing(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns whether the protocol can act as a source protocol.
|
||||
*
|
||||
* A source protocol retrieves data from or stores data to the
|
||||
* location specified by a URL.
|
||||
* A source protocol is the opposite of a filter protocol.
|
||||
*
|
||||
* The "source=" field in the protocol description file determines
|
||||
* whether a protocol is a source protocol or a filter protocol.
|
||||
* @param url the url to check
|
||||
* @return true if the protocol is a source of data (e.g. http), false if the
|
||||
* protocol is a filter (e.g. gzip)
|
||||
*/
|
||||
static bool isSourceProtocol(const QUrl &url);
|
||||
|
||||
/**
|
||||
* Returns which protocol handles this MIME type, if it's an archive MIME type.
|
||||
* For instance zip:/ handles application/x-zip.
|
||||
*
|
||||
* This is defined in the protocol description file using an entry like
|
||||
* "archiveMimetype=application/x-zip"
|
||||
*
|
||||
* @param mimeType the MIME type to check
|
||||
* @return the protocol that can handle this archive MIME type, for instance "zip".
|
||||
*/
|
||||
static QString protocolForArchiveMimetype(const QString &mimeType);
|
||||
|
||||
/*=============================== OTHERS ====================================*/
|
||||
|
||||
/**
|
||||
* Force a reload of the general config file of
|
||||
* KIO workers ( kioslaverc).
|
||||
*/
|
||||
static void reparseConfiguration();
|
||||
|
||||
/**
|
||||
* Returns the charset to use for the specified @ref url.
|
||||
*
|
||||
*/
|
||||
static QString charsetFor(const QUrl &url);
|
||||
|
||||
/**
|
||||
* @brief Returns whether the protocol suppports KIO/POSIX permissions handling.
|
||||
*
|
||||
* When this is false the Permissions properties tab may be hidden, for example. The protocol may still support
|
||||
* permission control through other means, specific to the individual KIO worker.
|
||||
*
|
||||
* @param url the url to check
|
||||
* @return whether the protocol supports permissions
|
||||
* @since 5.98
|
||||
*/
|
||||
static bool supportsPermissions(const QUrl &url);
|
||||
|
||||
private:
|
||||
friend class KIO::WorkerConfigPrivate;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* (Shared with WorkerConfig)
|
||||
*/
|
||||
KIOCORE_NO_EXPORT static QMap<QString, QString> entryMap(const QString &group);
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
SPDX-FileCopyrightText: 1999 Torben Weis <weis@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Waldo Bastain <bastain@kde.org>
|
||||
SPDX-FileCopyrightText: 2000 Dawit Alemayehu <adawit@kde.org>
|
||||
SPDX-FileCopyrightText: 2008 Jarosław Staniek <staniek@kde.org>
|
||||
SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-only
|
||||
*/
|
||||
|
||||
#ifndef KPROTOCOLMANAGER_P_H
|
||||
#define KPROTOCOLMANAGER_P_H
|
||||
|
||||
#include <kiocore_export.h>
|
||||
|
||||
#include <QMutex>
|
||||
#include <QString>
|
||||
#include <QUrl>
|
||||
|
||||
#include <KSharedConfig>
|
||||
|
||||
#include "kprotocolmanager.h"
|
||||
|
||||
class KIOCORE_EXPORT KProtocolManagerPrivate
|
||||
{
|
||||
public:
|
||||
KProtocolManagerPrivate();
|
||||
~KProtocolManagerPrivate();
|
||||
void sync();
|
||||
|
||||
QMutex mutex; // protects all member vars
|
||||
KSharedConfig::Ptr configPtr;
|
||||
|
||||
QMap<QString /*mimetype*/, QString /*protocol*/> protocolForArchiveMimetypes;
|
||||
};
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user