| 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 "servicemetadata_p.h" | 
| 35 | #include <QFile> | 
| 36 | #include <QDataStream> | 
| 37 | #include <QDebug> | 
| 38 | #include "qserviceinterfacedescriptor_p.h" | 
| 39 |  | 
| 40 | //XML tags and attributes | 
| 41 | //General | 
| 42 | #define NAME_TAG  "name" | 
| 43 | #define DESCRIPTION_TAG "description" | 
| 44 | #define SERVICEFW_TAG "SFW" | 
| 45 | #define XML_MAX "1.1" | 
| 46 |  | 
| 47 | //Service related | 
| 48 | #define SERVICE_TAG "service" | 
| 49 | #define SERVICE_FILEPATH "filepath" | 
| 50 | #define SERVICE_IPCADDRESS "ipcaddress" | 
| 51 |  | 
| 52 | //Interface related | 
| 53 | #define INTERFACE_TAG "interface" | 
| 54 | #define INTERFACE_VERSION "version" | 
| 55 | #define INTERFACE_CAPABILITY "capabilities" | 
| 56 | #define INTERFACE_CUSTOM_PROPERTY "customproperty" | 
| 57 |  | 
| 58 | //Service type prefix | 
| 59 | #define SERVICE_IPC_PREFIX "_q_ipc_addr:" | 
| 60 |  | 
| 61 | QT_BEGIN_NAMESPACE | 
| 62 |  | 
| 63 | #ifndef QT_NO_DATASTREAM | 
| 64 | SERVICEMETADATA_EXPORT QDataStream &operator<<(QDataStream &out, const ServiceMetaDataResults &r) | 
| 65 | { | 
| 66 |     out << r.type << r.name << r.location; | 
| 67 |     out << r.description << r.interfaces << r.latestInterfaces; | 
| 68 |  | 
| 69 |     return out; | 
| 70 | } | 
| 71 |  | 
| 72 | SERVICEMETADATA_EXPORT QDataStream &operator>>(QDataStream &in, ServiceMetaDataResults &r) | 
| 73 | { | 
| 74 |     in >> r.type >> r.name >> r.location; | 
| 75 |     in >> r.description >> r.interfaces >> r.latestInterfaces; | 
| 76 |  | 
| 77 |     return in; | 
| 78 | } | 
| 79 | #endif | 
| 80 |  | 
| 81 | /* | 
| 82 |     \class ServiceMetaData | 
| 83 |  | 
| 84 |  | 
| 85 |     Utility class (used by service database) that offers support for | 
| 86 |     parsing metadata service xml registry file during service registration. \n | 
| 87 |  | 
| 88 |     It uses QXMLStreamReader class for parsing. Supproted Operations are: | 
| 89 |         - Parse the service and interfaces defined in XML file | 
| 90 |         - name, version, capabilitiesList, description and filePath of service can be retrieved | 
| 91 |         - each interface can be retrieved | 
| 92 | */ | 
| 93 |  | 
| 94 | /* | 
| 95 |  *  Class constructor | 
| 96 |  * | 
| 97 |  * @param aXmlFilePath path to the xml file that describes the service. | 
| 98 |  */ | 
| 99 | ServiceMetaData::ServiceMetaData(const QString &aXmlFilePath) | 
| 100 |     : xmlDevice(new QFile(aXmlFilePath)), | 
| 101 |       ownsXmlDevice(true), | 
| 102 |       serviceType(QService::Plugin), | 
| 103 |       latestError(0) | 
| 104 | {} | 
| 105 |  | 
| 106 | /* | 
| 107 |  *  Class constructor | 
| 108 |  * | 
| 109 |  * @param device QIODevice that contains the XML data that describes the service. | 
| 110 |  */ | 
| 111 | ServiceMetaData::ServiceMetaData(QIODevice *device) | 
| 112 |     : xmlDevice(device), | 
| 113 |       ownsXmlDevice(false), | 
| 114 |       serviceType(QService::Plugin), | 
| 115 |       latestError(0) | 
| 116 | {} | 
| 117 |  | 
| 118 | /* | 
| 119 |  *  Class destructor | 
| 120 |  * | 
| 121 |  */ | 
| 122 | ServiceMetaData::~ServiceMetaData() | 
| 123 | { | 
| 124 |     if (ownsXmlDevice) | 
| 125 |         delete xmlDevice; | 
| 126 | } | 
| 127 |  | 
| 128 | /* | 
| 129 |     Sets the device containing the XML data that describes the service to \a device. | 
| 130 |  */ | 
| 131 | void ServiceMetaData::setDevice(QIODevice *device) | 
| 132 | { | 
| 133 |     clearMetadata(); | 
| 134 |     xmlDevice = device; | 
| 135 |     ownsXmlDevice = false; | 
| 136 | } | 
| 137 |  | 
| 138 | /* | 
| 139 |     Returns the device containing the XML data that describes the service. | 
| 140 | */ | 
| 141 | QIODevice *ServiceMetaData::device() const | 
| 142 | { | 
| 143 |     return xmlDevice; | 
| 144 | } | 
| 145 |  | 
| 146 | /* | 
| 147 |  *  Gets the service name | 
| 148 |  * | 
| 149 |  * @return service name or default value (empty string) if it is not available | 
| 150 |  */ | 
| 151 | /*QString ServiceMetaData::name() const | 
| 152 | { | 
| 153 |     return serviceName; | 
| 154 | }*/ | 
| 155 |  | 
| 156 | /* | 
| 157 |  *  Gets the path of the service implementation file | 
| 158 |  * | 
| 159 |  * @return service implementation filepath | 
| 160 |  */ | 
| 161 | /*QString ServiceMetaData::location() const | 
| 162 | { | 
| 163 |     return serviceLocation; | 
| 164 | }*/ | 
| 165 |  | 
| 166 | /* | 
| 167 |  *  Gets the service description | 
| 168 |  * | 
| 169 |  * @return service description or default value (empty string) if it is not available | 
| 170 |  */ | 
| 171 | /*QString ServiceMetaData::description() const | 
| 172 | { | 
| 173 |     return serviceDescription; | 
| 174 | }*/ | 
| 175 |  | 
| 176 | /* | 
| 177 |    Returns the metadata of the interace at \a index; otherwise | 
| 178 |    returns 0. | 
| 179 |  */ | 
| 180 | /*QList<QServiceInterfaceDescriptor> ServiceMetaData::getInterfaces() const | 
| 181 | { | 
| 182 |     return serviceInterfaces; | 
| 183 | } */ | 
| 184 |  | 
| 185 | /*! | 
| 186 |     \internal | 
| 187 |  | 
| 188 |     Returns a streamable object containing the results of the parsing. | 
| 189 | */ | 
| 190 | ServiceMetaDataResults ServiceMetaData::parseResults() const | 
| 191 | { | 
| 192 |     ServiceMetaDataResults results; | 
| 193 |     results.type = serviceType; | 
| 194 |     results.location = serviceLocation; | 
| 195 |     results.name = serviceName; | 
| 196 |     results.description = serviceDescription; | 
| 197 |     results.interfaces = serviceInterfaces; | 
| 198 |     results.latestInterfaces = latestInterfaces(); | 
| 199 |  | 
| 200 |     return results; | 
| 201 | } | 
| 202 |  | 
| 203 | /* | 
| 204 |     Parses the file and extracts the service metadata \n | 
| 205 |     Custom error codes: \n | 
| 206 |     SFW_ERROR_UNABLE_TO_OPEN_FILE in case can not open the XML file \n | 
| 207 |     SFW_ERROR_INVALID_XML_FILE in case service registry is not a valid XML file \n | 
| 208 |     SFW_ERROR_NO_SERVICE in case XML file has no service tag\n | 
| 209 |     @return true if the metadata was read properly, false if there is an error | 
| 210 |  */ | 
| 211 | bool ServiceMetaData::() | 
| 212 | { | 
| 213 |     Q_ASSERT(checkVersion(QLatin1String(XML_MAX))); | 
| 214 |  | 
| 215 |     latestError = 0; | 
| 216 |     clearMetadata(); | 
| 217 |     QXmlStreamReader xmlReader; | 
| 218 |     bool parseError = false; | 
| 219 |     //Open xml file | 
| 220 |     if (!xmlDevice->isOpen() && !xmlDevice->open(mode: QIODevice::ReadOnly)) { | 
| 221 |         qWarning() << xmlDevice->errorString(); | 
| 222 |         latestError = ServiceMetaData::SFW_ERROR_UNABLE_TO_OPEN_FILE; | 
| 223 |         parseError = true; | 
| 224 |     } else { | 
| 225 |         //Load xml content | 
| 226 |         xmlReader.setDevice(xmlDevice); | 
| 227 |         // Read XML doc | 
| 228 |         while (!xmlReader.atEnd() && !parseError) { | 
| 229 |             xmlReader.readNext(); | 
| 230 |             //Found <SFW> xml versioning tag introduced in 1.1 | 
| 231 |             //If this tag is not found the XML parser version will be 1.0 | 
| 232 |             if (xmlReader.isStartElement() && xmlReader.name() == QLatin1String(SERVICEFW_TAG)) { | 
| 233 |                 if (!processVersionElement(aXMLReader&: xmlReader)) { | 
| 234 |                     parseError = true; | 
| 235 |                 } | 
| 236 |             } | 
| 237 |             //Found a <service> node, read service related metadata | 
| 238 |             else if (xmlReader.isStartElement() && xmlReader.name() == QLatin1String(SERVICE_TAG)) { | 
| 239 |                 if (!processServiceElement(aXMLReader&: xmlReader)) { | 
| 240 |                     parseError = true; | 
| 241 |                 } | 
| 242 |             } | 
| 243 |             else if (xmlReader.isStartElement() && xmlReader.name() != QLatin1String(SERVICE_TAG) | 
| 244 |                                                 && xmlReader.name() != QLatin1String(SERVICEFW_TAG)) { | 
| 245 |                 latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE; | 
| 246 |                 parseError = true; | 
| 247 |             } | 
| 248 |             else if (xmlReader.tokenType() == QXmlStreamReader::Invalid) { | 
| 249 |                 latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; | 
| 250 |                 parseError = true; | 
| 251 |             } | 
| 252 |         } | 
| 253 |         if (ownsXmlDevice) | 
| 254 |             xmlDevice->close(); | 
| 255 |     } | 
| 256 |  | 
| 257 |     if (parseError) { | 
| 258 |         //provide better error reports | 
| 259 |         switch (latestError) { | 
| 260 |             case SFW_ERROR_NO_SERVICE:                              /* Can not find service root node in XML file*/ | 
| 261 |                 qDebug() << "Missing <service> tag" ; | 
| 262 |                 break; | 
| 263 |             case SFW_ERROR_NO_SERVICE_NAME:                          /* Can not find service name in XML file */ | 
| 264 |                 qDebug() << "Missing or empty <name> tag within <service>" ; | 
| 265 |                 break; | 
| 266 |             case SFW_ERROR_NO_SERVICE_PATH:                          /* Can not find service filepath or ipcaddress in XML file */ | 
| 267 |                 if (greaterThan(v1: xmlVersion, v2: QLatin1String("1.0" ))) | 
| 268 |                     qDebug() << "Missing or empty <filepath> or <ipcaddress> tag within <service>" ; | 
| 269 |                 else | 
| 270 |                     qDebug() << "Missing or empty <filepath> tag within <service>" ; | 
| 271 |                 break; | 
| 272 |             case SFW_ERROR_NO_SERVICE_INTERFACE:                     /* No interface for the service in XML file*/ | 
| 273 |                 qDebug() << "Missing <interface> tag" ; | 
| 274 |                 break; | 
| 275 |             case SFW_ERROR_NO_INTERFACE_VERSION:                     /* Can not find interface version in XML file */ | 
| 276 |                 qDebug() << "Missing or empty <version> tag within <interface>" ; | 
| 277 |                 break; | 
| 278 |             case SFW_ERROR_NO_INTERFACE_NAME:                        /* Can not find interface name in XML file*/ | 
| 279 |                 qDebug() << "Missing or empty <name> tag within <interface>" ; | 
| 280 |                 break; | 
| 281 |             case SFW_ERROR_UNABLE_TO_OPEN_FILE:                      /* Error opening XML file*/ | 
| 282 |                 qDebug() << "Unable to open service xml file" ; | 
| 283 |                 break; | 
| 284 |             case SFW_ERROR_INVALID_XML_FILE:                         /* Not a valid XML file*/ | 
| 285 |                 qDebug() << "Not a valid service xml" ; | 
| 286 |                 break; | 
| 287 |             case SFW_ERROR_PARSE_SERVICE:                            /* Error parsing service node */ | 
| 288 |                 qDebug().nospace() << "Invalid tag within <service> with the supplied version("  | 
| 289 |                                    << xmlVersion << ")" ; | 
| 290 |                 break; | 
| 291 |             case SFW_ERROR_PARSE_INTERFACE:                          /* Error parsing interface node */ | 
| 292 |                 qDebug() << "Invalid tag within <interface> tags" ; | 
| 293 |                 break; | 
| 294 |             case SFW_ERROR_DUPLICATED_INTERFACE:                     /* The same interface is defined twice */ | 
| 295 |                 qDebug() << "The same interface has been defined more than once" ; | 
| 296 |                 break; | 
| 297 |             case SFW_ERROR_INVALID_VERSION: | 
| 298 |                 qDebug() << "Invalid version string, expected: x.y" ; | 
| 299 |                 break; | 
| 300 |             case SFW_ERROR_DUPLICATED_TAG:                           /* The tag appears twice */ | 
| 301 |                 qDebug() << "XML tag appears twice" ; | 
| 302 |                 break; | 
| 303 |             case SFW_ERROR_INVALID_CUSTOM_TAG:                       /* The customproperty tag is not correctly formatted or otherwise incorrect*/ | 
| 304 |                 qDebug() << "Invalid custom property tag" ; | 
| 305 |                 break; | 
| 306 |             case SFW_ERROR_DUPLICATED_CUSTOM_KEY:                    /* The customproperty appears twice*/ | 
| 307 |                 qDebug() << "Same custom property appears multiple times" ; | 
| 308 |                 break; | 
| 309 |             case SFW_ERROR_MULTIPLE_SERVICE_TYPES:                   /* Both filepath and ipcaddress found in the XML file */ | 
| 310 |                 qDebug() << "Cannot specify both <filepath> and <ipcaddress> tags within <service>" ; | 
| 311 |                 break; | 
| 312 |             case SFW_ERROR_INVALID_FILEPATH:                         /* Service path cannot contain IPC prefix */ | 
| 313 |                 qDebug() << "Invalid service location, avoid private prefixes" ; | 
| 314 |                 break; | 
| 315 |             case SFW_ERROR_INVALID_XML_VERSION:                      /* Error parsing servicefw version node */ | 
| 316 |                 qDebug() << "Invalid or missing version attribute in <SFW> tag" ; | 
| 317 |                 break; | 
| 318 |             case SFW_ERROR_UNSUPPORTED_IPC:                          /* Servicefw version doesn't support IPC */ | 
| 319 |                 qDebug().nospace() << "Supplied service framework version("  << xmlVersion | 
| 320 |                                    << ") doesn't support the <ipcaddress> tag" ; | 
| 321 |                 break; | 
| 322 |             case SFW_ERROR_UNSUPPORTED_XML_VERSION:                  /* Unsupported servicefw version supplied */ | 
| 323 |                 qDebug().nospace() << "Service framework version("  << xmlVersion | 
| 324 |                                    << ") is higher than available support("  << QLatin1String(XML_MAX) << ")" ; | 
| 325 |                 break; | 
| 326 |         } | 
| 327 |         clearMetadata(); | 
| 328 |     } | 
| 329 |     return !parseError; | 
| 330 | } | 
| 331 |  | 
| 332 | /* | 
| 333 |     Gets the latest parsing error \n | 
| 334 |     @return parsing error(negative value) or 0 in case there is none | 
| 335 |  */ | 
| 336 | int ServiceMetaData::getLatestError() const | 
| 337 | { | 
| 338 |     return latestError; | 
| 339 | } | 
| 340 |  | 
| 341 | /* | 
| 342 |     Parses the service framework xml version tag and continues to parse the service | 
| 343 | */ | 
| 344 | bool ServiceMetaData::processVersionElement(QXmlStreamReader &aXMLReader) | 
| 345 | { | 
| 346 |     Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICEFW_TAG)); | 
| 347 |     bool parseError = false; | 
| 348 |  | 
| 349 |     if (aXMLReader.attributes().hasAttribute(qualifiedName: QLatin1String("version" ))) { | 
| 350 |         xmlVersion = aXMLReader.attributes().value(qualifiedName: QLatin1String("version" )).toString(); | 
| 351 |         bool success = checkVersion(version: xmlVersion); | 
| 352 |  | 
| 353 |         if (xmlVersion.isEmpty() || !success) { | 
| 354 |             latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_VERSION; | 
| 355 |             parseError = true; | 
| 356 |         } else { | 
| 357 |             if (greaterThan(v1: xmlVersion, v2: QLatin1String(XML_MAX))) { | 
| 358 |                 latestError = ServiceMetaData::SFW_ERROR_UNSUPPORTED_XML_VERSION; | 
| 359 |                 parseError = true; | 
| 360 |             } | 
| 361 |         } | 
| 362 |     } else { | 
| 363 |         latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_VERSION; | 
| 364 |         parseError = true; | 
| 365 |     } | 
| 366 |  | 
| 367 |     while (!parseError && !aXMLReader.atEnd()) { | 
| 368 |         aXMLReader.readNext(); | 
| 369 |         //Found a <service> node, read service related metadata | 
| 370 |         if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)) { | 
| 371 |             if (!processServiceElement(aXMLReader)) { | 
| 372 |                 parseError = true; | 
| 373 |             } | 
| 374 |         } | 
| 375 |         else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(SERVICEFW_TAG)) { | 
| 376 |             //Found </SFW>, leave the loop | 
| 377 |             break; | 
| 378 |         } | 
| 379 |         else if (aXMLReader.isStartElement() && aXMLReader.name() != QLatin1String(SERVICE_TAG)) { | 
| 380 |             latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE; | 
| 381 |             parseError = true; | 
| 382 |         } | 
| 383 |         else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { | 
| 384 |             latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; | 
| 385 |             parseError = true; | 
| 386 |         } | 
| 387 |     } | 
| 388 |  | 
| 389 |     return !parseError; | 
| 390 | } | 
| 391 |  | 
| 392 | /* | 
| 393 |     Parses and extracts the service metadata from the current xml <service> node \n | 
| 394 |  */ | 
| 395 | bool ServiceMetaData::processServiceElement(QXmlStreamReader &aXMLReader) | 
| 396 | { | 
| 397 |     Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)); | 
| 398 |     bool parseError = false; | 
| 399 |  | 
| 400 |     int dupSTags[4] = {0 //->tag name | 
| 401 |         ,0 //-> service description | 
| 402 |         ,0 //-> filepath | 
| 403 |         ,0 //-> ipcaddress | 
| 404 |     }; | 
| 405 |     while (!parseError && !aXMLReader.atEnd()) { | 
| 406 |         aXMLReader.readNext(); | 
| 407 |         if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(NAME_TAG)) { | 
| 408 |             //Found <name> tag | 
| 409 |             serviceName = aXMLReader.readElementText(); | 
| 410 |             dupSTags[0]++; | 
| 411 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(DESCRIPTION_TAG)) { | 
| 412 |             //Found <description> tag | 
| 413 |             serviceDescription = aXMLReader.readElementText(); | 
| 414 |             dupSTags[1]++; | 
| 415 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_FILEPATH) ) { | 
| 416 |             //Found <filepath> tag for plugin service | 
| 417 |             dupSTags[2]++; | 
| 418 |             serviceLocation = aXMLReader.readElementText(); | 
| 419 |             //Check if IPC prefix was used incorrectly here | 
| 420 |             if (serviceLocation.startsWith(s: QLatin1String(SERVICE_IPC_PREFIX))) { | 
| 421 |                 latestError = ServiceMetaData::SFW_ERROR_INVALID_FILEPATH; | 
| 422 |                 parseError = true; | 
| 423 |             } | 
| 424 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(SERVICE_IPCADDRESS) ) { | 
| 425 |             //Found <ipcaddress> tag for IPC service | 
| 426 |             //Check if servicefw XML version supports IPC | 
| 427 |             if (greaterThan(v1: xmlVersion, v2: QLatin1String("1.0" ))) { | 
| 428 |                 dupSTags[3]++; | 
| 429 |                 serviceLocation = aXMLReader.readElementText(); | 
| 430 |                 //Check if IPC prefix was used incorrectly here | 
| 431 |                 if (serviceLocation.startsWith(s: QLatin1String(SERVICE_IPC_PREFIX))) { | 
| 432 |                     latestError = ServiceMetaData::SFW_ERROR_INVALID_FILEPATH; | 
| 433 |                     parseError = true; | 
| 434 |                 } | 
| 435 |             } else { | 
| 436 |                 latestError = ServiceMetaData::SFW_ERROR_UNSUPPORTED_IPC; | 
| 437 |                 parseError = true; | 
| 438 |             } | 
| 439 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)) { | 
| 440 |             //Found interface> node, read module related metadata | 
| 441 |             if (!processInterfaceElement(aXMLReader)) | 
| 442 |                 parseError = true; | 
| 443 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String("version" )) { | 
| 444 |             //Found <version> tag on service level. We ignore this for now | 
| 445 |             aXMLReader.readElementText(); | 
| 446 |         } else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(SERVICE_TAG)) { | 
| 447 |             //Found </service>, leave the loop | 
| 448 |             break; | 
| 449 |         } else if (aXMLReader.isEndElement() || aXMLReader.isStartElement()) { | 
| 450 |             latestError = ServiceMetaData::SFW_ERROR_PARSE_SERVICE; | 
| 451 |             parseError = true; | 
| 452 |         } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { | 
| 453 |             latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; | 
| 454 |             parseError = true; | 
| 455 |         } | 
| 456 |     } | 
| 457 |  | 
| 458 |     if ( !parseError ) { | 
| 459 |         if (serviceName.isEmpty()) { | 
| 460 |             latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_NAME; | 
| 461 |             parseError = true; | 
| 462 |         } else if (serviceLocation.isEmpty()) { | 
| 463 |             latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_PATH; | 
| 464 |             parseError = true; | 
| 465 |         } | 
| 466 |     } | 
| 467 |  | 
| 468 |     if (dupSTags[3] > 0) | 
| 469 |         serviceType = QService::InterProcess; | 
| 470 |  | 
| 471 |     if ((dupSTags[2] > 0) && (dupSTags[3] > 0)) { | 
| 472 |         latestError = SFW_ERROR_MULTIPLE_SERVICE_TYPES; | 
| 473 |         parseError = true; | 
| 474 |     } | 
| 475 |  | 
| 476 |     for (int i=0; !parseError && i<4; i++) { | 
| 477 |         if (dupSTags[i] > 1) { | 
| 478 |             latestError = SFW_ERROR_DUPLICATED_TAG; | 
| 479 |             parseError = true; | 
| 480 |             break; | 
| 481 |         } | 
| 482 |     } | 
| 483 |  | 
| 484 |     //update all interfaces with service data | 
| 485 |     const int icount = serviceInterfaces.count(); | 
| 486 |     if (icount == 0 && latestError == 0) { | 
| 487 |         latestError = ServiceMetaData::SFW_ERROR_NO_SERVICE_INTERFACE; | 
| 488 |         parseError = true; | 
| 489 |     } | 
| 490 |     for (int i = 0; i<icount; i++) { | 
| 491 |         serviceInterfaces.at(i).d->serviceName = serviceName; | 
| 492 |         serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::Location] = serviceLocation; | 
| 493 |         serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::ServiceDescription] = serviceDescription; | 
| 494 |         serviceInterfaces.at(i).d->attributes[QServiceInterfaceDescriptor::ServiceType] = serviceType; | 
| 495 |     } | 
| 496 |  | 
| 497 |     return !parseError; | 
| 498 | } | 
| 499 |  | 
| 500 | /* | 
| 501 |     Parses and extracts the interface metadata from the current xml <interface> node \n | 
| 502 | */ | 
| 503 | bool ServiceMetaData::processInterfaceElement(QXmlStreamReader &aXMLReader) | 
| 504 | { | 
| 505 |     Q_ASSERT(aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)); | 
| 506 |     bool parseError = false; | 
| 507 |  | 
| 508 |     //Read interface parameter | 
| 509 |     QString tmp; | 
| 510 |     QServiceInterfaceDescriptor aInterface; | 
| 511 |     int dupITags[4] = { | 
| 512 |         0,  //->iface name tag | 
| 513 |         0,  //->version | 
| 514 |         0,  //->capabilities | 
| 515 |         0   //->description | 
| 516 |     }; | 
| 517 |     aInterface.d = new QServiceInterfaceDescriptorPrivate; | 
| 518 |  | 
| 519 |     while (!parseError && !aXMLReader.atEnd()) { | 
| 520 |         aXMLReader.readNext(); | 
| 521 |         //Read interface description | 
| 522 |         if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(NAME_TAG)) { | 
| 523 |             aInterface.d->interfaceName = aXMLReader.readElementText(); | 
| 524 |             dupITags[0]++; | 
| 525 |             //Found <name> tag for interface | 
| 526 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(DESCRIPTION_TAG)) { | 
| 527 |             //Found <description> tag | 
| 528 |             aInterface.d->attributes[QServiceInterfaceDescriptor::InterfaceDescription] = aXMLReader.readElementText(); | 
| 529 |             dupITags[3]++; | 
| 530 |         //Found </interface>, leave the loop | 
| 531 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_VERSION)) { | 
| 532 |             tmp.clear(); | 
| 533 |             tmp = aXMLReader.readElementText(); | 
| 534 |             if (tmp.isEmpty()) | 
| 535 |                 continue;  //creates NO_INTERFACE_VERSION error further below | 
| 536 |             bool success = checkVersion(version: tmp); | 
| 537 |             if ( success ) { | 
| 538 |                 int majorVer = -1; | 
| 539 |                 int minorVer = -1; | 
| 540 |                 transformVersion(version: tmp, major: &majorVer, minor: &minorVer); | 
| 541 |                 aInterface.d->major = majorVer; | 
| 542 |                 aInterface.d->minor = minorVer; | 
| 543 |                 dupITags[1]++; | 
| 544 |             } else { | 
| 545 |                 latestError = ServiceMetaData::SFW_ERROR_INVALID_VERSION; | 
| 546 |                 parseError = true; | 
| 547 |             } | 
| 548 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_CAPABILITY)) { | 
| 549 |             tmp.clear(); | 
| 550 |             tmp= aXMLReader.readElementText(); | 
| 551 |             aInterface.d->attributes[QServiceInterfaceDescriptor::Capabilities] = tmp.split(sep: QLatin1String("," ), behavior: QString::SkipEmptyParts); | 
| 552 |             dupITags[2]++; | 
| 553 |         } else if (aXMLReader.isStartElement() && aXMLReader.name() == QLatin1String(INTERFACE_CUSTOM_PROPERTY)) { | 
| 554 |             parseError = true; | 
| 555 |             if (aXMLReader.attributes().hasAttribute(qualifiedName: QLatin1String("key" ))) { | 
| 556 |                 const QString ref = aXMLReader.attributes().value(qualifiedName: QLatin1String("key" )).toString(); | 
| 557 |                 if (!ref.isEmpty()) { | 
| 558 |                     if (aInterface.d->customAttributes.contains(akey: ref)) { | 
| 559 |                         latestError = SFW_ERROR_DUPLICATED_CUSTOM_KEY; | 
| 560 |                         continue; | 
| 561 |                     } else { | 
| 562 |                         QString value = aXMLReader.readElementText(); | 
| 563 |                         if (value.isNull()) | 
| 564 |                             value = QLatin1String("" ); | 
| 565 |                         aInterface.d->customAttributes[ref] = value; | 
| 566 |                         parseError = false; | 
| 567 |                     } | 
| 568 |                 } | 
| 569 |             } | 
| 570 |             if (parseError) | 
| 571 |                 latestError = SFW_ERROR_INVALID_CUSTOM_TAG; | 
| 572 |         } else if (aXMLReader.isEndElement() && aXMLReader.name() == QLatin1String(INTERFACE_TAG)) { | 
| 573 |             break; | 
| 574 |         } else if (aXMLReader.isStartElement() || aXMLReader.isEndElement()) { | 
| 575 |             latestError = ServiceMetaData::SFW_ERROR_PARSE_INTERFACE; | 
| 576 |             parseError = true; | 
| 577 |         } else if (aXMLReader.tokenType() == QXmlStreamReader::Invalid) { | 
| 578 |             latestError = ServiceMetaData::SFW_ERROR_INVALID_XML_FILE; | 
| 579 |             parseError = true; | 
| 580 |         } | 
| 581 |     } | 
| 582 |  | 
| 583 |     if (!parseError) { | 
| 584 |         if (dupITags[1] == 0) { //no version tag found | 
| 585 |             latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_VERSION; | 
| 586 |             parseError = true; | 
| 587 |         } else if (aInterface.d->interfaceName.isEmpty()) { | 
| 588 |             latestError = ServiceMetaData::SFW_ERROR_NO_INTERFACE_NAME; | 
| 589 |             parseError = true; | 
| 590 |         } | 
| 591 |     } | 
| 592 |  | 
| 593 |     for (int i=0;!parseError && i<4;i++) { | 
| 594 |         if (dupITags[i] > 1) { | 
| 595 |             parseError = true; | 
| 596 |             latestError = SFW_ERROR_DUPLICATED_TAG; | 
| 597 |             break; | 
| 598 |         } | 
| 599 |     } | 
| 600 |  | 
| 601 |     if (!parseError) { | 
| 602 |         const QString ident = aInterface.d->interfaceName | 
| 603 |                                 + QString::number(aInterface.majorVersion()) | 
| 604 |                                 + QLatin1String("." ) | 
| 605 |                                 + QString::number(aInterface.minorVersion()); | 
| 606 |         if (duplicates.contains(value: ident.toLower())) { | 
| 607 |             latestError = ServiceMetaData::SFW_ERROR_DUPLICATED_INTERFACE; | 
| 608 |             parseError = true; | 
| 609 |         } else { | 
| 610 |             duplicates.insert(value: ident.toLower()); | 
| 611 |             serviceInterfaces.append(t: aInterface); | 
| 612 |             if (!m_latestIndex.contains(akey: aInterface.d->interfaceName.toLower()) | 
| 613 |                     || lessThan(d1: latestInterfaceVersion(interfaceName: aInterface.d->interfaceName), d2: aInterface)) | 
| 614 |  | 
| 615 |             { | 
| 616 |                     m_latestIndex[aInterface.d->interfaceName.toLower()] = serviceInterfaces.count() - 1; | 
| 617 |             } | 
| 618 |         } | 
| 619 |     } | 
| 620 |     return !parseError; | 
| 621 | } | 
| 622 |  | 
| 623 | QServiceInterfaceDescriptor ServiceMetaData::latestInterfaceVersion(const QString &interfaceName) | 
| 624 | { | 
| 625 |     QServiceInterfaceDescriptor ret; | 
| 626 |     if (m_latestIndex.contains(akey: interfaceName.toLower())) | 
| 627 |         return serviceInterfaces[m_latestIndex[interfaceName.toLower()]]; | 
| 628 |     else | 
| 629 |         return ret; | 
| 630 | } | 
| 631 |  | 
| 632 | QList<QServiceInterfaceDescriptor> ServiceMetaData::latestInterfaces() const | 
| 633 | { | 
| 634 |     QList<QServiceInterfaceDescriptor> interfaces; | 
| 635 |     QHash<QString,int>::const_iterator i = m_latestIndex.constBegin(); | 
| 636 |     while (i != m_latestIndex.constEnd()) | 
| 637 |     { | 
| 638 |         interfaces.append(t: serviceInterfaces[i.value()]); | 
| 639 |         ++i; | 
| 640 |     } | 
| 641 |     return interfaces; | 
| 642 | } | 
| 643 |  | 
| 644 | bool ServiceMetaData::lessThan(const QServiceInterfaceDescriptor &d1, | 
| 645 |                                 const QServiceInterfaceDescriptor &d2) const | 
| 646 | { | 
| 647 |     return (d1.majorVersion() < d2.majorVersion()) | 
| 648 |             || ( d1.majorVersion() == d2.majorVersion() | 
| 649 |                     && d1.minorVersion() < d2.minorVersion()); | 
| 650 |  | 
| 651 | } | 
| 652 |  | 
| 653 | bool ServiceMetaData::greaterThan(const QString &v1, const QString &v2) const | 
| 654 | { | 
| 655 |     int majorV1 = -1; | 
| 656 |     int minorV1 = -1; | 
| 657 |     transformVersion(version: v1, major: &majorV1, minor: &minorV1); | 
| 658 |  | 
| 659 |     int majorV2 = -1; | 
| 660 |     int minorV2 = -1; | 
| 661 |     transformVersion(version: v2, major: &majorV2, minor: &minorV2); | 
| 662 |  | 
| 663 |     return  (majorV1 > majorV2 | 
| 664 |              || (majorV1 == majorV2 && minorV1 > minorV2)); | 
| 665 | } | 
| 666 |  | 
| 667 | bool ServiceMetaData::checkVersion(const QString &version) const | 
| 668 | { | 
| 669 |     //match x.y as version format | 
| 670 |     QRegExp rx(QLatin1String("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$" )); | 
| 671 |     int pos = rx.indexIn(str: version); | 
| 672 |     QStringList list = rx.capturedTexts(); | 
| 673 |     bool success = false; | 
| 674 |     if (pos == 0 && list.count() == 3 | 
| 675 |             && rx.matchedLength() == version.length() ) | 
| 676 |     { | 
| 677 |         list[1].toInt(ok: &success); | 
| 678 |         if ( success ) { | 
| 679 |             list[2].toInt(ok: &success); | 
| 680 |         } | 
| 681 |     } | 
| 682 |     return success; | 
| 683 | } | 
| 684 |  | 
| 685 | void ServiceMetaData::transformVersion(const QString &version, int *major, int *minor) const | 
| 686 | { | 
| 687 |     Q_ASSERT(major != NULL); | 
| 688 |     Q_ASSERT(minor != NULL); | 
| 689 |     if (!checkVersion(version)) { | 
| 690 |         *major = -1; | 
| 691 |         *minor = -1; | 
| 692 |     } else { | 
| 693 |         QRegExp rx(QLatin1String("^([1-9][0-9]*)\\.(0+|[1-9][0-9]*)$" )); | 
| 694 |         rx.indexIn(str: version); | 
| 695 |         QStringList list = rx.capturedTexts(); | 
| 696 |         Q_ASSERT(list.count() == 3); | 
| 697 |         *major = list[1].toInt(); | 
| 698 |         *minor = list[2].toInt(); | 
| 699 |     } | 
| 700 | } | 
| 701 |  | 
| 702 |  /* | 
| 703 |  *  Clears the service metadata | 
| 704 |  * | 
| 705 |  */ | 
| 706 | void ServiceMetaData::clearMetadata() | 
| 707 | { | 
| 708 |     xmlVersion = QLatin1String("1.0" ); | 
| 709 |     serviceName.clear(); | 
| 710 |     serviceLocation.clear(); | 
| 711 |     serviceDescription.clear(); | 
| 712 |     serviceInterfaces.clear(); | 
| 713 |     duplicates.clear(); | 
| 714 |     m_latestIndex.clear(); | 
| 715 |     serviceType = QService::Plugin; | 
| 716 | } | 
| 717 |  | 
| 718 | QT_END_NAMESPACE | 
| 719 |  |