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