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