adding new settings for proper message redirection
[quassel.git] / src / core / sqlitestorage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by the Quassel IRC Team                         *
3  *   devel@quassel-irc.org                                                 *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) version 3.                                           *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20
21 #include "sqlitestorage.h"
22
23 #include <QtSql>
24
25 #include "network.h"
26
27 #include "util.h"
28 #include "logger.h"
29
30 SqliteStorage::SqliteStorage(QObject *parent)
31   : AbstractSqlStorage(parent)
32 {
33 }
34
35 SqliteStorage::~SqliteStorage() {
36 }
37
38 bool SqliteStorage::isAvailable() const {
39   if(!QSqlDatabase::isDriverAvailable("QSQLITE")) return false;
40   return true;
41 }
42
43 QString SqliteStorage::displayName() const {
44   return QString("SQLite");
45 }
46
47 QString SqliteStorage::description() const {
48   return tr("SQLite is a file-based database engine that does not require any setup. It is suitable for small and medium-sized "
49             "databases that do not require access via network. Use SQLite if your Quassel Core should store its data on the same machine "
50             "it is running on, and if you only expect a few users to use your core.");
51 }
52
53 int SqliteStorage::installedSchemaVersion() {
54   QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'");
55   if(query.first())
56     return query.value(0).toInt();
57
58   // maybe it's really old... (schema version 0)
59   query = logDb().exec("SELECT MAX(version) FROM coreinfo");
60   if(query.first())
61     return query.value(0).toInt();
62
63   return AbstractSqlStorage::installedSchemaVersion();
64 }
65
66 UserId SqliteStorage::addUser(const QString &user, const QString &password) {
67   QSqlQuery query(logDb());
68   query.prepare(queryString("insert_quasseluser"));
69   query.bindValue(":username", user);
70   query.bindValue(":password", cryptedPassword(password));
71   query.exec();
72   if(query.lastError().isValid() && query.lastError().number() == 19) { // user already exists - sadly 19 seems to be the general constraint violation error...
73     return 0;
74   }
75
76   query.prepare(queryString("select_userid"));
77   query.bindValue(":username", user);
78   query.exec();
79   query.first();
80   UserId uid = query.value(0).toInt();
81   emit userAdded(uid, user);
82   return uid;
83 }
84
85 void SqliteStorage::updateUser(UserId user, const QString &password) {
86   QSqlQuery query(logDb());
87   query.prepare(queryString("update_userpassword"));
88   query.bindValue(":userid", user.toInt());
89   query.bindValue(":password", cryptedPassword(password));
90   query.exec();
91 }
92
93 void SqliteStorage::renameUser(UserId user, const QString &newName) {
94   QSqlQuery query(logDb());
95   query.prepare(queryString("update_username"));
96   query.bindValue(":userid", user.toInt());
97   query.bindValue(":username", newName);
98   query.exec();
99   emit userRenamed(user, newName);
100 }
101
102 UserId SqliteStorage::validateUser(const QString &user, const QString &password) {
103   QSqlQuery query(logDb());
104   query.prepare(queryString("select_authuser"));
105   query.bindValue(":username", user);
106   query.bindValue(":password", cryptedPassword(password));
107   query.exec();
108
109   if(query.first()) {
110     return query.value(0).toInt();
111   } else {
112     return 0;
113   }
114 }
115
116 UserId SqliteStorage::internalUser() {
117   QSqlQuery query(logDb());
118   query.prepare(queryString("select_internaluser"));
119   query.exec();
120
121   if(query.first()) {
122     return query.value(0).toInt();
123   } else {
124     return 0;
125   }
126 }
127
128 void SqliteStorage::delUser(UserId user) {
129   QSqlQuery query(logDb());
130   query.prepare(queryString("delete_backlog_by_uid"));
131   query.bindValue(":userid", user.toInt());
132   query.exec();
133   
134   query.prepare(queryString("delete_buffers_by_uid"));
135   query.bindValue(":userid", user.toInt());
136   query.exec();
137   
138   query.prepare(queryString("delete_networks_by_uid"));
139   query.bindValue(":userid", user.toInt());
140   query.exec();
141   
142   query.prepare(queryString("delete_quasseluser"));
143   query.bindValue(":userid", user.toInt());
144   query.exec();
145   // I hate the lack of foreign keys and on delete cascade... :(
146   emit userRemoved(user);
147 }
148
149 void SqliteStorage::setUserSetting(UserId userId, const QString &settingName, const QVariant &data) {
150   QByteArray rawData;
151   QDataStream out(&rawData, QIODevice::WriteOnly);
152   out.setVersion(QDataStream::Qt_4_2);
153   out << data;
154
155   QSqlQuery query(logDb());
156   query.prepare(queryString("insert_user_setting"));
157   query.bindValue(":userid", userId.toInt());
158   query.bindValue(":settingname", settingName);
159   query.bindValue(":settingvalue", rawData);
160   query.exec();
161
162   if(query.lastError().isValid()) {
163     QSqlQuery updateQuery(logDb());
164     updateQuery.prepare(queryString("update_user_setting"));
165     updateQuery.bindValue(":userid", userId.toInt());
166     updateQuery.bindValue(":settingname", settingName);
167     updateQuery.bindValue(":settingvalue", rawData);
168     updateQuery.exec();
169   }
170                   
171 }
172
173 QVariant SqliteStorage::getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData) {
174   QSqlQuery query(logDb());
175   query.prepare(queryString("select_user_setting"));
176   query.bindValue(":userid", userId.toInt());
177   query.bindValue(":settingname", settingName);
178   query.exec();
179
180   if(query.first()) {
181     QVariant data;
182     QByteArray rawData = query.value(0).toByteArray();
183     QDataStream in(&rawData, QIODevice::ReadOnly);
184     in.setVersion(QDataStream::Qt_4_2);
185     in >> data;
186     return data;
187   } else {
188     return defaultData;
189   }
190 }
191
192 NetworkId SqliteStorage::createNetwork(UserId user, const NetworkInfo &info) {
193   NetworkId networkId;
194   QSqlQuery query(logDb());
195   query.prepare(queryString("insert_network"));
196   query.bindValue(":userid", user.toInt());
197   query.bindValue(":networkname", info.networkName);
198   query.exec();
199   
200   networkId = getNetworkId(user, info.networkName);
201   if(!networkId.isValid()) {
202     watchQuery(&query);
203   } else {
204     NetworkInfo newNetworkInfo = info;
205     newNetworkInfo.networkId = networkId;
206     updateNetwork(user, newNetworkInfo);
207   }
208   return networkId;
209 }
210
211 bool SqliteStorage::updateNetwork(UserId user, const NetworkInfo &info) {
212   if(!isValidNetwork(user, info.networkId))
213      return false;
214   
215   QSqlQuery updateQuery(logDb());
216   updateQuery.prepare(queryString("update_network"));
217   updateQuery.bindValue(":networkname", info.networkName);
218   updateQuery.bindValue(":identityid", info.identity.toInt());
219   updateQuery.bindValue(":usecustomencoding", info.useCustomEncodings ? 1 : 0);
220   updateQuery.bindValue(":encodingcodec", QString(info.codecForEncoding));
221   updateQuery.bindValue(":decodingcodec", QString(info.codecForDecoding));
222   updateQuery.bindValue(":servercodec", QString(info.codecForServer));
223   updateQuery.bindValue(":userandomserver", info.useRandomServer ? 1 : 0);
224   updateQuery.bindValue(":perform", info.perform.join("\n"));
225   updateQuery.bindValue(":useautoidentify", info.useAutoIdentify ? 1 : 0);
226   updateQuery.bindValue(":autoidentifyservice", info.autoIdentifyService);
227   updateQuery.bindValue(":autoidentifypassword", info.autoIdentifyPassword);
228   updateQuery.bindValue(":useautoreconnect", info.useAutoReconnect ? 1 : 0);
229   updateQuery.bindValue(":autoreconnectinterval", info.autoReconnectInterval);
230   updateQuery.bindValue(":autoreconnectretries", info.autoReconnectRetries);
231   updateQuery.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries ? 1 : 0);
232   updateQuery.bindValue(":rejoinchannels", info.rejoinChannels ? 1 : 0);
233   updateQuery.bindValue(":networkid", info.networkId.toInt());
234   updateQuery.exec();
235   if(!watchQuery(&updateQuery))
236     return false;
237
238   QSqlQuery dropServersQuery(logDb());
239   dropServersQuery.prepare("DELETE FROM ircserver WHERE networkid = :networkid");
240   dropServersQuery.bindValue(":networkid", info.networkId.toInt());
241   dropServersQuery.exec();
242   if(!watchQuery(&dropServersQuery))
243     return false;
244
245   QSqlQuery insertServersQuery(logDb());
246   insertServersQuery.prepare(queryString("insert_server"));
247   foreach(QVariant server_, info.serverList) {
248     QVariantMap server = server_.toMap();
249     insertServersQuery.bindValue(":hostname", server["Host"]);
250     insertServersQuery.bindValue(":port", server["Port"].toInt());
251     insertServersQuery.bindValue(":password", server["Password"]);
252     insertServersQuery.bindValue(":ssl", server["UseSSL"].toBool() ? 1 : 0);
253     insertServersQuery.bindValue(":userid", user.toInt());
254     insertServersQuery.bindValue(":networkid", info.networkId.toInt());
255
256     insertServersQuery.exec();
257     if(!watchQuery(&insertServersQuery))
258       return false;
259   }
260   
261   return true;
262 }
263
264 bool SqliteStorage::removeNetwork(UserId user, const NetworkId &networkId) {
265   if(!isValidNetwork(user, networkId))
266      return false;
267
268   bool withTransaction = logDb().driver()->hasFeature(QSqlDriver::Transactions);
269   if(withTransaction) {
270     sync();
271     if(!logDb().transaction()) {
272       qWarning() << "SqliteStorage::removeNetwork(): cannot start transaction. continuing with out rollback support!";
273       withTransaction = false;
274     }
275   }
276   
277   QSqlQuery deleteBacklogQuery(logDb());
278   deleteBacklogQuery.prepare(queryString("delete_backlog_for_network"));
279   deleteBacklogQuery.bindValue(":networkid", networkId.toInt());
280   deleteBacklogQuery.exec();
281   if(!watchQuery(&deleteBacklogQuery)) {
282     if(withTransaction)
283       logDb().rollback();
284     return false;
285   }
286   
287   QSqlQuery deleteBuffersQuery(logDb());
288   deleteBuffersQuery.prepare(queryString("delete_buffers_for_network"));
289   deleteBuffersQuery.bindValue(":networkid", networkId.toInt());
290   deleteBuffersQuery.exec();
291   if(!watchQuery(&deleteBuffersQuery)) {
292     if(withTransaction)
293       logDb().rollback();
294     return false;
295   }
296   
297   QSqlQuery deleteServersQuery(logDb());
298   deleteServersQuery.prepare(queryString("delete_ircservers_for_network"));
299   deleteServersQuery.bindValue(":networkid", networkId.toInt());
300   deleteServersQuery.exec();
301   if(!watchQuery(&deleteServersQuery)) {
302     if(withTransaction)
303       logDb().rollback();
304     return false;
305   }
306   
307   QSqlQuery deleteNetworkQuery(logDb());
308   deleteNetworkQuery.prepare(queryString("delete_network"));
309   deleteNetworkQuery.bindValue(":networkid", networkId.toInt());
310   deleteNetworkQuery.exec();
311   if(!watchQuery(&deleteNetworkQuery)) {
312     if(withTransaction)
313       logDb().rollback();
314     return false;
315   }
316
317   logDb().commit();
318   return true;
319 }
320
321 QList<NetworkInfo> SqliteStorage::networks(UserId user) {
322   QList<NetworkInfo> nets;
323
324   QSqlQuery networksQuery(logDb());
325   networksQuery.prepare(queryString("select_networks_for_user"));
326   networksQuery.bindValue(":userid", user.toInt());
327   
328   QSqlQuery serversQuery(logDb());
329   serversQuery.prepare(queryString("select_servers_for_network"));
330
331   networksQuery.exec();
332   if(!watchQuery(&networksQuery))
333     return nets;
334
335   while(networksQuery.next()) {
336     NetworkInfo net;
337     net.networkId = networksQuery.value(0).toInt();
338     net.networkName = networksQuery.value(1).toString();
339     net.identity = networksQuery.value(2).toInt();
340     net.codecForServer = networksQuery.value(3).toString().toAscii();
341     net.codecForEncoding = networksQuery.value(4).toString().toAscii();
342     net.codecForDecoding = networksQuery.value(5).toString().toAscii();
343     net.useRandomServer = networksQuery.value(6).toInt() == 1 ? true : false;
344     net.perform = networksQuery.value(7).toString().split("\n");
345     net.useAutoIdentify = networksQuery.value(8).toInt() == 1 ? true : false;
346     net.autoIdentifyService = networksQuery.value(9).toString();
347     net.autoIdentifyPassword = networksQuery.value(10).toString();
348     net.useAutoReconnect = networksQuery.value(11).toInt() == 1 ? true : false;
349     net.autoReconnectInterval = networksQuery.value(12).toUInt();
350     net.autoReconnectRetries = networksQuery.value(13).toInt();
351     net.unlimitedReconnectRetries = networksQuery.value(14).toInt() == 1 ? true : false;
352     net.rejoinChannels = networksQuery.value(15).toInt() == 1 ? true : false;
353
354     serversQuery.bindValue(":networkid", net.networkId.toInt());
355     serversQuery.exec();
356     if(!watchQuery(&serversQuery))
357       return nets;
358
359     QVariantList servers;
360     while(serversQuery.next()) {
361       QVariantMap server;
362       server["Host"] = serversQuery.value(0).toString();
363       server["Port"] = serversQuery.value(1).toInt();
364       server["Password"] = serversQuery.value(2).toString();
365       server["UseSSL"] = serversQuery.value(3).toInt() == 1 ? true : false;
366       servers << server;
367     }
368     net.serverList = servers;
369     nets << net;
370   }
371   return nets;
372 }
373
374 bool SqliteStorage::isValidNetwork(UserId user, const NetworkId &networkId) {
375   QSqlQuery query(logDb());
376   query.prepare(queryString("select_networkExists"));
377   query.bindValue(":userid", user.toInt());
378   query.bindValue(":networkid", networkId.toInt());
379   query.exec();
380
381   watchQuery(&query); // there should not occur any errors
382   if(!query.first())
383     return false;
384   
385   Q_ASSERT(!query.next());
386   return true;
387 }
388
389 bool SqliteStorage::isValidBuffer(const UserId &user, const BufferId &bufferId) {
390   QSqlQuery query(logDb());
391   query.prepare(queryString("select_bufferExists"));
392   query.bindValue(":userid", user.toInt());
393   query.bindValue(":bufferid", bufferId.toInt());
394   query.exec();
395
396   watchQuery(&query);
397   if(!query.first())
398     return false;
399
400   Q_ASSERT(!query.next());
401   return true;
402 }
403
404 NetworkId SqliteStorage::getNetworkId(UserId user, const QString &network) {
405   QSqlQuery query(logDb());
406   query.prepare("SELECT networkid FROM network "
407                 "WHERE userid = :userid AND networkname = :networkname");
408   query.bindValue(":userid", user.toInt());
409   query.bindValue(":networkname", network);
410   query.exec();
411   
412   if(query.first())
413     return query.value(0).toInt();
414   else
415     return NetworkId();
416 }
417
418 QList<NetworkId> SqliteStorage::connectedNetworks(UserId user) {
419   QList<NetworkId> connectedNets;
420   QSqlQuery query(logDb());
421   query.prepare(queryString("select_connected_networks"));
422   query.bindValue(":userid", user.toInt());
423   query.exec();
424   watchQuery(&query);
425
426   while(query.next()) {
427     connectedNets << query.value(0).toInt();
428   }
429   
430   return connectedNets;
431 }
432
433 void SqliteStorage::setNetworkConnected(UserId user, const NetworkId &networkId, bool isConnected) {
434   QSqlQuery query(logDb());
435   query.prepare(queryString("update_network_connected"));
436   query.bindValue(":userid", user.toInt());
437   query.bindValue(":networkid", networkId.toInt());
438   query.bindValue(":connected", isConnected ? 1 : 0);
439   query.exec();
440   watchQuery(&query);
441 }
442
443 QHash<QString, QString> SqliteStorage::persistentChannels(UserId user, const NetworkId &networkId) {
444   QHash<QString, QString> persistentChans;
445   QSqlQuery query(logDb());
446   query.prepare(queryString("select_persistent_channels"));
447   query.bindValue(":userid", user.toInt());
448   query.bindValue(":networkid", networkId.toInt());
449   query.exec();
450   watchQuery(&query);
451
452   while(query.next()) {
453     persistentChans[query.value(0).toString()] = query.value(1).toString();
454   }
455   
456   return persistentChans;
457 }
458
459 void SqliteStorage::setChannelPersistent(UserId user, const NetworkId &networkId, const QString &channel, bool isJoined) {
460   QSqlQuery query(logDb());
461   query.prepare(queryString("update_buffer_persistent_channel"));
462   query.bindValue(":userid", user.toInt());
463   query.bindValue(":networkId", networkId.toInt());
464   query.bindValue(":buffercname", channel.toLower());
465   query.bindValue(":joined", isJoined ? 1 : 0);
466   query.exec();
467   watchQuery(&query);
468 }
469
470 void SqliteStorage::setPersistentChannelKey(UserId user, const NetworkId &networkId, const QString &channel, const QString &key) {
471   QSqlQuery query(logDb());
472   query.prepare(queryString("update_buffer_set_channel_key"));
473   query.bindValue(":userid", user.toInt());
474   query.bindValue(":networkId", networkId.toInt());
475   query.bindValue(":buffercname", channel.toLower());
476   query.bindValue(":key", key);
477   query.exec();
478   watchQuery(&query);
479 }
480
481
482 void SqliteStorage::createBuffer(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
483   QSqlQuery *query = cachedQuery("insert_buffer");
484   query->bindValue(":userid", user.toInt());
485   query->bindValue(":networkid", networkId.toInt());
486   query->bindValue(":buffertype", (int)type);
487   query->bindValue(":buffername", buffer);
488   query->bindValue(":buffercname", buffer.toLower());
489   query->exec();
490
491   watchQuery(query);
492 }
493
494 BufferInfo SqliteStorage::getBufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
495   QSqlQuery *query = cachedQuery("select_bufferByName");
496   query->bindValue(":networkid", networkId.toInt());
497   query->bindValue(":userid", user.toInt());
498   query->bindValue(":buffercname", buffer.toLower());
499   query->exec();
500
501   if(!query->first()) {
502     createBuffer(user, networkId, type, buffer);
503     query->exec();
504     if(!query->first()) {
505       watchQuery(query);
506       qWarning() << "unable to create BufferInfo for:" << user << networkId << buffer;
507       return BufferInfo();
508     }
509   }
510
511   BufferInfo bufferInfo = BufferInfo(query->value(0).toInt(), networkId, (BufferInfo::Type)query->value(1).toInt(), 0, buffer);
512   if(query->next()) {
513     qCritical() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
514     qCritical() << "         Query:" << query->lastQuery();
515     qCritical() << "  bound Values:";
516     QList<QVariant> list = query->boundValues().values();
517     for (int i = 0; i < list.size(); ++i)
518       qCritical() << i << ":" << list.at(i).toString().toAscii().data();
519     Q_ASSERT(false);
520   }
521
522   return bufferInfo;
523 }
524
525 BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId) {
526   QSqlQuery query(logDb());
527   query.prepare(queryString("select_buffer_by_id"));
528   query.bindValue(":userid", user.toInt());
529   query.bindValue(":bufferid", bufferId.toInt());
530   query.exec();
531   if(!watchQuery(&query))
532     return BufferInfo();
533
534   if(!query.first())
535     return BufferInfo();
536
537   BufferInfo bufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
538   Q_ASSERT(!query.next());
539
540   return bufferInfo;
541 }
542
543 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user) {
544   QList<BufferInfo> bufferlist;
545   QSqlQuery query(logDb());
546   query.prepare(queryString("select_buffers"));
547   query.bindValue(":userid", user.toInt());
548   
549   query.exec();
550   watchQuery(&query);
551   while(query.next()) {
552     bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
553   }
554   return bufferlist;
555 }
556
557 QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) {
558   QList<BufferId> bufferList;
559   QSqlQuery query(logDb());
560   query.prepare(queryString("select_buffers_for_network"));
561   query.bindValue(":networkid", networkId.toInt());
562   query.bindValue(":userid", user.toInt());
563
564   query.exec();
565   watchQuery(&query);
566   while(query.next()) {
567     bufferList << BufferId(query.value(0).toInt());
568   }
569   return bufferList;
570 }
571
572 bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
573   if(!isValidBuffer(user, bufferId))
574     return false;
575
576   QSqlQuery delBacklogQuery(logDb());
577   delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
578   delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
579   delBacklogQuery.exec();
580   if(!watchQuery(&delBacklogQuery))
581     return false;
582
583   QSqlQuery delBufferQuery(logDb());
584   delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
585   delBufferQuery.bindValue(":bufferid", bufferId.toInt());
586   delBufferQuery.exec();
587   if(!watchQuery(&delBufferQuery))
588     return false;
589
590   return true;
591 }
592
593 BufferId SqliteStorage::renameBuffer(const UserId &user, const NetworkId &networkId, const QString &newName, const QString &oldName) {
594   // check if such a buffer exists...
595   QSqlQuery existsQuery(logDb());
596   existsQuery.prepare(queryString("select_bufferByName"));
597   existsQuery.bindValue(":networkid", networkId.toInt());
598   existsQuery.bindValue(":userid", user.toInt());
599   existsQuery.bindValue(":buffercname", oldName.toLower());
600   existsQuery.exec();
601   if(!watchQuery(&existsQuery))
602     return false;
603
604   if(!existsQuery.first())
605     return false;
606
607   const int bufferid = existsQuery.value(0).toInt();
608
609   Q_ASSERT(!existsQuery.next());
610
611   // ... and if the new name is still free.
612   existsQuery.bindValue(":networkid", networkId.toInt());
613   existsQuery.bindValue(":userid", user.toInt());
614   existsQuery.bindValue(":buffercname", newName.toLower());
615   existsQuery.exec();
616   if(!watchQuery(&existsQuery))
617     return false;
618
619   if(existsQuery.first())
620     return false;
621
622   QSqlQuery renameBufferQuery(logDb());
623   renameBufferQuery.prepare(queryString("update_buffer_name"));
624   renameBufferQuery.bindValue(":buffername", newName);
625   renameBufferQuery.bindValue(":buffercname", newName.toLower());
626   renameBufferQuery.bindValue(":bufferid", bufferid);
627   renameBufferQuery.exec();
628   if(watchQuery(&existsQuery))
629     return BufferId(bufferid);
630   else
631     return BufferId();
632 }
633
634 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
635   QSqlQuery *query = cachedQuery("update_buffer_lastseen");
636   query->bindValue(":userid", user.toInt());
637   query->bindValue(":bufferid", bufferId.toInt());
638   query->bindValue(":lastseenmsgid", msgId.toInt());
639   query->exec();
640   watchQuery(query);
641 }
642
643 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user) {
644   QHash<BufferId, MsgId> lastSeenHash;
645   QSqlQuery query(logDb());
646   query.prepare(queryString("select_buffer_lastseen_messages"));
647   query.bindValue(":userid", user.toInt());
648   query.exec();
649   if(!watchQuery(&query))
650     return lastSeenHash;
651
652   while(query.next()) {
653     lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
654   }
655   return lastSeenHash;
656 }
657
658 MsgId SqliteStorage::logMessage(Message msg) {
659   QSqlQuery *logMessageQuery = cachedQuery("insert_message");
660   logMessageQuery->bindValue(":time", msg.timestamp().toTime_t());
661   logMessageQuery->bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
662   logMessageQuery->bindValue(":type", msg.type());
663   logMessageQuery->bindValue(":flags", (int)msg.flags());
664   logMessageQuery->bindValue(":sender", msg.sender());
665   logMessageQuery->bindValue(":message", msg.contents());
666   logMessageQuery->exec();
667   
668   if(logMessageQuery->lastError().isValid()) {
669     // constraint violation - must be NOT NULL constraint - probably the sender is missing...
670     if(logMessageQuery->lastError().number() == 19) {
671       QSqlQuery *addSenderQuery = cachedQuery("insert_sender");
672       addSenderQuery->bindValue(":sender", msg.sender());
673       addSenderQuery->exec();
674       watchQuery(addSenderQuery);
675       logMessageQuery->exec();
676       if(!watchQuery(logMessageQuery))
677         return 0;
678     } else {
679       watchQuery(logMessageQuery);
680     }
681   }
682
683   MsgId msgId = logMessageQuery->lastInsertId().toInt();
684   Q_ASSERT(msgId.isValid());
685   return msgId;
686 }
687
688 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, int lastmsgs, int offset) {
689   QList<Message> messagelist;
690
691   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
692   if(!bufferInfo.isValid())
693     return messagelist;
694
695   if(offset == -1) {
696     offset = 0;
697   } else {
698     // we have to determine the real offset first
699     QSqlQuery *offsetQuery = cachedQuery("select_messagesOffset");
700     offsetQuery->bindValue(":bufferid", bufferId.toInt());
701     offsetQuery->bindValue(":messageid", offset);
702     offsetQuery->exec();
703     offsetQuery->first();
704     offset = offsetQuery->value(0).toInt();
705   }
706
707   // now let's select the messages
708   QSqlQuery *msgQuery = cachedQuery("select_messages");
709   msgQuery->bindValue(":bufferid", bufferId.toInt());
710   msgQuery->bindValue(":limit", lastmsgs);
711   msgQuery->bindValue(":offset", offset);
712   msgQuery->exec();
713   
714   watchQuery(msgQuery);
715   
716   while(msgQuery->next()) {
717     Message msg(QDateTime::fromTime_t(msgQuery->value(1).toInt()),
718                 bufferInfo,
719                 (Message::Type)msgQuery->value(2).toUInt(),
720                 msgQuery->value(5).toString(),
721                 msgQuery->value(4).toString(),
722                 (Message::Flags)msgQuery->value(3).toUInt());
723     msg.setMsgId(msgQuery->value(0).toInt());
724     messagelist << msg;
725   }
726   return messagelist;
727 }
728
729
730 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, QDateTime since, int offset) {
731   QList<Message> messagelist;
732
733   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
734   if(!bufferInfo.isValid())
735     return messagelist;
736
737   // we have to determine the real offset first
738   QSqlQuery *offsetQuery = cachedQuery("select_messagesSinceOffset");
739   offsetQuery->bindValue(":bufferid", bufferId.toInt());
740   offsetQuery->bindValue(":since", since.toTime_t());
741   offsetQuery->exec();
742   offsetQuery->first();
743   offset = offsetQuery->value(0).toInt();
744
745   // now let's select the messages
746   QSqlQuery *msgQuery = cachedQuery("select_messagesSince");
747   msgQuery->bindValue(":bufferid", bufferId.toInt());
748   msgQuery->bindValue(":since", since.toTime_t());
749   msgQuery->bindValue(":offset", offset);
750   msgQuery->exec();
751
752   watchQuery(msgQuery);
753   
754   while(msgQuery->next()) {
755     Message msg(QDateTime::fromTime_t(msgQuery->value(1).toInt()),
756                 bufferInfo,
757                 (Message::Type)msgQuery->value(2).toUInt(),
758                 msgQuery->value(5).toString(),
759                 msgQuery->value(4).toString(),
760                 (Message::Flags)msgQuery->value(3).toUInt());
761     msg.setMsgId(msgQuery->value(0).toInt());
762     messagelist << msg;
763   }
764
765   return messagelist;
766 }
767
768
769 QList<Message> SqliteStorage::requestMsgRange(UserId user, BufferId bufferId, int first, int last) {
770   QList<Message> messagelist;
771
772   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
773   if(!bufferInfo.isValid())
774     return messagelist;
775
776   QSqlQuery *rangeQuery = cachedQuery("select_messageRange");
777   rangeQuery->bindValue(":bufferid", bufferId.toInt());
778   rangeQuery->bindValue(":firstmsg", first);
779   rangeQuery->bindValue(":lastmsg", last);
780   rangeQuery->exec();
781
782   watchQuery(rangeQuery);
783   
784   while(rangeQuery->next()) {
785     Message msg(QDateTime::fromTime_t(rangeQuery->value(1).toInt()),
786                 bufferInfo,
787                 (Message::Type)rangeQuery->value(2).toUInt(),
788                 rangeQuery->value(5).toString(),
789                 rangeQuery->value(4).toString(),
790                 (Message::Flags)rangeQuery->value(3).toUInt());
791     msg.setMsgId(rangeQuery->value(0).toInt());
792     messagelist << msg;
793   }
794
795   return messagelist;
796 }
797
798 QString SqliteStorage::backlogFile() {
799   return quasselDir().absolutePath() + "/quassel-storage.sqlite";  
800 }
801
802
803 // ONLY NEEDED FOR MIGRATION
804 bool SqliteStorage::init(const QVariantMap &settings) {
805   if(!AbstractSqlStorage::init(settings))
806     return false;
807
808   QSqlQuery checkMigratedQuery(logDb());
809   checkMigratedQuery.prepare("SELECT DISTINCT typeOf(password) FROM quasseluser");
810   checkMigratedQuery.exec();
811   if(!watchQuery(&checkMigratedQuery))
812     return false;
813
814   if(!checkMigratedQuery.first())
815     return true;                // table is empty -> no work to be done
816
817   QString passType = checkMigratedQuery.value(0).toString().toLower();
818   if(passType == "text")
819     return true; // allready migrated
820
821   Q_ASSERT(passType == "blob");
822   
823   QSqlQuery getPasswordsQuery(logDb());
824   getPasswordsQuery.prepare("SELECT userid, password FROM quasseluser");
825   getPasswordsQuery.exec();
826
827   if(!watchQuery(&getPasswordsQuery)) {
828     qCritical() << "unable to migrate to new password format!";
829     return false;
830   }
831
832   QHash<int, QByteArray> passHash;
833   while(getPasswordsQuery.next()) {
834     passHash[getPasswordsQuery.value(0).toInt()] = getPasswordsQuery.value(1).toByteArray();
835   }
836
837   QSqlQuery setPasswordsQuery(logDb());
838   setPasswordsQuery.prepare("UPDATE quasseluser SET password = :password WHERE userid = :userid");
839   foreach(int userId, passHash.keys()) {
840     setPasswordsQuery.bindValue(":password", QString(passHash[userId]));
841     setPasswordsQuery.bindValue(":userid", userId);
842     setPasswordsQuery.exec();
843     watchQuery(&setPasswordsQuery);
844   }
845
846   qDebug() << "successfully migrated passwords!";
847   return true;
848 }