updating sequences after mirgration
[quassel.git] / src / core / postgresqlstorage.cpp
1 /***************************************************************************
2  *   Copyright (C) 2005-07 by the Quassel Project                          *
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 "postgresqlstorage.h"
22
23 #include <QtSql>
24
25 #include "logger.h"
26 #include "network.h"
27 #include "quassel.h"
28
29 PostgreSqlStorage::PostgreSqlStorage(QObject *parent)
30   : AbstractSqlStorage(parent),
31     _port(-1)
32 {
33 }
34
35 PostgreSqlStorage::~PostgreSqlStorage() {
36 }
37
38 bool PostgreSqlStorage::isAvailable() const {
39   if(!QSqlDatabase::isDriverAvailable("QPSQL")) return false;
40   return true;
41 }
42
43 QString PostgreSqlStorage::displayName() const {
44   return QString("PostgreSQL");
45 }
46
47 QString PostgreSqlStorage::description() const {
48   // FIXME: proper description
49   return tr("PostgreSQL Turbo Bomber HD!");
50 }
51
52 QVariantMap PostgreSqlStorage::setupKeys() const {
53   QVariantMap map;
54   map["Username"] = QVariant(QString("quassel"));
55   map["Password"] = QVariant(QString());
56   map["Hostname"] = QVariant(QString("localhost"));
57   map["Port"] = QVariant(5432);
58   map["Database"] = QVariant(QString("quassel"));
59   return map;
60 }
61
62 bool PostgreSqlStorage::setup(const QVariantMap &settings) {
63   bool success = AbstractSqlStorage::setup(settings);
64   if(success) {
65     logDb().exec(QString("ALTER USER %1 SET standard_conforming_strings TO on").arg(userName()));
66   }
67   return success;
68 }
69
70 void PostgreSqlStorage::setConnectionProperties(const QVariantMap &properties) {
71   _userName = properties["Username"].toString();
72   _password = properties["Password"].toString();
73   _hostName = properties["Hostname"].toString();
74   _port = properties["Port"].toInt();
75   _databaseName = properties["Database"].toString();
76 }
77
78 int PostgreSqlStorage::installedSchemaVersion() {
79   QSqlQuery query = logDb().exec("SELECT value FROM coreinfo WHERE key = 'schemaversion'");
80   if(query.first())
81     return query.value(0).toInt();
82
83   // maybe it's really old... (schema version 0)
84   query = logDb().exec("SELECT MAX(version) FROM coreinfo");
85   if(query.first())
86     return query.value(0).toInt();
87
88   return AbstractSqlStorage::installedSchemaVersion();
89 }
90
91 bool PostgreSqlStorage::updateSchemaVersion(int newVersion) {
92   QSqlQuery query(logDb());
93   query.prepare("UPDATE coreinfo SET value = :version WHERE key = 'schemaversion'");
94   query.bindValue(":version", newVersion);
95   query.exec();
96
97   bool success = true;
98   if(query.lastError().isValid()) {
99     qCritical() << "PostgreSqlStorage::updateSchemaVersion(int): Updating schema version failed!";
100     success = false;
101   }
102   return success;
103 }
104
105 bool PostgreSqlStorage::setupSchemaVersion(int version) {
106   QSqlQuery query(logDb());
107   query.prepare("INSERT INTO coreinfo (key, value) VALUES ('schemaversion', :version)");
108   query.bindValue(":version", version);
109   query.exec();
110
111   bool success = true;
112   if(query.lastError().isValid()) {
113     qCritical() << "PostgreSqlStorage::setupSchemaVersion(int): Updating schema version failed!";
114     success = false;
115   }
116   return success;
117 }
118
119 UserId PostgreSqlStorage::addUser(const QString &user, const QString &password) {
120   QSqlQuery query(logDb());
121   query.prepare(queryString("insert_quasseluser"));
122   query.bindValue(":username", user);
123   query.bindValue(":password", cryptedPassword(password));
124   safeExec(query);
125   if(!watchQuery(query))
126     return 0;
127
128   query.first();
129   UserId uid = query.value(0).toInt();
130   emit userAdded(uid, user);
131   return uid;
132 }
133
134 void PostgreSqlStorage::updateUser(UserId user, const QString &password) {
135   QSqlQuery query(logDb());
136   query.prepare(queryString("update_userpassword"));
137   query.bindValue(":userid", user.toInt());
138   query.bindValue(":password", cryptedPassword(password));
139   safeExec(query);
140 }
141
142 void PostgreSqlStorage::renameUser(UserId user, const QString &newName) {
143   QSqlQuery query(logDb());
144   query.prepare(queryString("update_username"));
145   query.bindValue(":userid", user.toInt());
146   query.bindValue(":username", newName);
147   safeExec(query);
148   emit userRenamed(user, newName);
149 }
150
151 UserId PostgreSqlStorage::validateUser(const QString &user, const QString &password) {
152   QSqlQuery query(logDb());
153   query.prepare(queryString("select_authuser"));
154   query.bindValue(":username", user);
155   query.bindValue(":password", cryptedPassword(password));
156   safeExec(query);
157
158   if(query.first()) {
159     return query.value(0).toInt();
160   } else {
161     return 0;
162   }
163 }
164
165 UserId PostgreSqlStorage::internalUser() {
166   QSqlQuery query(logDb());
167   query.prepare(queryString("select_internaluser"));
168   safeExec(query);
169
170   if(query.first()) {
171     return query.value(0).toInt();
172   } else {
173     return 0;
174   }
175 }
176
177 void PostgreSqlStorage::delUser(UserId user) {
178   QSqlDatabase db = logDb();
179   if(!db.transaction()) {
180     qWarning() << "PostgreSqlStorage::delUser(): cannot start transaction!";
181     return;
182   }
183
184   QSqlQuery query(db);
185   query.prepare(queryString("delete_quasseluser"));
186   query.bindValue(":userid", user.toInt());
187   safeExec(query);
188   if(!watchQuery(query)) {
189     db.rollback();
190     return;
191   } else {
192     db.commit();
193     emit userRemoved(user);
194   }
195 }
196
197 void PostgreSqlStorage::setUserSetting(UserId userId, const QString &settingName, const QVariant &data) {
198   QByteArray rawData;
199   QDataStream out(&rawData, QIODevice::WriteOnly);
200   out.setVersion(QDataStream::Qt_4_2);
201   out << data;
202
203   QSqlDatabase db = logDb();
204   QSqlQuery query(db);
205   query.prepare(queryString("insert_user_setting"));
206   query.bindValue(":userid", userId.toInt());
207   query.bindValue(":settingname", settingName);
208   query.bindValue(":settingvalue", rawData);
209   safeExec(query);
210
211   if(query.lastError().isValid()) {
212     QSqlQuery updateQuery(db);
213     updateQuery.prepare(queryString("update_user_setting"));
214     updateQuery.bindValue(":userid", userId.toInt());
215     updateQuery.bindValue(":settingname", settingName);
216     updateQuery.bindValue(":settingvalue", rawData);
217     safeExec(updateQuery);
218   }
219
220 }
221
222 QVariant PostgreSqlStorage::getUserSetting(UserId userId, const QString &settingName, const QVariant &defaultData) {
223   QSqlQuery query(logDb());
224   query.prepare(queryString("select_user_setting"));
225   query.bindValue(":userid", userId.toInt());
226   query.bindValue(":settingname", settingName);
227   safeExec(query);
228
229   if(query.first()) {
230     QVariant data;
231     QByteArray rawData = query.value(0).toByteArray();
232     QDataStream in(&rawData, QIODevice::ReadOnly);
233     in.setVersion(QDataStream::Qt_4_2);
234     in >> data;
235     return data;
236   } else {
237     return defaultData;
238   }
239 }
240
241 IdentityId PostgreSqlStorage::createIdentity(UserId user, CoreIdentity &identity) {
242   IdentityId identityId;
243
244   QSqlDatabase db = logDb();
245   if(!db.transaction()) {
246     qWarning() << "PostgreSqlStorage::createIdentity(): Unable to start Transaction!";
247     qWarning() << " -" << qPrintable(db.lastError().text());
248     return identityId;
249   }
250
251   QSqlQuery query(db);
252   query.prepare(queryString("insert_identity"));
253   query.bindValue(":userid", user.toInt());
254   query.bindValue(":identityname", identity.identityName());
255   query.bindValue(":realname", identity.realName());
256   query.bindValue(":awaynick", identity.awayNick());
257   query.bindValue(":awaynickenabled", identity.awayNickEnabled());
258   query.bindValue(":awayreason", identity.awayReason());
259   query.bindValue(":awayreasonenabled", identity.awayReasonEnabled());
260   query.bindValue(":autoawayenabled", identity.awayReasonEnabled());
261   query.bindValue(":autoawaytime", identity.autoAwayTime());
262   query.bindValue(":autoawayreason", identity.autoAwayReason());
263   query.bindValue(":autoawayreasonenabled", identity.autoAwayReasonEnabled());
264   query.bindValue(":detachawayenabled", identity.detachAwayEnabled());
265   query.bindValue(":detachawayreason", identity.detachAwayReason());
266   query.bindValue(":detachawayreasonenabled", identity.detachAwayReasonEnabled());
267   query.bindValue(":ident", identity.ident());
268   query.bindValue(":kickreason", identity.kickReason());
269   query.bindValue(":partreason", identity.partReason());
270   query.bindValue(":quitreason", identity.quitReason());
271 #ifdef HAVE_SSL
272   query.bindValue(":sslcert", identity.sslCert().toPem());
273   query.bindValue(":sslkey", identity.sslKey().toPem());
274 #else
275   query.bindValue(":sslcert", QByteArray());
276   query.bindValue(":sslkey", QByteArray());
277 #endif
278   safeExec(query);
279   if(query.lastError().isValid()) {
280     watchQuery(query);
281     db.rollback();
282     return IdentityId();
283   }
284
285   query.first();
286   identityId = query.value(0).toInt();
287   identity.setId(identityId);
288
289   if(!identityId.isValid()) {
290     watchQuery(query);
291     db.rollback();
292     return IdentityId();
293   }
294
295   QSqlQuery insertNickQuery(db);
296   insertNickQuery.prepare(queryString("insert_nick"));
297   foreach(QString nick, identity.nicks()) {
298     insertNickQuery.bindValue(":identityid", identityId.toInt());
299     insertNickQuery.bindValue(":nick", nick);
300     safeExec(insertNickQuery);
301     if(!watchQuery(insertNickQuery)) {
302       db.rollback();
303       return IdentityId();
304     }
305   }
306
307   if(!db.commit()) {
308     qWarning() << "PostgreSqlStorage::createIdentity(): commiting data failed!";
309     qWarning() << " -" << qPrintable(db.lastError().text());
310     return IdentityId();
311   }
312   return identityId;
313 }
314
315 bool PostgreSqlStorage::updateIdentity(UserId user, const CoreIdentity &identity) {
316   QSqlDatabase db = logDb();
317   if(!db.transaction()) {
318     qWarning() << "PostgreSqlStorage::updateIdentity(): Unable to start Transaction!";
319     qWarning() << " -" << qPrintable(db.lastError().text());
320     return false;
321   }
322
323   QSqlQuery checkQuery(db);
324   checkQuery.prepare(queryString("select_checkidentity"));
325   checkQuery.bindValue(":identityid", identity.id().toInt());
326   checkQuery.bindValue(":userid", user.toInt());
327   safeExec(checkQuery);
328
329   // there should be exactly one identity for the given id and user
330   if(!checkQuery.first() || checkQuery.value(0).toInt() != 1) {
331     db.rollback();
332     return false;
333   }
334
335   QSqlQuery query(db);
336   query.prepare(queryString("update_identity"));
337   query.bindValue(":identityname", identity.identityName());
338   query.bindValue(":realname", identity.realName());
339   query.bindValue(":awaynick", identity.awayNick());
340   query.bindValue(":awaynickenabled", identity.awayNickEnabled());
341   query.bindValue(":awayreason", identity.awayReason());
342   query.bindValue(":awayreasonenabled", identity.awayReasonEnabled());
343   query.bindValue(":autoawayenabled", identity.awayReasonEnabled());
344   query.bindValue(":autoawaytime", identity.autoAwayTime());
345   query.bindValue(":autoawayreason", identity.autoAwayReason());
346   query.bindValue(":autoawayreasonenabled", identity.autoAwayReasonEnabled());
347   query.bindValue(":detachawayenabled", identity.detachAwayEnabled());
348   query.bindValue(":detachawayreason", identity.detachAwayReason());
349   query.bindValue(":detachawayreasonenabled", identity.detachAwayReasonEnabled());
350   query.bindValue(":ident", identity.ident());
351   query.bindValue(":kickreason", identity.kickReason());
352   query.bindValue(":partreason", identity.partReason());
353   query.bindValue(":quitreason", identity.quitReason());
354 #ifdef HAVE_SSL
355   query.bindValue(":sslcert", identity.sslCert().toPem());
356   query.bindValue(":sslkey", identity.sslKey().toPem());
357 #else
358   query.bindValue(":sslcert", QByteArray());
359   query.bindValue(":sslkey", QByteArray());
360 #endif
361   query.bindValue(":identityid", identity.id().toInt());
362
363   safeExec(query);
364   if(!watchQuery(query)) {
365     db.rollback();
366     return false;
367   }
368
369   QSqlQuery deleteNickQuery(db);
370   deleteNickQuery.prepare(queryString("delete_nicks"));
371   deleteNickQuery.bindValue(":identityid", identity.id().toInt());
372   safeExec(deleteNickQuery);
373   if(!watchQuery(deleteNickQuery)) {
374     db.rollback();
375     return false;
376   }
377
378   QSqlQuery insertNickQuery(db);
379   insertNickQuery.prepare(queryString("insert_nick"));
380   foreach(QString nick, identity.nicks()) {
381     insertNickQuery.bindValue(":identityid", identity.id().toInt());
382     insertNickQuery.bindValue(":nick", nick);
383     safeExec(insertNickQuery);
384     if(!watchQuery(insertNickQuery)) {
385       db.rollback();
386       return false;
387     }
388   }
389
390   if(!db.commit()) {
391     qWarning() << "PostgreSqlStorage::updateIdentity(): commiting data failed!";
392     qWarning() << " -" << qPrintable(db.lastError().text());
393     return false;
394   }
395   return true;
396 }
397
398 void PostgreSqlStorage::removeIdentity(UserId user, IdentityId identityId) {
399   QSqlDatabase db = logDb();
400   if(!db.transaction()) {
401     qWarning() << "PostgreSqlStorage::removeIdentity(): Unable to start Transaction!";
402     qWarning() << " -" << qPrintable(db.lastError().text());
403     return;
404   }
405
406   QSqlQuery query(db);
407   query.prepare(queryString("delete_identity"));
408   query.bindValue(":identityid", identityId.toInt());
409   query.bindValue(":userid", user.toInt());
410   safeExec(query);
411   if(!watchQuery(query)) {
412     db.rollback();
413   } else {
414     db.commit();
415   }
416 }
417
418 QList<CoreIdentity> PostgreSqlStorage::identities(UserId user) {
419   QList<CoreIdentity> identities;
420
421   QSqlDatabase db = logDb();
422   if(!beginReadOnlyTransaction(db)) {
423     qWarning() << "PostgreSqlStorage::identites(): cannot start read only transaction!";
424     qWarning() << " -" << qPrintable(db.lastError().text());
425     return identities;
426   }
427
428   QSqlQuery query(db);
429   query.prepare(queryString("select_identities"));
430   query.bindValue(":userid", user.toInt());
431
432   QSqlQuery nickQuery(db);
433   nickQuery.prepare(queryString("select_nicks"));
434
435   safeExec(query);
436
437   while(query.next()) {
438     CoreIdentity identity(IdentityId(query.value(0).toInt()));
439
440     identity.setIdentityName(query.value(1).toString());
441     identity.setRealName(query.value(2).toString());
442     identity.setAwayNick(query.value(3).toString());
443     identity.setAwayNickEnabled(!!query.value(4).toInt());
444     identity.setAwayReason(query.value(5).toString());
445     identity.setAwayReasonEnabled(!!query.value(6).toInt());
446     identity.setAutoAwayEnabled(!!query.value(7).toInt());
447     identity.setAutoAwayTime(query.value(8).toInt());
448     identity.setAutoAwayReason(query.value(9).toString());
449     identity.setAutoAwayReasonEnabled(!!query.value(10).toInt());
450     identity.setDetachAwayEnabled(!!query.value(11).toInt());
451     identity.setDetachAwayReason(query.value(12).toString());
452     identity.setDetachAwayReasonEnabled(!!query.value(13).toInt());
453     identity.setIdent(query.value(14).toString());
454     identity.setKickReason(query.value(15).toString());
455     identity.setPartReason(query.value(16).toString());
456     identity.setQuitReason(query.value(17).toString());
457 #ifdef HAVE_SSL
458     identity.setSslCert(query.value(18).toByteArray());
459     identity.setSslKey(query.value(19).toByteArray());
460 #endif
461
462     nickQuery.bindValue(":identityid", identity.id().toInt());
463     QList<QString> nicks;
464     safeExec(nickQuery);
465     watchQuery(nickQuery);
466     while(nickQuery.next()) {
467       nicks << nickQuery.value(0).toString();
468     }
469     identity.setNicks(nicks);
470     identities << identity;
471   }
472   db.commit();
473   return identities;
474 }
475
476 NetworkId PostgreSqlStorage::createNetwork(UserId user, const NetworkInfo &info) {
477   NetworkId networkId;
478
479   QSqlDatabase db = logDb();
480   if(!db.transaction()) {
481     qWarning() << "PostgreSqlStorage::createNetwork(): failed to begin transaction!";
482     qWarning() << " -" << qPrintable(db.lastError().text());
483     return false;
484   }
485
486   QSqlQuery query(db);
487   query.prepare(queryString("insert_network"));
488   query.bindValue(":userid", user.toInt());
489   bindNetworkInfo(query, info);
490   safeExec(query);
491   if(query.lastError().isValid()) {
492     watchQuery(query);
493     db.rollback();
494     return NetworkId();
495   }
496
497   query.first();
498   networkId = query.value(0).toInt();
499
500   if(!networkId.isValid()) {
501     watchQuery(query);
502     db.rollback();
503     return NetworkId();
504   }
505
506   QSqlQuery insertServersQuery(db);
507   insertServersQuery.prepare(queryString("insert_server"));
508   foreach(Network::Server server, info.serverList) {
509     insertServersQuery.bindValue(":userid", user.toInt());
510     insertServersQuery.bindValue(":networkid", networkId.toInt());
511     bindServerInfo(insertServersQuery, server);
512     safeExec(insertServersQuery);
513     if(!watchQuery(insertServersQuery)) {
514       db.rollback();
515       return NetworkId();
516     }
517   }
518
519   if(!db.commit()) {
520     qWarning() << "PostgreSqlStorage::updateNetwork(): commiting data failed!";
521     qWarning() << " -" << qPrintable(db.lastError().text());
522     return NetworkId();
523   }
524   return networkId;
525 }
526
527 void PostgreSqlStorage::bindNetworkInfo(QSqlQuery &query, const NetworkInfo &info) {
528   query.bindValue(":networkname", info.networkName);
529   query.bindValue(":identityid", info.identity.toInt());
530   query.bindValue(":encodingcodec", QString(info.codecForEncoding));
531   query.bindValue(":decodingcodec", QString(info.codecForDecoding));
532   query.bindValue(":servercodec", QString(info.codecForServer));
533   query.bindValue(":userandomserver", info.useRandomServer);
534   query.bindValue(":perform", info.perform.join("\n"));
535   query.bindValue(":useautoidentify", info.useAutoIdentify);
536   query.bindValue(":autoidentifyservice", info.autoIdentifyService);
537   query.bindValue(":autoidentifypassword", info.autoIdentifyPassword);
538   query.bindValue(":useautoreconnect", info.useAutoReconnect);
539   query.bindValue(":autoreconnectinterval", info.autoReconnectInterval);
540   query.bindValue(":autoreconnectretries", info.autoReconnectRetries);
541   query.bindValue(":unlimitedconnectretries", info.unlimitedReconnectRetries);
542   query.bindValue(":rejoinchannels", info.rejoinChannels);
543   if(info.networkId.isValid())
544     query.bindValue(":networkid", info.networkId.toInt());
545 }
546
547 void PostgreSqlStorage::bindServerInfo(QSqlQuery &query, const Network::Server &server) {
548   query.bindValue(":hostname", server.host);
549   query.bindValue(":port", server.port);
550   query.bindValue(":password", server.password);
551   query.bindValue(":ssl", server.useSsl);
552   query.bindValue(":sslversion", server.sslVersion);
553   query.bindValue(":useproxy", server.useProxy);
554   query.bindValue(":proxytype", server.proxyType);
555   query.bindValue(":proxyhost", server.proxyHost);
556   query.bindValue(":proxyport", server.proxyPort);
557   query.bindValue(":proxyuser", server.proxyUser);
558   query.bindValue(":proxypass", server.proxyPass);
559 }
560
561 bool PostgreSqlStorage::updateNetwork(UserId user, const NetworkInfo &info) {
562   QSqlDatabase db = logDb();
563   if(!db.transaction()) {
564     qWarning() << "PostgreSqlStorage::updateNetwork(): failed to begin transaction!";
565     qWarning() << " -" << qPrintable(db.lastError().text());
566     return false;
567   }
568
569   QSqlQuery updateQuery(db);
570   updateQuery.prepare(queryString("update_network"));
571   updateQuery.bindValue(":userid", user.toInt());
572   bindNetworkInfo(updateQuery, info);
573   safeExec(updateQuery);
574   if(!watchQuery(updateQuery)) {
575     db.rollback();
576     return false;
577   }
578   if(updateQuery.numRowsAffected() != 1) {
579     // seems this is not our network...
580     db.rollback();
581     return false;
582   }
583
584   QSqlQuery dropServersQuery(db);
585   dropServersQuery.prepare("DELETE FROM ircserver WHERE networkid = :networkid");
586   dropServersQuery.bindValue(":networkid", info.networkId.toInt());
587   safeExec(dropServersQuery);
588   if(!watchQuery(dropServersQuery)) {
589     db.rollback();
590     return false;
591   }
592
593   QSqlQuery insertServersQuery(db);
594   insertServersQuery.prepare(queryString("insert_server"));
595   foreach(Network::Server server, info.serverList) {
596     insertServersQuery.bindValue(":userid", user.toInt());
597     insertServersQuery.bindValue(":networkid", info.networkId.toInt());
598     bindServerInfo(insertServersQuery, server);
599     safeExec(insertServersQuery);
600     if(!watchQuery(insertServersQuery)) {
601       db.rollback();
602       return false;
603     }
604   }
605
606   if(!db.commit()) {
607     qWarning() << "PostgreSqlStorage::updateNetwork(): commiting data failed!";
608     qWarning() << " -" << qPrintable(db.lastError().text());
609     return false;
610   }
611   return true;
612 }
613
614 bool PostgreSqlStorage::removeNetwork(UserId user, const NetworkId &networkId) {
615   QSqlDatabase db = logDb();
616   if(!db.transaction()) {
617     qWarning() << "PostgreSqlStorage::removeNetwork(): cannot start transaction!";
618     qWarning() << " -" << qPrintable(db.lastError().text());
619     return false;
620   }
621
622   QSqlQuery query(db);
623   query.prepare(queryString("delete_network"));
624   query.bindValue(":userid", user.toInt());
625   query.bindValue(":networkid", networkId.toInt());
626   safeExec(query);
627   if(!watchQuery(query)) {
628     db.rollback();
629     return false;
630   }
631
632   db.commit();
633   return true;
634 }
635
636 QList<NetworkInfo> PostgreSqlStorage::networks(UserId user) {
637   QList<NetworkInfo> nets;
638
639   QSqlDatabase db = logDb();
640   if(!beginReadOnlyTransaction(db)) {
641     qWarning() << "PostgreSqlStorage::networks(): cannot start read only transaction!";
642     qWarning() << " -" << qPrintable(db.lastError().text());
643     return nets;
644   }
645
646   QSqlQuery networksQuery(db);
647   networksQuery.prepare(queryString("select_networks_for_user"));
648   networksQuery.bindValue(":userid", user.toInt());
649
650   QSqlQuery serversQuery(db);
651   serversQuery.prepare(queryString("select_servers_for_network"));
652
653   safeExec(networksQuery);
654   if(!watchQuery(networksQuery)) {
655     db.rollback();
656     return nets;
657   }
658
659   while(networksQuery.next()) {
660     NetworkInfo net;
661     net.networkId = networksQuery.value(0).toInt();
662     net.networkName = networksQuery.value(1).toString();
663     net.identity = networksQuery.value(2).toInt();
664     net.codecForServer = networksQuery.value(3).toString().toAscii();
665     net.codecForEncoding = networksQuery.value(4).toString().toAscii();
666     net.codecForDecoding = networksQuery.value(5).toString().toAscii();
667     net.useRandomServer = networksQuery.value(6).toBool();
668     net.perform = networksQuery.value(7).toString().split("\n");
669     net.useAutoIdentify = networksQuery.value(8).toBool();
670     net.autoIdentifyService = networksQuery.value(9).toString();
671     net.autoIdentifyPassword = networksQuery.value(10).toString();
672     net.useAutoReconnect = networksQuery.value(11).toBool();
673     net.autoReconnectInterval = networksQuery.value(12).toUInt();
674     net.autoReconnectRetries = networksQuery.value(13).toInt();
675     net.unlimitedReconnectRetries = networksQuery.value(14).toBool();
676     net.rejoinChannels = networksQuery.value(15).toBool();
677
678     serversQuery.bindValue(":networkid", net.networkId.toInt());
679     safeExec(serversQuery);
680     if(!watchQuery(serversQuery)) {
681       db.rollback();
682       return nets;
683     }
684
685     Network::ServerList servers;
686     while(serversQuery.next()) {
687       Network::Server server;
688       server.host = serversQuery.value(0).toString();
689       server.port = serversQuery.value(1).toUInt();
690       server.password = serversQuery.value(2).toString();
691       server.useSsl = serversQuery.value(3).toBool();
692       server.sslVersion = serversQuery.value(4).toInt();
693       server.useProxy = serversQuery.value(5).toBool();
694       server.proxyType = serversQuery.value(6).toInt();
695       server.proxyHost = serversQuery.value(7).toString();
696       server.proxyPort = serversQuery.value(8).toUInt();
697       server.proxyUser = serversQuery.value(9).toString();
698       server.proxyPass = serversQuery.value(10).toString();
699       servers << server;
700     }
701     net.serverList = servers;
702     nets << net;
703   }
704   db.commit();
705   return nets;
706 }
707
708 QList<NetworkId> PostgreSqlStorage::connectedNetworks(UserId user) {
709   QList<NetworkId> connectedNets;
710
711   QSqlDatabase db = logDb();
712   if(!beginReadOnlyTransaction(db)) {
713     qWarning() << "PostgreSqlStorage::connectedNetworks(): cannot start read only transaction!";
714     qWarning() << " -" << qPrintable(db.lastError().text());
715     return connectedNets;
716   }
717
718   QSqlQuery query(db);
719   query.prepare(queryString("select_connected_networks"));
720   query.bindValue(":userid", user.toInt());
721   safeExec(query);
722   watchQuery(query);
723
724   while(query.next()) {
725     connectedNets << query.value(0).toInt();
726   }
727
728   db.commit();
729   return connectedNets;
730 }
731
732 void PostgreSqlStorage::setNetworkConnected(UserId user, const NetworkId &networkId, bool isConnected) {
733   QSqlQuery query(logDb());
734   query.prepare(queryString("update_network_connected"));
735   query.bindValue(":userid", user.toInt());
736   query.bindValue(":networkid", networkId.toInt());
737   query.bindValue(":connected", isConnected);
738   safeExec(query);
739   watchQuery(query);
740 }
741
742 QHash<QString, QString> PostgreSqlStorage::persistentChannels(UserId user, const NetworkId &networkId) {
743   QHash<QString, QString> persistentChans;
744
745   QSqlDatabase db = logDb();
746   if(!beginReadOnlyTransaction(db)) {
747     qWarning() << "PostgreSqlStorage::persistentChannels(): cannot start read only transaction!";
748     qWarning() << " -" << qPrintable(db.lastError().text());
749     return persistentChans;
750   }
751
752   QSqlQuery query(db);
753   query.prepare(queryString("select_persistent_channels"));
754   query.bindValue(":userid", user.toInt());
755   query.bindValue(":networkid", networkId.toInt());
756   safeExec(query);
757   watchQuery(query);
758
759   while(query.next()) {
760     persistentChans[query.value(0).toString()] = query.value(1).toString();
761   }
762
763   db.commit();
764   return persistentChans;
765 }
766
767 void PostgreSqlStorage::setChannelPersistent(UserId user, const NetworkId &networkId, const QString &channel, bool isJoined) {
768   QSqlQuery query(logDb());
769   query.prepare(queryString("update_buffer_persistent_channel"));
770   query.bindValue(":userid", user.toInt());
771   query.bindValue(":networkId", networkId.toInt());
772   query.bindValue(":buffercname", channel.toLower());
773   query.bindValue(":joined", isJoined);
774   safeExec(query);
775   watchQuery(query);
776 }
777
778 void PostgreSqlStorage::setPersistentChannelKey(UserId user, const NetworkId &networkId, const QString &channel, const QString &key) {
779   QSqlQuery query(logDb());
780   query.prepare(queryString("update_buffer_set_channel_key"));
781   query.bindValue(":userid", user.toInt());
782   query.bindValue(":networkId", networkId.toInt());
783   query.bindValue(":buffercname", channel.toLower());
784   query.bindValue(":key", key);
785   safeExec(query);
786   watchQuery(query);
787 }
788
789 QString PostgreSqlStorage::awayMessage(UserId user, NetworkId networkId) {
790   QSqlQuery query(logDb());
791   query.prepare(queryString("select_network_awaymsg"));
792   query.bindValue(":userid", user.toInt());
793   query.bindValue(":networkid", networkId.toInt());
794   safeExec(query);
795   watchQuery(query);
796   QString awayMsg;
797   if(query.first())
798     awayMsg = query.value(0).toString();
799   return awayMsg;
800 }
801
802 void PostgreSqlStorage::setAwayMessage(UserId user, NetworkId networkId, const QString &awayMsg) {
803   QSqlQuery query(logDb());
804   query.prepare(queryString("update_network_set_awaymsg"));
805   query.bindValue(":userid", user.toInt());
806   query.bindValue(":networkid", networkId.toInt());
807   query.bindValue(":awaymsg", awayMsg);
808   safeExec(query);
809   watchQuery(query);
810 }
811
812 QString PostgreSqlStorage::userModes(UserId user, NetworkId networkId) {
813   QSqlQuery query(logDb());
814   query.prepare(queryString("select_network_usermode"));
815   query.bindValue(":userid", user.toInt());
816   query.bindValue(":networkid", networkId.toInt());
817   safeExec(query);
818   watchQuery(query);
819   QString modes;
820   if(query.first())
821     modes = query.value(0).toString();
822   return modes;
823 }
824
825 void PostgreSqlStorage::setUserModes(UserId user, NetworkId networkId, const QString &userModes) {
826   QSqlQuery query(logDb());
827   query.prepare(queryString("update_network_set_usermode"));
828   query.bindValue(":userid", user.toInt());
829   query.bindValue(":networkid", networkId.toInt());
830   query.bindValue(":usermode", userModes);
831   safeExec(query);
832   watchQuery(query);
833 }
834
835 BufferInfo PostgreSqlStorage::bufferInfo(UserId user, const NetworkId &networkId, BufferInfo::Type type, const QString &buffer, bool create) {
836   QSqlDatabase db = logDb();
837   if(!db.transaction()) {
838     qWarning() << "PostgreSqlStorage::bufferInfo(): cannot start read only transaction!";
839     qWarning() << " -" << qPrintable(db.lastError().text());
840     return BufferInfo();
841   }
842
843   QSqlQuery query(db);
844   query.prepare(queryString("select_bufferByName"));
845   query.bindValue(":networkid", networkId.toInt());
846   query.bindValue(":userid", user.toInt());
847   query.bindValue(":buffercname", buffer.toLower());
848   safeExec(query);
849
850   if(query.first()) {
851     BufferInfo bufferInfo = BufferInfo(query.value(0).toInt(), networkId, (BufferInfo::Type)query.value(1).toInt(), 0, buffer);
852     if(query.next()) {
853       qCritical() << "PostgreSqlStorage::bufferInfo(): received more then one Buffer!";
854       qCritical() << "         Query:" << query.lastQuery();
855       qCritical() << "  bound Values:";
856       QList<QVariant> list = query.boundValues().values();
857       for (int i = 0; i < list.size(); ++i)
858         qCritical() << i << ":" << list.at(i).toString().toAscii().data();
859       Q_ASSERT(false);
860     }
861     db.commit();
862     return bufferInfo;
863   }
864
865   if(!create) {
866     db.rollback();
867     return BufferInfo();
868   }
869
870   QSqlQuery createQuery(db);
871   createQuery.prepare(queryString("insert_buffer"));
872   createQuery.bindValue(":userid", user.toInt());
873   createQuery.bindValue(":networkid", networkId.toInt());
874   createQuery.bindValue(":buffertype", (int)type);
875   createQuery.bindValue(":buffername", buffer);
876   createQuery.bindValue(":buffercname", buffer.toLower());
877   safeExec(createQuery);
878
879   if(createQuery.lastError().isValid()) {
880     qWarning() << "PostgreSqlStorage::bufferInfo(): unable to create buffer";
881     watchQuery(createQuery);
882     db.rollback();
883     return BufferInfo();
884   }
885
886   createQuery.first();
887
888   BufferInfo bufferInfo = BufferInfo(createQuery.value(0).toInt(), networkId, type, 0, buffer);
889   db.commit();
890   return bufferInfo;
891 }
892
893 BufferInfo PostgreSqlStorage::getBufferInfo(UserId user, const BufferId &bufferId) {
894   QSqlQuery query(logDb());
895   query.prepare(queryString("select_buffer_by_id"));
896   query.bindValue(":userid", user.toInt());
897   query.bindValue(":bufferid", bufferId.toInt());
898   safeExec(query);
899   if(!watchQuery(query))
900     return BufferInfo();
901
902   if(!query.first())
903     return BufferInfo();
904
905   BufferInfo bufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), 0, query.value(4).toString());
906   Q_ASSERT(!query.next());
907
908   return bufferInfo;
909 }
910
911 QList<BufferInfo> PostgreSqlStorage::requestBuffers(UserId user) {
912   QList<BufferInfo> bufferlist;
913
914   QSqlDatabase db = logDb();
915   if(!beginReadOnlyTransaction(db)) {
916     qWarning() << "PostgreSqlStorage::requestBuffers(): cannot start read only transaction!";
917     qWarning() << " -" << qPrintable(db.lastError().text());
918     return bufferlist;
919   }
920
921   QSqlQuery query(db);
922   query.prepare(queryString("select_buffers"));
923   query.bindValue(":userid", user.toInt());
924
925   safeExec(query);
926   watchQuery(query);
927   while(query.next()) {
928     bufferlist << BufferInfo(query.value(0).toInt(), query.value(1).toInt(), (BufferInfo::Type)query.value(2).toInt(), query.value(3).toInt(), query.value(4).toString());
929   }
930   db.commit();
931   return bufferlist;
932 }
933
934 QList<BufferId> PostgreSqlStorage::requestBufferIdsForNetwork(UserId user, NetworkId networkId) {
935   QList<BufferId> bufferList;
936
937   QSqlDatabase db = logDb();
938   if(!beginReadOnlyTransaction(db)) {
939     qWarning() << "PostgreSqlStorage::requestBufferIdsForNetwork(): cannot start read only transaction!";
940     qWarning() << " -" << qPrintable(db.lastError().text());
941     return bufferList;
942   }
943
944   QSqlQuery query(db);
945   query.prepare(queryString("select_buffers_for_network"));
946   query.bindValue(":networkid", networkId.toInt());
947   query.bindValue(":userid", user.toInt());
948
949   safeExec(query);
950   watchQuery(query);
951   while(query.next()) {
952     bufferList << BufferId(query.value(0).toInt());
953   }
954   db.commit();
955   return bufferList;
956 }
957
958 bool PostgreSqlStorage::removeBuffer(const UserId &user, const BufferId &bufferId) {
959   QSqlDatabase db = logDb();
960   if(!db.transaction()) {
961     qWarning() << "PostgreSqlStorage::removeBuffer(): cannot start transaction!";
962     return false;
963   }
964
965   QSqlQuery query(db);
966   query.prepare(queryString("delete_buffer_for_bufferid"));
967   query.bindValue(":userid", user.toInt());
968   query.bindValue(":bufferid", bufferId.toInt());
969   safeExec(query);
970   if(!watchQuery(query)) {
971     db.rollback();
972     return false;
973   }
974
975   int numRows = query.numRowsAffected();
976   switch(numRows) {
977   case 0:
978     db.commit();
979     return false;
980   case 1:
981     db.commit();
982     return false;
983   default:
984     // there was more then one buffer deleted...
985     qWarning() << "PostgreSqlStorage::removeBuffer(): Userid" << user << "BufferId" << "caused deletion of" << numRows << "Buffers! Rolling back transaction...";
986     db.rollback();
987     return false;
988   }
989 }
990
991 bool PostgreSqlStorage::renameBuffer(const UserId &user, const BufferId &bufferId, const QString &newName) {
992   QSqlDatabase db = logDb();
993   if(!db.transaction()) {
994     qWarning() << "PostgreSqlStorage::renameBuffer(): cannot start transaction!";
995     return false;
996   }
997
998   QSqlQuery query(db);
999   query.prepare(queryString("update_buffer_name"));
1000   query.bindValue(":buffername", newName);
1001   query.bindValue(":buffercname", newName.toLower());
1002   query.bindValue(":userid", user.toInt());
1003   query.bindValue(":bufferid", bufferId.toInt());
1004   safeExec(query);
1005   if(query.lastError().isValid()) {
1006     watchQuery(query);
1007     db.rollback();
1008     return false;
1009   }
1010
1011   int numRows = query.numRowsAffected();
1012   switch(numRows) {
1013   case 0:
1014     db.commit();
1015     return false;
1016   case 1:
1017     db.commit();
1018     return false;
1019   default:
1020     // there was more then one buffer deleted...
1021     qWarning() << "PostgreSqlStorage::renameBuffer(): Userid" << user << "BufferId" << "affected" << numRows << "Buffers! Rolling back transaction...";
1022     db.rollback();
1023     return false;
1024   }
1025 }
1026
1027 bool PostgreSqlStorage::mergeBuffersPermanently(const UserId &user, const BufferId &bufferId1, const BufferId &bufferId2) {
1028   QSqlDatabase db = logDb();
1029   if(!db.transaction()) {
1030     qWarning() << "PostgreSqlStorage::mergeBuffersPermanently(): cannot start transaction!";
1031     qWarning() << " -" << qPrintable(db.lastError().text());
1032     return false;
1033   }
1034
1035   QSqlQuery checkQuery(db);
1036   checkQuery.prepare("SELECT count(*) FROM buffer"
1037                      "WHERE userid = :userid AND bufferid IN (:buffer1, :buffer2)");
1038   checkQuery.bindValue(":userid", user.toInt());
1039   checkQuery.bindValue(":buffer1", bufferId1.toInt());
1040   checkQuery.bindValue(":buffer2", bufferId2.toInt());
1041   safeExec(checkQuery);
1042   if(!watchQuery(checkQuery)) {
1043     db.rollback();
1044     return false;
1045   }
1046   checkQuery.first();
1047   if(checkQuery.value(0).toInt() != 2) {
1048     db.rollback();
1049     return false;
1050   }
1051
1052   QSqlQuery query(db);
1053   query.prepare(queryString("update_backlog_bufferid"));
1054   query.bindValue(":oldbufferid", bufferId2.toInt());
1055   query.bindValue(":newbufferid", bufferId1.toInt());
1056   safeExec(query);
1057   if(!watchQuery(query)) {
1058     db.rollback();
1059     return false;
1060   }
1061
1062   QSqlQuery delBufferQuery(logDb());
1063   delBufferQuery.prepare(queryString("delete_buffer_for_bufferid"));
1064   delBufferQuery.bindValue(":bufferid", bufferId2.toInt());
1065   safeExec(delBufferQuery);
1066   if(!watchQuery(delBufferQuery)) {
1067     db.rollback();
1068     return false;
1069   }
1070
1071   db.commit();
1072   return true;
1073 }
1074
1075 void PostgreSqlStorage::setBufferLastSeenMsg(UserId user, const BufferId &bufferId, const MsgId &msgId) {
1076   QSqlQuery query(logDb());
1077   query.prepare(queryString("update_buffer_lastseen"));
1078
1079   query.bindValue(":userid", user.toInt());
1080   query.bindValue(":bufferid", bufferId.toInt());
1081   query.bindValue(":lastseenmsgid", msgId.toInt());
1082   safeExec(query);
1083   watchQuery(query);
1084 }
1085
1086 QHash<BufferId, MsgId> PostgreSqlStorage::bufferLastSeenMsgIds(UserId user) {
1087   QHash<BufferId, MsgId> lastSeenHash;
1088
1089   QSqlDatabase db = logDb();
1090   if(!beginReadOnlyTransaction(db)) {
1091     qWarning() << "PostgreSqlStorage::bufferLastSeenMsgIds(): cannot start read only transaction!";
1092     qWarning() << " -" << qPrintable(db.lastError().text());
1093     return lastSeenHash;
1094   }
1095
1096   QSqlQuery query(db);
1097   query.prepare(queryString("select_buffer_lastseen_messages"));
1098   query.bindValue(":userid", user.toInt());
1099   safeExec(query);
1100   if(!watchQuery(query)) {
1101     db.rollback();
1102     return lastSeenHash;
1103   }
1104
1105   while(query.next()) {
1106     lastSeenHash[query.value(0).toInt()] = query.value(1).toInt();
1107   }
1108
1109   db.commit();
1110   return lastSeenHash;
1111 }
1112
1113 bool PostgreSqlStorage::logMessage(Message &msg) {
1114   QSqlDatabase db = logDb();
1115   if(!db.transaction()) {
1116     qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!";
1117     qWarning() << " -" << qPrintable(db.lastError().text());
1118     return false;
1119   }
1120
1121   if(!prepareQuery("insert_message", queryString("insert_message"), db)) {
1122     qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_message");
1123     qWarning() << "  Error:" << db.lastError().text();
1124     db.rollback();
1125     return false;
1126   }
1127
1128   QVariantList params;
1129   params << msg.timestamp()
1130          << msg.bufferInfo().bufferId().toInt()
1131          << msg.type()
1132          << (int)msg.flags()
1133          << msg.sender()
1134          << msg.contents();
1135   QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db);
1136
1137   if(logMessageQuery.lastError().isValid()) {
1138     // first we need to reset the transaction
1139     db.rollback();
1140     db.transaction();
1141
1142
1143     // it's possible that the sender was already added by another thread
1144     // since the insert might fail we're setting a savepoint
1145     savePoint("sender_sp1", db);
1146     QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", msg.sender(), db);
1147     if(addSenderQuery.lastError().isValid())
1148       rollbackSavePoint("sender_sp1", db);
1149     else
1150       releaseSavePoint("sender_sp1", db);
1151
1152     logMessageQuery = db.exec(logMessageQuery.lastQuery());
1153     if(!watchQuery(logMessageQuery)) {
1154       qDebug() << "==================== Sender Query:";
1155       watchQuery(addSenderQuery);
1156       qDebug() << "==================== /Sender Query";
1157       db.rollback();
1158       return false;
1159     }
1160   }
1161
1162   logMessageQuery.first();
1163   MsgId msgId = logMessageQuery.value(0).toInt();
1164   db.commit();
1165   if(msgId.isValid()) {
1166     msg.setMsgId(msgId);
1167     return true;
1168   } else {
1169     return false;
1170   }
1171 }
1172
1173 bool PostgreSqlStorage::logMessages(MessageList &msgs) {
1174   QSqlDatabase db = logDb();
1175   if(!db.transaction()) {
1176     qWarning() << "PostgreSqlStorage::logMessage(): cannot start transaction!";
1177     qWarning() << " -" << qPrintable(db.lastError().text());
1178     return false;
1179   }
1180
1181   if(!prepareQuery("insert_sender", queryString("insert_sender"), db)) {
1182     qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_sender");
1183     qWarning() << "  Error:" << db.lastError().text();
1184     db.rollback();
1185     return false;
1186   }
1187   QSet<QString> senders;
1188   for(int i = 0; i < msgs.count(); i++) {
1189     const QString &sender = msgs.at(i).sender();
1190     if(senders.contains(sender))
1191       continue;
1192     senders << sender;
1193
1194     savePoint("sender_sp", db);
1195     QSqlQuery addSenderQuery = executePreparedQuery("insert_sender", sender, db);
1196     if(addSenderQuery.lastError().isValid())
1197       rollbackSavePoint("sender_sp", db);
1198     else
1199       releaseSavePoint("sender_sp", db);
1200   }
1201
1202   // yes we loop twice over the same list. This avoids alternating queries.
1203   if(!prepareQuery("insert_message", queryString("insert_message"), db)) {
1204     qWarning() << "PostgreSqlStorage::logMessages(): unable to prepare query:" << queryString("insert_message");
1205     qWarning() << "  Error:" << db.lastError().text();
1206     db.rollback();
1207     return false;
1208   }
1209   bool error = false;
1210   for(int i = 0; i < msgs.count(); i++) {
1211     Message &msg = msgs[i];
1212     QVariantList params;
1213     params << msg.timestamp()
1214            << msg.bufferInfo().bufferId().toInt()
1215            << msg.type()
1216            << (int)msg.flags()
1217            << msg.sender()
1218            << msg.contents();
1219     QSqlQuery logMessageQuery = executePreparedQuery("insert_message", params, db);
1220     if(!watchQuery(logMessageQuery)) {
1221       db.rollback();
1222       error = true;
1223       break;
1224     } else {
1225       logMessageQuery.first();
1226       msg.setMsgId(logMessageQuery.value(0).toInt());
1227     }
1228   }
1229
1230   if(error) {
1231     // we had a rollback in the db so we need to reset all msgIds
1232     for(int i = 0; i < msgs.count(); i++) {
1233       msgs[i].setMsgId(MsgId());
1234     }
1235     return false;
1236   }
1237
1238   db.commit();
1239   return true;
1240 }
1241
1242 QList<Message> PostgreSqlStorage::requestMsgs(UserId user, BufferId bufferId, MsgId first, MsgId last, int limit) {
1243   QList<Message> messagelist;
1244
1245   QSqlDatabase db = logDb();
1246   if(!beginReadOnlyTransaction(db)) {
1247     qWarning() << "PostgreSqlStorage::requestMsgs(): cannot start read only transaction!";
1248     qWarning() << " -" << qPrintable(db.lastError().text());
1249     return messagelist;
1250   }
1251
1252   BufferInfo bufferInfo = getBufferInfo(user, bufferId);
1253   if(!bufferInfo.isValid()) {
1254     db.rollback();
1255     return messagelist;
1256   }
1257
1258   QSqlQuery query(db);
1259   if(last == -1 && first == -1) {
1260     query.prepare(queryString("select_messages"));
1261   } else if(last == -1) {
1262     query.prepare(queryString("select_messagesNewerThan"));
1263     query.bindValue(":firstmsg", first.toInt());
1264   } else {
1265     query.prepare(queryString("select_messagesRange"));
1266     query.bindValue(":lastmsg", last.toInt());
1267     query.bindValue(":firstmsg", first.toInt());
1268   }
1269   query.bindValue(":bufferid", bufferId.toInt());
1270   safeExec(query);
1271   if(!watchQuery(query)) {
1272     db.rollback();
1273     return messagelist;
1274   }
1275
1276   QDateTime timestamp;
1277   for(int i = 0; i < limit && query.next(); i++) {
1278     timestamp = query.value(1).toDateTime();
1279     timestamp.setTimeSpec(Qt::UTC);
1280     Message msg(timestamp,
1281                 bufferInfo,
1282                 (Message::Type)query.value(2).toUInt(),
1283                 query.value(5).toString(),
1284                 query.value(4).toString(),
1285                 (Message::Flags)query.value(3).toUInt());
1286     msg.setMsgId(query.value(0).toInt());
1287     messagelist << msg;
1288   }
1289
1290   db.commit();
1291   return messagelist;
1292 }
1293
1294 QList<Message> PostgreSqlStorage::requestAllMsgs(UserId user, MsgId first, MsgId last, int limit) {
1295   QList<Message> messagelist;
1296
1297   // requestBuffers uses it's own transaction.
1298   QHash<BufferId, BufferInfo> bufferInfoHash;
1299   foreach(BufferInfo bufferInfo, requestBuffers(user)) {
1300     bufferInfoHash[bufferInfo.bufferId()] = bufferInfo;
1301   }
1302
1303   QSqlDatabase db = logDb();
1304   if(!beginReadOnlyTransaction(db)) {
1305     qWarning() << "PostgreSqlStorage::requestAllMsgs(): cannot start read only transaction!";
1306     qWarning() << " -" << qPrintable(db.lastError().text());
1307     return messagelist;
1308   }
1309
1310   QSqlQuery query(db);
1311   if(last == -1) {
1312     query.prepare(queryString("select_messagesAllNew"));
1313   } else {
1314     query.prepare(queryString("select_messagesAll"));
1315     query.bindValue(":lastmsg", last.toInt());
1316   }
1317   query.bindValue(":userid", user.toInt());
1318   query.bindValue(":firstmsg", first.toInt());
1319   safeExec(query);
1320   if(!watchQuery(query)) {
1321     db.rollback();
1322     return messagelist;
1323   }
1324
1325   QDateTime timestamp;
1326   for(int i = 0; i < limit && query.next(); i++) {
1327     timestamp = query.value(1).toDateTime();
1328     timestamp.setTimeSpec(Qt::UTC);
1329     Message msg(timestamp,
1330                 bufferInfoHash[query.value(1).toInt()],
1331                 (Message::Type)query.value(3).toUInt(),
1332                 query.value(6).toString(),
1333                 query.value(5).toString(),
1334                 (Message::Flags)query.value(4).toUInt());
1335     msg.setMsgId(query.value(0).toInt());
1336     messagelist << msg;
1337   }
1338
1339   db.commit();
1340   return messagelist;
1341 }
1342
1343 // void PostgreSqlStorage::safeExec(QSqlQuery &query) {
1344 //   qDebug() << "PostgreSqlStorage::safeExec";
1345 //   qDebug() << "   executing:\n" << query.executedQuery();
1346 //   qDebug() << "   bound Values:";
1347 //   QList<QVariant> list = query.boundValues().values();
1348 //   for (int i = 0; i < list.size(); ++i)
1349 //     qCritical() << i << ": " << list.at(i).toString().toAscii().data();
1350
1351 //   query.exec();
1352
1353 //   qDebug() << "Success:" << !query.lastError().isValid();
1354 //   qDebug();
1355
1356 //   if(!query.lastError().isValid())
1357 //     return;
1358
1359 //   qDebug() << "==================== ERROR ====================";
1360 //   watchQuery(query);
1361 //   qDebug() << "===============================================";
1362 //   qDebug();
1363 //   return;
1364 // }
1365
1366 bool PostgreSqlStorage::beginReadOnlyTransaction(QSqlDatabase &db) {
1367   QSqlQuery query = db.exec("BEGIN TRANSACTION READ ONLY");
1368   return !query.lastError().isValid();
1369 }
1370
1371 bool PostgreSqlStorage::prepareQuery(const QString &handle, const QString &query, const QSqlDatabase &db) {
1372   if(_preparedQueries.contains(db.connectionName()) && _preparedQueries[db.connectionName()].contains(handle))
1373     return true; // already prepared
1374
1375   QMutexLocker locker(&_queryHashMutex);
1376
1377   static unsigned int stmtCount = 0;
1378   QString queryId = QLatin1String("quassel_") + QString::number(++stmtCount, 16);
1379   // qDebug() << "prepare:" << QString("PREPARE %1 AS %2").arg(queryId).arg(query);
1380   db.exec(QString("PREPARE %1 AS %2").arg(queryId).arg(query));
1381   if(db.lastError().isValid()) {
1382     return false;
1383   } else {
1384     _preparedQueries[db.connectionName()][handle] = queryId;
1385     return true;
1386   }
1387 }
1388
1389 QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &handle, const QVariantList &params, const QSqlDatabase &db) {
1390   if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
1391     qWarning() << "PostgreSqlStorage::executePreparedQuery() no prepared Query with handle" << handle << "on Database" << db.connectionName();
1392     return QSqlQuery();
1393   }
1394
1395   QSqlDriver *driver = db.driver();
1396
1397   QStringList paramStrings;
1398   QSqlField field;
1399   for(int i = 0; i < params.count(); i++) {
1400     const QVariant &value = params.at(i);
1401     field.setType(value.type());
1402     if(value.isNull())
1403       field.clear();
1404     else
1405       field.setValue(value);
1406
1407     paramStrings << driver->formatValue(field);
1408   }
1409
1410   const QString &queryId = _preparedQueries[db.connectionName()][handle];
1411   if(params.isEmpty()) {
1412     return db.exec(QString("EXECUTE %1").arg(queryId));
1413   } else {
1414     // qDebug() << "preparedExec:" << QString("EXECUTE %1 (%2)").arg(queryId).arg(paramStrings.join(", "));
1415     return db.exec(QString("EXECUTE %1 (%2)").arg(queryId).arg(paramStrings.join(", ")));
1416   }
1417 }
1418
1419 QSqlQuery PostgreSqlStorage::executePreparedQuery(const QString &handle, const QVariant &param, const QSqlDatabase &db) {
1420   if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
1421     qWarning() << "PostgreSqlStorage::executePreparedQuery() no prepared Query with handle" << handle << "on Database" << db.connectionName();
1422     return QSqlQuery();
1423   }
1424
1425   QSqlField field;
1426   field.setType(param.type());
1427   if(param.isNull())
1428     field.clear();
1429   else
1430     field.setValue(param);
1431
1432   const QString &queryId = _preparedQueries[db.connectionName()][handle];
1433   QString paramString = db.driver()->formatValue(field);
1434
1435   // qDebug() << "preparedExec:" << QString("EXECUTE %1 (%2)").arg(queryId).arg(paramString);
1436   return db.exec(QString("EXECUTE %1 (%2)").arg(queryId).arg(paramString));
1437 }
1438
1439 void PostgreSqlStorage::deallocateQuery(const QString &handle, const QSqlDatabase &db) {
1440   if(!_preparedQueries.contains(db.connectionName()) || !_preparedQueries[db.connectionName()].contains(handle)) {
1441     return;
1442   }
1443   QMutexLocker locker(&_queryHashMutex);
1444   QString queryId = _preparedQueries[db.connectionName()].take(handle);
1445   db.exec(QString("DEALLOCATE %1").arg(queryId));
1446 }
1447
1448
1449
1450
1451
1452 // ========================================
1453 //  PostgreSqlMigrationWriter
1454 // ========================================
1455 PostgreSqlMigrationWriter::PostgreSqlMigrationWriter()
1456   : PostgreSqlStorage()
1457 {
1458 }
1459
1460 bool PostgreSqlMigrationWriter::prepareQuery(MigrationObject mo) {
1461   QString query;
1462   switch(mo) {
1463   case QuasselUser:
1464     query = queryString("migrate_write_quasseluser");
1465     break;
1466   case Sender:
1467     query = queryString("migrate_write_sender");
1468     break;
1469   case Identity:
1470     query = queryString("migrate_write_identity");
1471     break;
1472   case IdentityNick:
1473     query = queryString("migrate_write_identity_nick");
1474     break;
1475   case Network:
1476     query = queryString("migrate_write_network");
1477     break;
1478   case Buffer:
1479     query = queryString("migrate_write_buffer");
1480     break;
1481   case Backlog:
1482     query = queryString("migrate_write_backlog");
1483     break;
1484   case IrcServer:
1485     query = queryString("migrate_write_ircserver");
1486     break;
1487   case UserSetting:
1488     query = queryString("migrate_write_usersetting");
1489     break;
1490   }
1491   newQuery(query, logDb());
1492   return true;
1493 }
1494
1495 //bool PostgreSqlMigrationWriter::writeUser(const QuasselUserMO &user) {
1496 bool PostgreSqlMigrationWriter::writeMo(const QuasselUserMO &user) {
1497   bindValue(0, user.id.toInt());
1498   bindValue(1, user.username);
1499   bindValue(2, user.password);
1500   return exec();
1501 }
1502
1503 //bool PostgreSqlMigrationWriter::writeSender(const SenderMO &sender) {
1504 bool PostgreSqlMigrationWriter::writeMo(const SenderMO &sender) {
1505   bindValue(0, sender.senderId);
1506   bindValue(1, sender.sender);
1507   return exec();
1508 }
1509
1510 //bool PostgreSqlMigrationWriter::writeIdentity(const IdentityMO &identity) {
1511 bool PostgreSqlMigrationWriter::writeMo(const IdentityMO &identity) {
1512   bindValue(0, identity.id.toInt());
1513   bindValue(1, identity.userid.toInt());
1514   bindValue(2, identity.identityname);
1515   bindValue(3, identity.realname);
1516   bindValue(4, identity.awayNick);
1517   bindValue(5, identity.awayNickEnabled);
1518   bindValue(6, identity.awayReason);
1519   bindValue(7, identity.awayReasonEnabled);
1520   bindValue(8, identity.autoAwayEnabled);
1521   bindValue(9, identity.autoAwayTime);
1522   bindValue(10, identity.autoAwayReason);
1523   bindValue(11, identity.autoAwayReasonEnabled);
1524   bindValue(12, identity.detachAwayEnabled);
1525   bindValue(13, identity.detachAwayReason);
1526   bindValue(14, identity.detchAwayReasonEnabled);
1527   bindValue(15, identity.ident);
1528   bindValue(16, identity.kickReason);
1529   bindValue(17, identity.partReason);
1530   bindValue(18, identity.quitReason);
1531   bindValue(19, identity.sslCert);
1532   bindValue(20, identity.sslKey);
1533   return exec();
1534 }
1535
1536 //bool PostgreSqlMigrationWriter::writeIdentityNick(const IdentityNickMO &identityNick) {
1537 bool PostgreSqlMigrationWriter::writeMo(const IdentityNickMO &identityNick) {
1538   bindValue(0, identityNick.nickid);
1539   bindValue(1, identityNick.identityId.toInt());
1540   bindValue(2, identityNick.nick);
1541   return exec();
1542 }
1543
1544 //bool PostgreSqlMigrationWriter::writeNetwork(const NetworkMO &network) {
1545 bool PostgreSqlMigrationWriter::writeMo(const NetworkMO &network) {
1546   bindValue(0, network.networkid.toInt());
1547   bindValue(1, network.userid.toInt());
1548   bindValue(2, network.networkname);
1549   bindValue(3, network.identityid.toInt());
1550   bindValue(4, network.encodingcodec);
1551   bindValue(5, network.decodingcodec);
1552   bindValue(6, network.servercodec);
1553   bindValue(7, network.userandomserver);
1554   bindValue(8, network.perform);
1555   bindValue(9, network.useautoidentify);
1556   bindValue(10, network.autoidentifyservice);
1557   bindValue(11, network.autoidentifypassword);
1558   bindValue(12, network.useautoreconnect);
1559   bindValue(13, network.autoreconnectinterval);
1560   bindValue(14, network.autoreconnectretries);
1561   bindValue(15, network.unlimitedconnectretries);
1562   bindValue(16, network.rejoinchannels);
1563   bindValue(17, network.connected);
1564   bindValue(18, network.usermode);
1565   bindValue(19, network.awaymessage);
1566   bindValue(20, network.attachperform);
1567   bindValue(21, network.detachperform);
1568   return exec();
1569 }
1570
1571 //bool PostgreSqlMigrationWriter::writeBuffer(const BufferMO &buffer) {
1572 bool PostgreSqlMigrationWriter::writeMo(const BufferMO &buffer) {
1573   bindValue(0, buffer.bufferid.toInt());
1574   bindValue(1, buffer.userid.toInt());
1575   bindValue(2, buffer.groupid);
1576   bindValue(3, buffer.networkid.toInt());
1577   bindValue(4, buffer.buffername);
1578   bindValue(5, buffer.buffercname);
1579   bindValue(6, (int)buffer.buffertype);
1580   bindValue(7, buffer.lastseenmsgid);
1581   bindValue(8, buffer.key);
1582   bindValue(9, buffer.joined);
1583   return exec();
1584 }
1585
1586 //bool PostgreSqlMigrationWriter::writeBacklog(const BacklogMO &backlog) {
1587 bool PostgreSqlMigrationWriter::writeMo(const BacklogMO &backlog) {
1588   bindValue(0, backlog.messageid.toInt());
1589   bindValue(1, backlog.time);
1590   bindValue(2, backlog.bufferid.toInt());
1591   bindValue(3, backlog.type);
1592   bindValue(4, (int)backlog.flags);
1593   bindValue(5, backlog.senderid);
1594   bindValue(6, backlog.message);
1595   return exec();
1596 }
1597
1598 //bool PostgreSqlMigrationWriter::writeIrcServer(const IrcServerMO &ircserver) {
1599 bool PostgreSqlMigrationWriter::writeMo(const IrcServerMO &ircserver) {
1600   bindValue(0, ircserver.serverid);
1601   bindValue(1, ircserver.userid.toInt());
1602   bindValue(2, ircserver.networkid.toInt());
1603   bindValue(3, ircserver.hostname);
1604   bindValue(4, ircserver.port);
1605   bindValue(5, ircserver.password);
1606   bindValue(6, ircserver.ssl);
1607   bindValue(7, ircserver.sslversion);
1608   bindValue(8, ircserver.useproxy);
1609   bindValue(9, ircserver.proxytype);
1610   bindValue(10, ircserver.proxyhost);
1611   bindValue(11, ircserver.proxyport);
1612   bindValue(12, ircserver.proxyuser);
1613   bindValue(13, ircserver.proxypass);
1614   return exec();
1615 }
1616
1617 //bool PostgreSqlMigrationWriter::writeUserSetting(const UserSettingMO &userSetting) {
1618 bool PostgreSqlMigrationWriter::writeMo(const UserSettingMO &userSetting) {
1619   bindValue(0, userSetting.userid.toInt());
1620   bindValue(1, userSetting.settingname);
1621   bindValue(2, userSetting.settingvalue);
1622   return exec();
1623 }
1624
1625 bool PostgreSqlMigrationWriter::postProcess() {
1626   QSqlDatabase db = logDb();
1627   QList<Sequence> sequences;
1628   sequences << Sequence("backlog", "messageid")
1629             << Sequence("identity", "identityid")
1630             << Sequence("identity_nick", "nickid")
1631             << Sequence("ircserver", "serverid")
1632             << Sequence("network", "networkid")
1633             << Sequence("quasseluser", "userid")
1634             << Sequence("sender", "senderid");
1635   QList<Sequence>::const_iterator iter;
1636   for(iter = sequences.constBegin(); iter != sequences.constEnd(); iter++) {
1637     resetQuery();
1638     newQuery(QString("SELECT setval('%1_%2_seq', max(%2)) FROM %1").arg(iter->table, iter->field), db);
1639     if(!exec())
1640       return false;
1641   }
1642   return true;
1643 }