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