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 | |
428 | Swaps the native IPC key and type \a other with this object. |
429 | This operation is very fast and never fails. |
430 | */ |
431 | |
432 | /*! |
433 | \fn swap(QNativeIpcKey &value1, QNativeIpcKey &value2) noexcept |
434 | \relates QNativeIpcKey |
435 | |
436 | Swaps the native IPC key and type \a value1 with \a value2. |
437 | This operation is very fast and never fails. |
438 | */ |
439 | |
440 | /*! |
441 | \fn QNativeIpcKey::isEmpty() const |
442 | |
443 | Returns true if the nativeKey() is empty. |
444 | |
445 | \sa nativeKey() |
446 | */ |
447 | |
448 | /*! |
449 | \fn QNativeIpcKey::isValid() const |
450 | |
451 | Returns true if this object contains a valid native IPC key type. Invalid |
452 | types are usually the result of a failure to parse a string representation |
453 | using fromString(). |
454 | |
455 | This function performs no check on the whether the key string is actually |
456 | supported or valid for the current operating system. |
457 | |
458 | \sa type(), fromString() |
459 | */ |
460 | |
461 | /*! |
462 | \fn QNativeIpcKey::type() const noexcept |
463 | |
464 | Returns the key type associated with this object. |
465 | |
466 | \sa nativeKey(), setType() |
467 | */ |
468 | |
469 | /*! |
470 | \fn QNativeIpcKey::setType(Type type) |
471 | |
472 | Sets the IPC type of this object to \a type. |
473 | |
474 | \sa type(), setNativeKey() |
475 | */ |
476 | void QNativeIpcKey::setType_internal(Type type) |
477 | { |
478 | Q_UNUSED(type); |
479 | } |
480 | |
481 | /*! |
482 | \fn QNativeIpcKey::nativeKey() const noexcept |
483 | |
484 | Returns the native key string associated with this object. |
485 | |
486 | \sa setNativeKey(), type() |
487 | */ |
488 | |
489 | /*! |
490 | \fn QNativeIpcKey::setNativeKey(const QString &newKey) |
491 | |
492 | Sets the native key for this object to \a newKey. |
493 | |
494 | \sa nativeKey(), setType() |
495 | */ |
496 | void QNativeIpcKey::setNativeKey_internal(const QString &) |
497 | { |
498 | d->legacyKey_.clear(); |
499 | } |
500 | |
501 | /*! |
502 | \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey) noexcept |
503 | |
504 | Returns the hash value for \a ipcKey, using a default seed of \c 0. |
505 | */ |
506 | |
507 | /*! |
508 | \fn size_t QNativeIpcKey::qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept |
509 | |
510 | Returns the hash value for \a ipcKey, using \a seed to seed the calculation. |
511 | */ |
512 | size_t qHash(const QNativeIpcKey &ipcKey, size_t seed) noexcept |
513 | { |
514 | // by *choice*, we're not including d->legacyKey_ in the hash -- it's |
515 | // already partially encoded in the key |
516 | return qHashMulti(seed, args: ipcKey.key, args: ipcKey.type()); |
517 | } |
518 | |
519 | /*! |
520 | \fn bool QNativeIpcKey::operator==(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept |
521 | \fn bool QNativeIpcKey::operator!=(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept |
522 | |
523 | Returns true if the \a lhs and \a rhs objects hold the same (or different) contents. |
524 | */ |
525 | int QNativeIpcKey::compare_internal(const QNativeIpcKey &lhs, const QNativeIpcKey &rhs) noexcept |
526 | { |
527 | return (QNativeIpcKeyPrivate::legacyKey(key: lhs) == QNativeIpcKeyPrivate::legacyKey(key: rhs)) ? 0 : 1; |
528 | } |
529 | |
530 | /*! |
531 | Returns the string representation of this object. String representations |
532 | are useful to inform other processes of the key this process created and |
533 | that they should attach to. |
534 | |
535 | This function returns a null string if the current object is |
536 | \l{isValid()}{invalid}. |
537 | |
538 | \sa fromString() |
539 | */ |
540 | QString QNativeIpcKey::toString() const |
541 | { |
542 | QString prefix = typeToString(type: type()); |
543 | if (prefix.isEmpty()) { |
544 | Q_ASSERT(prefix.isNull()); |
545 | return prefix; |
546 | } |
547 | |
548 | QString copy = nativeKey(); |
549 | copy.replace(c: u'%', after: "%25"_L1 ); |
550 | if (copy.startsWith(s: "//"_L1 )) |
551 | copy.replace(i: 0, len: 2, after: u"/%2F"_s ); // ensure it's parsed as a URL path |
552 | |
553 | QUrl u; |
554 | u.setScheme(prefix); |
555 | u.setPath(path: copy, mode: QUrl::TolerantMode); |
556 | if (isSlowPath()) { |
557 | QUrlQuery q; |
558 | if (!d->legacyKey_.isEmpty()) |
559 | q.addQueryItem(key: u"legacyKey"_s , value: QString(d->legacyKey_).replace(c: u'%', after: "%25"_L1 )); |
560 | u.setQuery(q); |
561 | } |
562 | return u.toString(options: QUrl::DecodeReserved); |
563 | } |
564 | |
565 | /*! |
566 | Parses the string form \a text and returns the corresponding QNativeIpcKey. |
567 | String representations are useful to inform other processes of the key this |
568 | process created and they should attach to. |
569 | |
570 | If the string could not be parsed, this function returns an |
571 | \l{isValid()}{invalid} object. |
572 | |
573 | \sa toString(), isValid() |
574 | */ |
575 | QNativeIpcKey QNativeIpcKey::fromString(const QString &text) |
576 | { |
577 | QUrl u(text, QUrl::TolerantMode); |
578 | Type invalidType = {}; |
579 | Type type = stringToType(typeString: u.scheme()); |
580 | if (type == invalidType || !u.isValid() || !u.userInfo().isEmpty() || !u.host().isEmpty() |
581 | || u.port() != -1) |
582 | return QNativeIpcKey(invalidType); |
583 | |
584 | QNativeIpcKey result(QString(), type); |
585 | if (result.type() != type) // range check, just in case |
586 | return QNativeIpcKey(invalidType); |
587 | |
588 | // decode the payload |
589 | result.setNativeKey(u.path()); |
590 | |
591 | if (u.hasQuery()) { |
592 | const QList items = QUrlQuery(u).queryItems(); |
593 | for (const auto &item : items) { |
594 | if (item.first == u"legacyKey"_s ) { |
595 | QString legacyKey = QUrl::fromPercentEncoding(item.second.toUtf8()); |
596 | QNativeIpcKeyPrivate::setLegacyKey(key&: result, legacyKey: std::move(legacyKey)); |
597 | } else { |
598 | // unknown query item |
599 | return QNativeIpcKey(invalidType); |
600 | } |
601 | } |
602 | } |
603 | return result; |
604 | } |
605 | |
606 | QT_END_NAMESPACE |
607 | |
608 | #include "moc_qtipccommon.cpp" |
609 | |
610 | #endif // QT_CONFIG(sharedmemory) || QT_CONFIG(systemsemaphore) |
611 | |