| 1 | /**************************************************************************** |
| 2 | ** |
| 3 | ** Copyright (C) 2015 The Qt Company Ltd and/or its subsidiary(-ies). |
| 4 | ** Contact: http://www.qt-project.org/legal |
| 5 | ** |
| 6 | ** This file is part of the QtSystems module of the Qt Toolkit. |
| 7 | ** |
| 8 | ** $QT_BEGIN_LICENSE:LGPL21$ |
| 9 | ** Commercial License Usage |
| 10 | ** Licensees holding valid commercial Qt licenses may use this file in |
| 11 | ** accordance with the commercial license agreement provided with the |
| 12 | ** Software or, alternatively, in accordance with the terms contained in |
| 13 | ** a written agreement between you and The Qt Company. For licensing terms |
| 14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
| 15 | ** information use the contact form at http://www.qt.io/contact-us. |
| 16 | ** |
| 17 | ** GNU Lesser General Public License Usage |
| 18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
| 19 | ** General Public License version 2.1 or version 3 as published by the Free |
| 20 | ** Software Foundation and appearing in the file LICENSE.LGPLv21 and |
| 21 | ** LICENSE.LGPLv3 included in the packaging of this file. Please review the |
| 22 | ** following information to ensure the GNU Lesser General Public License |
| 23 | ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and |
| 24 | ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| 25 | ** |
| 26 | ** As a special exception, The Qt Company gives you certain additional |
| 27 | ** rights. These rights are described in The Qt Company LGPL Exception |
| 28 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| 29 | ** |
| 30 | ** $QT_END_LICENSE$ |
| 31 | ** |
| 32 | ****************************************************************************/ |
| 33 | |
| 34 | #include <QRegExp> |
| 35 | #include <QStringList> |
| 36 | #include <QDebug> |
| 37 | #ifndef QT_NO_DATASTREAM |
| 38 | #include <qdatastream.h> |
| 39 | #endif |
| 40 | |
| 41 | #include "qservicefilter.h" |
| 42 | |
| 43 | QT_BEGIN_NAMESPACE |
| 44 | |
| 45 | class QServiceFilterPrivate |
| 46 | { |
| 47 | public: |
| 48 | QString interface; |
| 49 | QString service; |
| 50 | int majorVersion; |
| 51 | int minorVersion; |
| 52 | QServiceFilter::VersionMatchRule matchingRule; |
| 53 | QHash<QString,QString> customAttributes; |
| 54 | QStringList capabilities; |
| 55 | QServiceFilter::CapabilityMatchRule capMatchingRule; |
| 56 | }; |
| 57 | |
| 58 | |
| 59 | /*! |
| 60 | \class QServiceFilter |
| 61 | |
| 62 | \ingroup servicefw |
| 63 | \inmodule QtServiceFramework |
| 64 | \brief The QServiceFilter class defines criteria for defining a sub-set of |
| 65 | all available services. |
| 66 | |
| 67 | A QServiceFilter can be used to constrain the number of services when searching |
| 68 | for services. Only those services that match all filter criteria are returned |
| 69 | by \l QServiceManager::findInterfaces(). |
| 70 | |
| 71 | |
| 72 | \sa QServiceInterfaceDescriptor, QServiceManager |
| 73 | */ |
| 74 | |
| 75 | /*! |
| 76 | \enum QServiceFilter::VersionMatchRule |
| 77 | |
| 78 | This enum describes how interface version matching is performed. |
| 79 | |
| 80 | \value ExactVersionMatch The filter matches any interface implementation that implements |
| 81 | the exact version provided. |
| 82 | \value MinimumVersionMatch The filter matches any interface implementation that implements |
| 83 | either the given major/minor version or any subsequent version. |
| 84 | */ |
| 85 | |
| 86 | /*! |
| 87 | \enum QServiceFilter::CapabilityMatchRule |
| 88 | |
| 89 | This enum describes the capability/permission matching rules. Some platforms restrict what services clients |
| 90 | can access using "capabilities" or permissions. Services with more capabilities require |
| 91 | more privileged clients. Platforms without capabilities may ignore this type of matching |
| 92 | rule as the default behavior is to ignore any capability restrictions. |
| 93 | |
| 94 | This is a brief example. Assuming that the system knows the services S1 - S6 which require capabilities as stated below: |
| 95 | \table |
| 96 | \header \li Service \li Required capabilities |
| 97 | \row \li S1 \li \{\} |
| 98 | \row \li S2 \li \{A\} |
| 99 | \row \li S3 \li \{A,B\} |
| 100 | \row \li S4 \li \{A,B,C,D\} |
| 101 | \row \li S5 \li \{A,D\} |
| 102 | \row \li S6 \li \{F\} |
| 103 | \endtable |
| 104 | |
| 105 | The matching rules would apply as follows: |
| 106 | |
| 107 | \table |
| 108 | \header \li Matching rule \li Filter's capabilities \li Matching services |
| 109 | \row \li MatchLoadable \li \{\} \li S1 |
| 110 | \row \li MatchLoadable \li \{A\} \li S1, S2 |
| 111 | \row \li MatchLoadable \li \{A,B,C\} \li S1, S2, S3 |
| 112 | \row \li MatchMinimum \li \{\} \li S1, S2, S3, S4, S5, S6 |
| 113 | \row \li MatchMinimum \li \{A\} \li S2, S3, S4, S5 |
| 114 | \row \li MatchMinimum \li \{A,B,C\} \li S4 |
| 115 | \endtable |
| 116 | |
| 117 | \value MatchMinimum The filter matches any service that requires at least the given |
| 118 | filter capabilities. This may mean that the returned services |
| 119 | may require more capabilities than the specified ones. |
| 120 | Such a search is equivalent to a wildcard match if the passed filter's capability list is empty. In mathematical set notation |
| 121 | this rule is equivalent to Cap\sub{(Filter)} \\ Cap\sub{(Service)} = {}. This is the default matching rule. |
| 122 | \value MatchLoadable The filter matches any service that could be loaded by the client. |
| 123 | Using this matching rule guarantees that the returned services do not |
| 124 | require more capabilites than specified by this rule. It includes services |
| 125 | with no capability requirements. If this rule |
| 126 | is provided alongside an empty capability search list the returned |
| 127 | services do not require any capabilities and thus can be accessed |
| 128 | by any client. The equivalent set notation is Cap\sub{(Service)} \\ Cap\sub{(Filter)} = {}. |
| 129 | */ |
| 130 | |
| 131 | /*! |
| 132 | Creates a new filter object that matches all service implementations. |
| 133 | */ |
| 134 | QServiceFilter::QServiceFilter() |
| 135 | { |
| 136 | d = new QServiceFilterPrivate(); |
| 137 | d->majorVersion = -1; |
| 138 | d->minorVersion = -1; |
| 139 | d->matchingRule = QServiceFilter::MinimumVersionMatch; |
| 140 | d->capMatchingRule = QServiceFilter::MatchMinimum; |
| 141 | } |
| 142 | |
| 143 | /*! |
| 144 | Creates a copy of QServiceFilter object contained in \a other. |
| 145 | */ |
| 146 | QServiceFilter::QServiceFilter(const QServiceFilter& other) |
| 147 | { |
| 148 | d = new QServiceFilterPrivate(); |
| 149 | (*this) = other; |
| 150 | } |
| 151 | |
| 152 | /*! |
| 153 | \fn QServiceFilter::QServiceFilter(const QString& interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) |
| 154 | |
| 155 | Creates a new filter object that matches all service |
| 156 | implementations implementing \a interfaceName that match the specified |
| 157 | \a version using the given \a rule. |
| 158 | */ |
| 159 | QServiceFilter::QServiceFilter(const QString& interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) |
| 160 | { |
| 161 | d = new QServiceFilterPrivate(); |
| 162 | d->majorVersion = -1; |
| 163 | d->minorVersion = -1; |
| 164 | d->matchingRule = QServiceFilter::MinimumVersionMatch; |
| 165 | d->capMatchingRule = QServiceFilter::MatchMinimum; |
| 166 | setInterface(interfaceName, version, rule); |
| 167 | } |
| 168 | |
| 169 | /*! |
| 170 | Destroys this instance of QServiceFilter. |
| 171 | */ |
| 172 | QServiceFilter::~QServiceFilter() |
| 173 | { |
| 174 | delete d; |
| 175 | } |
| 176 | |
| 177 | /*! |
| 178 | \fn QServiceFilter& QServiceFilter::operator=(const QServiceFilter& other) |
| 179 | |
| 180 | Copies the content of the QServiceFilter object contained in |
| 181 | \a other into this one. |
| 182 | */ |
| 183 | QServiceFilter& QServiceFilter::operator=(const QServiceFilter& other) |
| 184 | { |
| 185 | if (&other == this) |
| 186 | return *this; |
| 187 | |
| 188 | d->interface = other.d->interface; |
| 189 | d->service = other.d->service; |
| 190 | d->majorVersion = other.d->majorVersion; |
| 191 | d->minorVersion = other.d->minorVersion; |
| 192 | d->matchingRule = other.d->matchingRule; |
| 193 | d->customAttributes = other.d->customAttributes; |
| 194 | d->capabilities = other.d->capabilities; |
| 195 | d->capMatchingRule = other.d->capMatchingRule; |
| 196 | |
| 197 | return *this; |
| 198 | } |
| 199 | |
| 200 | /*! |
| 201 | \fn void QServiceFilter::setServiceName(const QString& serviceName) |
| 202 | |
| 203 | The filter only matches implementations which are provided by the service |
| 204 | specified by \a serviceName. |
| 205 | |
| 206 | If the \a serviceName is empty the filter matches any service. |
| 207 | */ |
| 208 | void QServiceFilter::setServiceName(const QString& serviceName) |
| 209 | { |
| 210 | d->service = serviceName; |
| 211 | } |
| 212 | |
| 213 | /*! |
| 214 | \fn void QServiceFilter::setInterface(const QString &interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) |
| 215 | |
| 216 | Sets the filter to match any interface implementation that implements |
| 217 | \a interfaceName with version \a version. The version is matched |
| 218 | according to the given \a rule. If \a version is not set, the filter matches any version of the |
| 219 | interface implementation. |
| 220 | |
| 221 | This method does nothing if \a version is not a valid version string or |
| 222 | if \a interfaceName is empty. |
| 223 | |
| 224 | A valid version string has the format x.y whereby x and y are positive integer |
| 225 | numbers. |
| 226 | */ |
| 227 | void QServiceFilter::setInterface(const QString &interfaceName, const QString& version, QServiceFilter::VersionMatchRule rule) |
| 228 | { |
| 229 | //unset interface name |
| 230 | if (interfaceName.isEmpty() && version.isEmpty()) |
| 231 | { |
| 232 | d->interface = interfaceName; |
| 233 | d->majorVersion = d->minorVersion = -1; |
| 234 | d->matchingRule = rule; |
| 235 | return; |
| 236 | } |
| 237 | |
| 238 | if (interfaceName.isEmpty()) { |
| 239 | qWarning() << "Empty interface name. Ignoring filter details" ; |
| 240 | return; |
| 241 | } |
| 242 | |
| 243 | if (version.isEmpty()) { |
| 244 | d->majorVersion = d->minorVersion = -1; |
| 245 | d->matchingRule = rule; |
| 246 | d->interface = interfaceName; |
| 247 | return; |
| 248 | } |
| 249 | |
| 250 | // Match x.y as version format. |
| 251 | // This differs from regex in servicemetadata in that 0.x versions are |
| 252 | // accepted for the search filter. |
| 253 | QRegExp rx(QLatin1String("^(0+|[1-9][0-9]*)\\.(0+|[1-9][0-9]*)$" )); |
| 254 | int pos = rx.indexIn(str: version); |
| 255 | QStringList list = rx.capturedTexts(); |
| 256 | bool success = false; |
| 257 | int temp_major = -1; |
| 258 | int temp_minor = -1; |
| 259 | if (pos == 0 && list.count() == 3 |
| 260 | && rx.matchedLength() == version.length() ) |
| 261 | { |
| 262 | temp_major = list[1].toInt(ok: &success); |
| 263 | if ( success ) { |
| 264 | temp_minor = list[2].toInt(ok: &success); |
| 265 | } |
| 266 | } |
| 267 | |
| 268 | if (success) { |
| 269 | d->majorVersion = temp_major; |
| 270 | d->minorVersion = temp_minor; |
| 271 | d->interface = interfaceName; |
| 272 | d->matchingRule = rule; |
| 273 | } else { |
| 274 | qWarning() << "Invalid version tag" << version << ". Ignoring filter details." ; |
| 275 | } |
| 276 | } |
| 277 | |
| 278 | /*! |
| 279 | \fn QString QServiceFilter::serviceName() const |
| 280 | |
| 281 | Returns the service name for this filter. |
| 282 | |
| 283 | \sa setServiceName() |
| 284 | */ |
| 285 | QString QServiceFilter::serviceName() const |
| 286 | { |
| 287 | return d->service; |
| 288 | } |
| 289 | |
| 290 | /*! |
| 291 | \fn QString QServiceFilter::interfaceName() const |
| 292 | |
| 293 | Returns the interface name for this filter. |
| 294 | |
| 295 | \sa setInterface() |
| 296 | */ |
| 297 | QString QServiceFilter::interfaceName() const |
| 298 | { |
| 299 | return d->interface; |
| 300 | } |
| 301 | |
| 302 | /*! |
| 303 | \fn int QServiceFilter::majorVersion() const |
| 304 | |
| 305 | Returns the major interface version for this filter. |
| 306 | |
| 307 | \sa setInterface() |
| 308 | */ |
| 309 | int QServiceFilter::majorVersion() const |
| 310 | { |
| 311 | return d->majorVersion; |
| 312 | } |
| 313 | |
| 314 | /*! |
| 315 | \fn int QServiceFilter::minorVersion() const |
| 316 | |
| 317 | Returns the minor interface version for this filter. |
| 318 | |
| 319 | \sa setInterface() |
| 320 | */ |
| 321 | int QServiceFilter::minorVersion() const |
| 322 | { |
| 323 | return d->minorVersion; |
| 324 | } |
| 325 | |
| 326 | /*! |
| 327 | \fn void QServiceFilter::setCustomAttribute(const QString& key, const QString& value) |
| 328 | |
| 329 | The filter only matches implementations which have the custom attribute |
| 330 | \a key with the given \a value. Such constraints are specified via the |
| 331 | \e{<customproperty>} tag within the service xml. |
| 332 | |
| 333 | \sa customAttribute(), clearCustomAttribute() |
| 334 | */ |
| 335 | void QServiceFilter::setCustomAttribute(const QString& key, const QString& value) |
| 336 | { |
| 337 | d->customAttributes.insert(akey: key, avalue: value); |
| 338 | } |
| 339 | |
| 340 | /*! |
| 341 | \fn QString QServiceFilter::customAttribute(const QString& key) const |
| 342 | |
| 343 | Returns the value for the custom attribute \a key; otherwise |
| 344 | returns a null string. |
| 345 | |
| 346 | \sa setCustomAttribute(), clearCustomAttribute() |
| 347 | */ |
| 348 | QString QServiceFilter::customAttribute(const QString& key) const |
| 349 | { |
| 350 | return d->customAttributes.value(akey: key); |
| 351 | } |
| 352 | |
| 353 | /*! |
| 354 | \fn void QServiceFilter::clearCustomAttribute(const QString &key) |
| 355 | |
| 356 | Clears the custom attribute \a key from the filter's set of constraints. |
| 357 | If \a key is empty all custom attributes are cleared. |
| 358 | |
| 359 | \sa setCustomAttribute() |
| 360 | */ |
| 361 | void QServiceFilter::clearCustomAttribute(const QString &key) |
| 362 | { |
| 363 | if (key.isEmpty()) |
| 364 | d->customAttributes.clear(); |
| 365 | else |
| 366 | d->customAttributes.remove(akey: key); |
| 367 | } |
| 368 | |
| 369 | /*! |
| 370 | \fn QServiceFilter::VersionMatchRule QServiceFilter::versionMatchRule() const |
| 371 | |
| 372 | Returns the version match rule for this filter. |
| 373 | |
| 374 | \sa setInterface() |
| 375 | */ |
| 376 | QServiceFilter::VersionMatchRule QServiceFilter::versionMatchRule() const |
| 377 | { |
| 378 | return d->matchingRule; |
| 379 | } |
| 380 | |
| 381 | /*! |
| 382 | \fn QList<QString> QServiceFilter::customAttributes() const |
| 383 | |
| 384 | Returns the list of custom keys which have been added to the filter. |
| 385 | */ |
| 386 | QStringList QServiceFilter::customAttributes() const |
| 387 | { |
| 388 | return d->customAttributes.keys(); |
| 389 | } |
| 390 | |
| 391 | /*! |
| 392 | \fn void QServiceFilter::setCapabilities(QServiceFilter::CapabilityMatchRule rule, const QStringList& capabilities ) |
| 393 | |
| 394 | Sets the list of \a capabilities which are used to constrain |
| 395 | searches for services. The capabilities are matched according |
| 396 | to the given \a rule. |
| 397 | |
| 398 | \sa capabilities() |
| 399 | */ |
| 400 | void QServiceFilter::setCapabilities(QServiceFilter::CapabilityMatchRule rule, const QStringList& capabilities ) |
| 401 | { |
| 402 | d->capMatchingRule = rule; |
| 403 | d->capabilities = capabilities; |
| 404 | } |
| 405 | |
| 406 | /*! |
| 407 | \fn QStringList QServiceFilter::capabilities() const |
| 408 | |
| 409 | Returns the list of capabilities which are used to limit services searches. |
| 410 | |
| 411 | The filter matches any services that requires the given or less |
| 412 | capabilities and thus enabling clients to query for services |
| 413 | for which they have the required capabilties. |
| 414 | |
| 415 | \sa setCapabilities(), capabilityMatchRule() |
| 416 | */ |
| 417 | QStringList QServiceFilter::capabilities() const |
| 418 | { |
| 419 | return d->capabilities; |
| 420 | } |
| 421 | |
| 422 | /*! |
| 423 | Returns the capability matching rule for this filter. |
| 424 | |
| 425 | \sa setCapabilities(), capabilities() |
| 426 | */ |
| 427 | QServiceFilter::CapabilityMatchRule QServiceFilter::capabilityMatchRule() const |
| 428 | { |
| 429 | return d->capMatchingRule; |
| 430 | } |
| 431 | |
| 432 | #ifndef QT_NO_DATASTREAM |
| 433 | /*! |
| 434 | \fn QDataStream &operator<<(QDataStream &out, const QServiceFilter &sf) |
| 435 | \relates QServiceFilter |
| 436 | |
| 437 | Writes service filter \a sf to the stream \a out and returns a reference |
| 438 | to the stream. |
| 439 | */ |
| 440 | QDataStream &operator<<(QDataStream &out, const QServiceFilter &sf) |
| 441 | { |
| 442 | const quint32 magicNumber = 0x78AFAFA; |
| 443 | const qint32 mj = sf.d->majorVersion; |
| 444 | const qint32 mn = sf.d->minorVersion; |
| 445 | const qint8 versionrule = (qint32) sf.d->matchingRule; |
| 446 | const qint8 caprule = (qint8) sf.d->capMatchingRule; |
| 447 | const quint16 majorVersion = 1; |
| 448 | const quint16 minorVersion = 0; |
| 449 | |
| 450 | out << magicNumber |
| 451 | << majorVersion |
| 452 | << minorVersion |
| 453 | << sf.d->interface |
| 454 | << sf.d->service |
| 455 | << mj |
| 456 | << mn |
| 457 | << versionrule |
| 458 | << sf.d->customAttributes |
| 459 | << caprule |
| 460 | << sf.d->capabilities; |
| 461 | return out; |
| 462 | } |
| 463 | |
| 464 | /*! |
| 465 | \fn QDataStream &operator>>(QDataStream &in, QServiceFilter &sf) |
| 466 | \relates QServiceFilter |
| 467 | |
| 468 | Reads a service filter into \a sf from the stream \a in and returns a |
| 469 | reference to the stream. |
| 470 | */ |
| 471 | QDataStream &operator>>(QDataStream &in, QServiceFilter &sf) |
| 472 | { |
| 473 | const quint32 magicNumber = 0x78AFAFA; |
| 474 | qint32 mj, mn; |
| 475 | qint8 versionrule, caprule; |
| 476 | |
| 477 | quint32 storedMagicNumber; |
| 478 | in >> storedMagicNumber; |
| 479 | if (storedMagicNumber != magicNumber) { |
| 480 | qWarning() << Q_FUNC_INFO << "Datastream doesn't provide serialized QServiceFilter" ; |
| 481 | return in; |
| 482 | } |
| 483 | |
| 484 | const quint16 currentMajorVersion = 1; |
| 485 | quint16 majorVersion = 0; |
| 486 | quint16 minorVersion = 0; |
| 487 | |
| 488 | in >> majorVersion >> minorVersion; |
| 489 | if (majorVersion != currentMajorVersion) { |
| 490 | qWarning() << "Unknown serialization format for QServiceFilter." ; |
| 491 | return in; |
| 492 | } |
| 493 | //Allow all minor versions. |
| 494 | |
| 495 | in >> sf.d->interface |
| 496 | >> sf.d->service |
| 497 | >> mj |
| 498 | >> mn |
| 499 | >> versionrule |
| 500 | >> sf.d->customAttributes |
| 501 | >> caprule |
| 502 | >> sf.d->capabilities; |
| 503 | |
| 504 | sf.d->majorVersion = mj; |
| 505 | sf.d->minorVersion = mn; |
| 506 | sf.d->matchingRule = (QServiceFilter::VersionMatchRule) versionrule; |
| 507 | sf.d->capMatchingRule = (QServiceFilter::CapabilityMatchRule) caprule; |
| 508 | |
| 509 | return in; |
| 510 | } |
| 511 | #endif //QT_NO_DATASTREAM |
| 512 | |
| 513 | |
| 514 | QT_END_NAMESPACE |
| 515 | |
| 516 | |