b1d3d2688090a07a8a407d9ee3ecedc2e3f4542d
[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) {
483   QList<BufferInfo> bufferlist;
484   QSqlQuery query(logDb());
485   query.prepare(queryString("select_buffers"));
486   query.bindValue(":userid", user.toInt());
487   
488   query.exec();
489   watchQuery(&query);
490   while(query.next()) {
491     bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
492   }
493   return bufferlist;
494 }
495
496 bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
497   if(!isValidBuffer(user, bufferId))
498     return false;
499
500   QSqlQuery delBacklogQuery(logDb());
501   delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
502   delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
503   delBacklogQuery.exec();
504   if(!watchQuery(&delBacklogQuery))
505     return false;
506
507   QSqlQuery delBufferQuery(logDb());
508   delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
509   delBufferQuery.bindValue(":bufferid", bufferId.toInt());
510   delBufferQuery.exec();
511   if(!watchQuery(&delBufferQuery))
512     return false;
513
514   return true;
515 }
516
517 BufferId SqliteStorage::renameBuffer(const UserId &user, const NetworkId &networkId, const QString &newName, const QString &oldName) {
518   // check if such a buffer exists...
519   QSqlQuery existsQuery(logDb());
520   existsQuery.prepare(queryString("select_bufferByName"));
521   existsQuery.bindValue(":networkid", networkId.toInt());
522   existsQuery.bindValue(":userid", user.toInt());
523   existsQuery.bindValue(":buffercname", oldName.toLower());
524   existsQuery.exec();
525   if(!watchQuery(&existsQuery))
526     return false;
527
528   if(!existsQuery.first())
529     return false;
530
531   const int bufferid = existsQuery.value(0).toInt();
532
533   Q_ASSERT(!existsQuery.next());
534
535   // ... and if the new name is still free.
536   existsQuery.bindValue(":networkid", networkId.toInt());
537   existsQuery.bindValue(":userid", user.toInt());
538   existsQuery.bindValue(":buffercname", newName.toLower());
539   existsQuery.exec();
540   if(!watchQuery(&existsQuery))
541     return false;
542
543   if(existsQuery.first())
544     return false;
545
546   QSqlQuery renameBufferQuery(logDb());
547   renameBufferQuery.prepare(queryString("update_buffer_name"));
548   renameBufferQuery.bindValue(":buffername", newName);
549   renameBufferQuery.bindValue(":buffercname", newName.toLower());
550   renameBufferQuery.bindValue(":bufferid", bufferid);
551   renameBufferQuery.exec();
552   if(watchQuery(&existsQuery))
553     return BufferId(bufferid);
554   else
555     return BufferId();
556 }
557
558 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
559   QSqlQuery *query = cachedQuery("update_buffer_lastseen");
560   query->bindValue(":userid", user.toInt());
561   query->bindValue(":bufferid", bufferId.toInt());
562   query->bindValue(":lastseenmsgid", msgId.toInt());
563   query->exec();
564   watchQuery(query);
565 }
566
567 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user) {
568   QHash<BufferId, MsgId> lastSeenHash;
569   QSqlQuery query(logDb());
570   query.prepare(queryString("select_buffer_lastseen_messages"));
571   query.bindValue(":userid", user.toInt());
572   query.exec();
573   if(!watchQuery(&query))
574     return lastSeenHash;
575
576   while(query.next()) {
577     lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
578   }
579   return lastSeenHash;
580 }
581
582 MsgId SqliteStorage::logMessage(Message msg) {
583   QSqlQuery *logMessageQuery = cachedQuery("insert_message");
584   logMessageQuery->bindValue(":time", msg.timestamp().toTime_t());
585   logMessageQuery->bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
586   logMessageQuery->bindValue(":type", msg.type());
587   logMessageQuery->bindValue(":flags", msg.flags());
588   logMessageQuery->bindValue(":sender", msg.sender());
589   logMessageQuery->bindValue(":message", msg.text());
590   logMessageQuery->exec();
591   
592   if(logMessageQuery->lastError().isValid()) {
593     // constraint violation - must be NOT NULL constraint - probably the sender is missing...
594     if(logMessageQuery->lastError().number() == 19) {
595       QSqlQuery *addSenderQuery = cachedQuery("insert_sender");
596       addSenderQuery->bindValue(":sender", msg.sender());
597       addSenderQuery->exec();
598       watchQuery(addSenderQuery);
599       logMessageQuery->exec();
600       if(!watchQuery(logMessageQuery))
601         return 0;
602     } else {
603       watchQuery(logMessageQuery);
604     }
605   }
606
607   MsgId msgId = logMessageQuery->lastInsertId().toInt();
608   Q_ASSERT(msgId.isValid());
609   return msgId;
610 }
611
612 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, int lastmsgs, int offset) {
613   QList<Message> messagelist;
614
615   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
616   if(!bufferInfo.isValid())
617     return messagelist;
618
619   if(offset == -1) {
620     offset = 0;
621   } else {
622     // we have to determine the real offset first
623     QSqlQuery *offsetQuery = cachedQuery("select_messagesOffset");
624     offsetQuery->bindValue(":bufferid", bufferId.toInt());
625     offsetQuery->bindValue(":messageid", offset);
626     offsetQuery->exec();
627     offsetQuery->first();
628     offset = offsetQuery->value(0).toInt();
629   }
630
631   // now let's select the messages
632   QSqlQuery *msgQuery = cachedQuery("select_messages");
633   msgQuery->bindValue(":bufferid", bufferId.toInt());
634   msgQuery->bindValue(":limit", lastmsgs);
635   msgQuery->bindValue(":offset", offset);
636   msgQuery->exec();
637   
638   watchQuery(msgQuery);
639   
640   while(msgQuery->next()) {
641     Message msg(QDateTime::fromTime_t(msgQuery->value(1).toInt()),
642                 bufferInfo,
643                 (Message::Type)msgQuery->value(2).toUInt(),
644                 msgQuery->value(5).toString(),
645                 msgQuery->value(4).toString(),
646                 msgQuery->value(3).toUInt());
647     msg.setMsgId(msgQuery->value(0).toInt());
648     messagelist << msg;
649   }
650   return messagelist;
651 }
652
653
654 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, QDateTime since, int offset) {
655   QList<Message> messagelist;
656
657   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
658   if(!bufferInfo.isValid())
659     return messagelist;
660
661   // we have to determine the real offset first
662   QSqlQuery *offsetQuery = cachedQuery("select_messagesSinceOffset");
663   offsetQuery->bindValue(":bufferid", bufferId.toInt());
664   offsetQuery->bindValue(":since", since.toTime_t());
665   offsetQuery->exec();
666   offsetQuery->first();
667   offset = offsetQuery->value(0).toInt();
668
669   // now let's select the messages
670   QSqlQuery *msgQuery = cachedQuery("select_messagesSince");
671   msgQuery->bindValue(":bufferid", bufferId.toInt());
672   msgQuery->bindValue(":since", since.toTime_t());
673   msgQuery->bindValue(":offset", offset);
674   msgQuery->exec();
675
676   watchQuery(msgQuery);
677   
678   while(msgQuery->next()) {
679     Message msg(QDateTime::fromTime_t(msgQuery->value(1).toInt()),
680                 bufferInfo,
681                 (Message::Type)msgQuery->value(2).toUInt(),
682                 msgQuery->value(5).toString(),
683                 msgQuery->value(4).toString(),
684                 msgQuery->value(3).toUInt());
685     msg.setMsgId(msgQuery->value(0).toInt());
686     messagelist << msg;
687   }
688
689   return messagelist;
690 }
691
692
693 QList<Message> SqliteStorage::requestMsgRange(UserId user, BufferId bufferId, int first, int last) {
694   QList<Message> messagelist;
695
696   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
697   if(!bufferInfo.isValid())
698     return messagelist;
699
700   QSqlQuery *rangeQuery = cachedQuery("select_messageRange");
701   rangeQuery->bindValue(":bufferid", bufferId.toInt());
702   rangeQuery->bindValue(":firstmsg", first);
703   rangeQuery->bindValue(":lastmsg", last);
704   rangeQuery->exec();
705
706   watchQuery(rangeQuery);
707   
708   while(rangeQuery->next()) {
709     Message msg(QDateTime::fromTime_t(rangeQuery->value(1).toInt()),
710                 bufferInfo,
711                 (Message::Type)rangeQuery->value(2).toUInt(),
712                 rangeQuery->value(5).toString(),
713                 rangeQuery->value(4).toString(),
714                 rangeQuery->value(3).toUInt());
715     msg.setMsgId(rangeQuery->value(0).toInt());
716     messagelist << msg;
717   }
718
719   return messagelist;
720 }
721
722 QString SqliteStorage::backlogFile() {
723   return quasselDir().absolutePath() + "/quassel-storage.sqlite";  
724 }
725
726
727 // ONLY NEEDED FOR MIGRATION
728 bool SqliteStorage::init(const QVariantMap &settings) {
729   if(!AbstractSqlStorage::init(settings))
730     return false;
731
732   QSqlQuery checkMigratedQuery(logDb());
733   checkMigratedQuery.prepare("SELECT DISTINCT typeOf(password) FROM quasseluser");
734   checkMigratedQuery.exec();
735   if(!watchQuery(&checkMigratedQuery))
736     return false;
737
738   if(!checkMigratedQuery.first())
739     return true;                // table is empty -> no work to be done
740
741   QString passType = checkMigratedQuery.value(0).toString().toLower();
742   if(passType == "text")
743     return true; // allready migrated
744
745   Q_ASSERT(passType == "blob");
746   
747   QSqlQuery getPasswordsQuery(logDb());
748   getPasswordsQuery.prepare("SELECT userid, password FROM quasseluser");
749   getPasswordsQuery.exec();
750
751   if(!watchQuery(&getPasswordsQuery)) {
752     qWarning() << "unable to migrate to new password format!";
753     return false;
754   }
755
756   QHash<int, QByteArray> passHash;
757   while(getPasswordsQuery.next()) {
758     passHash[getPasswordsQuery.value(0).toInt()] = getPasswordsQuery.value(1).toByteArray();
759   }
760
761   QSqlQuery setPasswordsQuery(logDb());
762   setPasswordsQuery.prepare("UPDATE quasseluser SET password = :password WHERE userid = :userid");
763   foreach(int userId, passHash.keys()) {
764     setPasswordsQuery.bindValue(":password", QString(passHash[userId]));
765     setPasswordsQuery.bindValue(":userid", userId);
766     setPasswordsQuery.exec();
767     watchQuery(&setPasswordsQuery);
768   }
769
770   qDebug() << "successfully migrated passwords!";
771   return true;
772 }