source: SMSSender/src/persistence/storage/DAStorage.cpp @ 72:fc91522ef9be

3.0
Last change on this file since 72:fc91522ef9be was 72:fc91522ef9be, checked in by Sämy Zehnder <saemy.zehnder@…>, 12 years ago
  • Started exception handling
  • Fixed several bugs from the removal of the iloginaccount gateway
File size: 17.6 KB
Line 
1/*
2 * DASQLiteStorage.cpp
3 *
4 *  Created on: May 1, 2009
5 *      Author: saemy
6 */
7
8#include "DAStorage.h"
9
10#include <QBuffer>
11#include <QStringList>
12#include <QDebug>
13#include <QSqlError>
14
15#include <crypto++/default.h>
16
17#include <iaccount.h>
18
19#include <stdexceptions.h>
20#include <persistence/storageexceptions.h>
21
22#include <scontact.h>
23#include <sgroup.h>
24#include <sshortcut.h>
25#include "business/BCSettings.h"
26#include "business/BCContactManager.h"
27
28#include <typeconvert.h>
29
30DAStorage* DAStorage::instance_=0;
31DAStorage* DAStorage::instance(){
32    return instance_ ? instance_ : (instance_ = new DAStorage);
33}
34
35
36
37DAStorage::DAStorage() {
38    dbConnection_ = QSqlDatabase::addDatabase("QSQLITE");
39    dbConnection_.setDatabaseName(BCSettings::instance()->getSettingsPath().absoluteFilePath("data.db"));
40    if (!dbConnection_.open()) {
41        qCritical() << dbConnection_.lastError().text();
42        throw Storage::EReadException(tr("Can't open the database!"))
43                        .addDebugInfo("sql-error", dbConnection_.lastError().text());
44    }
45
46    QSqlQuery query;
47    try {
48        if (!query.exec("CREATE TABLE IF NOT EXISTS t_contacts ("
49                          "contactId INTEGER PRIMARY KEY AUTOINCREMENT, "
50                          "name      TEXT    NOT NULL UNIQUE, "
51                          "number    TEXT    NOT NULL UNIQUE "
52                        "); ")) {
53            throw EException(query.lastError().text());
54        }
55        if (!query.exec("CREATE TABLE IF NOT EXISTS t_groups ("
56                          "groupId INTEGER PRIMARY KEY AUTOINCREMENT, "
57                          "name      TEXT    NOT NULL UNIQUE "
58                        "); ")) {
59            throw EException(query.lastError().text());
60        }
61        if (!query.exec("CREATE TABLE IF NOT EXISTS t_settings ("
62                          "namespace TEXT NOT NULL, "
63                          "key       TEXT NOT NULL, "
64                          "value     BLOB, "
65                          "PRIMARY KEY(namespace, key) "
66                        "); ")) {
67            throw EException(query.lastError().text());
68        }
69    } catch (EException e) {
70        throw Storage::EWriteException(tr("Could not write the table definitions to the database"))
71                        .addDebugInfo("sql-error", e.unchainedWhat());
72    }
73}
74
75QSqlDatabase DAStorage::connection() const {
76    return dbConnection_;
77}
78QSqlQuery DAStorage::createQuery() const {
79    return QSqlQuery(connection());
80}
81
82
83QVariant DAStorage::readValue(const QString& _namespace, const QString& key, const QVariant& defaultValue){
84    QSqlQuery query = createQuery();
85    query.prepare("SELECT value "
86                    "FROM t_settings "
87                    "WHERE (namespace = :namespace) "
88                      "AND (key = :key); ");
89    query.bindValue(":namespace", _namespace);
90    query.bindValue(":key", key);
91    if (!query.exec() || !query.next()) {
92        qWarning() << query.lastError().text();
93        return defaultValue;
94    }
95    return query.value(0);
96}
97bool DAStorage::readBool(const QString& _namespace, const QString& key, bool defaultValue){
98    return readValue(_namespace, key, defaultValue).toBool();
99}
100int DAStorage::readInt(const QString& _namespace, const QString& key, int defaultValue){
101    return readValue(_namespace, key, defaultValue).toInt();
102}
103QString DAStorage::readString(const QString& _namespace, const QString& key, const QString& defaultValue){
104    return readValue(_namespace, key, defaultValue).toString();
105}
106QString DAStorage::readEncryptedString(const QString& _namespace, const QString& key, const QString& password, const QString& defaultValue) {
107    QString value = readString(_namespace, key, defaultValue);
108    if (value == defaultValue){
109        return defaultValue;
110    }
111
112    std::string decrypted;
113
114    try {
115        CryptoPP::StringSource(
116            value.toStdString(),
117            true,
118            new CryptoPP::DefaultDecryptor(
119                password.toUtf8().data(),
120                new CryptoPP::StringSink(decrypted)
121            )
122        );
123
124        return QString::fromStdString(decrypted);
125    } catch (...) {
126        qCritical() << "Could not decrypt an encrypted password";
127        return "";
128    }
129}
130QImage DAStorage::readImage(const QString& _namespace, const QString& key, const QImage& defaultValue) {
131    return QImage::fromData(readValue(_namespace, key, defaultValue).toByteArray());
132}
133
134IContact* DAStorage::readContact(int contactId){
135    IContact* contact = new SContact();
136
137    QSqlQuery query = createQuery();
138    query.exec("SELECT name, number "
139                    "FROM t_contacts "
140                    "WHERE (contactId = '" + QString::number(contactId) + "'); ");
141    if (!query.next()) {
142        throw Storage::EReadException(tr("No such contact in the database!"))
143                        .addDebugInfo("contactId", QString::number(contactId))
144                        .addDebugInfo("sql-error", query.lastError().text());
145    }
146
147    contact->setId(contactId);
148    contact->setName(query.value(0).toString());
149    contact->setNumber(query.value(1).toString());
150
151    QString aliasesStr  = readString("contact_" + QString::number(contactId), "aliases", "");
152    QStringList aliases = aliasesStr.split("\t", QString::SkipEmptyParts);
153    contact->setAliases(aliases);
154
155    contact->setImage(readImage("contact_" + QString::number(contactId), "image", QImage()));
156    return contact;
157}
158
159IGroup* DAStorage::readGroup(int groupId){
160    IGroup* group = new SGroup();
161
162    QSqlQuery query = createQuery();
163    query.exec("SELECT name "
164                    "FROM t_groups "
165                    "WHERE (groupId = '" + QString::number(groupId) + "'); ");
166    if (!query.next()) {
167        throw Storage::EReadException(tr("No such group in the database!"))
168                        .addDebugInfo("groupId", QString::number(groupId))
169                        .addDebugInfo("sql-error", query.lastError().text());
170    }
171
172    group->setId(groupId);
173    group->setName(query.value(0).toString());
174    group->setImage(readImage("group_" + QString::number(groupId), "image", QImage()));
175
176    QString contactIdsStr  = readString("group_" + QString::number(groupId), "contacts", "");
177    QStringList contactIds = contactIdsStr.split("\t", QString::SkipEmptyParts);
178    foreach (QString contactStr, contactIds) {
179        bool ok;
180        int contactId = contactStr.toInt(&ok);
181        if (ok) {
182            IContact* contact = BCContactManager::instance()->getContact(contactId);
183            group->addContact(contact);
184        }
185    }
186
187    return group;
188}
189
190SShortcut DAStorage::readShortcut(const QString& key){
191    SShortcut shortcut;
192    shortcut.fromString(readString("shortcut", key, ""));
193    return shortcut;
194//    BCShortcut shortcut = from_string<BCShortcut>(readString("shortcut", key, ""));
195//    return new BCShortcut(shortcut);
196}
197
198
199QSet<IContact*> DAStorage::readContactList(){
200    QSet<IContact*> contactList;
201
202    QSqlQuery query = createQuery();
203    query.exec("SELECT contactId "
204                 "FROM t_contacts; ");
205    while (query.next()) {
206        try{
207            int contactId = query.value(0).toInt();
208            contactList.insert(readContact(contactId));
209        }catch (const Storage::EReadException& e){
210            // TODO: What shall be done here?
211        }catch (const EParseException& e){
212            // ... and here?
213        }
214    }
215    return contactList;
216}
217
218QSet<IGroup*> DAStorage::readGroupList(){
219    QSet<IGroup*> groupList;
220
221    QSqlQuery query = createQuery();
222    query.exec("SELECT groupId "
223                 "FROM t_groups; ");
224    while (query.next()) {
225        try{
226            int groupId = query.value(0).toInt();
227            groupList.insert(readGroup(groupId));
228        }catch (const Storage::EReadException& e){
229            // TODO: What shall be done here?
230        }catch (const EParseException& e){
231            // ... and here?
232        }
233    }
234    return groupList;
235}
236
237
238void DAStorage::writeValue(const QString& _namespace, const QString& key, const QVariant& value) {
239    QSqlQuery query = createQuery();
240    query.prepare("INSERT OR REPLACE INTO t_settings (namespace, key, value) "
241                    "VALUES (:namespace, :key, :value)");
242    query.bindValue(":namespace", _namespace);
243    query.bindValue(":key", key);
244    query.bindValue(":value", value);
245
246    if (!query.exec()) {
247        throw Storage::EWriteException(tr("Could not write setting to the database."))
248                        .addDebugInfo("namespace", _namespace)
249                        .addDebugInfo("key", key)
250                        .addDebugInfo("value", value.toString())
251                        .addDebugInfo("sql-error", query.lastError().text());
252    }
253
254}
255void DAStorage::writeBool(const QString& _namespace, const QString& key, bool value){
256    writeValue(_namespace, key, value);
257}
258void DAStorage::writeInt(const QString& _namespace, const QString& key, int value){
259    writeValue(_namespace, key, value);
260}
261void DAStorage::writeString(const QString& _namespace, const QString& key, const QString& value){
262    writeValue(_namespace, key, value);
263}
264void DAStorage::writeEncryptedString(const QString& _namespace, const QString& key, const QString& password, const QString& value){
265    int x = 0;
266    while (readEncryptedString(_namespace, key, password, "") != value) {
267        /* Do not write the password, if it is already in the storage. (Because it is an other every write -> hack is possible)
268         * Write the password as many times, until the reading of it results in the same value as written... (prevents data-loss because of invalid chars)
269         */
270
271        if (x > 10) {
272            throw Storage::EWriteException(tr("Could not write an encrypted string to the database - maximum attemps reached."))
273                    .addDebugInfo("namespace", _namespace)
274                    .addDebugInfo("key", key);
275        }
276
277        std::string encrypted;
278        CryptoPP::StringSource(
279            value.toStdString(),
280            true,
281            new CryptoPP::DefaultEncryptor(
282                password.toUtf8().data(),
283                new CryptoPP::StringSink(encrypted)
284            )
285        );
286
287        writeString(_namespace, key, QString::fromStdString(encrypted));
288        x++;
289    }
290}
291void DAStorage::writeImage(const QString& _namespace, const QString& key, const QImage& image) {
292    QByteArray ba;
293    QBuffer buffer(&ba);
294    buffer.open(QIODevice::WriteOnly);
295    image.save(&buffer, "PNG");
296    writeValue(_namespace, key, ba);
297}
298
299
300void DAStorage::writeContact(IContact* contact) {
301    QSqlQuery query = createQuery();
302    query.exec("SELECT contactId "
303                 "FROM t_contacts "
304                 "WHERE (contactId = '" + QString::number(contact->id()) + "'); ");
305
306    if (!query.next()){
307        // Insert
308        query.prepare("INSERT INTO t_contacts (name, number) "
309                        "VALUES (:name, :number);");
310        query.bindValue(":name", contact->name());
311        query.bindValue(":number", contact->number().toString());
312        if (!query.exec()) {
313            throw Storage::EWriteException(tr("The contact could not have been written to the database."))
314                            .addDebugInfo("contactId", contact->id())
315                            .addDebugInfo("sql-error", query.lastError().text());
316        }
317
318        // Set the contactId since it is undefined if the contact was not saved before.
319        query.exec("SELECT contactId "
320                     "FROM t_contacts "
321                     "WHERE (name = '" + contact->name() + "'); ");
322        if (!query.next()) {
323            throw Storage::EWriteException(tr("The contact could not have been written to the database."))
324                            .addDebugInfo("contactId", contact->id())
325                            .addDebugInfo("sql-error", query.lastError().text());
326        }
327        contact->setId(query.value(0).toInt());
328    }else{
329        // Update
330        query.prepare("UPDATE t_contacts SET "
331                        "name=:name, "
332                        "number=:number "
333                      "WHERE (contactId = :contactId); ");
334        query.bindValue(":name", contact->name());
335        query.bindValue(":number", contact->number().toString());
336        query.bindValue(":contactId", contact->id());
337
338        if (!query.exec()) {
339            throw Storage::EWriteException(tr("The contact could not have been written to the database."))
340                            .addDebugInfo("contactId", contact->id())
341                            .addDebugInfo("sql-error", query.lastError().text());
342        }
343    }
344
345    // Save the aliases of the contact
346    QString aliasesStr = "";
347    QStringList aliases = contact->aliases();
348    for (int x = 0; x < aliases.size(); ++x){
349        QString alias = aliases.at(x);
350        alias.replace("\t", " ");
351
352        aliasesStr += alias + "\t";
353    }
354    aliasesStr.remove(QRegExp("\\t$")); // Remove last "\t"
355    writeString("contact_" + QString::number(contact->id()), "aliases", aliasesStr);
356
357    writeImage("contact_" + QString::number(contact->id()), "image", contact->image());
358}
359
360void DAStorage::writeGroup(IGroup* group){
361    QSqlQuery query = createQuery();
362    query.exec("SELECT groupId "
363                 "FROM t_groups "
364                 "WHERE (groupId = '" + QString::number(group->id()) + "'); ");
365    if (!query.next()) {
366        // Insert
367        query.prepare("INSERT INTO t_groups (name) "
368                        "VALUES (:name);");
369        query.bindValue(":name", group->name());
370        if (!query.exec()) {
371            throw Storage::EWriteException(tr("The group could not have been written to the database."))
372                            .addDebugInfo("groupId", group->id())
373                            .addDebugInfo("sql-error", query.lastError().text());
374        }
375
376        // Set the groupId since it is undefined if the group was not saved before.
377        query.exec("SELECT groupId "
378                     "FROM t_groups "
379                     "WHERE (name = '" + group->name() + "'); ");
380        if (!query.next()) {
381            throw Storage::EWriteException(tr("The group could not have been written to the database."))
382                            .addDebugInfo("groupId", group->id())
383                            .addDebugInfo("sql-error", query.lastError().text());
384        }
385        group->setId(query.value(0).toInt());
386    }else{
387        // Update
388        query.prepare("UPDATE t_groups SET "
389                        "name=:name "
390                      "WHERE (groupId = :groupId); ");
391        query.bindValue(":name", group->name());
392        query.bindValue(":groupId", group->id());
393
394        if (!query.exec()) {
395            throw Storage::EWriteException(tr("The group could not have been written to the database."))
396                            .addDebugInfo("groupId", group->id())
397                            .addDebugInfo("sql-error", query.lastError().text());
398        }
399    }
400    writeImage("group_" + QString::number(group->id()), "image", group->image());
401
402    // Save the contacts of the group
403    QString contactIdsStr = "";
404    foreach (IContact* contact, group->contacts()) {
405        contactIdsStr += to_string(contact->id()) + "\t";
406    }
407    contactIdsStr.resize(contactIdsStr.size() - 1); // Remove last "\t"
408    writeString("group_" + to_string(group->id()), "contacts", contactIdsStr);
409}
410
411void DAStorage::writeShortcut(const QString& key, const SShortcut& shortcut){
412    if (shortcut.isValid()) {
413        writeString("shortcut", key, shortcut.toString());
414    } else {
415        removeValue("shortcut", key);
416    }
417}
418
419
420void DAStorage::removeValue(const QString& _namespace, const QString& key) {
421    QSqlQuery query = createQuery();
422    query.prepare("DELETE FROM t_settings "
423                    "WHERE (namespace = '" + _namespace + "') "
424                      "AND (key = '" + key + "'); ");
425    if (!query.exec()) {
426        throw Storage::EWriteException(tr("Could not remove the setting from the database."))
427                        .addDebugInfo("namespace", _namespace)
428                        .addDebugInfo("key", key)
429                        .addDebugInfo("sql-error", query.lastError().text());
430    }
431}
432
433void DAStorage::removeValues(const QString& _namespace) {
434    QSqlQuery query = createQuery();
435    query.prepare("DELETE FROM t_settings "
436                    "WHERE (namespace = '" + _namespace + "'); ");
437    if (!query.exec()) {
438        throw Storage::EWriteException(tr("Could not remove the settings from the database."))
439                        .addDebugInfo("namespace", _namespace)
440                        .addDebugInfo("sql-error", query.lastError().text());
441    }
442}
443
444
445void DAStorage::removeContact(int contactId){
446    try {
447        // Remove the settings of the contact
448        removeValues("contact_" + contactId);
449
450        // Remove the contact itself
451        QSqlQuery query = createQuery();
452        query.prepare("DELETE FROM t_contacts "
453                        "WHERE (contactId = '" + QString::number(contactId) + "'); ");
454        if (!query.exec()) {
455            throw EException(query.lastError().text());
456        }
457    } catch (EException e) {
458        throw Storage::EWriteException(tr("Could not remove the contact from the database"))
459                        .addDebugInfo("contactId", contactId)
460                        .chain(e);
461    }
462}
463
464void DAStorage::removeGroup(int groupId){
465    try {
466        // Remove the settings of the group
467        removeValues("group_" + groupId);
468
469        // Remove the group itself
470        QSqlQuery query = createQuery();
471        query.prepare("DELETE FROM t_groups "
472                        "WHERE (groupId = '" + QString::number(groupId) + "'); ");
473        if (!query.exec()) {
474            throw EException(query.lastError().text());
475        }
476    } catch (EException e) {
477        throw Storage::EWriteException(tr("Could not remove the group from the database"))
478                        .addDebugInfo("groupId", groupId)
479                        .chain(e);
480    }
481}
Note: See TracBrowser for help on using the repository browser.