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 | |
---|
30 | DAStorage* DAStorage::instance_=0; |
---|
31 | DAStorage* DAStorage::instance(){ |
---|
32 | return instance_ ? instance_ : (instance_ = new DAStorage); |
---|
33 | } |
---|
34 | |
---|
35 | |
---|
36 | |
---|
37 | DAStorage::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 | } |
---|
44 | |
---|
45 | bool somethingFailed = false; |
---|
46 | QSqlQuery query; |
---|
47 | somethingFailed |= !query.exec("CREATE TABLE IF NOT EXISTS t_contacts (" |
---|
48 | "contactId INTEGER PRIMARY KEY AUTOINCREMENT, " |
---|
49 | "name TEXT NOT NULL UNIQUE, " |
---|
50 | "number TEXT NOT NULL UNIQUE " |
---|
51 | "); "); |
---|
52 | somethingFailed |= !query.exec("CREATE TABLE IF NOT EXISTS t_groups (" |
---|
53 | "groupId INTEGER PRIMARY KEY AUTOINCREMENT, " |
---|
54 | "name TEXT NOT NULL UNIQUE " |
---|
55 | "); "); |
---|
56 | somethingFailed |= !query.exec("CREATE TABLE IF NOT EXISTS t_settings (" |
---|
57 | "namespace TEXT NOT NULL, " |
---|
58 | "key TEXT NOT NULL, " |
---|
59 | "value BLOB, " |
---|
60 | "PRIMARY KEY(namespace, key) " |
---|
61 | "); "); |
---|
62 | |
---|
63 | if (somethingFailed) { |
---|
64 | throw Storage::EWriteException(tr("Could not write the table definitions to the database.")); |
---|
65 | } |
---|
66 | } |
---|
67 | |
---|
68 | QSqlDatabase DAStorage::connection() const { |
---|
69 | return dbConnection_; |
---|
70 | } |
---|
71 | QSqlQuery DAStorage::createQuery() const { |
---|
72 | return QSqlQuery(connection()); |
---|
73 | } |
---|
74 | |
---|
75 | |
---|
76 | QVariant DAStorage::readValue(const QString& _namespace, const QString& key, const QVariant& defaultValue){ |
---|
77 | QSqlQuery query = createQuery(); |
---|
78 | query.prepare("SELECT value " |
---|
79 | "FROM t_settings " |
---|
80 | "WHERE (namespace = :namespace) " |
---|
81 | "AND (key = :key); "); |
---|
82 | query.bindValue(":namespace", _namespace); |
---|
83 | query.bindValue(":key", key); |
---|
84 | if (!query.exec() || !query.next()) { |
---|
85 | qWarning() << query.lastError().text(); |
---|
86 | return defaultValue; |
---|
87 | } |
---|
88 | return query.value(0); |
---|
89 | } |
---|
90 | bool DAStorage::readBool(const QString& _namespace, const QString& key, bool defaultValue){ |
---|
91 | return readValue(_namespace, key, defaultValue).toBool(); |
---|
92 | } |
---|
93 | int DAStorage::readInt(const QString& _namespace, const QString& key, int defaultValue){ |
---|
94 | return readValue(_namespace, key, defaultValue).toInt(); |
---|
95 | } |
---|
96 | QString DAStorage::readString(const QString& _namespace, const QString& key, const QString& defaultValue){ |
---|
97 | return readValue(_namespace, key, defaultValue).toString(); |
---|
98 | } |
---|
99 | QString DAStorage::readEncryptedString(const QString& _namespace, const QString& key, const QString& password, const QString& defaultValue) { |
---|
100 | QString value = readString(_namespace, key, defaultValue); |
---|
101 | if (value == defaultValue){ |
---|
102 | return defaultValue; |
---|
103 | } |
---|
104 | |
---|
105 | std::string decrypted; |
---|
106 | |
---|
107 | try { |
---|
108 | CryptoPP::StringSource( |
---|
109 | value.toStdString(), |
---|
110 | true, |
---|
111 | new CryptoPP::DefaultDecryptor( |
---|
112 | password.toUtf8().data(), |
---|
113 | new CryptoPP::StringSink(decrypted) |
---|
114 | ) |
---|
115 | ); |
---|
116 | |
---|
117 | return QString::fromStdString(decrypted); |
---|
118 | } catch (...) { |
---|
119 | qCritical() << "Could not decrypt an encrypted password"; |
---|
120 | return ""; |
---|
121 | } |
---|
122 | } |
---|
123 | QImage DAStorage::readImage(const QString& _namespace, const QString& key, const QImage& defaultValue) { |
---|
124 | return QImage::fromData(readValue(_namespace, key, defaultValue).toByteArray()); |
---|
125 | } |
---|
126 | |
---|
127 | IContact* DAStorage::readContact(int contactId){ |
---|
128 | IContact* contact = new SContact(); |
---|
129 | |
---|
130 | QSqlQuery query = createQuery(); |
---|
131 | query.exec("SELECT name, number " |
---|
132 | "FROM t_contacts " |
---|
133 | "WHERE (contactId = '" + QString::number(contactId) + "'); "); |
---|
134 | if (!query.next()) { |
---|
135 | throw Storage::EReadException(tr("No such contact in the database! [contactId: %1]").arg(contactId)); |
---|
136 | } |
---|
137 | |
---|
138 | contact->setId(contactId); |
---|
139 | contact->setName(query.value(0).toString()); |
---|
140 | contact->setNumber(query.value(1).toString()); |
---|
141 | |
---|
142 | QString aliasesStr = readString("contact_" + QString::number(contactId), "aliases", ""); |
---|
143 | QStringList aliases = aliasesStr.split("\t", QString::SkipEmptyParts); |
---|
144 | contact->setAliases(aliases); |
---|
145 | |
---|
146 | contact->setImage(readImage("contact_" + QString::number(contactId), "image", QImage())); |
---|
147 | return contact; |
---|
148 | } |
---|
149 | |
---|
150 | IGroup* DAStorage::readGroup(int groupId){ |
---|
151 | IGroup* group = new SGroup(); |
---|
152 | |
---|
153 | QSqlQuery query = createQuery(); |
---|
154 | query.exec("SELECT name " |
---|
155 | "FROM t_groups " |
---|
156 | "WHERE (groupId = '" + QString::number(groupId) + "'); "); |
---|
157 | if (!query.next()) { |
---|
158 | throw Storage::EReadException(tr("No such group in the database! [groupId: %1]").arg(groupId)); |
---|
159 | } |
---|
160 | |
---|
161 | group->setId(groupId); |
---|
162 | group->setName(query.value(0).toString()); |
---|
163 | group->setImage(readImage("group_" + QString::number(groupId), "image", QImage())); |
---|
164 | |
---|
165 | QString contactIdsStr = readString("group_" + QString::number(groupId), "contacts", ""); |
---|
166 | QStringList contactIds = contactIdsStr.split("\t", QString::SkipEmptyParts); |
---|
167 | foreach (QString contactStr, contactIds) { |
---|
168 | bool ok; |
---|
169 | int contactId = contactStr.toInt(&ok); |
---|
170 | if (ok) { |
---|
171 | IContact* contact = BCContactManager::instance()->getContact(contactId); |
---|
172 | group->addContact(contact); |
---|
173 | } |
---|
174 | } |
---|
175 | |
---|
176 | return group; |
---|
177 | } |
---|
178 | |
---|
179 | SShortcut DAStorage::readShortcut(const QString& key){ |
---|
180 | SShortcut shortcut; |
---|
181 | shortcut.fromString(readString("shortcut", key, "")); |
---|
182 | return shortcut; |
---|
183 | // BCShortcut shortcut = from_string<BCShortcut>(readString("shortcut", key, "")); |
---|
184 | // return new BCShortcut(shortcut); |
---|
185 | } |
---|
186 | |
---|
187 | |
---|
188 | QSet<IContact*> DAStorage::readContactList(){ |
---|
189 | QSet<IContact*> contactList; |
---|
190 | |
---|
191 | QSqlQuery query = createQuery(); |
---|
192 | query.exec("SELECT contactId " |
---|
193 | "FROM t_contacts; "); |
---|
194 | while (query.next()) { |
---|
195 | try{ |
---|
196 | int contactId = query.value(0).toInt(); |
---|
197 | contactList.insert(readContact(contactId)); |
---|
198 | }catch (const Storage::EReadException& e){ |
---|
199 | // TODO: What shall be done here? |
---|
200 | }catch (const EParseException& e){ |
---|
201 | // ... and here? |
---|
202 | } |
---|
203 | } |
---|
204 | return contactList; |
---|
205 | } |
---|
206 | |
---|
207 | QSet<IGroup*> DAStorage::readGroupList(){ |
---|
208 | QSet<IGroup*> groupList; |
---|
209 | |
---|
210 | QSqlQuery query = createQuery(); |
---|
211 | query.exec("SELECT groupId " |
---|
212 | "FROM t_groups; "); |
---|
213 | while (query.next()) { |
---|
214 | try{ |
---|
215 | int groupId = query.value(0).toInt(); |
---|
216 | groupList.insert(readGroup(groupId)); |
---|
217 | }catch (const Storage::EReadException& e){ |
---|
218 | // TODO: What shall be done here? |
---|
219 | }catch (const EParseException& e){ |
---|
220 | // ... and here? |
---|
221 | } |
---|
222 | } |
---|
223 | return groupList; |
---|
224 | } |
---|
225 | |
---|
226 | |
---|
227 | void DAStorage::writeValue(const QString& _namespace, const QString& key, const QVariant& value) { |
---|
228 | QSqlQuery query = createQuery(); |
---|
229 | query.prepare("INSERT OR REPLACE INTO t_settings (namespace, key, value) " |
---|
230 | "VALUES (:namespace, :key, :value)"); |
---|
231 | query.bindValue(":namespace", _namespace); |
---|
232 | query.bindValue(":key", key); |
---|
233 | query.bindValue(":value", value); |
---|
234 | |
---|
235 | if (!query.exec()) { |
---|
236 | throw Storage::EWriteException(tr("Could not write setting to the database.")); |
---|
237 | } |
---|
238 | |
---|
239 | } |
---|
240 | void DAStorage::writeBool(const QString& _namespace, const QString& key, bool value){ |
---|
241 | writeValue(_namespace, key, value); |
---|
242 | } |
---|
243 | void DAStorage::writeInt(const QString& _namespace, const QString& key, int value){ |
---|
244 | writeValue(_namespace, key, value); |
---|
245 | } |
---|
246 | void DAStorage::writeString(const QString& _namespace, const QString& key, const QString& value){ |
---|
247 | writeValue(_namespace, key, value); |
---|
248 | } |
---|
249 | void DAStorage::writeEncryptedString(const QString& _namespace, const QString& key, const QString& password, const QString& value){ |
---|
250 | int x = 0; |
---|
251 | while (readEncryptedString(_namespace, key, password, "") != value) { |
---|
252 | /* Do not write the password, if it is already in the storage. (Because it is an other every write -> hack is possible) |
---|
253 | * 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) |
---|
254 | */ |
---|
255 | |
---|
256 | if (x > 10) { |
---|
257 | throw Storage::EWriteException(tr("Could not write an encrypted string to the database. [%1, %2]").arg(_namespace).arg(key)); |
---|
258 | } |
---|
259 | |
---|
260 | std::string encrypted; |
---|
261 | CryptoPP::StringSource( |
---|
262 | value.toStdString(), |
---|
263 | true, |
---|
264 | new CryptoPP::DefaultEncryptor( |
---|
265 | password.toUtf8().data(), |
---|
266 | new CryptoPP::StringSink(encrypted) |
---|
267 | ) |
---|
268 | ); |
---|
269 | |
---|
270 | writeString(_namespace, key, QString::fromStdString(encrypted)); |
---|
271 | x++; |
---|
272 | } |
---|
273 | } |
---|
274 | void DAStorage::writeImage(const QString& _namespace, const QString& key, const QImage& image) { |
---|
275 | QByteArray ba; |
---|
276 | QBuffer buffer(&ba); |
---|
277 | buffer.open(QIODevice::WriteOnly); |
---|
278 | image.save(&buffer, "PNG"); |
---|
279 | writeValue(_namespace, key, ba); |
---|
280 | } |
---|
281 | |
---|
282 | |
---|
283 | void DAStorage::writeContact(IContact* contact) { |
---|
284 | QSqlQuery query = createQuery(); |
---|
285 | query.exec("SELECT contactId " |
---|
286 | "FROM t_contacts " |
---|
287 | "WHERE (contactId = '" + QString::number(contact->id()) + "'); "); |
---|
288 | |
---|
289 | if (!query.next()){ |
---|
290 | // Insert |
---|
291 | query.prepare("INSERT INTO t_contacts (name, number) " |
---|
292 | "VALUES (:name, :number);"); |
---|
293 | query.bindValue(":name", contact->name()); |
---|
294 | query.bindValue(":number", contact->number().toString()); |
---|
295 | if (!query.exec()) { |
---|
296 | throw Storage::EWriteException(tr("The contact could not have been written to the database.")); |
---|
297 | } |
---|
298 | |
---|
299 | // Set the contactId since it is undefined if the contact was not saved before. |
---|
300 | query.exec("SELECT contactId " |
---|
301 | "FROM t_contacts " |
---|
302 | "WHERE (name = '" + contact->name() + "'); "); |
---|
303 | if (!query.next()) { |
---|
304 | throw Storage::EWriteException(tr("The contact could not have been written to the database.")); |
---|
305 | } |
---|
306 | contact->setId(query.value(0).toInt()); |
---|
307 | }else{ |
---|
308 | // Update |
---|
309 | query.prepare("UPDATE t_contacts SET " |
---|
310 | "name=:name, " |
---|
311 | "number=:number " |
---|
312 | "WHERE (contactId = :contactId); "); |
---|
313 | query.bindValue(":name", contact->name()); |
---|
314 | query.bindValue(":number", contact->number().toString()); |
---|
315 | query.bindValue(":contactId", contact->id()); |
---|
316 | |
---|
317 | if (!query.exec()) { |
---|
318 | throw Storage::EWriteException(tr("The contact could not have been written to the database.")); |
---|
319 | } |
---|
320 | } |
---|
321 | |
---|
322 | // Save the aliases of the contact |
---|
323 | QString aliasesStr = ""; |
---|
324 | QStringList aliases = contact->aliases(); |
---|
325 | for (int x = 0; x < aliases.size(); ++x){ |
---|
326 | QString alias = aliases.at(x); |
---|
327 | alias.replace("\t", " "); |
---|
328 | |
---|
329 | aliasesStr += alias + "\t"; |
---|
330 | } |
---|
331 | aliasesStr.remove(QRegExp("\\t$")); // Remove last "\t" |
---|
332 | writeString("contact_" + QString::number(contact->id()), "aliases", aliasesStr); |
---|
333 | |
---|
334 | writeImage("contact_" + QString::number(contact->id()), "image", contact->image()); |
---|
335 | } |
---|
336 | |
---|
337 | void DAStorage::writeGroup(IGroup* group){ |
---|
338 | QSqlQuery query = createQuery(); |
---|
339 | query.exec("SELECT groupId " |
---|
340 | "FROM t_groups " |
---|
341 | "WHERE (groupId = '" + QString::number(group->id()) + "'); "); |
---|
342 | if (!query.next()) { |
---|
343 | // Insert |
---|
344 | query.prepare("INSERT INTO t_groups (name) " |
---|
345 | "VALUES (:name);"); |
---|
346 | query.bindValue(":name", group->name()); |
---|
347 | if (!query.exec()) { |
---|
348 | throw Storage::EWriteException(tr("The group could not have been written to the database.")); |
---|
349 | } |
---|
350 | |
---|
351 | // Set the groupId since it is undefined if the group was not saved before. |
---|
352 | query.exec("SELECT groupId " |
---|
353 | "FROM t_groups " |
---|
354 | "WHERE (name = '" + group->name() + "'); "); |
---|
355 | if (!query.next()) { |
---|
356 | throw Storage::EWriteException(tr("The group could not have been written to the database.")); |
---|
357 | } |
---|
358 | group->setId(query.value(0).toInt()); |
---|
359 | }else{ |
---|
360 | // Update |
---|
361 | query.prepare("UPDATE t_groups SET " |
---|
362 | "name=:name " |
---|
363 | "WHERE (groupId = :groupId); "); |
---|
364 | query.bindValue(":name", group->name()); |
---|
365 | query.bindValue(":groupId", group->id()); |
---|
366 | |
---|
367 | if (!query.exec()) { |
---|
368 | throw Storage::EWriteException(tr("The group could not have been written to the database.")); |
---|
369 | } |
---|
370 | } |
---|
371 | writeImage("group_" + QString::number(group->id()), "image", group->image()); |
---|
372 | |
---|
373 | // Save the contacts of the group |
---|
374 | QString contactIdsStr = ""; |
---|
375 | foreach (IContact* contact, group->contacts()) { |
---|
376 | contactIdsStr += to_string(contact->id()) + "\t"; |
---|
377 | } |
---|
378 | contactIdsStr.resize(contactIdsStr.size() - 1); // Remove last "\t" |
---|
379 | writeString("group_" + to_string(group->id()), "contacts", contactIdsStr); |
---|
380 | } |
---|
381 | |
---|
382 | void DAStorage::writeShortcut(const QString& key, const SShortcut& shortcut){ |
---|
383 | if (shortcut.isValid()) { |
---|
384 | writeString("shortcut", key, shortcut.toString()); |
---|
385 | } else { |
---|
386 | removeValue("shortcut", key); |
---|
387 | } |
---|
388 | } |
---|
389 | |
---|
390 | |
---|
391 | void DAStorage::removeValue(const QString& _namespace, const QString& key) { |
---|
392 | QSqlQuery query = createQuery(); |
---|
393 | query.prepare("DELETE FROM t_settings " |
---|
394 | "WHERE (namespace = '" + _namespace + "') " |
---|
395 | "AND (key = '" + key + "'); "); |
---|
396 | if (!query.exec()) { |
---|
397 | throw Storage::EWriteException(tr("Could not remove the setting from the database.")); |
---|
398 | } |
---|
399 | } |
---|
400 | |
---|
401 | void DAStorage::removeValues(const QString& _namespace) { |
---|
402 | QSqlQuery query = createQuery(); |
---|
403 | query.prepare("DELETE FROM t_settings " |
---|
404 | "WHERE (namespace = '" + _namespace + "'); "); |
---|
405 | if (!query.exec()) { |
---|
406 | throw Storage::EWriteException(tr("Could not remove the settings from the database.")); |
---|
407 | } |
---|
408 | } |
---|
409 | |
---|
410 | |
---|
411 | void DAStorage::removeContact(int contactId){ |
---|
412 | try { |
---|
413 | // Remove the settings of the contact |
---|
414 | removeValues("contact_" + contactId); |
---|
415 | |
---|
416 | // Remove the contact itself |
---|
417 | QSqlQuery query = createQuery(); |
---|
418 | query.prepare("DELETE FROM t_contacts " |
---|
419 | "WHERE (contactId = '" + QString::number(contactId) + "'); "); |
---|
420 | if (!query.exec()) { |
---|
421 | throw; |
---|
422 | } |
---|
423 | } catch (...) { |
---|
424 | throw Storage::EWriteException(tr("Could not remove the contact from the database.")); |
---|
425 | } |
---|
426 | } |
---|
427 | |
---|
428 | void DAStorage::removeGroup(int groupId){ |
---|
429 | try { |
---|
430 | // Remove the settings of the group |
---|
431 | removeValues("group_" + groupId); |
---|
432 | |
---|
433 | // Remove the group itself |
---|
434 | QSqlQuery query = createQuery(); |
---|
435 | query.prepare("DELETE FROM t_groups " |
---|
436 | "WHERE (groupId = '" + QString::number(groupId) + "'); "); |
---|
437 | if (!query.exec()) { |
---|
438 | throw; |
---|
439 | } |
---|
440 | } catch (...) { |
---|
441 | throw Storage::EWriteException(tr("Could not remove the group from the database.")); |
---|
442 | } |
---|
443 | } |
---|