Solid

udisksstorageaccess.cpp
1/*
2 SPDX-FileCopyrightText: 2009 Pino Toscano <pino@kde.org>
3 SPDX-FileCopyrightText: 2009-2012 Lukáš Tinkl <ltinkl@redhat.com>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "udisksstorageaccess.h"
9#include "udisks2.h"
10#include "udisks_debug.h"
11
12#include <QDBusConnection>
13#include <QDBusInterface>
14#include <QDBusMetaType>
15#include <QDir>
16#include <QGuiApplication>
17#include <QWindow>
18
19#include <config-solid.h>
20#if HAVE_LIBMOUNT
21#include <libmount.h>
22#endif
23
24struct AvailableAnswer {
25 bool checkResult;
26 QString binaryName;
27};
28Q_DECLARE_METATYPE(AvailableAnswer)
29
30QDBusArgument &operator<<(QDBusArgument &argument, const AvailableAnswer &answer)
31{
32 argument.beginStructure();
33 argument << answer.checkResult << answer.binaryName;
34 argument.endStructure();
35 return argument;
36}
37
38const QDBusArgument &operator>>(const QDBusArgument &argument, AvailableAnswer &answer)
39{
40 argument.beginStructure();
41 argument >> answer.checkResult >> answer.binaryName;
42 argument.endStructure();
43 return argument;
44}
45
46using namespace Solid::Backends::UDisks2;
47
48StorageAccess::StorageAccess(Device *device)
49 : DeviceInterface(device)
50 , m_setupInProgress(false)
51 , m_teardownInProgress(false)
52 , m_checkInProgress(false)
53 , m_repairInProgress(false)
54 , m_passphraseRequested(false)
55{
56 qDBusRegisterMetaType<AvailableAnswer>();
57
58 connect(device, SIGNAL(changed()), this, SLOT(checkAccessibility()));
59 updateCache();
60
61 // Delay connecting to DBus signals to avoid the related time penalty
62 // in hot paths such as predicate matching
63 QTimer::singleShot(0, this, SLOT(connectDBusSignals()));
64}
65
66StorageAccess::~StorageAccess()
67{
68}
69
70void StorageAccess::connectDBusSignals()
71{
72 m_device->registerAction(QStringLiteral("setup"), this, SLOT(slotSetupRequested()), SLOT(slotSetupDone(int, QString)));
73
74 m_device->registerAction(QStringLiteral("teardown"), this, SLOT(slotTeardownRequested()), SLOT(slotTeardownDone(int, QString)));
75
76 m_device->registerAction(QStringLiteral("check"), this, SLOT(slotCheckRequested()), SLOT(slotCheckDone(int, QString)));
77
78 m_device->registerAction(QStringLiteral("repair"), this, SLOT(slotRepairRequested()), SLOT(slotRepairDone(int, QString)));
79}
80
81bool StorageAccess::isLuksDevice() const
82{
83 return m_device->isEncryptedContainer(); // encrypted device
84}
85
86bool StorageAccess::isAccessible() const
87{
88 if (isLuksDevice()) { // check if the cleartext slave is mounted
89 const QString path = clearTextPath();
90 // qDebug() << Q_FUNC_INFO << "CLEARTEXT device path: " << path;
91 if (path.isEmpty() || path == QLatin1String("/")) {
92 return false;
93 }
94 Device holderDevice(path);
95 return holderDevice.isMounted();
96 }
97
98 return m_device->isMounted();
99}
100
101bool StorageAccess::isEncrypted() const
102{
103 // FIXME We should also check if physical device is encrypted
104 // FIXME Gocryptfs is not supported
105 return isLuksDevice() || m_device->isEncryptedCleartext();
106}
107
108bool StorageAccess::canCheck() const
109{
110 const auto idType = m_device->prop(QStringLiteral("IdType")).toString();
112 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
113 QStringLiteral(UD2_DBUS_PATH_MANAGER),
114 QStringLiteral("org.freedesktop.UDisks2.Manager"),
115 QStringLiteral("CanCheck"));
116 msg << idType;
117 QDBusReply<AvailableAnswer> r = c.call(msg);
118 if (!r.isValid()) {
119 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << "DBus error, code" << r.error().type();
120 return false;
121 }
122
123 const bool ret = r.value().checkResult;
124 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << ret << r.value().binaryName;
125 return ret;
126}
127
128bool StorageAccess::check()
129{
130 if (m_setupInProgress || m_teardownInProgress || m_checkInProgress || m_repairInProgress) {
131 return false;
132 }
133 m_checkInProgress = true;
134 m_device->broadcastActionRequested(QStringLiteral("check"));
135
136 const auto path = dbusPath();
138 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Check"));
139 msg << QVariantMap{};
140
141 return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
142}
143
144bool StorageAccess::canRepair() const
145{
146 const auto idType = m_device->prop(QStringLiteral("IdType")).toString();
148 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
149 QStringLiteral(UD2_DBUS_PATH_MANAGER),
150 QStringLiteral("org.freedesktop.UDisks2.Manager"),
151 QStringLiteral("CanRepair"));
152 msg << idType;
153 QDBusReply<AvailableAnswer> r = c.call(msg);
154 if (!r.isValid()) {
155 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << "DBus error, code" << r.error().type();
156 return false;
157 }
158
159 const bool ret = r.value().checkResult;
160 qCDebug(UDISKS2) << Q_FUNC_INFO << dbusPath() << idType << ret << r.value().binaryName;
161 return ret;
162}
163
164bool StorageAccess::repair()
165{
166 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
167 return false;
168 }
169 m_repairInProgress = true;
170 m_device->broadcastActionRequested(QStringLiteral("repair"));
171
172 const auto path = dbusPath();
174 auto msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Repair"));
175 QVariantMap options;
176 msg << options;
177
178 qCDebug(UDISKS2) << Q_FUNC_INFO << path;
179 return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
180}
181
182static QString baseMountPoint(const QByteArray &dev)
183{
184 QString mountPoint;
185
186#if HAVE_LIBMOUNT
187 // UDisks "MountPoints" property contains multiple paths, this happens with
188 // devices with bind mounts; try finding the "base" mount point
189 if (struct libmnt_table *table = mnt_new_table()) {
190 // This parses "/etc/mtab" if present or "/proc/self/mountinfo" by default
191 if (mnt_table_parse_mtab(table, "/proc/self/mountinfo") == 0) {
192 // BACKWARD because the fs's we're interested in, /dev/sdXY, are typically at the end
193 struct libmnt_iter *itr = mnt_new_iter(MNT_ITER_BACKWARD);
194 struct libmnt_fs *fs;
195
196 const QByteArray devicePath = dev.endsWith('\x00') ? dev.chopped(1) : dev;
197
198 while (mnt_table_next_fs(table, itr, &fs) == 0) {
199 if (mnt_fs_get_srcpath(fs) == devicePath //
200 && (qstrcmp(mnt_fs_get_root(fs), "/") == 0) // Base mount point will have "/" as root fs
201 ) {
202 mountPoint = QFile::decodeName(mnt_fs_get_target(fs));
203 break;
204 }
205 }
206
207 mnt_free_iter(itr);
208 }
209
210 mnt_free_table(table);
211 }
212#else
213 Q_UNUSED(dev);
214#endif
215
216 return mountPoint;
217}
218
219QString StorageAccess::filePath() const
220{
221 if (isLuksDevice()) { // encrypted (and unlocked) device
222 const QString path = clearTextPath();
223 if (path.isEmpty() || path == QLatin1String("/")) {
224 return QString();
225 }
226 Device holderDevice(path);
227 const auto mntPoints = qdbus_cast<QByteArrayList>(holderDevice.prop(QStringLiteral("MountPoints")));
228 if (!mntPoints.isEmpty()) {
229 QByteArray first = mntPoints.first();
230 if (first.endsWith('\x00')) {
231 first.chop(1);
232 }
233 return QFile::decodeName(first); // FIXME Solid doesn't support multiple mount points
234 } else {
235 return QString();
236 }
237 }
238
239 const auto mntPoints = qdbus_cast<QByteArrayList>(m_device->prop(QStringLiteral("MountPoints")));
240 if (mntPoints.isEmpty()) {
241 return {};
242 }
243
244 QByteArray first = mntPoints.first();
245 if (first.endsWith('\x00')) {
246 first.chop(1);
247 }
248 const QString potentialMountPoint = QFile::decodeName(first);
249
250 if (mntPoints.size() == 1) {
251 return potentialMountPoint;
252 }
253
254 // Device has bind mounts?
255 const QString basePoint = baseMountPoint(m_device->prop(QStringLiteral("Device")).toByteArray());
256
257 return !basePoint.isEmpty() ? basePoint : potentialMountPoint;
258}
259
260bool StorageAccess::isIgnored() const
261{
262 if (m_device->prop(QStringLiteral("HintIgnore")).toBool()) {
263 return true;
264 }
265
266 const QStringList mountOptions = m_device->prop(QStringLiteral("UserspaceMountOptions")).toStringList();
267 if (mountOptions.contains(QLatin1String("x-gdu.hide"))) {
268 return true;
269 }
270
271 const QString path = filePath();
272
273 const bool inUserPath = (path.startsWith(QLatin1String("/media/")) //
274 || path.startsWith(QLatin1String("/run/media/")) //
276 return !inUserPath;
277}
278
279bool StorageAccess::setup()
280{
281 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
282 return false;
283 }
284 m_setupInProgress = true;
285 m_device->broadcastActionRequested(QStringLiteral("setup"));
286
287 if (m_device->isEncryptedContainer() && clearTextPath().isEmpty()) {
288 return requestPassphrase();
289 } else {
290 return mount();
291 }
292}
293
294bool StorageAccess::teardown()
295{
296 if (m_teardownInProgress || m_setupInProgress || m_checkInProgress || m_repairInProgress) {
297 return false;
298 }
299 m_teardownInProgress = true;
300 m_device->broadcastActionRequested(QStringLiteral("teardown"));
301
302 return unmount();
303}
304
305void StorageAccess::updateCache()
306{
307 m_isAccessible = isAccessible();
308}
309
310void StorageAccess::checkAccessibility()
311{
312 const bool old_isAccessible = m_isAccessible;
313 updateCache();
314
315 if (old_isAccessible != m_isAccessible) {
316 Q_EMIT accessibilityChanged(m_isAccessible, m_device->udi());
317 }
318}
319
320void StorageAccess::slotDBusReply(const QDBusMessage &reply)
321{
322 if (m_setupInProgress) {
323 if (isLuksDevice() && !isAccessible()) { // unlocked device, now mount it
324 mount();
325 } else { // Don't broadcast setupDone unless the setup is really done. (Fix kde#271156)
326 m_setupInProgress = false;
327 m_device->invalidateCache();
328 m_device->broadcastActionDone(QStringLiteral("setup"));
329
330 checkAccessibility();
331 }
332 } else if (m_teardownInProgress) { // FIXME
333 const QString ctPath = clearTextPath();
334 qCDebug(UDISKS2) << "Successfully unmounted " << m_device->udi();
335 if (isLuksDevice() && !ctPath.isEmpty() && ctPath != QStringLiteral("/")) { // unlocked device, lock it
336 callCryptoTeardown();
337 } else if (!ctPath.isEmpty() && ctPath != QStringLiteral("/")) {
338 callCryptoTeardown(true); // Lock encrypted parent
339 } else {
340 // try to "eject" (aka safely remove) from the (parent) drive, e.g. SD card from a reader
341 QString drivePath = m_device->drivePath();
342 if (!drivePath.isEmpty() || drivePath != QStringLiteral("/")) {
343 Device drive(drivePath);
344 QDBusConnection c = QDBusConnection::systemBus();
345
346 if (drive.prop(QStringLiteral("MediaRemovable")).toBool() //
347 && drive.prop(QStringLiteral("MediaAvailable")).toBool() //
348 && !m_device->isOpticalDisc()) { // optical drives have their Eject method
349 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
350 drivePath,
351 QStringLiteral(UD2_DBUS_INTERFACE_DRIVE),
352 QStringLiteral("Eject"));
353 msg << QVariantMap(); // options, unused now
354 c.call(msg, QDBus::NoBlock);
355 } else if (drive.prop(QStringLiteral("CanPowerOff")).toBool() //
356 && !m_device->isOpticalDisc()) { // avoid disconnecting optical drives from the bus
357 qCDebug(UDISKS2) << "Drive can power off:" << drivePath;
358 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
359 drivePath,
360 QStringLiteral(UD2_DBUS_INTERFACE_DRIVE),
361 QStringLiteral("PowerOff"));
362 msg << QVariantMap(); // options, unused now
363 c.call(msg, QDBus::NoBlock);
364 }
365 }
366
367 m_teardownInProgress = false;
368 m_device->invalidateCache();
369 m_device->broadcastActionDone(QStringLiteral("teardown"));
370
371 checkAccessibility();
372 }
373 } else if (m_checkInProgress) {
374 QDBusReply<bool> r = reply;
375 qCDebug(UDISKS2) << "Check reply received " << m_device->udi() << r;
376 m_checkInProgress = false;
377 if (r.isValid()) {
378 m_device->broadcastActionDone(QStringLiteral("check"), Solid::NoError, QString::number(r.value()));
379 } else {
380 m_device->broadcastActionDone(QStringLiteral("check"), Solid::OperationFailed, reply.errorMessage());
381 }
382 } else if (m_repairInProgress) {
383 qCDebug(UDISKS2) << "Successfully repaired " << m_device->udi();
384 m_repairInProgress = false;
385 m_device->broadcastActionDone(QStringLiteral("repair"));
386 }
387}
388
389void StorageAccess::slotDBusError(const QDBusError &error)
390{
391 // qDebug() << Q_FUNC_INFO << "DBUS ERROR:" << error.name() << error.message();
392
393 if (m_setupInProgress) {
394 m_setupInProgress = false;
395 m_device->broadcastActionDone(QStringLiteral("setup"), //
396 m_device->errorToSolidError(error.name()),
397 m_device->errorToString(error.name()) + QStringLiteral(": ") + error.message());
398
399 checkAccessibility();
400 } else if (m_teardownInProgress) {
401 m_teardownInProgress = false;
402 m_device->broadcastActionDone(QStringLiteral("teardown"), //
403 m_device->errorToSolidError(error.name()),
404 m_device->errorToString(error.name()) + QStringLiteral(": ") + error.message());
405 checkAccessibility();
406 } else if (m_checkInProgress) {
407 m_checkInProgress = false;
408 m_device->broadcastActionDone(QStringLiteral("check"),
409 m_device->errorToSolidError(error.name()),
410 m_device->errorToString(error.name()) + QStringLiteral(": ") + error.message());
411 } else if (m_repairInProgress) {
412 m_repairInProgress = false;
413 m_device->broadcastActionDone(QStringLiteral("repair"),
414 m_device->errorToSolidError(error.name()),
415 m_device->errorToString(error.name()) + QStringLiteral(": ") + error.message());
416 }
417}
418
419void StorageAccess::slotSetupRequested()
420{
421 m_setupInProgress = true;
422 // qDebug() << "SETUP REQUESTED:" << m_device->udi();
423 Q_EMIT setupRequested(m_device->udi());
424}
425
426void StorageAccess::slotSetupDone(int error, const QString &errorString)
427{
428 m_setupInProgress = false;
429 // qDebug() << "SETUP DONE:" << m_device->udi();
430 checkAccessibility();
431 Q_EMIT setupDone(static_cast<Solid::ErrorType>(error), errorString, m_device->udi());
432}
433
434void StorageAccess::slotTeardownRequested()
435{
436 m_teardownInProgress = true;
437 Q_EMIT teardownRequested(m_device->udi());
438}
439
440void StorageAccess::slotTeardownDone(int error, const QString &errorString)
441{
442 m_teardownInProgress = false;
443 checkAccessibility();
444 Q_EMIT teardownDone(static_cast<Solid::ErrorType>(error), errorString, m_device->udi());
445}
446
447void StorageAccess::slotCheckRequested()
448{
449 m_checkInProgress = true;
450 Q_EMIT checkRequested(m_device->udi());
451}
452
453void StorageAccess::slotCheckDone(int error, const QString &errorString)
454{
455 m_checkInProgress = false;
456 Q_EMIT checkDone(static_cast<Solid::ErrorType>(error), errorString, m_device->udi());
457}
458
459void StorageAccess::slotRepairRequested()
460{
461 m_repairInProgress = true;
462 Q_EMIT repairRequested(m_device->udi());
463}
464
465void StorageAccess::slotRepairDone(int error, const QString &errorString)
466{
467 m_repairInProgress = false;
468 Q_EMIT repairDone(static_cast<Solid::ErrorType>(error), errorString, m_device->udi());
469}
470
471bool StorageAccess::mount()
472{
473 const auto path = dbusPath();
474
475 QDBusConnection c = QDBusConnection::systemBus();
476 QDBusMessage msg =
477 QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Mount"));
478 QVariantMap options;
479
480 if (m_device->prop(QStringLiteral("IdType")).toString() == QLatin1String("vfat")) {
481 options.insert(QStringLiteral("options"), QStringLiteral("flush"));
482 }
483
484 msg << options;
485
486 return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
487}
488
489bool StorageAccess::unmount()
490{
491 const auto path = dbusPath();
492
493 QDBusConnection c = QDBusConnection::systemBus();
494 QDBusMessage msg =
495 QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE), path, QStringLiteral(UD2_DBUS_INTERFACE_FILESYSTEM), QStringLiteral("Unmount"));
496
497 msg << QVariantMap(); // options, unused now
498
499 qCDebug(UDISKS2) << "Initiating unmount of " << path;
500 return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)), s_unmountTimeout);
501}
502
503QString StorageAccess::generateReturnObjectPath()
504{
505 static QAtomicInt number = 1;
506
507 return QStringLiteral("/org/kde/solid/UDisks2StorageAccess_") + QString::number(number++);
508}
509
510QString StorageAccess::clearTextPath() const
511{
512 const QString path = m_device->prop(QStringLiteral("CleartextDevice")).value<QDBusObjectPath>().path();
513 if (path != QLatin1String("/")) {
514 return path;
515 }
516 return QString();
517}
518
519QString StorageAccess::dbusPath() const
520{
521 QString path = m_device->udi();
522 if (isLuksDevice()) { // mount options for the cleartext volume
523 const QString ctPath = clearTextPath();
524 if (!ctPath.isEmpty()) {
525 path = ctPath;
526 }
527 }
528 return path;
529}
530
531bool StorageAccess::requestPassphrase()
532{
533 QString udi = m_device->udi();
534 QString returnService = QDBusConnection::sessionBus().baseService();
535 m_lastReturnObject = generateReturnObjectPath();
536
538
539 // TODO: this only works on X11, Wayland doesn't have global window ids.
540 // Passing ids to other processes doesn't make any sense
541 auto activeWindow = QGuiApplication::focusWindow();
542 uint wId = 0;
543 if (activeWindow != nullptr) {
544 wId = (uint)activeWindow->winId();
545 }
546
547 QString appId = QCoreApplication::applicationName();
548
549 const auto plasmaVersionMajor = qEnvironmentVariable("KDE_SESSION_VERSION", QStringLiteral("6"));
550
551 // TODO KF6: remove hard dep on Plasma here which provides the SolidUiServer kded plugin
552 QDBusInterface soliduiserver(QStringLiteral("org.kde.kded") + plasmaVersionMajor,
553 QStringLiteral("/modules/soliduiserver"),
554 QStringLiteral("org.kde.SolidUiServer"));
555 QDBusReply<void> reply = soliduiserver.call(QStringLiteral("showPassphraseDialog"), udi, returnService, m_lastReturnObject, wId, appId);
556 m_passphraseRequested = reply.isValid();
557 if (!m_passphraseRequested) {
558 qCWarning(UDISKS2) << "Failed to call the SolidUiServer, D-Bus said:" << reply.error();
559 }
560
561 return m_passphraseRequested;
562}
563
564void StorageAccess::passphraseReply(const QString &passphrase)
565{
566 if (m_passphraseRequested) {
567 QDBusConnection::sessionBus().unregisterObject(m_lastReturnObject);
568 m_passphraseRequested = false;
569 if (!passphrase.isEmpty()) {
570 callCryptoSetup(passphrase);
571 } else {
572 m_setupInProgress = false;
573 m_device->broadcastActionDone(QStringLiteral("setup"), Solid::UserCanceled);
574 }
575 }
576}
577
578void StorageAccess::callCryptoSetup(const QString &passphrase)
579{
580 QDBusConnection c = QDBusConnection::systemBus();
581 QDBusMessage msg = QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
582 m_device->udi(),
583 QStringLiteral(UD2_DBUS_INTERFACE_ENCRYPTED),
584 QStringLiteral("Unlock"));
585
586 msg << passphrase;
587 msg << QVariantMap(); // options, unused now
588
589 c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
590}
591
592bool StorageAccess::callCryptoTeardown(bool actOnParent)
593{
594 QDBusConnection c = QDBusConnection::systemBus();
595 QDBusMessage msg =
596 QDBusMessage::createMethodCall(QStringLiteral(UD2_DBUS_SERVICE),
597 actOnParent ? (m_device->prop(QStringLiteral("CryptoBackingDevice")).value<QDBusObjectPath>().path()) : m_device->udi(),
598 QStringLiteral(UD2_DBUS_INTERFACE_ENCRYPTED),
599 QStringLiteral("Lock"));
600 msg << QVariantMap(); // options, unused now
601
602 return c.callWithCallback(msg, this, SLOT(slotDBusReply(QDBusMessage)), SLOT(slotDBusError(QDBusError)));
603}
604
605#include "moc_udisksstorageaccess.cpp"
char * toString(const EngineQuery &query)
KIOCORE_EXPORT QString number(KIO::filesize_t size)
QString path(const QString &relativePath)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
void chop(qsizetype n)
QByteArray chopped(qsizetype len) const const
bool endsWith(QByteArrayView bv) const const
QByteArray first(qsizetype n) const const
void beginStructure()
void endStructure()
QString baseService() const const
QDBusMessage call(const QDBusMessage &message, QDBus::CallMode mode, int timeout) const const
bool callWithCallback(const QDBusMessage &message, QObject *receiver, const char *returnMethod, const char *errorMethod, int timeout) const const
bool registerObject(const QString &path, QObject *object, RegisterOptions options)
QDBusConnection sessionBus()
QDBusConnection systemBus()
void unregisterObject(const QString &path, UnregisterMode mode)
ErrorType type() const const
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
QString errorMessage() const const
const QDBusError & error()
bool isValid() const const
QString homePath()
QString decodeName(const QByteArray &localFileName)
QWindow * focusWindow()
Q_EMITQ_EMIT
bool isEmpty() const const
QString number(double n, char format, int precision)
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:49:37 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.