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// #define QT_SFW_SERVICEDATABASE_DEBUG
35
36#include "servicedatabase_p.h"
37#include <QDir>
38#include <QSet>
39#include "qserviceinterfacedescriptor.h"
40#include "qserviceinterfacedescriptor_p.h"
41#include <QUuid>
42#include "dberror_p.h"
43
44//database name
45#define RESOLVERDATABASE "services.db"
46
47//database table names
48#define SERVICE_TABLE "Service"
49#define INTERFACE_TABLE "Interface"
50#define DEFAULTS_TABLE "Defaults"
51#define SERVICE_PROPERTY_TABLE "ServiceProperty"
52#define INTERFACE_PROPERTY_TABLE "InterfaceProperty"
53
54//separator
55#define RESOLVERDATABASE_PATH_SEPARATOR "//"
56
57#ifdef QT_SFW_SERVICEDATABASE_DEBUG
58#include <QDebug>
59#endif
60
61#define SERVICE_DESCRIPTION_KEY "DESCRIPTION"
62#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
63#define SECURITY_TOKEN_KEY "SECURITYTOKEN"
64#endif
65#define INTERFACE_DESCRIPTION_KEY "DESCRIPTION"
66#define SERVICE_INITIALIZED_KEY SERVICE_INITIALIZED_ATTR
67#define INTERFACE_CAPABILITY_KEY "CAPABILITIES"
68#define INTERFACE_SERVICETYPE_KEY "SERVICETYPE"
69
70//service prefixes
71#define SERVICE_IPC_PREFIX "_q_ipc_addr:"
72
73QT_BEGIN_NAMESPACE
74
75enum TBindIndexes
76 {
77 EBindIndex=0,
78 EBindIndex1,
79 EBindIndex2,
80 EBindIndex3,
81 EBindIndex4,
82 EBindIndex5,
83 EBindIndex6,
84 EBindIndex7
85 };
86
87
88/*
89 \class ServiceDatabase
90 The ServiceDatabase is responsible for the management of a single
91 service database. It provides operations for:
92 - opening and closing a connection with the database,
93 - registering and unregistering services
94 - querying for services and interfaces
95 - setting and getting default interface implementations.
96*/
97
98/*
99 Constructor
100*/
101ServiceDatabase::ServiceDatabase(void)
102:m_isDatabaseOpen(false),m_inTransaction(false)
103{
104}
105
106/*
107 Destructor
108*/
109ServiceDatabase::~ServiceDatabase()
110{
111 close();
112}
113
114/*
115 Opens the service database
116 The method creates or opens database and creates tables if they are not present
117 Returns true if the operation was successful, false if not.
118*/
119bool ServiceDatabase::open()
120{
121 if (m_isDatabaseOpen)
122 return true;
123
124 QString path;
125
126 //Create full path to database
127 if (m_databasePath.isEmpty ())
128 m_databasePath = databasePath();
129
130 path = m_databasePath;
131 QFileInfo dbFileInfo(path);
132 if (!dbFileInfo.dir().exists()) {
133 QDir::root().mkpath(dirPath: dbFileInfo.path());
134 QFile file(path);
135 if (!file.open(flags: QIODevice::ReadWrite)) {
136 QString errorText(QLatin1String("Could not create database directory: %1"));
137 m_lastError.setError(error: DBError::CannotCreateDbDir, errorText: errorText.arg(a: dbFileInfo.path()));
138#ifdef QT_SFW_SERVICEDATABASE_DEBUG
139 qWarning() << "ServiceDatabase::open():-"
140 << "Problem:" << qPrintable(m_lastError.text());
141#endif
142 close();
143 return false;
144 }
145 file.close();
146 }
147
148 m_connectionName = dbFileInfo.completeBaseName() + QStringLiteral("--") + QString::number(reinterpret_cast<quintptr>(QThread::currentThreadId()));
149 QSqlDatabase database;
150 if (QSqlDatabase::contains(connectionName: m_connectionName)) {
151 database = QSqlDatabase::database(connectionName: m_connectionName);
152 } else {
153 database = QSqlDatabase::addDatabase(type: QLatin1String("QSQLITE"), connectionName: m_connectionName);
154 database.setDatabaseName(path);
155 }
156
157 if (!database.isValid()){
158 m_lastError.setError(error: DBError::InvalidDatabaseConnection);
159 close();
160 return false;
161 }
162
163 //Create or open database
164 if (!database.isOpen()) {
165 if (!database.open()) {
166 m_lastError.setError(error: DBError::SqlError, errorText: database.lastError().text());
167#ifdef QT_SFW_SERVICEDATABASE_DEBUG
168 qWarning() << "ServiceDatabase::open():-"
169 << "Problem:" << "Could not open database. "
170 << "Reason:" << m_lastError.text();
171#endif
172 close();
173 return false;
174 }
175 }
176 m_isDatabaseOpen = true;
177
178 //Check database structure (tables) and recreate tables if neccessary
179 //If one of tables is missing remove all tables and recreate them
180 //This operation is required in order to avoid data coruption
181 if (!checkTables()) {
182 if (dropTables()) {
183 if (createTables()) {
184#ifdef QT_SFW_SERVICEDATABASE_DEBUG
185 qDebug() << "ServiceDatabase::open():-"
186 << "Database tables recreated";
187#endif
188 } else {
189 //createTable() should've handled error message
190 //and warning
191 close();
192 return false;
193 }
194 }
195 else {
196 //dropTables() should've handled error message
197 //and warning
198 close();
199 return false;
200 }
201 }
202 return true;
203}
204
205/*
206 Adds a \a service into the database.
207
208 May set the following error codes
209 DBError::NoError
210 DBError::LocationAlreadyRegistered
211 DBError::IfaceImplAlreadyRegistered
212 DBError::SqlError
213 DBError::DatabaseNotOpen
214 DBError::InvalidDatabaseConnection
215 DBError::NoWritePermissions
216 DBError::InvalidDatabaseFile
217*/
218//bool ServiceDatabase::registerService(ServiceMetaData &service)
219bool ServiceDatabase::registerService(const ServiceMetaDataResults &service, const QString &securityToken)
220{
221 // Derive the location name with the service type prefix to be stored
222 QString locationPrefix = service.location;
223 int type = service.interfaces[0].d->attributes[QServiceInterfaceDescriptor::ServiceType].toInt();
224 if (type == QService::InterProcess)
225 locationPrefix = QLatin1String(SERVICE_IPC_PREFIX) + service.location;
226
227#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
228 Q_UNUSED(securityToken);
229#else
230 if (securityToken.isEmpty()) {
231 QString errorText("Access denied, no security token provided (for registering service: \"%1\")");
232 m_lastError.setError(DBError::NoWritePermissions, errorText.arg(service.name));
233#ifdef QT_SFW_SERVICEDATABASE_DEBUG
234 qWarning() << "ServiceDatabase::registerService():-"
235 << "Problem: Unable to register service,"
236 << "reason:" << qPrintable(m_lastError.text());
237#endif
238 return false;
239 }
240#endif
241
242 if (!checkConnection()) {
243#ifdef QT_SFW_SERVICEDATABASE_DEBUG
244 qWarning() << "ServiceDatabase::registerService():-"
245 << "Problem:" << qPrintable(m_lastError.text());
246#endif
247 return false;
248 }
249
250 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
251 QSqlQuery query(database);
252
253 if (!beginTransaction(query: &query, Write)) {
254#ifdef QT_SFW_SERVICEDATABASE_DEBUG
255 qWarning() << "ServiceDatabase::registerService():-"
256 << "Unable to begin transaction,"
257 << "reason:" << qPrintable(m_lastError.text());
258#endif
259 return false;
260 }
261 //See if the service's location has already been previously registered
262 QString statement(QLatin1String("SELECT Name from Service WHERE Location=? COLLATE NOCASE"));
263 QList<QVariant> bindValues;
264 bindValues.append(t: locationPrefix);
265 if (!executeQuery(query: &query, statement, bindValues)) {
266 rollbackTransaction(query: &query);
267#ifdef QT_SFW_SERVICEDATABASE_DEBUG
268 qWarning() << "ServiceDatabase::registerService():-"
269 << qPrintable(m_lastError.text());
270#endif
271 return false;
272 }
273
274 if (query.next()) {
275 QString alreadyRegisteredService = query.value(i: EBindIndex).toString();
276 const QString errorText = QLatin1String("Cannot register service \"%1\". Service location \"%2\" is already "
277 "registered to service \"%3\". \"%3\" must first be deregistered "
278 "for new registration to take place.");
279
280 m_lastError.setError(error: DBError::LocationAlreadyRegistered,
281 errorText: errorText.arg(a: service.name)
282 .arg(a: service.location)
283 .arg(a: alreadyRegisteredService));
284
285 rollbackTransaction(query: &query);
286#ifdef QT_SFW_SERVICEDATABASE_DEBUG
287 qWarning() << "ServiceDatabase::registerService():-"
288 << "Problem:" << qPrintable(m_lastError.text());
289#endif
290 return false;
291 }
292
293#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
294 // If service(s) have already been registered with same name, they must all come from
295 // same application. Fetch a service with given name and if such exists, check that its
296 // security ID equals to the security ID of the current registrar.
297 // One application may register multiple services with same name (different location),
298 // hence the keyword DISTINCT.
299 statement = "SELECT DISTINCT ServiceProperty.Value FROM Service, ServiceProperty "
300 "WHERE Service.ID = ServiceProperty.ServiceID "
301 "AND ServiceProperty.Key = ? "
302 "AND Service.Name = ?";
303 bindValues.clear();
304 bindValues.append(SECURITY_TOKEN_KEY);
305 bindValues.append(service.name);
306 if (!executeQuery(&query, statement, bindValues)) {
307 rollbackTransaction(&query);
308#ifdef QT_SFW_SERVICEDATABASE_DEBUG
309 qWarning() << "ServiceDatabase::registerService():-"
310 << qPrintable(m_lastError.text());
311#endif
312 return false;
313 }
314 QString existingSecurityToken;
315 if (query.next()) {
316 existingSecurityToken = query.value(EBindIndex).toString();
317 }
318 if (!existingSecurityToken.isEmpty() && (existingSecurityToken != securityToken)) {
319 QString errorText("Access denied: \"%1\"");
320 m_lastError.setError(DBError::NoWritePermissions, errorText.arg(service.name));
321 rollbackTransaction(&query);
322#ifdef QT_SFW_SERVICEDATABASE_DEBUG
323 qWarning() << "ServiceDatabase::registerService():-"
324 << "Problem: Unable to register service,"
325 << "reason:" << qPrintable(m_lastError.text());
326#endif
327 return false;
328 }
329#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
330
331 // Checks done, create new rows into tables.
332 statement = QLatin1String("INSERT INTO Service(ID,Name,Location) VALUES(?,?,?)");
333
334 qsrand(seed: QTime::currentTime().msec());
335 QString serviceID = QUuid::createUuid().toString();
336
337 bindValues.clear();
338 bindValues.append(t: serviceID);
339 bindValues.append(t: service.name);
340 bindValues.append(t: locationPrefix);
341
342 if (!executeQuery(query: &query, statement, bindValues)) {
343 rollbackTransaction(query: &query);
344#ifdef QT_SFW_SERVICEDATABASE_DEBUG
345 qWarning() << "ServiceDatabase::registerService():-"
346 << qPrintable(m_lastError.text());
347#endif
348 return false;
349 }
350
351 statement = QLatin1String("INSERT INTO ServiceProperty(ServiceID,Key,Value) VALUES(?,?,?)");
352 bindValues.clear();
353 bindValues.append(t: serviceID);
354 bindValues.append(t: QLatin1String(SERVICE_DESCRIPTION_KEY));
355 if (service.description.isNull())
356 bindValues.append(t: QLatin1String("")); // This relies on !QString::isNull().
357 else
358 bindValues.append(t: service.description);
359
360 if (!executeQuery(query: &query, statement, bindValues)) {
361 rollbackTransaction(query: &query);
362#ifdef QT_SFW_SERVICEDATABASE_DEBUG
363 qWarning() << "ServiceDatabase::registerService():-"
364 << qPrintable(m_lastError.text());
365#endif
366 return false;
367 }
368
369#ifdef QT_SFW_SERVICEDATABASE_GENERATE
370 statement = "INSERT INTO ServiceProperty(ServiceId,Key,Value) VALUES(?,?,?)";
371 bindValues.clear();
372 bindValues.append(serviceID);
373 bindValues.append(SERVICE_INITIALIZED_KEY);
374 bindValues.append(QString("NO"));
375 if (!executeQuery(&query, statement, bindValues)) {
376 rollbackTransaction(&query);
377#ifdef QT_SFW_SERVICEDATABASE_DEBUG
378 qWarning() << "ServiceDatabase::registerService():-"
379 << qPrintable(m_lastError.text());
380#endif
381 return false;
382 }
383#endif
384
385#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
386 // Insert a security token for the particular service
387 statement = "INSERT INTO ServiceProperty(ServiceID,Key,Value) VALUES(?,?,?)";
388 bindValues.clear();
389 bindValues.append(serviceID);
390 bindValues.append(SECURITY_TOKEN_KEY);
391 bindValues.append(securityToken);
392
393 if (!executeQuery(&query, statement, bindValues)) {
394 rollbackTransaction(&query);
395#ifdef QT_SFW_SERVICEDATABASE_DEBUG
396 qWarning() << "ServiceDatabase::registerService():-"
397 << qPrintable(m_lastError.text());
398#endif
399 return false;
400 }
401#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
402
403 QList <QServiceInterfaceDescriptor> interfaces = service.interfaces;
404 QString interfaceID;;
405 foreach (const QServiceInterfaceDescriptor &serviceInterface, interfaces) {
406 interfaceID = getInterfaceID(query: &query, serviceInterface);
407 if (m_lastError.code() == DBError::NoError) {
408 QString errorText;
409 errorText = QLatin1String("Cannot register service \"%1\". \"%1\" is already registered "
410 "and implements interface \"%2\", Version \"%3.%4.\" \"%1\" must "
411 "first be deregistered for new registration to take place.");
412 m_lastError.setError(error: DBError::IfaceImplAlreadyRegistered,
413 errorText: errorText.arg(a: serviceInterface.serviceName())
414 .arg(a: serviceInterface.interfaceName())
415 .arg(a: serviceInterface.majorVersion())
416 .arg(a: serviceInterface.minorVersion()));
417
418 rollbackTransaction(query: &query);
419#ifdef QT_SFW_SERVICEDATABASE_DEBUG
420 qWarning() << "ServiceDatabase::registerService():-"
421 << "Problem:" << qPrintable(m_lastError.text());
422#endif
423 return false;
424 } else if (m_lastError.code() == DBError::NotFound){
425 //No interface implementation already exists for the service
426 //so add it
427 if (!insertInterfaceData(query: &query, anInterface: serviceInterface, serviceID)) {
428 rollbackTransaction(query: &query);
429 return false;
430 } else {
431 continue;
432 }
433 } else {
434 rollbackTransaction(query: &query);
435#ifdef QT_SFW_SERVICEDATABASE_DEBUG
436 qWarning() << "ServiceDatabase::registerService():-"
437 << "Unable to confirm if implementation version"
438 << (QString::number(serviceInterface.majorVersion()) + "."
439 + QString::number(serviceInterface.minorVersion())).toLatin1()
440 << "for interface" << serviceInterface.interfaceName()
441 << "is already registered for service "
442 << serviceInterface.serviceName()
443 << "\n" << m_lastError.text();
444#endif
445 return false;
446 }
447 }
448
449 interfaces = service.latestInterfaces;
450 QServiceInterfaceDescriptor defaultInterface;
451 foreach (const QServiceInterfaceDescriptor &serviceInterface, interfaces) {
452 defaultInterface = interfaceDefault(interfaceName: serviceInterface.interfaceName(), NULL, inTransaction: true);
453 if (m_lastError.code() == DBError::NoError
454 || m_lastError.code() == DBError::ExternalIfaceIDFound) {
455 continue; //default already exists so don't do anything
456 } else if (m_lastError.code() == DBError::NotFound) {
457 //default does not already exist so create one
458 interfaceID = getInterfaceID(query: &query, serviceInterface);
459 if (m_lastError.code() != DBError::NoError) {
460 rollbackTransaction(query: &query);
461#ifdef QT_SFW_SERVICEDATABASE_DEBUG
462 qWarning() << "ServiceDatabase::registerService():-"
463 << "Unable to retrieve interfaceID for "
464 "interface" << serviceInterface.interfaceName()
465 << "\n" << m_lastError.text();
466#endif
467 return false;
468 }
469
470 statement = QLatin1String("INSERT INTO Defaults(InterfaceName, InterfaceID) VALUES(?,?)");
471 bindValues.clear();
472 bindValues.append(t: serviceInterface.interfaceName());
473 bindValues.append(t: interfaceID);
474 if (!executeQuery(query: &query, statement, bindValues)) {
475 rollbackTransaction(query: &query);
476#ifdef QT_SFW_SERVICEDATABASE_DEBUG
477 qWarning() << "ServiceDatabase::registerService():-"
478 << qPrintable(m_lastError.text());
479#endif
480 return false;
481 }
482 } else {
483 rollbackTransaction(query: &query);
484#ifdef QT_SFW_SERVICEDATABASE_DEBUG
485 qWarning() << "ServiceDatabase::registerService()"
486 << "Problem: Unable to confirm if interface"
487 << serviceInterface.interfaceName()
488 << "already has a default implementation";
489#endif
490 return false;
491 }
492 }
493
494 if (!commitTransaction(query: &query)) {
495 rollbackTransaction(query: &query);
496 return false;
497 }
498 m_lastError.setError(error: DBError::NoError);
499 return true;
500}
501
502/*
503 Obtains an interface ID corresponding to a given interface \a descriptor
504
505 May set the following error codes:
506 DBError::NoError
507 DBError::NotFound
508 DBError::SqlError
509 DBError::DatabaseNotOpen
510 DBError::InvalidDatabaseConnection
511*/
512QString ServiceDatabase::getInterfaceID(const QServiceInterfaceDescriptor &serviceInterface) {
513 QString interfaceID;
514 if (!checkConnection()) {
515#ifdef QT_SFW_SERVICEDATABASE_DEBUG
516 qWarning() << "ServiceDatabase::getInterfaceID():-"
517 << "Problem:" << qPrintable(m_lastError.text());
518#endif
519 return interfaceID;
520 }
521
522 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
523 QSqlQuery query(database);
524
525 return getInterfaceID(query: &query, serviceInterface);
526}
527
528/*
529 This function should only ever be called on a user scope database.
530 It returns a list of Interface Name and Interface ID pairs, where
531 the Interface ID refers to an external interface implementation
532 in the system scope database.
533
534 May set the last error to:
535 DBError::NoError
536 DBError::SqlError
537 DBError::DatabaseNotOpen
538 DBError::InvalidDatabaseConnection
539
540 Aside: There is only one query which implicitly gets
541 wrapped in it's own transaction.
542*/
543QList<QPair<QString,QString> > ServiceDatabase::externalDefaultsInfo()
544{
545 QList<QPair<QString,QString> > ret;
546 if (!checkConnection()) {
547#ifdef QT_SFW_SERVICEDATABASE_DEBUG
548 qWarning() << "ServiceDatabase::externalDefaultsInfo():-"
549 << "Problem:" << qPrintable(m_lastError.text());
550#endif
551 return ret;
552 }
553
554 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
555 QSqlQuery query(database);
556
557 //Prepare search query, bind criteria values and execute search
558 QString selectComponent = QLatin1String("SELECT InterfaceName, InterfaceID ");
559 QString fromComponent = QLatin1String("FROM Defaults ");
560 QString whereComponent = QLatin1String("WHERE InterfaceID NOT IN (SELECT Interface.ID FROM Interface) ");
561
562 //Aside: this individual query is implicitly wrapped in a transaction
563 if (!executeQuery(query: &query, statement: selectComponent + fromComponent + whereComponent)) {
564#ifdef QT_SFW_SERVICEDATABASE_DEBUG
565 qWarning() << "ServiceDatabase::externalDefaultsInfo():-"
566 << "Problem:" << qPrintable(m_lastError.text());
567#endif
568 return ret;
569 }
570
571 while (query.next()) {
572 ret.append(t: qMakePair(x: query.value(i: EBindIndex).toString(),
573 y: query.value(i: EBindIndex1).toString()));
574 }
575
576 m_lastError.setError(error: DBError::NoError);
577 return ret;
578}
579
580/*
581 Helper function that obtains an interfaceID for a given \a descriptor.
582
583 May set last error to one of the following error codes:
584 DBError::NoError
585 DBError::NotFound
586 DBError::SqlError
587
588 Aside: This function may be safely called standalone or within an explicit
589 transaction. If called standalone, it's single query is implicitly
590 wrapped in it's own transaction.
591*/
592QString ServiceDatabase::getInterfaceID(QSqlQuery *query, const QServiceInterfaceDescriptor &serviceInterface)
593{
594 QString statement = QLatin1String("SELECT Interface.ID "
595 "FROM Interface, Service "
596 "WHERE Service.ID = Interface.ServiceID "
597 "AND Service.Name = ? COLLATE NOCASE "
598 "AND Interface.Name = ? COLLATE NOCASE "
599 "AND Interface.VerMaj = ? AND Interface.VerMin = ?");
600 QList<QVariant> bindValues;
601 bindValues.append(t: serviceInterface.serviceName());
602 bindValues.append(t: serviceInterface.interfaceName());
603 bindValues.append(t: serviceInterface.majorVersion());
604 bindValues.append(t: serviceInterface.minorVersion());
605
606 if (!executeQuery(query, statement, bindValues)) {
607 return QString();
608 }
609
610 if (!query->next()) {
611 QString errorText(QLatin1String("No Interface Descriptor found with "
612 "Service name: %1 "
613 "Interface name: %2 "
614 "Version: %3.%4"));
615 m_lastError.setError(error: DBError::NotFound, errorText: errorText.arg(a: serviceInterface.serviceName())
616 .arg(a: serviceInterface.interfaceName())
617 .arg(a: serviceInterface.majorVersion())
618 .arg(a: serviceInterface.minorVersion()));
619 return QString();
620 }
621
622 m_lastError.setError(error: DBError::NoError);
623 return query->value(i: EBindIndex).toString();
624}
625
626/*
627 Helper functions that saves \a interface related data in the Interface table
628 The \a interface data is recorded as belonging to the service assocciated
629 with \a serviceID.
630
631 May set the last error to one of the following error codes:
632 DBError::NoError
633 DBError::SqlError
634
635 Aside: It is already assumed that a write transaction has been started by the
636 time this function is called; and this function will not rollback/commit
637 the transaction.
638*/
639bool ServiceDatabase::insertInterfaceData(QSqlQuery *query,const QServiceInterfaceDescriptor &serviceInterface, const QString &serviceID)
640{
641 QString statement = QLatin1String("INSERT INTO Interface(ID, ServiceID,Name,VerMaj, VerMin) "
642 "VALUES(?,?,?,?,?)");
643 QString interfaceID = QUuid::createUuid().toString();
644
645 QList<QVariant> bindValues;
646 bindValues.append(t: interfaceID);
647 bindValues.append(t: serviceID);
648 bindValues.append(t: serviceInterface.interfaceName());
649 bindValues.append(t: serviceInterface.majorVersion());
650 bindValues.append(t: serviceInterface.minorVersion());
651
652 if (!executeQuery(query, statement, bindValues)) {
653#ifdef QT_SFW_SERVICEDATABASE_DEBUG
654 qWarning() << "ServiceDatabase::insertInterfaceData():-"
655 << qPrintable(m_lastError.text());
656#endif
657 return false;
658 }
659
660 statement = QLatin1String("INSERT INTO InterfaceProperty(InterfaceID, Key, Value) VALUES(?,?,?)");
661 QHash<QServiceInterfaceDescriptor::Attribute, QVariant>::const_iterator iter = serviceInterface.d->attributes.constBegin();
662 bool isValidInterfaceProperty;
663 QString capabilities;
664 QString interfaceDescription;
665 while (iter != serviceInterface.d->attributes.constEnd()) {
666 isValidInterfaceProperty = true;
667
668 bindValues.clear();
669 bindValues.append(t: interfaceID);
670 switch (iter.key()) {
671 case (QServiceInterfaceDescriptor::Capabilities):
672 bindValues.append(t: QLatin1String(INTERFACE_CAPABILITY_KEY));
673 capabilities = serviceInterface.attribute(which: QServiceInterfaceDescriptor::Capabilities).toStringList().join(sep: QLatin1String(","));
674 if (capabilities.isNull())
675 capabilities = QLatin1String("");
676 bindValues.append(t: capabilities);
677 break;
678 case(QServiceInterfaceDescriptor::Location):
679 isValidInterfaceProperty = false;
680 break;
681 case(QServiceInterfaceDescriptor::ServiceDescription):
682 isValidInterfaceProperty = false;
683 break;
684 case(QServiceInterfaceDescriptor::InterfaceDescription):
685 bindValues.append(t: QLatin1String(INTERFACE_DESCRIPTION_KEY));
686 interfaceDescription = serviceInterface.attribute(which: QServiceInterfaceDescriptor::InterfaceDescription).toString();
687 if (interfaceDescription.isNull())
688 interfaceDescription = QLatin1String("");
689 bindValues.append(t: interfaceDescription);
690 break;
691 default:
692 isValidInterfaceProperty = false;
693 break;
694 }
695
696 if (isValidInterfaceProperty) {
697 if (!executeQuery(query, statement, bindValues)) {
698#ifdef QT_SFW_SERVICEDATABASE_DEBUG
699 qWarning() << "ServiceDatabase::insertInterfaceData():-"
700 << qPrintable(m_lastError.text());
701#endif
702 return false;
703 }
704 }
705 ++iter;
706 }
707
708 // add custom attributes
709 QHash<QString, QString>::const_iterator customIter = serviceInterface.d->customAttributes.constBegin();
710 while (customIter!=serviceInterface.d->customAttributes.constEnd()) {
711 bindValues.clear();
712 bindValues.append(t: interfaceID);
713 // to avoid key clashes use separate c_ namespace ->is this sufficient?
714 bindValues.append(t: QVariant(QStringLiteral("c_") + customIter.key()));
715 bindValues.append(t: customIter.value());
716 if (!executeQuery(query, statement, bindValues)) {
717#ifdef QT_SFW_SERVICEDATABASE_DEBUG
718 qWarning() << "ServiceDatabase::insertInterfaceData(customProps):-"
719 << qPrintable(m_lastError.text());
720#endif
721 return false;
722 }
723 ++customIter;
724 }
725 m_lastError.setError(error: DBError::NoError);
726
727 return true;
728}
729
730/*
731 Helper function that executes the sql query specified in \a statement.
732 It is assumed that the \a statement uses positional placeholders and
733 corresponding parameters are placed in the list of \a bindValues.
734
735 Aside: This function may be safely called standalone or within an explicit
736 transaction. If called standalone, it's single query is implicitly
737 wrapped in it's own transaction.
738
739 May set the last error to one of the following error codes:
740 DBError::NoError
741 DBError::SqlError
742 DBError::NoWritePermissions
743 DBError::InvalidDatabaseFile
744*/
745bool ServiceDatabase::executeQuery(QSqlQuery *query, const QString &statement, const QList<QVariant> &bindValues)
746{
747 Q_ASSERT(query != NULL);
748
749 bool success = false;
750 enum {Prepare =0 , Execute=1};
751 for (int stage=Prepare; stage <= Execute; ++stage) {
752 if ( stage == Prepare)
753 success = query->prepare(query: statement);
754 else // stage == Execute
755 success = query->exec();
756
757 if (!success) {
758 QString errorText;
759 errorText = QLatin1String("Problem: Could not %1 statement: %2"
760 "Reason: %3"
761 "Parameters: %4\n");
762 QString parameters;
763 if (bindValues.count() > 0) {
764 for (int i = 0; i < bindValues.count(); ++i) {
765 parameters.append(QStringLiteral("\n\t[") + QString::number(i) + QStringLiteral("]: ") + bindValues.at(i).toString());
766 }
767 } else {
768 parameters = QLatin1String("None");
769 }
770
771 DBError::ErrorCode errorType;
772 int result = query->lastError().number();
773 if (result == 26 || result == 11) {//SQLILTE_NOTADB || SQLITE_CORRUPT
774 qWarning() << "Service Framework:- Database file is corrupt or invalid:" << databasePath();
775 errorType = DBError::InvalidDatabaseFile;
776 }
777 else if ( result == 8) //SQLITE_READONLY
778 errorType = DBError::NoWritePermissions;
779 else
780 errorType = DBError::SqlError;
781
782 m_lastError.setError(error: errorType,
783 errorText: errorText
784 .arg(a: stage == Prepare ?QLatin1String("prepare"):QLatin1String("execute"))
785 .arg(a: statement)
786 .arg(a: query->lastError().text())
787 .arg(a: parameters));
788
789 query->finish();
790 query->clear();
791 return false;
792 }
793
794 if (stage == Prepare) {
795 foreach (const QVariant &bindValue, bindValues)
796 query->addBindValue(val: bindValue);
797 }
798 }
799
800 m_lastError.setError(error: DBError::NoError);
801 return true;
802}
803
804/*
805 Obtains a list of QServiceInterfaceDescriptors that match the constraints supplied
806 by \a filter.
807
808 May set last error to one of the following error codes:
809 DBError::NoError
810 DBError::SqlError
811 DBError::DatabaseNotOpen
812 DBError::InvalidDatabaseConnection
813 DBError::NoWritePermissions
814 DBError::InvalidDatabaseFile
815*/
816QList<QServiceInterfaceDescriptor> ServiceDatabase::getInterfaces(const QServiceFilter &filter)
817{
818 QList<QServiceInterfaceDescriptor> interfaces;
819 if (!checkConnection()) {
820#ifdef QT_SFW_SERVICEDATABASE_DEBUG
821 qWarning() << "ServiceDatabase::getInterfaces():-"
822 << "Problem:" << qPrintable(m_lastError.text());
823#endif
824 return interfaces;
825 }
826
827 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
828 QSqlQuery query(database);
829
830 //multiple read queries are performed so wrap them
831 //in a read only transaction
832 if (!beginTransaction(query: &query, Read)) {
833#ifdef QT_SFW_SERVICEDATABASE_DEBUG
834 qWarning() << "ServiceDatabase::getInterfaces():-"
835 << "Unable to begin transaction. "
836 << "Reason:" << qPrintable(m_lastError.text());
837#endif
838 return interfaces;
839 }
840
841 //Prepare search query, bind criteria values
842 QString selectComponent = QLatin1String("SELECT Interface.Name, "
843 "Service.Name, Interface.VerMaj, "
844 "Interface.VerMin, "
845 "Service.Location, "
846 "Service.ID, "
847 "Interface.ID ");
848 QString fromComponent = QLatin1String("FROM Interface, Service ");
849 QString whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID ");
850 QList<QVariant> bindValues;
851
852 if (filter.serviceName().isEmpty() && filter.interfaceName().isEmpty()) {
853 //do nothing, (don't add any extra constraints to the query
854 } else {
855
856 if (!filter.serviceName().isEmpty()) {
857 whereComponent.append(s: QLatin1String("AND Service.Name = ?")).append(s: QLatin1String(" COLLATE NOCASE "));
858 bindValues.append(t: filter.serviceName());
859 }
860 if (!filter.interfaceName().isEmpty()) {
861 whereComponent.append(s: QLatin1String("AND Interface.Name = ?")).append(s: QLatin1String(" COLLATE NOCASE "));
862 bindValues.append(t: filter.interfaceName());
863 if (filter.majorVersion() >=0 && filter.minorVersion() >=0) {
864 if (filter.versionMatchRule() == QServiceFilter::ExactVersionMatch) {
865 whereComponent.append(s: QLatin1String("AND Interface.VerMaj = ?")).append(s: QLatin1String(" AND Interface.VerMin = ? "));
866 bindValues.append(t: QString::number(filter.majorVersion()));
867 bindValues.append(t: QString::number(filter.minorVersion()));
868 }
869 else if (filter.versionMatchRule() == QServiceFilter::MinimumVersionMatch) {
870 whereComponent.append(s: QLatin1String("AND ((Interface.VerMaj > ?"))
871 .append(s: QLatin1String(") OR Interface.VerMaj = ?")).append(s: QLatin1String(" AND Interface.VerMin >= ?")).append(s: QLatin1String(") "));
872 bindValues.append(t: QString::number(filter.majorVersion()));
873 bindValues.append(t: QString::number(filter.majorVersion()));
874 bindValues.append(t: QString::number(filter.minorVersion()));
875 }
876 }
877 }
878 }
879
880 if (!executeQuery(query: &query, statement: selectComponent + fromComponent + whereComponent, bindValues)) {
881#ifdef QT_SFW_SERVICEDATABASE_DEBUG
882 qWarning() << "ServiceDatabase::getInterfaces():-"
883 << "Problem:" << qPrintable(m_lastError.text());
884#endif
885 rollbackTransaction(query: &query);
886 return interfaces;
887 }
888
889 QServiceInterfaceDescriptor serviceInterface;
890 serviceInterface.d = new QServiceInterfaceDescriptorPrivate;
891 QStringList capabilities;
892 QString serviceID;
893 QString interfaceID;
894 const QSet<QString> filterCaps = filter.capabilities().toSet();
895 QSet<QString> difference;
896
897 while (query.next()){
898 difference.clear();
899 serviceInterface.d->customAttributes.clear();
900 serviceInterface.d->attributes.clear();
901 serviceInterface.d->interfaceName = query.value(i: EBindIndex).toString();
902 serviceInterface.d->serviceName = query.value(i: EBindIndex1).toString();
903 serviceInterface.d->major = query.value(i: EBindIndex2).toInt();
904 serviceInterface.d->minor = query.value(i: EBindIndex3).toInt();
905
906 QString location = query.value(i: EBindIndex4).toString();
907 if (location.startsWith(s: QLatin1String(SERVICE_IPC_PREFIX))) {
908 serviceInterface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess;
909 serviceInterface.d->attributes[QServiceInterfaceDescriptor::Location]
910 = location.remove(i: 0,len: QString(QLatin1String(SERVICE_IPC_PREFIX)).size());
911 } else {
912 serviceInterface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin;
913 serviceInterface.d->attributes[QServiceInterfaceDescriptor::Location] = location;
914 }
915
916 serviceID = query.value(i: EBindIndex5).toString();
917 if (!populateServiceProperties(descriptor: &serviceInterface, serviceID)) {
918 //populateServiceProperties should already give a warning message
919 //and set the last error
920 interfaces.clear();
921 rollbackTransaction(query: &query);
922 return interfaces;
923 }
924
925 interfaceID = query.value(i: EBindIndex6).toString();
926 if (!populateInterfaceProperties(descriptor: &serviceInterface, interfaceID)) {
927 //populateInterfaceProperties should already give a warning message
928 //and set the last error
929 interfaces.clear();
930 rollbackTransaction(query: &query);
931 return interfaces;
932 }
933
934 const QSet<QString> ifaceCaps = serviceInterface.d->attributes.value(akey: QServiceInterfaceDescriptor::Capabilities).toStringList().toSet();
935 difference = ((filter.capabilityMatchRule() == QServiceFilter::MatchMinimum) ? (filterCaps-ifaceCaps) : (ifaceCaps-filterCaps));
936 if (!difference.isEmpty())
937 continue;
938
939 //only return those interfaces that comply with set custom filters
940 if (filter.customAttributes().size() > 0) {
941 QSet<QString> keyDiff = filter.customAttributes().toSet();
942 keyDiff.subtract(other: serviceInterface.d->customAttributes.uniqueKeys().toSet());
943 if (keyDiff.isEmpty()) { //target descriptor has same custom keys as filter
944 bool isMatch = true;
945 const QStringList keys = filter.customAttributes();
946 for (int i = 0; i<keys.count(); i++) {
947 if (serviceInterface.d->customAttributes.value(akey: keys[i]) !=
948 filter.customAttribute(which: keys[i])) {
949 isMatch = false;
950 break;
951 }
952 }
953 if (isMatch)
954 interfaces.append(t: serviceInterface);
955 }
956 } else { //no custom keys -> SQL statement ensures proper selection already
957 interfaces.append(t: serviceInterface);
958 }
959 }
960
961 rollbackTransaction(query: &query);//read-only operation so just rollback
962 m_lastError.setError(error: DBError::NoError);
963 return interfaces;
964}
965
966/*
967 Obtains a QServiceInterfaceDescriptor that
968 corresponds to a given \a interfaceID
969
970 May set last error to one of the following error codes:
971 DBError::NoError
972 DBError::NotFound
973 DBError::SqlError
974 DBError::DatabaseNotOpen
975 DBError::InvalidDatabaseConnection
976 DBError::NoWritePermissions
977 DBError::InvalidDatabaseFile
978*/
979QServiceInterfaceDescriptor ServiceDatabase::getInterface(const QString &interfaceID)
980{
981 QServiceInterfaceDescriptor serviceInterface;
982 if (!checkConnection()) {
983#ifdef QT_SFW_SERVICEDATABASE_DEBUG
984 qWarning() << "ServiceDatabase::getInterface():-"
985 << "Problem:" << qPrintable(m_lastError.text());
986#endif
987 return serviceInterface;
988 }
989
990 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
991 QSqlQuery query(database);
992
993 if (!beginTransaction(query: &query, Read)) {
994#ifdef QT_SFW_SERVICEDATABASE_DEBUG
995 qWarning() << "ServiceDatabase::getInterface():-"
996 << "Unable to begin transaction. "
997 << "Reason:" << qPrintable(m_lastError.text());
998#endif
999 return serviceInterface;
1000 }
1001
1002 QString selectComponent = QLatin1String("SELECT Interface.Name, "
1003 "Service.Name, Interface.VerMaj, "
1004 "Interface.VerMin, "
1005 "Service.Location, "
1006 "Service.ID ");
1007 QString fromComponent = QLatin1String("FROM Interface, Service ");
1008 QString whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID "
1009 "AND Interface.ID = ? ");
1010 QList<QVariant> bindValues;
1011 bindValues.append(t: interfaceID);
1012
1013 if (!executeQuery(query: &query, statement: selectComponent + fromComponent + whereComponent, bindValues)) {
1014 rollbackTransaction(query: &query);
1015#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1016 qWarning() << "ServiceDatabase::getInterfaces():-"
1017 << "Problem:" << qPrintable(m_lastError.text());
1018#endif
1019 return serviceInterface;
1020 }
1021
1022 if (!query.next()) {
1023 rollbackTransaction(query: &query);
1024 QString errorText(QLatin1String("Interface implementation not found for Interface ID: %1"));
1025 m_lastError.setError(error: DBError::NotFound, errorText: errorText.arg(a: interfaceID));
1026 return serviceInterface;
1027 }
1028
1029 serviceInterface.d = new QServiceInterfaceDescriptorPrivate;
1030 serviceInterface.d->interfaceName =query.value(i: EBindIndex).toString();
1031 serviceInterface.d->serviceName = query.value(i: EBindIndex1).toString();
1032 serviceInterface.d->major = query.value(i: EBindIndex2).toInt();
1033 serviceInterface.d->minor = query.value(i: EBindIndex3).toInt();
1034
1035 QString location = query.value(i: EBindIndex4).toString();
1036 if (location.startsWith(s: QLatin1String(SERVICE_IPC_PREFIX))) {
1037 serviceInterface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess;
1038 serviceInterface.d->attributes[QServiceInterfaceDescriptor::Location]
1039 = location.remove(i: 0,len: QString(QLatin1String(SERVICE_IPC_PREFIX)).size());
1040 } else {
1041 serviceInterface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin;
1042 serviceInterface.d->attributes[QServiceInterfaceDescriptor::Location] = location;
1043 }
1044
1045 QString serviceID = query.value(i: EBindIndex5).toString();
1046 if (!populateServiceProperties(descriptor: &serviceInterface, serviceID)) {
1047 //populateServiceProperties should already give a warning message
1048 //and set the last error
1049 rollbackTransaction(query: &query);
1050 return QServiceInterfaceDescriptor();
1051 }
1052
1053 if (!populateInterfaceProperties(descriptor: &serviceInterface, interfaceID)) {
1054 //populateInterfaceProperties should already give a warning message
1055 //and set the last error
1056 rollbackTransaction(query: &query);
1057 return QServiceInterfaceDescriptor();
1058 }
1059
1060 rollbackTransaction(query: &query);//read only operation so just rollback
1061 m_lastError.setError(error: DBError::NoError);
1062 return serviceInterface;
1063}
1064
1065/*
1066 Obtains a list of services names. If \a interfaceName is empty,
1067 then all service names are returned. If \a interfaceName specifies
1068 an interface then the names of all services implementing that interface
1069 are returned
1070
1071 May set last error to one of the following error codes:
1072 DBError::NoError
1073 DBError::SqlError
1074 DBError::DatabaseNotOpen
1075 DBError::InvalidDatabaseConnection
1076 DBError::NoWritePermissions
1077 DBError::InvalidDatabaseFile
1078
1079 Aside: There is only one query which implicitly gets
1080 wrapped in it's own transaction.
1081*/
1082QStringList ServiceDatabase::getServiceNames(const QString &interfaceName)
1083{
1084 QStringList services;
1085 if (!checkConnection()) {
1086#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1087 qWarning() << "ServiceDatabase::getServiceNames():-"
1088 << "Problem:" << qPrintable(m_lastError.text());
1089#endif
1090 return services;
1091 }
1092 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
1093 QSqlQuery query(database);
1094 QString selectComponent(QLatin1String("SELECT DISTINCT Service.Name COLLATE NOCASE "));
1095 QString fromComponent;
1096 QString whereComponent;
1097 QList<QVariant> bindValues;
1098 if (interfaceName.isEmpty()) {
1099 fromComponent = QLatin1String("FROM Service ");
1100 } else {
1101 fromComponent = QLatin1String("FROM Interface,Service ");
1102 whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID AND Interface.Name = ? COLLATE NOCASE ");
1103 bindValues.append(t: interfaceName);
1104 }
1105
1106 if (!executeQuery(query: &query, statement: selectComponent + fromComponent + whereComponent, bindValues)) {
1107#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1108 qWarning() << "ServiceDatabase::getServiceNames():-"
1109 << qPrintable(m_lastError.text());
1110#endif
1111 return services;
1112 }
1113
1114 while ( query.next()) {
1115 services.append(t: query.value(i: EBindIndex).toString());
1116 }
1117 query.finish();
1118 query.clear();
1119 m_lastError.setError(error: DBError::NoError);
1120 return services;
1121}
1122
1123/*
1124 Returns a descriptor for the default interface implementation of
1125 \a interfaceName.
1126
1127 For user scope databases only, \a defaultInterfaceID is set if the default
1128 in the user scope database refers to a interface implementation in the
1129 system scope database. In this case the descriptor will be invalid and
1130 the \a defaultInterfaceID must be used to query the system scope database,
1131 The last error set to DBError::ExternalIfaceIDFound
1132
1133 If this function is called within a transaction, \a inTransaction
1134 must be set to true. If \a inTransaction is false, this fuction
1135 will begin and end its own transaction.
1136
1137 The last error may be set to one of the following error codes:
1138 DBError::NoError
1139 DBError::ExternalIfaceIDFound
1140 DBError::SqlError
1141 DBError::DatabaseNotOpen
1142 DBError::InvalidDatabaseConnection
1143 DBError::NoWritePermissions
1144 DBError::InvalidDatabaseFile
1145*/
1146QServiceInterfaceDescriptor ServiceDatabase::interfaceDefault(const QString &interfaceName, QString *defaultInterfaceID,
1147 bool inTransaction)
1148{
1149 QServiceInterfaceDescriptor serviceInterface;
1150 if (!checkConnection())
1151 {
1152#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1153 qWarning() << "ServiceDatabase::interfaceDefault():-"
1154 << "Problem:" << qPrintable(m_lastError.text());
1155#endif
1156 return serviceInterface;
1157 }
1158
1159 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
1160 QSqlQuery query(database);
1161
1162 if (!inTransaction && !beginTransaction(query: &query, Read)) {
1163#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1164 qWarning() << "ServiceDatabase::interfaceDefault(QString, QString):-"
1165 << "Unable to begin transaction. "
1166 << "Reason:" << qPrintable(m_lastError.text());
1167#endif
1168 return serviceInterface;
1169 }
1170
1171 QString statement(QLatin1String("SELECT InterfaceID FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE"));
1172 QList<QVariant> bindValues;
1173 bindValues.append(t: interfaceName);
1174 if (!executeQuery(query: &query, statement, bindValues)) {
1175 if (!inTransaction)
1176 rollbackTransaction(query: &query);
1177#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1178 qWarning() << "ServiceDatabase::interfaceDefault():-"
1179 << qPrintable(m_lastError.text());
1180#endif
1181 return serviceInterface;
1182 }
1183
1184 QString interfaceID;
1185 if (!query.next())
1186 {
1187 if (!inTransaction)
1188 rollbackTransaction(query: &query);
1189 QString errorText(QLatin1String("No default service found for interface: \"%1\""));
1190 m_lastError.setError(error: DBError::NotFound, errorText: errorText.arg(a: interfaceName));
1191 return serviceInterface;
1192 }
1193 else
1194 interfaceID = query.value(i: EBindIndex).toString();
1195 Q_ASSERT(!interfaceID.isEmpty());
1196
1197 statement = QLatin1String("SELECT Interface.Name, "
1198 "Service.Name, Interface.VerMaj, "
1199 "Interface.VerMin, "
1200 "Service.Location, "
1201 "Service.ID "
1202 "FROM Service, Interface "
1203 "WHERE Service.ID = Interface.ServiceID AND Interface.ID = ?");
1204 bindValues.clear();
1205 bindValues.append(t: interfaceID);
1206 if (!executeQuery(query: &query, statement, bindValues))
1207 {
1208 if (!inTransaction)
1209 rollbackTransaction(query: &query);
1210#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1211 qWarning() << "ServiceDatabase::interfaceDefault():-"
1212 << qPrintable(m_lastError.text());
1213#endif
1214 return serviceInterface;
1215 }
1216
1217 if (!query.next()) {
1218 if (!inTransaction)
1219 rollbackTransaction(query: &query);
1220 if (defaultInterfaceID != NULL )
1221 *defaultInterfaceID = interfaceID;
1222 m_lastError.setError(error: DBError::ExternalIfaceIDFound);
1223 return serviceInterface;
1224 }
1225
1226 serviceInterface.d = new QServiceInterfaceDescriptorPrivate;
1227 serviceInterface.d->interfaceName =query.value(i: EBindIndex).toString();
1228 serviceInterface.d->serviceName = query.value(i: EBindIndex1).toString();
1229 serviceInterface.d->major = query.value(i: EBindIndex2).toInt();
1230 serviceInterface.d->minor = query.value(i: EBindIndex3).toInt();
1231
1232 QString location = query.value(i: EBindIndex4).toString();
1233 if (location.startsWith(s: QLatin1String(SERVICE_IPC_PREFIX))) {
1234 serviceInterface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess;
1235 serviceInterface.d->attributes[QServiceInterfaceDescriptor::Location]
1236 = location.remove(i: 0,len: QString(QLatin1String(SERVICE_IPC_PREFIX)).size());
1237 } else {
1238 serviceInterface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin;
1239 serviceInterface.d->attributes[QServiceInterfaceDescriptor::Location] = location;
1240 }
1241
1242 QString serviceID = query.value(i: EBindIndex5).toString();
1243 if (!populateServiceProperties(descriptor: &serviceInterface, serviceID)) {
1244 //populateServiceProperties should already give a warning
1245 //and set the last error
1246 if (!inTransaction)
1247 rollbackTransaction(query: &query);
1248 return QServiceInterfaceDescriptor();
1249 }
1250
1251 if (!populateInterfaceProperties(descriptor: &serviceInterface, interfaceID)) {
1252 //populateInterfaceProperties should already give a warning
1253 //and set the last error
1254 if (!inTransaction)
1255 rollbackTransaction(query: &query);
1256 return QServiceInterfaceDescriptor();
1257 }
1258
1259 if (!inTransaction)
1260 rollbackTransaction(query: &query); //Read only operation so just rollback
1261 m_lastError.setError(error: DBError::NoError);
1262 return serviceInterface;
1263}
1264
1265/*
1266 Sets a particular service's \a interface implementation as a the default
1267 implementation to look up when using the interface's name in
1268 interfaceDefault().
1269
1270 For a user scope database an \a externalInterfaceID can be provided
1271 so that the Defaults table will contain a "link" to an interface
1272 implmentation provided in the system scope database.
1273
1274 May set the last error to one of the following error codes:
1275 DBError::NoError
1276 DBerror::NotFound
1277 DBError::SqlError
1278 DBError::DatabaseNotOpen
1279 DBError::InvalidDatabaseConnection
1280 DBError::NoWritePermissions
1281 DBError::InvalidDatabaseFile
1282*/
1283bool ServiceDatabase::setInterfaceDefault(const QServiceInterfaceDescriptor &serviceInterface, const QString &externalInterfaceID)
1284{
1285 if (!checkConnection()) {
1286#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1287 qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
1288 << "Problem:" << qPrintable(m_lastError.text());
1289#endif
1290 return false;
1291 }
1292
1293 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
1294 QSqlQuery query(database);
1295
1296 //Begin Transaction
1297 if (!beginTransaction(query: &query, Write)) {
1298#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1299 qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
1300 << "Problem: Unable to begin transaction."
1301 << "Reason:" << qPrintable(m_lastError.text());
1302#endif
1303 return false;
1304 }
1305
1306 QString statement;
1307 QList<QVariant> bindValues;
1308 QString interfaceID = externalInterfaceID;
1309 if (interfaceID.isEmpty()) {
1310 statement = QLatin1String("SELECT Interface.ID from Interface, Service "
1311 "WHERE Service.ID = Interface.ServiceID "
1312 "AND Service.Name = ? COLLATE NOCASE "
1313 "AND Interface.Name = ? COLLATE NOCASE "
1314 "AND Interface.VerMaj = ? "
1315 "AND Interface.VerMin = ? ");
1316 bindValues.append(t: serviceInterface.serviceName());
1317 bindValues.append(t: serviceInterface.interfaceName());
1318 bindValues.append(t: serviceInterface.majorVersion());
1319 bindValues.append(t: serviceInterface.minorVersion());
1320
1321 if (!executeQuery(query: &query, statement, bindValues)) {
1322 rollbackTransaction(query: &query);
1323#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1324 qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
1325 << qPrintable(m_lastError.text());
1326#endif
1327 return false;
1328 }
1329
1330 if (!query.next()) {
1331 QString errorText;
1332 errorText = QLatin1String("No implementation for interface: %1, Version: %2.%3 found "
1333 "for service: %4");
1334 m_lastError.setNotFoundError(errorText.arg(a: serviceInterface.interfaceName())
1335 .arg(a: serviceInterface.majorVersion())
1336 .arg(a: serviceInterface.minorVersion())
1337 .arg(a: serviceInterface.serviceName()));
1338
1339 rollbackTransaction(query: &query);
1340#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1341 qWarning() << "ServiceDatbase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
1342 << "Problem: Unable to set default service. "
1343 << "Reason:" << qPrintable(m_lastError.text());
1344#endif
1345 return false;
1346 }
1347
1348 interfaceID = query.value(i: EBindIndex).toString();
1349 Q_ASSERT(!interfaceID.isEmpty());
1350 }
1351
1352 statement = QLatin1String("SELECT InterfaceName FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE");
1353 bindValues.clear();
1354 bindValues.append(t: serviceInterface.interfaceName());
1355 if (!executeQuery(query: &query, statement, bindValues)) {
1356 rollbackTransaction(query: &query);
1357#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1358 qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
1359 << qPrintable(m_lastError.text());
1360#endif
1361 return false;
1362 }
1363
1364 if (query.next()) {
1365 statement = QLatin1String("UPDATE Defaults "
1366 "SET InterfaceID = ? "
1367 "WHERE InterfaceName = ? COLLATE NOCASE");
1368 bindValues.clear();
1369 bindValues.append(t: interfaceID);
1370 bindValues.append(t: serviceInterface.interfaceName());
1371
1372 if (!executeQuery(query: &query, statement, bindValues)) {
1373 rollbackTransaction(query: &query);
1374#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1375 qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
1376 << qPrintable(m_lastError.text());
1377#endif
1378 return false;
1379 }
1380 } else {
1381 statement = QLatin1String("INSERT INTO Defaults(InterfaceName,InterfaceID) VALUES(?,?)");
1382 bindValues.clear();
1383 bindValues.append(t: serviceInterface.interfaceName());
1384 bindValues.append(t: interfaceID);
1385
1386 if (!executeQuery(query: &query, statement, bindValues)) {
1387 rollbackTransaction(query: &query);
1388#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1389 qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
1390 << qPrintable(m_lastError.text());
1391#endif
1392 return false;
1393 }
1394 }
1395
1396 //End Transaction
1397 if (!commitTransaction(query: &query)) {
1398 rollbackTransaction(query: &query);
1399 return false;
1400 }
1401 m_lastError.setError(error: DBError::NoError);
1402 return true;
1403}
1404
1405/*
1406 Removes the service with name \a serviceName.
1407 If the service provides a default interface implementation, then
1408 another service implementing the highest interface implementation
1409 version becomes the new default(if any). If more than one service
1410 provides same the highest version number, an arbitrary choice is made
1411 between them.
1412
1413 May set the last error to the folowing error codes:
1414 DBError::NoError
1415 DBError::NotFound
1416 DBError::SqlError
1417 DBError::DatabaseNotOpen
1418 DBError::InvalidDatabaseConnection
1419 DBError::NoWritePermissions
1420 DBError::InvalidDatabaseFile
1421*/
1422bool ServiceDatabase::unregisterService(const QString &serviceName, const QString &securityToken)
1423{
1424#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
1425 Q_UNUSED(securityToken);
1426#else
1427 if (securityToken.isEmpty()) {
1428 QString errorText(QLatin1String("Access denied, no security token provided (for unregistering service: \"%1\")"));
1429 m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName));
1430#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1431 qWarning() << "ServiceDatabase::unregisterService():-"
1432 << "Problem: Unable to unregister service. "
1433 << "Reason:" << qPrintable(m_lastError.text());
1434#endif
1435 return false;
1436 }
1437#endif
1438
1439
1440 if (!checkConnection()) {
1441#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1442 qWarning() << "ServiceDatabase::unregisterService():-"
1443 << "Problem:" << qPrintable(m_lastError.text());
1444#endif
1445 return false;
1446 }
1447
1448 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
1449 QSqlQuery query(database);
1450
1451 if (!beginTransaction(query: &query, Write)) {
1452#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1453 qWarning() << "ServiceDatabase::unregisterService():-"
1454 << "Problem: Unable to begin transaction. "
1455 << "Reason:" << qPrintable(m_lastError.text());
1456#endif
1457 return false;
1458 }
1459
1460 QString statement(QLatin1String("SELECT Service.ID from Service WHERE Service.Name = ? COLLATE NOCASE"));
1461 QList<QVariant> bindValues;
1462 bindValues.append(t: serviceName);
1463 if (!executeQuery(query: &query, statement, bindValues)) {
1464 rollbackTransaction(query: &query);
1465#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1466 qWarning() << "ServiceDatabase::unregisterService():-"
1467 << qPrintable(m_lastError.text());
1468#endif
1469 return false;
1470 }
1471
1472 QStringList serviceIDs;
1473 while (query.next()) {
1474 serviceIDs << query.value(i: EBindIndex).toString();
1475 }
1476
1477#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
1478 // Only the application that registered the service is allowed to unregister that
1479 // service. Fetch a security ID of a service (with given name) and verify that it matches
1480 // with current apps security id. Only one application is allowed to register services with
1481 // same name, hence a distinct (just any of the) security token will do because they are identical.
1482 if (!serviceIDs.isEmpty()) {
1483 statement = QLatin1String("SELECT DISTINCT Value FROM ServiceProperty WHERE ServiceID = ? AND Key = ?");
1484 bindValues.clear();
1485 bindValues.append(serviceIDs.first());
1486 bindValues.append(SECURITY_TOKEN_KEY);
1487
1488 if (!executeQuery(&query, statement, bindValues)) {
1489 rollbackTransaction(&query);
1490#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1491 qWarning() << "ServiceDatabase::unregisterService():-"
1492 << qPrintable(m_lastError.text());
1493#endif
1494 return false;
1495 }
1496 QString existingSecurityToken;
1497 if (query.next()) {
1498 existingSecurityToken = query.value(EBindIndex).toString();
1499 }
1500 if (existingSecurityToken != securityToken) {
1501 QString errorText(QLatin1String("Access denied: \"%1\""));
1502 m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName));
1503 rollbackTransaction(&query);
1504#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1505 qWarning() << "ServiceDatabase::unregisterService():-"
1506 << "Problem: Unable to unregister service"
1507 << "Reason:" << qPrintable(m_lastError.text());
1508#endif
1509 return false;
1510 }
1511 }
1512#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
1513
1514 statement = QLatin1String("SELECT Interface.ID from Interface, Service "
1515 "WHERE Interface.ServiceID = Service.ID "
1516 "AND Service.Name =? COLLATE NOCASE");
1517 bindValues.clear();
1518 bindValues.append(t: serviceName);
1519 if (!executeQuery(query: &query, statement, bindValues)) {
1520 rollbackTransaction(query: &query);
1521#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1522 qWarning() << "ServiceDatabase::unregisterService():-"
1523 << qPrintable(m_lastError.text());
1524#endif
1525 return false;
1526 }
1527
1528 QStringList interfaceIDs;
1529 while (query.next()) {
1530 interfaceIDs << query.value(i: EBindIndex).toString();
1531 }
1532
1533 if (serviceIDs.count() == 0) {
1534 QString errorText(QLatin1String("Service not found: \"%1\""));
1535 m_lastError.setError(error: DBError::NotFound, errorText: errorText.arg(a: serviceName));
1536 rollbackTransaction(query: &query);
1537#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1538 qWarning() << "ServiceDatabase::unregisterService():-"
1539 << "Problem: Unable to unregister service"
1540 << "Reason:" << qPrintable(m_lastError.text());
1541#endif
1542 return false;
1543 }
1544
1545 statement = QLatin1String("SELECT Defaults.InterfaceName "
1546 "FROM Defaults, Interface, Service "
1547 "WHERE Defaults.InterfaceID = Interface.ID "
1548 "AND Interface.ServiceID = Service.ID "
1549 "AND Service.Name = ? COLLATE NOCASE");
1550 bindValues.clear();
1551 bindValues.append(t: serviceName);
1552 if (!executeQuery(query: &query, statement, bindValues)) {
1553 rollbackTransaction(query: &query);
1554#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1555 qWarning() << "ServiceDatabase:unregisterService():-"
1556 << qPrintable(m_lastError.text());
1557#endif
1558 return false;
1559 }
1560
1561 QStringList serviceDefaultInterfaces;
1562 while (query.next()) {
1563 serviceDefaultInterfaces << query.value(i: EBindIndex).toString();
1564 }
1565
1566
1567 statement = QLatin1String("DELETE FROM Service WHERE Service.Name = ? COLLATE NOCASE");
1568 bindValues.clear();
1569 bindValues.append(t: serviceName);
1570 if (!executeQuery(query: &query, statement, bindValues)) {
1571 rollbackTransaction(query: &query);
1572#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1573 qWarning() << "ServiceDatabase::unregisterService():-"
1574 << qPrintable(m_lastError.text());
1575#endif
1576 return false;
1577 }
1578
1579 statement = QLatin1String("DELETE FROM Interface WHERE Interface.ServiceID = ?");
1580 foreach (const QString &serviceID, serviceIDs) {
1581 bindValues.clear();
1582 bindValues.append(t: serviceID);
1583 if (!executeQuery(query: &query, statement, bindValues)) {
1584 rollbackTransaction(query: &query);
1585#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1586 qWarning() << "ServiceDatabase::unregisterService():-"
1587 << qPrintable(m_lastError.text());
1588#endif
1589 return false;
1590 }
1591 }
1592
1593 statement = QLatin1String("DELETE FROM ServiceProperty WHERE ServiceID = ?");
1594
1595 foreach (const QString &serviceID, serviceIDs) {
1596 bindValues.clear();
1597 bindValues.append(t: serviceID);
1598 if (!executeQuery(query: &query, statement, bindValues)) {
1599 rollbackTransaction(query: &query);
1600#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1601 qWarning() << "ServiceDatabase::unregisterService():-"
1602 << qPrintable(m_lastError.text());
1603#endif
1604 return false;
1605 }
1606 }
1607
1608 statement = QLatin1String("DELETE FROM InterfaceProperty WHERE InterfaceID = ?");
1609 foreach (const QString &interfaceID, interfaceIDs) {
1610 bindValues.clear();
1611 bindValues.append(t: interfaceID);
1612 if (!executeQuery(query: &query, statement, bindValues)) {
1613 rollbackTransaction(query: &query);
1614#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1615 qWarning() << "ServiceDatabase::unregisterService():-"
1616 << qPrintable(m_lastError.text());
1617#endif
1618 return false;
1619 }
1620 }
1621
1622 foreach (const QString &interfaceName, serviceDefaultInterfaces) {
1623 statement = QLatin1String("SELECT ID FROM Interface WHERE Interface.Name = ? COLLATE NOCASE "
1624 "ORDER BY Interface.VerMaj DESC, Interface.VerMin DESC");
1625 bindValues.clear();
1626 bindValues.append(t: interfaceName);
1627 if (!executeQuery(query: &query, statement, bindValues)) {
1628 rollbackTransaction(query: &query);
1629#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1630 qWarning() << "ServiceDatabase::unregisterService():-"
1631 << qPrintable(m_lastError.text());
1632#endif
1633 return false;
1634 }
1635
1636 if (query.next()) {
1637 QString newDefaultID = query.value(i: EBindIndex).toString();
1638 statement = QLatin1String("UPDATE Defaults SET InterfaceID = ? WHERE InterfaceName = ? COLLATE NOCASE ");
1639 bindValues.clear();
1640 bindValues.append(t: newDefaultID);
1641 bindValues.append(t: interfaceName);
1642 if (!executeQuery(query: &query, statement, bindValues)) {
1643 rollbackTransaction(query: &query);
1644#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1645 qWarning() << "ServiceDatabase::unregisterService():-"
1646 << qPrintable(m_lastError.text());
1647#endif
1648 return false;
1649 }
1650 } else {
1651 statement = QLatin1String("DELETE FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE ");
1652 bindValues.clear();
1653 bindValues.append(t: interfaceName);
1654 if (!executeQuery(query: &query, statement, bindValues)) {
1655 rollbackTransaction(query: &query);
1656#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1657 qWarning() << "ServiceDatabase::unregisterService():-"
1658 << qPrintable(m_lastError.text());
1659#endif
1660 return false;
1661 }
1662 }
1663 }
1664
1665 //databaseCommit
1666 if (!commitTransaction(query: &query)) {
1667 rollbackTransaction(query: &query);
1668 return false;
1669 }
1670 m_lastError.setError(error: DBError::NoError);
1671 return true;
1672}
1673
1674/*
1675 Registers the service initialization into the database.
1676*/
1677bool ServiceDatabase::serviceInitialized(const QString &serviceName, const QString &securityToken)
1678{
1679#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
1680 Q_UNUSED(securityToken);
1681#endif
1682
1683 if (!checkConnection()) {
1684#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1685 qWarning() << "ServiceDatabase::unregisterService():-"
1686 << "Problem:" << qPrintable(m_lastError.text());
1687#endif
1688 return false;
1689 }
1690
1691 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
1692 QSqlQuery query(database);
1693
1694 if (!beginTransaction(query: &query, Write)) {
1695#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1696 qWarning() << "ServiceDatabase::serviceInitialized():-"
1697 << "Problem: Unable to begin transaction"
1698 << "\nReason:" << qPrintable(m_lastError.text());
1699#endif
1700 return false;
1701 }
1702
1703 QString statement(QLatin1String("SELECT Service.ID from Service WHERE Service.Name = ? COLLATE NOCASE"));
1704 QList<QVariant> bindValues;
1705 bindValues.append(t: serviceName);
1706 if (!executeQuery(query: &query, statement, bindValues)) {
1707 rollbackTransaction(query: &query);
1708#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1709 qWarning() << "ServiceDatabase::serviceInitialized():-"
1710 << qPrintable(m_lastError.text());
1711#endif
1712 return false;
1713 }
1714
1715 QStringList serviceIDs;
1716 while (query.next()) {
1717 serviceIDs << query.value(i: EBindIndex).toString();
1718 }
1719
1720
1721#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
1722 statement = QLatin1String("SELECT Value FROM ServiceProperty WHERE ServiceID = ? AND Key = ?");
1723 bindValues.clear();
1724 bindValues.append(serviceName);
1725 bindValues.append(SECURITY_TOKEN_KEY);
1726 if (!executeQuery(&query, statement, bindValues)) {
1727 rollbackTransaction(&query);
1728#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1729 qWarning() << "ServiceDatabase::unregisterService():-"
1730 << qPrintable(m_lastError.text());
1731#endif
1732 return false;
1733 }
1734
1735 QStringList securityTokens;
1736 while (query.next()) {
1737 securityTokens << query.value(EBindIndex).toString();
1738 }
1739
1740 if (!securityTokens.isEmpty() && (securityTokens.first() != securityToken)) {
1741 QString errorText(QLatin1String("Access denied: \"%1\""));
1742 m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName));
1743 rollbackTransaction(&query);
1744 #ifdef QT_SFW_SERVICEDATABASE_DEBUG
1745 qWarning() << "ServiceDatabase::serviceInitialized():-"
1746 << "Problem: Unable to update service initialization"
1747 << "\nReason:" << qPrintable(m_lastError.text());
1748 #endif
1749 }
1750#endif
1751
1752 statement = QLatin1String("DELETE FROM ServiceProperty WHERE ServiceID = ? AND Key = ?");
1753 foreach (const QString &serviceID, serviceIDs) {
1754 bindValues.clear();
1755 bindValues.append(t: serviceID);
1756 bindValues.append(t: QLatin1String(SERVICE_INITIALIZED_KEY));
1757 if (!executeQuery(query: &query, statement, bindValues)) {
1758 rollbackTransaction(query: &query);
1759#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1760 qWarning() << "ServiceDatabase::serviceInitialized():-"
1761 << qPrintable(m_lastError.text());
1762#endif
1763 return false;
1764 }
1765 }
1766
1767 //databaseCommit
1768 if (!commitTransaction(query: &query)) {
1769 rollbackTransaction(query: &query);
1770 return false;
1771 }
1772 m_lastError.setError(error: DBError::NoError);
1773 return true;
1774}
1775
1776/*
1777 Closes the database
1778
1779 May set the following error codes:
1780 DBError::NoError
1781 DBError::InvalidDatabaseConnection
1782*/
1783bool ServiceDatabase::close()
1784{
1785 if (m_isDatabaseOpen) {
1786 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName, open: false);
1787 if (database.isValid()) {
1788 if (database.isOpen()) {
1789 database.close();
1790 m_isDatabaseOpen = false;
1791 return true;
1792 }
1793 } else {
1794 m_lastError.setError(error: DBError::InvalidDatabaseConnection);
1795#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1796 qWarning() << "ServiceDatabase::close():-"
1797 << "Problem: " << qPrintable(m_lastError.text());
1798#endif
1799 return false;
1800 }
1801 }
1802 m_lastError.setError(error: DBError::NoError);
1803 return true;
1804}
1805
1806/*
1807 Sets the path of the service database to \a databasePath
1808*/
1809void ServiceDatabase::setDatabasePath(const QString &databasePath)
1810{
1811 m_databasePath = QDir::toNativeSeparators(pathName: databasePath);
1812}
1813
1814/*
1815 Returns the path of the service database
1816*/
1817QString ServiceDatabase::databasePath() const
1818{
1819 QString path;
1820 if (m_databasePath.isEmpty()) {
1821 QSettings settings(QSettings::SystemScope, QLatin1String("Nokia"), QLatin1String("Services"));
1822 path = settings.value(key: QLatin1String("ServicesDB/Path")).toString();
1823 if (path.isEmpty()) {
1824 path = QDir::currentPath();
1825 if (path.lastIndexOf(s: QLatin1String(RESOLVERDATABASE_PATH_SEPARATOR)) != path.length() -1) {
1826 path.append(s: QLatin1String(RESOLVERDATABASE_PATH_SEPARATOR));
1827 }
1828 path.append(s: QLatin1String(RESOLVERDATABASE));
1829 }
1830 path = QDir::toNativeSeparators(pathName: path);
1831 } else {
1832 path = m_databasePath;
1833 }
1834
1835 return path;
1836}
1837
1838/*
1839 Helper method that creates the database tables: Service, Interface,
1840 Defaults, ServiceProperty and InterfaceProperty
1841
1842 May set the last error to one of the following error codes:
1843 DBError::NoError
1844 DBError::SqlError
1845 DBError::NoWritePermissions
1846 DBError::InvalidDatabaseFile
1847*/
1848bool ServiceDatabase::createTables()
1849{
1850 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
1851 QSqlQuery query(database);
1852
1853 //Begin Transaction
1854 if (!beginTransaction(query: &query, Write)) {
1855#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1856 qWarning() << "ServiceDatabase::createTables():-"
1857 << "Unable to begin transaction. "
1858 << "Reason:" << qPrintable(m_lastError.text());
1859#endif
1860 return false;
1861 }
1862
1863 QString statement(QLatin1String("CREATE TABLE Service("
1864 "ID TEXT NOT NULL PRIMARY KEY UNIQUE,"
1865 "Name TEXT NOT NULL, "
1866 "Location TEXT NOT NULL)"));
1867 if (!executeQuery(query: &query, statement)) {
1868 rollbackTransaction(query: &query);
1869#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1870 qWarning() << "ServiceDatabase::createTables():-"
1871 << qPrintable(m_lastError.text());
1872#endif
1873 return false;
1874 }
1875
1876 statement = QLatin1String("CREATE TABLE Interface("
1877 "ID TEXT NOT NULL PRIMARY KEY UNIQUE,"
1878 "ServiceID TEXT NOT NULL, "
1879 "Name TEXT NOT NULL, "
1880 "VerMaj INTEGER NOT NULL, "
1881 "VerMin INTEGER NOT NULL)");
1882 if (!executeQuery(query: &query, statement)) {
1883 rollbackTransaction(query: &query);
1884#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1885 qWarning() << "ServiceDatabase::createTables():-"
1886 << qPrintable(m_lastError.text());
1887#endif
1888 return false;
1889 }
1890
1891 statement = QLatin1String("CREATE TABLE Defaults("
1892 "InterfaceName TEXT PRIMARY KEY UNIQUE NOT NULL,"
1893 "InterfaceID TEXT NOT NULL)");
1894 if (!executeQuery(query: &query, statement)) {
1895 rollbackTransaction(query: &query);
1896#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1897 qWarning() << "ServiceDatabase::createTables():-"
1898 << qPrintable(m_lastError.text());
1899#endif
1900 return false;
1901 }
1902
1903 statement = QLatin1String("CREATE TABLE ServiceProperty("
1904 "ServiceID TEXT NOT NULL,"
1905 "Key TEXT NOT NULL,"
1906 "Value TEXT NOT NULL)");
1907 if (!executeQuery(query: &query, statement)) {
1908 rollbackTransaction(query: &query);
1909#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1910 qWarning() << "ServiceDatabase::createTables():-"
1911 << qPrintable(m_lastError.text());
1912#endif
1913 return false;
1914 }
1915
1916 statement = QLatin1String("CREATE TABLE InterfaceProperty("
1917 "InterfaceID TEXT NOT NULL,"
1918 "Key TEXT NOT NULL,"
1919 "Value TEXT NOT NULL)");
1920
1921 if (!executeQuery(query: &query, statement)) {
1922 rollbackTransaction(query: &query);
1923#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1924 qWarning() << "ServiceDatabase::createTables():-"
1925 << qPrintable(m_lastError.text());
1926#endif
1927 return false;
1928 }
1929
1930 if (!commitTransaction(query: &query)) {
1931 rollbackTransaction(query: &query);
1932 return false;
1933 }
1934 m_lastError.setError(error: DBError::NoError);
1935 return true;
1936}
1937
1938/*!
1939 Helper method that checks if the all expected tables exist in the database
1940 Returns true if they all exist and false if any of them don't
1941*/
1942bool ServiceDatabase::checkTables()
1943{
1944 bool bTables(false);
1945 QStringList tables = QSqlDatabase::database(connectionName: m_connectionName).tables();
1946 if (tables.contains(str: QLatin1String(SERVICE_TABLE))
1947 && tables.contains(str: QLatin1String(INTERFACE_TABLE))
1948 && tables.contains(str: QLatin1String(DEFAULTS_TABLE))
1949 && tables.contains(str: QLatin1String(SERVICE_PROPERTY_TABLE))
1950 && tables.contains(str: QLatin1String(INTERFACE_PROPERTY_TABLE))){
1951 bTables = true;
1952 }
1953 return bTables;
1954}
1955
1956/*
1957 This function should only ever be used on a user scope database
1958 It removes an entry from the Defaults table where the default
1959 refers to an interface implementation in the system scope database.
1960 The particular default that is removed is specified by
1961 \a interfaceID.
1962
1963 May set the last error to one of the following error codes:
1964 DBError::NoError
1965 DBError::IfaceIDNotExternal
1966 DBError::SqlError
1967 DBError::NoWritePermissions
1968 DBError::InvalidDatabaseFile
1969*/
1970bool ServiceDatabase::removeExternalDefaultServiceInterface(const QString &interfaceID)
1971{
1972 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
1973 QSqlQuery query(database);
1974
1975 //begin transaction
1976 if (!beginTransaction(query: &query, Write)) {
1977#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1978 qWarning() << "ServiceDatabase::removeExternalDefaultServiceInterface():-"
1979 << "Problem: Unable to begin transaction. "
1980 << "Reason:" << qPrintable(m_lastError.text());
1981#endif
1982 return false;
1983 }
1984
1985 QString statement(QLatin1String("SELECT Name FROM Interface WHERE Interface.ID = ?"));
1986 QList<QVariant> bindValues;
1987 bindValues.append(t: interfaceID);
1988 if (!executeQuery(query: &query, statement, bindValues)) {
1989 rollbackTransaction(query: &query);
1990#ifdef QT_SFW_SERVICEDATABASE_DEBUG
1991 qWarning() << "ServiceDatabase::removeDefaultServiceInterface():-"
1992 << qPrintable(m_lastError.text());
1993#endif
1994 return false;
1995 }
1996 if (query.next()) {
1997 QString interfaceName = query.value(i: EBindIndex).toString();
1998 QString errorText(QLatin1String("Local interface implementation exists for interface \"%1\" "
1999 "with interfaceID: \"%2\""));
2000 m_lastError.setError(error: DBError::IfaceIDNotExternal,
2001 errorText: errorText.arg(a: interfaceName).arg(a: interfaceID));
2002 rollbackTransaction(query: &query);
2003 return false;
2004 }
2005
2006 statement = QLatin1String("DELETE FROM Defaults WHERE InterfaceID = ? COLLATE NOCASE");
2007 bindValues.clear();
2008 bindValues.append(t: interfaceID);
2009 if (!executeQuery(query: &query, statement, bindValues)) {
2010 rollbackTransaction(query: &query);
2011#ifdef QT_SFW_SERVICEDATABASE_DEBUG
2012 qWarning() << "ServiceDatabase::removeDefaultServiceInterface():-"
2013 << qPrintable(m_lastError.text());
2014#endif
2015 return false;
2016 }
2017
2018 //end transaction
2019 if (!commitTransaction(query: &query)){
2020 rollbackTransaction(query: &query);
2021 return false;
2022 }
2023 m_lastError.setError(error: DBError::NoError);
2024 return true;
2025}
2026
2027/*
2028 Removes all tables from the database
2029
2030 In future this function may be deprecated or removed.
2031
2032 May set the last error to one of the following error codes:
2033 DBError::NoError
2034 DBError::SqlError
2035 DBError::NoWritePermissions
2036 DBError::InvalidDatabaseFile
2037*/
2038bool ServiceDatabase::dropTables()
2039{
2040 //Execute transaction for deleting the database tables
2041 QSqlDatabase database = QSqlDatabase::database(connectionName: m_connectionName);
2042 QSqlQuery query(database);
2043 QStringList expectedTables;
2044 expectedTables << QLatin1String(SERVICE_TABLE)
2045 << QLatin1String(INTERFACE_TABLE)
2046 << QLatin1String(DEFAULTS_TABLE)
2047 << QLatin1String(SERVICE_PROPERTY_TABLE)
2048 << QLatin1String(INTERFACE_PROPERTY_TABLE);
2049
2050 if (database.tables().count() > 0) {
2051 if (!beginTransaction(query: &query, Write)) {
2052#ifdef QT_SFW_SERVICEDATABASE_DEBUG
2053 qWarning() << "ServiceDatabase::dropTables():-"
2054 << "Unable to begin transaction. "
2055 << "Reason:" << qPrintable(m_lastError.text());
2056#endif
2057 return false;
2058 }
2059 QStringList actualTables = database.tables();
2060
2061 foreach (const QString expectedTable, expectedTables) {
2062 if ((actualTables.contains(str: expectedTable))
2063 && (!executeQuery(query: &query, statement: QLatin1String("DROP TABLE ") + expectedTable))) {
2064 rollbackTransaction(query: &query);
2065#ifdef QT_SFW_SERVICEDATABASE_DEBUG
2066 qWarning() << "ServiceDatabase::dropTables():-"
2067 << qPrintable(m_lastError.text());
2068#endif
2069 return false;
2070 }
2071 }
2072 if (!commitTransaction(query: &query)) {
2073 rollbackTransaction(query: &query);
2074 return false;
2075 }
2076 }
2077 m_lastError.setError(error: DBError::NoError);
2078 return true;
2079}
2080
2081/*
2082 Checks if the database is open
2083*/
2084bool ServiceDatabase::isOpen() const
2085{
2086 return m_isDatabaseOpen;
2087}
2088
2089/*
2090 Checks the database connection.
2091
2092 May set the last error to one of the following error codes:
2093 DBError::DatabaseNotOpen
2094 DBError::InvalidDatabaseConnection
2095*/
2096bool ServiceDatabase::checkConnection()
2097{
2098 if (!m_isDatabaseOpen)
2099 {
2100 m_lastError.setError(error: DBError::DatabaseNotOpen);
2101 return false;
2102 }
2103
2104 if (!QSqlDatabase::database(connectionName: m_connectionName).isValid())
2105 {
2106 m_lastError.setError(error: DBError::InvalidDatabaseConnection);
2107 return false;
2108 }
2109
2110 return true;
2111}
2112
2113/*
2114 Begins a transcaction based on the \a type which can be Read or Write.
2115
2116 May set the last error to one of the following error codes:
2117 DBError::NoError
2118 DBError::SqlError
2119 DBError::NoWritePermissions
2120 DBError::InvalidDatabaseFile
2121*/
2122bool ServiceDatabase::beginTransaction(QSqlQuery *query, TransactionType type)
2123{
2124 bool success;
2125 if (type == Read)
2126 success = query->exec(query: QLatin1String("BEGIN"));
2127 else
2128 success = query->exec(query: QLatin1String("BEGIN IMMEDIATE"));
2129
2130 if (!success) {
2131 int result = query->lastError().number();
2132 if (result == 26 || result == 11) {//SQLITE_NOTADB || SQLITE_CORRUPT
2133 qWarning() << "Service Framework:- Database file is corrupt or invalid:" << databasePath();
2134 m_lastError.setError(error: DBError::InvalidDatabaseFile, errorText: query->lastError().text());
2135 }
2136 else if (result == 8) { //SQLITE_READONLY
2137 qWarning() << "Service Framework:- Insufficient permissions to write to database:" << databasePath();
2138 m_lastError.setError(error: DBError::NoWritePermissions, errorText: query->lastError().text());
2139 }
2140 else
2141 m_lastError.setError(error: DBError::SqlError, errorText: query->lastError().text());
2142 return false;
2143 }
2144
2145 m_lastError.setError(error: DBError::NoError);
2146 return true;
2147}
2148
2149/*
2150 Commits a transaction
2151
2152 May set the last error to one of the following error codes:
2153 DBError::NoError
2154 DBError::SqlError
2155*/
2156bool ServiceDatabase::commitTransaction(QSqlQuery *query)
2157{
2158 Q_ASSERT(query != NULL);
2159 query->finish();
2160 query->clear();
2161 if (!query->exec(query: QLatin1String("COMMIT"))) {
2162 m_lastError.setError(error: DBError::SqlError, errorText: query->lastError().text());
2163 return false;
2164 }
2165 m_lastError.setError(error: DBError::NoError);
2166 return true;
2167}
2168
2169/*
2170 Rolls back a transaction
2171
2172 May set the last error to one of the following error codes:
2173 DBError::NoError
2174 DBError::SqlError
2175*/
2176bool ServiceDatabase::rollbackTransaction(QSqlQuery *query)
2177{
2178 Q_ASSERT(query !=NULL);
2179 query->finish();
2180 query->clear();
2181
2182 if (!query->exec(query: QLatin1String("ROLLBACK"))) {
2183 m_lastError.setError(error: DBError::SqlError, errorText: query->lastError().text());
2184 return false;
2185 }
2186 return true;
2187}
2188
2189/*
2190 Helper function that populates a service \a interface descriptor
2191 with interface related attributes corresponding to the interface
2192 represented by \a interfaceID
2193
2194 It is already assumed that a transaction has been started by the time
2195 this function is called. This function will not rollback/commit the
2196 transaction.
2197
2198 May set the last error to one of the following error codes:
2199 DBError::NoError
2200 DBError::SqlError
2201*/
2202bool ServiceDatabase::populateInterfaceProperties(QServiceInterfaceDescriptor *serviceInterface, const QString &interfaceID)
2203{
2204 QSqlQuery query(QSqlDatabase::database(connectionName: m_connectionName));
2205 QString statement(QLatin1String("SELECT Key, Value FROM InterfaceProperty WHERE InterfaceID = ?"));
2206 QList<QVariant> bindValues;
2207 bindValues.append(t: interfaceID);
2208 if (!executeQuery(query: &query, statement, bindValues)) {
2209#ifdef QT_SFW_SERVICEDATABASE_DEBUG
2210 qWarning() << "ServiceDatabase::populateInterfaceProperties():-"
2211 << qPrintable(m_lastError.text());
2212#endif
2213 return false;
2214 }
2215
2216 bool isFound = false;
2217 QString attribute;
2218 while (query.next()) {
2219 isFound = true;
2220 attribute = query.value(i: EBindIndex).toString();
2221 if (attribute == QLatin1String(INTERFACE_CAPABILITY_KEY)) {
2222 const QStringList capabilities = query.value(i: EBindIndex1).toString().split(sep: QLatin1String(","));
2223 if (capabilities.count() == 1 && capabilities[0].isEmpty()) {
2224 serviceInterface->d->attributes[QServiceInterfaceDescriptor::Capabilities]
2225 = QStringList();
2226 } else {
2227 serviceInterface->d->attributes[QServiceInterfaceDescriptor::Capabilities]
2228 = capabilities;
2229 }
2230 } else if (attribute == QLatin1String(INTERFACE_DESCRIPTION_KEY)) {
2231 serviceInterface->d->attributes[QServiceInterfaceDescriptor::InterfaceDescription]
2232 = query.value(i: EBindIndex1).toString();
2233 } else if (attribute.startsWith(s: QLatin1String("c_"))) {
2234 serviceInterface->d->customAttributes[attribute.mid(position: 2)]
2235 = query.value(i: EBindIndex1).toString();
2236 }
2237 }
2238
2239 if (!isFound) {
2240 QString errorText(QLatin1String("Database integrity corrupted, Properties for InterfaceID: %1 does not exist in the InterfaceProperty table for interface \"%2\""));
2241 m_lastError.setError(error: DBError::SqlError, errorText: errorText.arg(a: interfaceID).arg(a: serviceInterface->interfaceName()));
2242#ifdef QT_SFW_SERVICEDATABASE_DEBUG
2243 qWarning() << "ServiceDatabase::populateInterfaceProperties():-"
2244 << "Problem:" << qPrintable(m_lastError.text());
2245#endif
2246 return false;
2247 }
2248 m_lastError.setError(error: DBError::NoError);
2249 return true;
2250}
2251
2252/*
2253 Helper function that populates a service \a interface descriptor
2254 with service related attributes corresponding to the service
2255 represented by \a serviceID
2256
2257 It is already assumed that a transaction has been started by the time
2258 this function is called. This function will not rollback/commit the
2259 transaction.
2260
2261 May set the last error to one of the following error codes:
2262 DBError::NoError
2263 DBError::SqlError
2264*/
2265bool ServiceDatabase::populateServiceProperties(QServiceInterfaceDescriptor *serviceInterface, const QString &serviceID)
2266{
2267 QSqlQuery query(QSqlDatabase::database(connectionName: m_connectionName));
2268 QString statement(QLatin1String("SELECT Key, Value FROM ServiceProperty WHERE ServiceID = ?"));
2269 QList<QVariant> bindValues;
2270 bindValues.append(t: serviceID);
2271 if (!executeQuery(query: &query, statement, bindValues)) {
2272#ifdef QT_SFW_SERVICEDATABASE_DEBUG
2273 qWarning() << "ServiceDatabase::populateServiceProperties():-"
2274 << qPrintable(m_lastError.text());
2275#endif
2276 return false;
2277 }
2278
2279 bool isFound = false;
2280 QString attribute;
2281 while (query.next()) {
2282 isFound = true;
2283 attribute = query.value(i: EBindIndex).toString();
2284 if (attribute == QLatin1String(SERVICE_DESCRIPTION_KEY)) {
2285 serviceInterface->d->attributes[QServiceInterfaceDescriptor::ServiceDescription]
2286 = query.value(i: EBindIndex1).toString();
2287 }
2288 // fetch initialized and put it as a custom attribute
2289 if (attribute == QLatin1String(SERVICE_INITIALIZED_KEY)) {
2290 serviceInterface->d->customAttributes[attribute] = query.value(i: EBindIndex1).toString();
2291 }
2292 }
2293
2294 if (!isFound) {
2295 QString errorText(QLatin1String("Database integrity corrupted, Service Properties for ServiceID: \"%1\" does not exist in the ServiceProperty table for service \"%2\""));
2296 m_lastError.setError(error: DBError::SqlError, errorText: errorText.arg(a: serviceID).arg(a: serviceInterface->serviceName()));
2297#ifdef QT_SFW_SERVICEDATABASE_DEBUG
2298 qWarning() << "ServiceDatabase::populateServiceProperties():-"
2299 << "Problem:" << qPrintable(m_lastError.text());
2300#endif
2301 return false;
2302 }
2303 m_lastError.setError(error: DBError::NoError);
2304 return true;
2305}
2306
2307#include "moc_servicedatabase_p.cpp"
2308
2309QT_END_NAMESPACE
2310

source code of qtsystems/src/serviceframework/servicedatabase.cpp