| 1 | // Copyright (C) 2022 Intel Corporation. |
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
| 3 | // Qt-Security score:significant reason:default |
| 4 | |
| 5 | #include "qtipccommon.h" |
| 6 | #include "qtipccommon_p.h" |
| 7 | |
| 8 | #include <qcryptographichash.h> |
| 9 | #include <qstandardpaths.h> |
| 10 | #include <qstringconverter.h> |
| 11 | #include <qurl.h> |
| 12 | #include <qurlquery.h> |
| 13 | |
| 14 | #if defined(Q_OS_DARWIN) |
| 15 | # include "private/qcore_mac_p.h" |
| 16 | # if !defined(SHM_NAME_MAX) |
| 17 | // Based on PSEMNAMLEN in XNU's posix_sem.c, which would |
| 18 | // indicate the max length is 31, _excluding_ the zero |
| 19 | // terminator. But in practice (possibly due to an off- |
| 20 | // by-one bug in the kernel) the usable bytes are only 30. |
| 21 | # define SHM_NAME_MAX 30 |
| 22 | # endif |
| 23 | #elif defined(Q_OS_WINDOWS) |
| 24 | # include "qt_windows.h" |
| 25 | #endif |
| 26 | |
| 27 | #if QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore) |
| 28 | |
| 29 | QT_BEGIN_NAMESPACE |
| 30 | |
| 31 | using namespace Qt::StringLiterals; |
| 32 | |
| 33 | static QStringView staticTypeToString(QNativeIpcKey::Type type) |
| 34 | { |
| 35 | switch (type) { |
| 36 | case QNativeIpcKey::Type::SystemV: |
| 37 | return u"systemv" ; |
| 38 | case QNativeIpcKey::Type::PosixRealtime: |
| 39 | return u"posix" ; |
| 40 | case QNativeIpcKey::Type::Windows: |
| 41 | return u"windows" ; |
| 42 | } |
| 43 | return {}; |
| 44 | } |
| 45 | |
| 46 | static QString typeToString(QNativeIpcKey::Type type) |
| 47 | { |
| 48 | QStringView typeString = staticTypeToString(type); |
| 49 | switch (type) { |
| 50 | case QNativeIpcKey::Type::SystemV: |
| 51 | case QNativeIpcKey::Type::PosixRealtime: |
| 52 | case QNativeIpcKey::Type::Windows: |
| 53 | return QString::fromRawData(typeString.constData(), size: typeString.size()); |
| 54 | } |
| 55 | |
| 56 | int value = int(type); |
| 57 | if (value >= 1 && value <= 0xff) { |
| 58 | // System V key with id different from 'Q' |
| 59 | typeString = staticTypeToString(type: QNativeIpcKey::Type::SystemV); |
| 60 | return typeString + QString::number(-value); // negative so it prepends a dash |
| 61 | } |
| 62 | |
| 63 | return QString(); // invalid! |
| 64 | } |
| 65 | |
| 66 | static QNativeIpcKey::Type stringToType(QStringView typeString) |
| 67 | { |
| 68 | if (typeString == staticTypeToString(type: QNativeIpcKey::Type::PosixRealtime)) |
| 69 | return QNativeIpcKey::Type::PosixRealtime; |
| 70 | if (typeString == staticTypeToString(type: QNativeIpcKey::Type::Windows)) |
| 71 | return QNativeIpcKey::Type::Windows; |
| 72 | |
| 73 | auto fromNumber = [](QStringView number, int low, int high) { |
| 74 | bool ok; |
| 75 | int n = -number.toInt(ok: &ok, base: 10); |
| 76 | if (!ok || n < low || n > high) |
| 77 | return QNativeIpcKey::Type{}; |
| 78 | return QNativeIpcKey::Type(n); |
| 79 | }; |
| 80 | |
| 81 | QStringView sysv = staticTypeToString(type: QNativeIpcKey::Type::SystemV); |
| 82 | if (typeString.startsWith(s: sysv)) { |
| 83 | if (typeString.size() == sysv.size()) |
| 84 | return QNativeIpcKey::Type::SystemV; |
| 85 | return fromNumber(typeString.sliced(pos: sysv.size()), 1, 0xff); |
| 86 | } |
| 87 | |
| 88 | // invalid! |
| 89 | return QNativeIpcKey::Type{}; |
| 90 | } |
| 91 | |
| 92 | /*! |
| 93 | \internal |
| 94 | |
| 95 | Legacy: this exists for compatibility with QSharedMemory and |
| 96 | QSystemSemaphore between 4.4 and 6.6. |
| 97 | |
| 98 | Returns a QNativeIpcKey that contains a platform-safe key using rules |
| 99 | similar to QtIpcCommon::platformSafeKey() below, but using an algorithm |
| 100 | that is compatible with Qt 4.4 to 6.6. Additionally, the returned |
| 101 | QNativeIpcKey will record the input \a key so it can be included in the |
| 102 | string form if necessary to pass to other processes. |
| 103 | */ |
| 104 | QNativeIpcKey QtIpcCommon::legacyPlatformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType, |
| 105 | QNativeIpcKey::Type type) |
| 106 | { |
| 107 | QNativeIpcKey k(type); |
| 108 | if (key.isEmpty()) |
| 109 | return k; |
| 110 | |
| 111 | QByteArray hex = QCryptographicHash::hash(data: key.toUtf8(), method: QCryptographicHash::Sha1).toHex(); |
| 112 | |
| 113 | if (type == QNativeIpcKey::Type::PosixRealtime) { |
| 114 | #if defined(Q_OS_DARWIN) |
| 115 | if (qt_apple_isSandboxed()) { |
| 116 | // Sandboxed applications on Apple platforms require the shared memory name |
| 117 | // to be in the form <application group identifier>/<custom identifier>. |
| 118 | // Since we don't know which application group identifier the user wants |
| 119 | // to apply, we instead document that requirement, and use the key directly. |
| 120 | QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, key, key); |
| 121 | } else { |
| 122 | // The shared memory name limit on Apple platforms is very low (30 characters), |
| 123 | // so we can't use the logic below of combining the prefix, key, and a hash, |
| 124 | // to ensure a unique and valid name. Instead we use the first part of the |
| 125 | // hash, which should still long enough to avoid collisions in practice. |
| 126 | QString native = u'/' + QLatin1StringView(hex).left(SHM_NAME_MAX - 1); |
| 127 | QNativeIpcKeyPrivate::setNativeAndLegacyKeys(k, native, key); |
| 128 | } |
| 129 | return k; |
| 130 | #endif |
| 131 | } |
| 132 | |
| 133 | QString result; |
| 134 | result.reserve(asize: 1 + 18 + key.size() + 40); |
| 135 | switch (ipcType) { |
| 136 | case IpcType::SharedMemory: |
| 137 | result += "qipc_sharedmemory_"_L1 ; |
| 138 | break; |
| 139 | case IpcType::SystemSemaphore: |
| 140 | result += "qipc_systemsem_"_L1 ; |
| 141 | break; |
| 142 | } |
| 143 | |
| 144 | for (QChar ch : key) { |
| 145 | if ((ch >= u'a' && ch <= u'z') || |
| 146 | (ch >= u'A' && ch <= u'Z')) |
| 147 | result += ch; |
| 148 | } |
| 149 | result.append(s: QLatin1StringView(hex)); |
| 150 | |
| 151 | switch (type) { |
| 152 | case QNativeIpcKey::Type::Windows: |
| 153 | if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::Windows)) |
| 154 | QNativeIpcKeyPrivate::setNativeAndLegacyKeys(key&: k, nativeKey: result, legacyKey: key); |
| 155 | return k; |
| 156 | case QNativeIpcKey::Type::PosixRealtime: |
| 157 | result.prepend(c: u'/'); |
| 158 | if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::PosixRealtime)) |
| 159 | QNativeIpcKeyPrivate::setNativeAndLegacyKeys(key&: k, nativeKey: result, legacyKey: key); |
| 160 | return k; |
| 161 | case QNativeIpcKey::Type::SystemV: |
| 162 | break; |
| 163 | } |
| 164 | if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::SystemV)) { |
| 165 | result = QStandardPaths::writableLocation(type: QStandardPaths::TempLocation) + u'/' + result; |
| 166 | QNativeIpcKeyPrivate::setNativeAndLegacyKeys(key&: k, nativeKey: result, legacyKey: key); |
| 167 | } |
| 168 | return k; |
| 169 | } |
| 170 | |
| 171 | /*! |
| 172 | \internal |
| 173 | Returns a QNativeIpcKey of type \a type, suitable for QSystemSemaphore or |
| 174 | QSharedMemory depending on \a ipcType. The returned native key is generated |
| 175 | from the Unicode input \a key and is safe for use on for the key type in |
| 176 | question in the current OS. |
| 177 | */ |
| 178 | QNativeIpcKey QtIpcCommon::platformSafeKey(const QString &key, QtIpcCommon::IpcType ipcType, |
| 179 | QNativeIpcKey::Type type) |
| 180 | { |
| 181 | QNativeIpcKey k(type); |
| 182 | if (key.isEmpty()) |
| 183 | return k; |
| 184 | |
| 185 | switch (type) { |
| 186 | case QNativeIpcKey::Type::PosixRealtime: |
| 187 | if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::PosixRealtime)) { |
| 188 | #ifdef SHM_NAME_MAX |
| 189 | // The shared memory name limit on Apple platforms is very low (30 |
| 190 | // characters), so we have to cut it down to avoid ENAMETOOLONG. We |
| 191 | // hope that there won't be too many collisions... |
| 192 | k.setNativeKey(u'/' + QStringView(key).left(SHM_NAME_MAX - 1)); |
| 193 | #else |
| 194 | k.setNativeKey(u'/' + key); |
| 195 | #endif |
| 196 | } |
| 197 | return k; |
| 198 | |
| 199 | case QNativeIpcKey::Type::Windows: |
| 200 | if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::Windows)) { |
| 201 | QStringView prefix; |
| 202 | QStringView payload = key; |
| 203 | // see https://learn.microsoft.com/en-us/windows/win32/termserv/kernel-object-namespaces |
| 204 | for (QStringView candidate : { u"Local\\"_sv , u"Global\\"_sv }) { |
| 205 | if (!key.startsWith(s: candidate)) |
| 206 | continue; |
| 207 | prefix = candidate; |
| 208 | payload = payload.sliced(pos: prefix.size()); |
| 209 | break; |
| 210 | } |
| 211 | |
| 212 | QStringView mid; |
| 213 | switch (ipcType) { |
| 214 | case IpcType::SharedMemory: mid = u"shm_" ; break; |
| 215 | case IpcType::SystemSemaphore: mid = u"sem_" ; break; |
| 216 | } |
| 217 | |
| 218 | QString result = prefix + mid + payload; |
| 219 | #ifdef Q_OS_WINDOWS |
| 220 | result.truncate(MAX_PATH); |
| 221 | #endif |
| 222 | k.setNativeKey(result); |
| 223 | } |
| 224 | return k; |
| 225 | |
| 226 | case QNativeIpcKey::Type::SystemV: |
| 227 | break; |
| 228 | } |
| 229 | |
| 230 | // System V |
| 231 | if (isIpcSupported(ipcType, type: QNativeIpcKey::Type::SystemV)) { |
| 232 | if (key.startsWith(c: u'/')) { |
| 233 | k.setNativeKey(key); |
| 234 | } else { |
| 235 | QString baseDir = QStandardPaths::writableLocation(type: QStandardPaths::RuntimeLocation); |
| 236 | k.setNativeKey(baseDir + u'/' + key); |
| 237 | } |
| 238 | } |
| 239 | return k; |
| 240 | } |
| 241 | |
| 242 | /*! |
| 243 | \class QNativeIpcKey |
| 244 | \inmodule QtCore |
| 245 | \since 6.6 |
| 246 | \brief The QNativeIpcKey class holds a native key used by QSystemSemaphore and QSharedMemory. |
| 247 | |
| 248 | \compares equality |
| 249 | |
| 250 | The \l QSharedMemory and \l QSystemSemaphore classes identify their |
| 251 | resource using a system-wide identifier known as a "key". The low-level key |
| 252 | value as well as the key type are encapsulated in Qt using the \l |
| 253 | QNativeIpcKey class. |
| 254 | |
| 255 | Those two classes also provide the means to create native keys from a |
| 256 | cross-platform identifier, using QSharedMemory::platformSafeKey() and |
| 257 | QSystemSemaphore::platformSafeKey(). Applications should never share the |
| 258 | input to those functions, as different versions of Qt may perform different |
| 259 | transformations, resulting in different native keys. Instead, the |
| 260 | application that created the IPC object should communicate the resulting |
| 261 | native key using the methods described below. |
| 262 | |
| 263 | For details on the key types, platform-specific limitations, and |
| 264 | interoperability with older or non-Qt applications, see the \l{Native IPC |
| 265 | Keys} documentation. That includes important information for sandboxed |
| 266 | applications on Apple platforms, including all apps obtained via the Apple |
| 267 | App Store. |
| 268 | |
| 269 | \section1 Communicating keys to other processes |
| 270 | \section2 Communicating keys to other Qt processes |
| 271 | |
| 272 | If the other process supports QNativeIpcKey, the best way of communicating |
| 273 | is via the string representation obtained from toString() and parsing it |
| 274 | using fromString(). This representation can be stored on a file whose name |
| 275 | is well-known or passed on the command-line to a child process using |
| 276 | QProcess::setArguments(). |
| 277 | |
| 278 | If the other process does not support QNativeIpcKey, then the two processes |
| 279 | can exchange the nativeKey() but the older code is likely unable to adjust |
| 280 | its key type. The legacyDefaultTypeForOs() function returns the type that |
| 281 | legacy code used, which may not match the \l{DefaultTypeForOs} constant. |
| 282 | This is still true even if the old application is not using the same build |
| 283 | as the new one (for example, it is a Qt 5 application), provided the |
| 284 | options passed to the Qt configure script are the same. |
| 285 | |
| 286 | \section2 Communicating keys to non-Qt processes |
| 287 | |
| 288 | When communicating with non-Qt processes, the application must arrange to |
| 289 | obtain the key type the other process is using. This is important |
| 290 | particularly on Unix systems, where both \l PosixRealtime and \l SystemV |
| 291 | are common. |
| 292 | |
| 293 | \section1 String representation of native keys |
| 294 | |
| 295 | The format of the string representation of a QNativeIpcKey is meant to be |
| 296 | stable and therefore backwards and forwards compatible, provided the key |
| 297 | type is supported by the Qt version in question. That is to say, an older |
| 298 | Qt will fail to parse the string representation of a key type introduced |
| 299 | after it was released. However, successfully parsing a string |
| 300 | representation does not imply the Qt classes can successfully create an |
| 301 | object of that type; applications should verify support using |
| 302 | QSharedMemory::isKeyTypeSupported() and QSystemSemaphore::isKeyTypeSupported(). |
| 303 | |
| 304 | The format of the string representation is formed by two components, |
| 305 | separated by a colon (':'). The first component is the key type, described |
| 306 | in the table below. The second component is a type-specific payload, using |
| 307 | \l{QByteArray::fromPercentEncoding}{percent-encoding}. For all currently |
| 308 | supported key types, the decoded form is identical to the contents of the |
| 309 | nativeKey() field. |
| 310 | |
| 311 | \table |
| 312 | \row \li Key type \li String representation |
| 313 | \row \li \l PosixRealtime \li \c "posix" |
| 314 | \row \li \l SystemV \li \c "systemv" |
| 315 | \row \li \l Windows \li \c "windows" |
| 316 | \row \li Non-standard SystemV \li \c "systemv-" followed by a decimal number |
| 317 | \endtable |
| 318 | |
| 319 | This format resembles a URI and allows parsing using URI/URL-parsing |
| 320 | functions, such as \l QUrl. When parsed by such API, the key type will show |
| 321 | up as the \l{QUrl::scheme()}{scheme}, and the payload will be the |
| 322 | \l{QUrl::path()}{path}. Use of query or fragments is reserved. |
| 323 | |
| 324 | \sa QSharedMemory, QSystemSemaphore |
| 325 | */ |
| 326 | |
| 327 | /*! |
| 328 | \enum QNativeIpcKey::Type |
| 329 | |
| 330 | This enum describes the backend type for the IPC object. For details on the |
| 331 | key types, see the \l{Native IPC Keys} documentation. |
| 332 | |
| 333 | \value SystemV X/Open System Initiative (XSI) or System V (SVr4) API |
| 334 | \value PosixRealtime IEEE 1003.1b (POSIX.1b) API |
| 335 | \value Windows Win32 API |
| 336 | |
| 337 | \sa setType(), type() |
| 338 | */ |
| 339 | |
| 340 | /*! |
| 341 | \variable QNativeIpcKey::DefaultTypeForOs |
| 342 | |
| 343 | This constant expression variable holds the default native IPC type for the |
| 344 | current OS. It will be Type::Windows for Windows systems and |
| 345 | Type::PosixRealtime elsewhere. Note that this constant is different from |
| 346 | what \l QSharedMemory and \l QSystemSemaphore defaulted to on the majority |
| 347 | of Unix systems prior to Qt 6.6; see legacyDefaultTypeForOs() for more |
| 348 | information. |
| 349 | */ |
| 350 | |
| 351 | /*! |
| 352 | \fn QNativeIpcKey::legacyDefaultTypeForOs() noexcept |
| 353 | |
| 354 | Returns the \l{Type} that corresponds to the native IPC key that |
| 355 | \l{QSharedMemory} and \l{QSystemSemaphore} used to use prior to Qt 6.6. |
| 356 | Applications and libraries that must retain compatibility with code using |
| 357 | either class that was compiled with Qt prior to version 6.6 can use this |
| 358 | function to determine what IPC type the other applications may be using. |
| 359 | |
| 360 | Note that this function relies on Qt having been built with identical |
| 361 | configure-time options. |
| 362 | */ |
| 363 | #if defined(Q_OS_DARWIN) |
| 364 | QNativeIpcKey::Type QNativeIpcKey::defaultTypeForOs_internal() noexcept |
| 365 | { |
| 366 | if (qt_apple_isSandboxed()) |
| 367 | return Type::PosixRealtime; |
| 368 | return Type::SystemV; |
| 369 | } |
| 370 | #endif |
| 371 | |
| 372 | /*! |
| 373 | \fn QNativeIpcKey::QNativeIpcKey() noexcept |
| 374 | |
| 375 | Constructs a QNativeIpcKey object of type \l DefaultTypeForOs with an empty key. |
| 376 | */ |
| 377 | |
| 378 | /*! |
| 379 | \fn QNativeIpcKey::QNativeIpcKey(Type type) noexcept |
| 380 | \fn QNativeIpcKey::QNativeIpcKey(const QString &key, Type type) |
| 381 | |
| 382 | Constructs a QNativeIpcKey object holding native key \a key (or empty on |
| 383 | the overload without the parameter) for type \a type. |
| 384 | */ |
| 385 | |
| 386 | /*! |
| 387 | \fn QNativeIpcKey::QNativeIpcKey(const QNativeIpcKey &other) |
| 388 | \fn QNativeIpcKey::QNativeIpcKey(QNativeIpcKey &&other) noexcept |
| 389 | \fn QNativeIpcKey &QNativeIpcKey::operator=(const QNativeIpcKey &other) |
| 390 | \fn QNativeIpcKey &QNativeIpcKey::operator=(QNativeIpcKey &&other) noexcept |
| 391 | |
| 392 | Copies or moves the content of \a other. |
| 393 | */ |
| 394 | void QNativeIpcKey::copy_internal(const QNativeIpcKey &other) |
| 395 | { |
| 396 | d = new QNativeIpcKeyPrivate(*other.d); |
| 397 | } |
| 398 | |
| 399 | void QNativeIpcKey::move_internal(QNativeIpcKey &&) noexcept |
| 400 | { |
| 401 | // inline code already moved properly, nothing for us to do here |
| 402 | } |
| 403 | |
| 404 | QNativeIpcKey &QNativeIpcKey::assign_internal(const QNativeIpcKey &other) |
| 405 | { |
| 406 | Q_ASSERT(d || other.d); // only 3 cases to handle |
| 407 | if (d && !other.d) |
| 408 | *d = {}; |
| 409 | else if (d) |
| 410 | *d = *other.d; |
| 411 | else |
| 412 | d = new QNativeIpcKeyPrivate(*other.d); |
| 413 | return *this; |
| 414 | } |
| 415 | |
| 416 | /*! |
| 417 | \fn QNativeIpcKey::~QNativeIpcKey() |
| 418 | |
| 419 | Disposes of this QNativeIpcKey object. |
| 420 | */ |
| 421 | void QNativeIpcKey::destroy_internal() noexcept |
| 422 | { |
| 423 | delete d; |
| 424 | } |
| 425 | |
| 426 | /*! |
| 427 | \fn QNativeIpcKey::swap(QNativeIpcKey &other) noexcept |
| 428 | \memberswap{native IPC key and type} |
| 429 | */ |
| 430 | |
| 431 | /*! |
| 432 | \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept |
| 433 | \relates QNativeIpcKey |
| 434 | |
| 435 | Swaps the native IPC key and type \a value1 with \a value2. |
| 436 | This operation is very fast and never fails. |
| 437 | */ |
| 438 | |
| 439 | /*! |
| 440 | \fn QNativeIpcKey::isEmpty() const |
| 441 | |
| 442 | Returns true if the nativeKey() is empty. |
| 443 | |
| 444 | \sa nativeKey() |
| 445 | */ |
| 446 | |
| 447 | /*! |
| 448 | \fn QNativeIpcKey::isValid() const |
| 449 | |
| 450 | Returns true if this object contains a valid native IPC key type. Invalid |
| 451 | types are usually the result of a failure to parse a string representation |
| 452 | using fromString(). |
| 453 | |
| 454 | This function performs no check on the whether the key string is actually |
| 455 | supported or valid for the current operating system. |
| 456 | |
| 457 | \sa type(), fromString() |
| 458 | */ |
| 459 | |
| 460 | /*! |
| 461 | \fn QNativeIpcKey::type() const noexcept |
| 462 | |
| 463 | Returns the key type associated with this object. |
| 464 | |
| 465 | \sa nativeKey(), setType() |
| 466 | */ |
| 467 | |
| 468 | /*! |
| 469 | \fn QNativeIpcKey::setType(Type type) |
| 470 | |
| 471 | Sets the IPC type of this object to \a type. |
| 472 | |
| 473 | \sa type(), setNativeKey() |
| 474 | */ |
| 475 | void QNativeIpcKey::setType_internal(Type type) |
| 476 | { |
| 477 | Q_UNUSED(type); |
| 478 | } |
| 479 | |
| 480 | /*! |
| 481 | \fn QNativeIpcKey::nativeKey() const noexcept |
| 482 | |
| 483 | Returns the native key string associated with this object. |
| 484 | |
| 485 | \sa setNativeKey(), type() |
| 486 | */ |
| 487 | |
| 488 | /*! |
| 489 | \fn QNativeIpcKey::setNativeKey(const QString &newKey) |
| 490 | |
| 491 | Sets the native key for this object to \a newKey. |
| 492 | |
| 493 | \sa nativeKey(), setType() |
| 494 | */ |
| 495 | void QNativeIpcKey::setNativeKey_internal(const QString &) |
| 496 | { |
| 497 | d->legacyKey_.clear(); |
| 498 | } |
| 499 | |
| 500 | /*! |
| 501 | \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &key, size_t seed) |
| 502 | \qhash{QNativeIpcKey} |
| 503 | */ |
| 504 | size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept |
| 505 | { |
| 506 | // by *choice*, we're not including d->legacyKey_ in the hash -- it's |
| 507 | // already partially encoded in the key |
| 508 | return qHashMulti(seed, args: ipcKey.key, args: ipcKey.type()); |
| 509 | } |
| 510 | |
| 511 | /*! |
| 512 | \fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept |
| 513 | \fn bool QNativeIpcKey::operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept |
| 514 | |
| 515 | Returns true if the \a lhs and \a rhs objects hold the same (or different) contents. |
| 516 | */ |
| 517 | int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept |
| 518 | { |
| 519 | return (QNativeIpcKeyPrivate::legacyKey(key: lhs) == QNativeIpcKeyPrivate::legacyKey(key: rhs)) ? 0 : 1; |
| 520 | } |
| 521 | |
| 522 | /*! |
| 523 | Returns the string representation of this object. String representations |
| 524 | are useful to inform other processes of the key this process created and |
| 525 | that they should attach to. |
| 526 | |
| 527 | This function returns a null string if the current object is |
| 528 | \l{isValid()}{invalid}. |
| 529 | |
| 530 | \sa fromString() |
| 531 | */ |
| 532 | QString QNativeIpcKey::toString() const |
| 533 | { |
| 534 | QString prefix = typeToString(type: type()); |
| 535 | if (prefix.isEmpty()) { |
| 536 | Q_ASSERT(prefix.isNull()); |
| 537 | return prefix; |
| 538 | } |
| 539 | |
| 540 | QString copy = nativeKey(); |
| 541 | copy.replace(c: u'%', after: "%25"_L1 ); |
| 542 | if (copy.startsWith(s: "//"_L1 )) |
| 543 | copy.replace(i: 0, len: 2, after: u"/%2F"_s ); // ensure it's parsed as a URL path |
| 544 | |
| 545 | QUrl u; |
| 546 | u.setScheme(prefix); |
| 547 | u.setPath(path: copy, mode: QUrl::TolerantMode); |
| 548 | if (isSlowPath()) { |
| 549 | QUrlQuery q; |
| 550 | if (!d->legacyKey_.isEmpty()) |
| 551 | q.addQueryItem(key: u"legacyKey"_s , value: QString(d->legacyKey_).replace(c: u'%', after: "%25"_L1 )); |
| 552 | u.setQuery(q); |
| 553 | } |
| 554 | return u.toString(options: QUrl::DecodeReserved); |
| 555 | } |
| 556 | |
| 557 | /*! |
| 558 | Parses the string form \a text and returns the corresponding QNativeIpcKey. |
| 559 | String representations are useful to inform other processes of the key this |
| 560 | process created and they should attach to. |
| 561 | |
| 562 | If the string could not be parsed, this function returns an |
| 563 | \l{isValid()}{invalid} object. |
| 564 | |
| 565 | \sa toString(), isValid() |
| 566 | */ |
| 567 | QNativeIpcKey QNativeIpcKey::fromString(const QString &text) |
| 568 | { |
| 569 | QUrl u(text, QUrl::TolerantMode); |
| 570 | Type invalidType = {}; |
| 571 | Type type = stringToType(typeString: u.scheme()); |
| 572 | if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty() |
| 573 | || u.port() != -1) |
| 574 | return QNativeIpcKey(invalidType); |
| 575 | |
| 576 | QNativeIpcKey result(QString(), type); |
| 577 | if (result.type() != type) // range check, just in case |
| 578 | return QNativeIpcKey(invalidType); |
| 579 | |
| 580 | // decode the payload |
| 581 | result.setNativeKey(u.path()); |
| 582 | |
| 583 | if (u.hasQuery()) { |
| 584 | const QList items = QUrlQuery(u).queryItems(); |
| 585 | for (const auto &item : items) { |
| 586 | if (item.first == u"legacyKey"_s ) { |
| 587 | QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8()); |
| 588 | QNativeIpcKeyPrivate::setLegacyKey(key&: result, legacyKey: std::move(legacyKey)); |
| 589 | } else { |
| 590 | // unknown query item |
| 591 | return QNativeIpcKey(invalidType); |
| 592 | } |
| 593 | } |
| 594 | } |
| 595 | return result; |
| 596 | } |
| 597 | |
| 598 | QT_END_NAMESPACE |
| 599 | |
| 600 | #include "moc_qtipccommon.cpp" |
| 601 | |
| 602 | #endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore) |
| 603 | |