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