6e9a47d35c7263ef0041902fbcffd69467a11549
[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   QSqlQuery query(logDb());
485   query.prepare(queryString("insert_buffer"));
486   query.bindValue(":userid", user.toInt());
487   query.bindValue(":networkid", networkId.toInt());
488   query.bindValue(":buffertype", (int)type);
489   query.bindValue(":buffername", buffer);
490   query.bindValue(":buffercname", buffer.toLower());
491   query.exec();
492
493   watchQuery(query);
494 }
495
496 BufferInfo SqliteStorage::getBufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer) {
497   // QSqlQuery &query = cachedQuery("select_bufferByName");
498   QSqlQuery query(logDb());
499   query.prepare(queryString("select_bufferByName"));
500   query.bindValue(":networkid", networkId.toInt());
501   query.bindValue(":userid", user.toInt());
502   query.bindValue(":buffercname", buffer.toLower());
503   query.exec();
504
505   if(!query.first()) {
506     createBuffer(user, networkId, type, buffer);
507     query.exec();
508     if(!query.first()) {
509       watchQuery(query);
510       qWarning() << "unable to create BufferInfo for:" << user << networkId << buffer;
511       return BufferInfo();
512     }
513   }
514
515   BufferInfo bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer);
516   if(query.next()) {
517     qCritical() << "SqliteStorage::getBufferInfo(): received more then one Buffer!";
518     qCritical() << "         Query:" << query.lastQuery();
519     qCritical() << "  bound Values:";
520     QList<QVariant> list = query.boundValues().values();
521     for (int i = 0; i < list.size(); ++i)
522       qCritical() << i << ":" << list.at(i).toString().toAscii().data();
523     Q_ASSERT(false);
524   }
525
526   return bufferInfo;
527 }
528
529 BufferInfo SqliteStorage::getBufferInfo(UserId user, const BufferId &bufferId) {
530   QSqlQuery query(logDb());
531   query.prepare(queryString("select_buffer_by_id"));
532   query.bindValue(":userid", user.toInt());
533   query.bindValue(":bufferid", bufferId.toInt());
534   query.exec();
535   if(!watchQuery(query))
536     return BufferInfo();
537
538   if(!query.first())
539     return BufferInfo();
540
541   BufferInfo bufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
542   Q_ASSERT(!query.next());
543
544   return bufferInfo;
545 }
546
547 QList<BufferInfo> SqliteStorage::requestBuffers(UserId user) {
548   QList<BufferInfo> bufferlist;
549   QSqlQuery query(logDb());
550   query.prepare(queryString("select_buffers"));
551   query.bindValue(":userid", user.toInt());
552   
553   query.exec();
554   watchQuery(query);
555   while(query.next()) {
556     bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
557   }
558   return bufferlist;
559 }
560
561 QList<BufferId> SqliteStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) {
562   QList<BufferId> bufferList;
563   QSqlQuery query(logDb());
564   query.prepare(queryString("select_buffers_for_network"));
565   query.bindValue(":networkid", networkId.toInt());
566   query.bindValue(":userid", user.toInt());
567
568   query.exec();
569   watchQuery(query);
570   while(query.next()) {
571     bufferList << BufferId(query.value(0).toInt());
572   }
573   return bufferList;
574 }
575
576 bool SqliteStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
577   if(!isValidBuffer(user, bufferId))
578     return false;
579
580   QSqlQuery delBacklogQuery(logDb());
581   delBacklogQuery.prepare(queryString("delete_backlog_for_buffer"));
582   delBacklogQuery.bindValue(":bufferid", bufferId.toInt());
583   delBacklogQuery.exec();
584   if(!watchQuery(delBacklogQuery))
585     return false;
586
587   QSqlQuery delBufferQuery(logDb());
588   delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
589   delBufferQuery.bindValue(":bufferid", bufferId.toInt());
590   delBufferQuery.exec();
591   if(!watchQuery(delBufferQuery))
592     return false;
593
594   return true;
595 }
596
597 BufferId SqliteStorage::renameBuffer(const UserId &user, const NetworkId &networkId, const QString &newName, const QString &oldName) {
598   // check if such a buffer exists...
599   QSqlQuery existsQuery(logDb());
600   existsQuery.prepare(queryString("select_bufferByName"));
601   existsQuery.bindValue(":networkid", networkId.toInt());
602   existsQuery.bindValue(":userid", user.toInt());
603   existsQuery.bindValue(":buffercname", oldName.toLower());
604   existsQuery.exec();
605   if(!watchQuery(existsQuery))
606     return false;
607
608   if(!existsQuery.first())
609     return false;
610
611   const int bufferid = existsQuery.value(0).toInt();
612
613   Q_ASSERT(!existsQuery.next());
614
615   // ... and if the new name is still free.
616   existsQuery.bindValue(":networkid", networkId.toInt());
617   existsQuery.bindValue(":userid", user.toInt());
618   existsQuery.bindValue(":buffercname", newName.toLower());
619   existsQuery.exec();
620   if(!watchQuery(existsQuery))
621     return false;
622
623   if(existsQuery.first())
624     return false;
625
626   QSqlQuery renameBufferQuery(logDb());
627   renameBufferQuery.prepare(queryString("update_buffer_name"));
628   renameBufferQuery.bindValue(":buffername", newName);
629   renameBufferQuery.bindValue(":buffercname", newName.toLower());
630   renameBufferQuery.bindValue(":bufferid", bufferid);
631   renameBufferQuery.exec();
632   if(watchQuery(existsQuery))
633     return BufferId(bufferid);
634   else
635     return BufferId();
636 }
637
638 void SqliteStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
639   // QSqlQuery &query = cachedQuery("update_buffer_lastseen");
640   QSqlQuery query(logDb());
641   query.prepare(queryString("update_buffer_lastseen"));
642
643   query.bindValue(":userid", user.toInt());
644   query.bindValue(":bufferid", bufferId.toInt());
645   query.bindValue(":lastseenmsgid", msgId.toInt());
646   query.exec();
647   watchQuery(query);
648 }
649
650 QHash<BufferId, MsgId> SqliteStorage::bufferLastSeenMsgIds(UserId user) {
651   QHash<BufferId, MsgId> lastSeenHash;
652   QSqlQuery query(logDb());
653   query.prepare(queryString("select_buffer_lastseen_messages"));
654   query.bindValue(":userid", user.toInt());
655   query.exec();
656   if(!watchQuery(query))
657     return lastSeenHash;
658
659   while(query.next()) {
660     lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
661   }
662   return lastSeenHash;
663 }
664
665 MsgId SqliteStorage::logMessage(Message msg) {
666   // QSqlQuery &logMessageQuery = cachedQuery("insert_message");
667   QSqlQuery logMessageQuery(logDb());
668   logMessageQuery.prepare(queryString("insert_message"));
669
670   logMessageQuery.bindValue(":time", msg.timestamp().toTime_t());
671   logMessageQuery.bindValue(":bufferid", msg.bufferInfo().bufferId().toInt());
672   logMessageQuery.bindValue(":type", msg.type());
673   logMessageQuery.bindValue(":flags", (int)msg.flags());
674   logMessageQuery.bindValue(":sender", msg.sender());
675   logMessageQuery.bindValue(":message", msg.contents());
676   logMessageQuery.exec();
677   
678   if(logMessageQuery.lastError().isValid()) {
679     // constraint violation - must be NOT NULL constraint - probably the sender is missing...
680     if(logMessageQuery.lastError().number() == 19) {
681       // QSqlQuery &addSenderQuery = cachedQuery("insert_sender");
682       QSqlQuery addSenderQuery(logDb());
683       addSenderQuery.prepare(queryString("insert_sender"));
684       addSenderQuery.bindValue(":sender", msg.sender());
685       addSenderQuery.exec();
686       logMessageQuery.exec();
687       if(!watchQuery(logMessageQuery))
688         return 0;
689     } else {
690       watchQuery(logMessageQuery);
691     }
692   }
693
694   MsgId msgId = logMessageQuery.lastInsertId().toInt();
695   Q_ASSERT(msgId.isValid());
696   return msgId;
697 }
698
699 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, int lastmsgs, int offset) {
700   QList<Message> messagelist;
701
702   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
703   if(!bufferInfo.isValid())
704     return messagelist;
705
706   if(offset == -1) {
707     offset = 0;
708   } else {
709     // we have to determine the real offset first
710     // QSqlQuery &offsetQuery = cachedQuery("select_messagesOffset");
711     QSqlQuery offsetQuery(logDb());
712     offsetQuery.prepare(queryString("select_messagesOffset"));
713
714     offsetQuery.bindValue(":bufferid", bufferId.toInt());
715     offsetQuery.bindValue(":messageid", offset);
716     offsetQuery.exec();
717     offsetQuery.first();
718     offset = offsetQuery.value(0).toInt();
719   }
720
721   // now let's select the messages
722   // QSqlQuery &msgQuery = cachedQuery("select_messages");
723   QSqlQuery msgQuery(logDb());
724   msgQuery.prepare(queryString("select_messages"));
725
726   msgQuery.bindValue(":bufferid", bufferId.toInt());
727   msgQuery.bindValue(":limit", lastmsgs);
728   msgQuery.bindValue(":offset", offset);
729   msgQuery.exec();
730   
731   watchQuery(msgQuery);
732   
733   while(msgQuery.next()) {
734     Message msg(QDateTime::fromTime_t(msgQuery.value(1).toInt()),
735                 bufferInfo,
736                 (Message::Type)msgQuery.value(2).toUInt(),
737                 msgQuery.value(5).toString(),
738                 msgQuery.value(4).toString(),
739                 (Message::Flags)msgQuery.value(3).toUInt());
740     msg.setMsgId(msgQuery.value(0).toInt());
741     messagelist << msg;
742   }
743   return messagelist;
744 }
745
746
747 QList<Message> SqliteStorage::requestMsgs(UserId user, BufferId bufferId, QDateTime since, int offset) {
748   QList<Message> messagelist;
749
750   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
751   if(!bufferInfo.isValid())
752     return messagelist;
753
754   // we have to determine the real offset first
755   // QSqlQuery &offsetQuery = cachedQuery("select_messagesSinceOffset");
756   QSqlQuery offsetQuery(logDb());
757   offsetQuery.prepare(queryString("select_messagesSinceOffset"));
758
759   offsetQuery.bindValue(":bufferid", bufferId.toInt());
760   offsetQuery.bindValue(":since", since.toTime_t());
761   offsetQuery.exec();
762   offsetQuery.first();
763   offset = offsetQuery.value(0).toInt();
764
765   // now let's select the messages
766   // QSqlQuery &msgQuery = cachedQuery("select_messagesSince");
767   QSqlQuery msgQuery(logDb());
768   msgQuery.prepare(queryString("select_messagesSince"));
769   msgQuery.bindValue(":bufferid", bufferId.toInt());
770   msgQuery.bindValue(":since", since.toTime_t());
771   msgQuery.bindValue(":offset", offset);
772   msgQuery.exec();
773
774   watchQuery(msgQuery);
775   
776   while(msgQuery.next()) {
777     Message msg(QDateTime::fromTime_t(msgQuery.value(1).toInt()),
778                 bufferInfo,
779                 (Message::Type)msgQuery.value(2).toUInt(),
780                 msgQuery.value(5).toString(),
781                 msgQuery.value(4).toString(),
782                 (Message::Flags)msgQuery.value(3).toUInt());
783     msg.setMsgId(msgQuery.value(0).toInt());
784     messagelist << msg;
785   }
786
787   return messagelist;
788 }
789
790
791 QList<Message> SqliteStorage::requestMsgRange(UserId user, BufferId bufferId, int first, int last) {
792   QList<Message> messagelist;
793
794   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
795   if(!bufferInfo.isValid())
796     return messagelist;
797
798   // QSqlQuery &rangeQuery = cachedQuery("select_messageRange");
799   QSqlQuery rangeQuery(logDb());
800   rangeQuery.prepare(queryString("select_messageRange"));
801   rangeQuery.bindValue(":bufferid", bufferId.toInt());
802   rangeQuery.bindValue(":firstmsg", first);
803   rangeQuery.bindValue(":lastmsg", last);
804   rangeQuery.exec();
805
806   watchQuery(rangeQuery);
807   
808   while(rangeQuery.next()) {
809     Message msg(QDateTime::fromTime_t(rangeQuery.value(1).toInt()),
810                 bufferInfo,
811                 (Message::Type)rangeQuery.value(2).toUInt(),
812                 rangeQuery.value(5).toString(),
813                 rangeQuery.value(4).toString(),
814                 (Message::Flags)rangeQuery.value(3).toUInt());
815     msg.setMsgId(rangeQuery.value(0).toInt());
816     messagelist << msg;
817   }
818
819   return messagelist;
820 }
821
822 QString SqliteStorage::backlogFile() {
823   return quasselDir().absolutePath() + "/quassel-storage.sqlite";  
824 }