2
* eventdb.cpp - asynchronous I/O event database
3
* Copyright (C) 2001, 2002 Justin Karneges
5
* This program is free software; you can redistribute it and/or
6
* modify it under the terms of the GNU General Public License
7
* as published by the Free Software Foundation; either version 2
8
* of the License, or (at your option) any later version.
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.
15
* You should have received a copy of the GNU General Public License
16
* along with this library; if not, write to the Free Software
17
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28
#include<qtextstream.h>
29
#include<qvaluevector.h>
34
//----------------------------------------------------------------------------
36
//----------------------------------------------------------------------------
37
EDBItem::EDBItem(PsiEvent *event, const QString &id, const QString &prevId, const QString &nextId)
50
PsiEvent *EDBItem::event() const
55
const QString & EDBItem::id() const
60
const QString & EDBItem::nextId() const
65
const QString & EDBItem::prevId() const
71
//----------------------------------------------------------------------------
73
//----------------------------------------------------------------------------
74
class EDBHandle::Private
87
EDBHandle::EDBHandle(EDB *edb)
94
d->writeSuccess = false;
96
d->lastRequestType = Read;
101
EDBHandle::~EDBHandle()
109
void EDBHandle::getLatest(const Jid &j, int len)
112
d->lastRequestType = Read;
113
d->listeningFor = d->edb->op_getLatest(j, len);
116
void EDBHandle::getOldest(const Jid &j, int len)
119
d->lastRequestType = Read;
120
d->listeningFor = d->edb->op_getOldest(j, len);
123
void EDBHandle::get(const Jid &j, const QString &id, int direction, int len)
126
d->lastRequestType = Read;
127
d->listeningFor = d->edb->op_get(j, id, direction, len);
130
void EDBHandle::find(const QString &str, const Jid &j, const QString &id, int direction)
133
d->lastRequestType = Read;
134
d->listeningFor = d->edb->op_find(str, j, id, direction);
137
void EDBHandle::append(const Jid &j, PsiEvent *e)
140
d->lastRequestType = Write;
141
d->listeningFor = d->edb->op_append(j, e);
144
bool EDBHandle::busy() const
149
const EDBResult *EDBHandle::result() const
154
bool EDBHandle::writeSuccess() const
156
return d->writeSuccess;
159
void EDBHandle::edb_resultReady(EDBResult *r)
169
d->listeningFor = -1;
173
void EDBHandle::edb_writeFinished(bool b)
177
d->listeningFor = -1;
181
int EDBHandle::listeningFor() const
183
return d->listeningFor;
186
int EDBHandle::lastRequestType() const
188
return d->lastRequestType;
192
//----------------------------------------------------------------------------
194
//----------------------------------------------------------------------------
200
QPtrList<EDBHandle> list;
212
d->list.setAutoDelete(true);
217
int EDB::genUniqueId() const
219
return d->reqid_base++;
222
void EDB::reg(EDBHandle *h)
227
void EDB::unreg(EDBHandle *h)
229
d->list.removeRef(h);
232
int EDB::op_getLatest(const Jid &j, int len)
234
return getLatest(j, len);
237
int EDB::op_getOldest(const Jid &j, int len)
239
return getOldest(j, len);
242
int EDB::op_get(const Jid &jid, const QString &id, int direction, int len)
244
return get(jid, id, direction, len);
247
int EDB::op_find(const QString &str, const Jid &j, const QString &id, int direction)
249
return find(str, j, id, direction);
252
int EDB::op_append(const Jid &j, PsiEvent *e)
257
void EDB::resultReady(int req, EDBResult *r)
260
QPtrListIterator<EDBHandle> it(d->list);
261
for(EDBHandle *h; (h = it.current()); ++it) {
262
if(h->listeningFor() == req) {
263
h->edb_resultReady(r);
270
void EDB::writeFinished(int req, bool b)
273
QPtrListIterator<EDBHandle> it(d->list);
274
for(EDBHandle *h; (h = it.current()); ++it) {
275
if(h->listeningFor() == req) {
276
h->edb_writeFinished(b);
283
//----------------------------------------------------------------------------
285
//----------------------------------------------------------------------------
289
int type; // 0 = latest, 1 = oldest, 2 = random, 3 = write
298
class EDBFlatFile::Private
303
QPtrList<File> flist;
304
QPtrList<item_file_req> rlist;
307
EDBFlatFile::EDBFlatFile()
313
EDBFlatFile::~EDBFlatFile()
315
d->rlist.setAutoDelete(true);
316
d->flist.setAutoDelete(true);
322
int EDBFlatFile::getLatest(const Jid &j, int len)
324
item_file_req *r = new item_file_req;
327
r->len = len < 1 ? 1: len;
328
r->id = genUniqueId();
331
QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
335
int EDBFlatFile::getOldest(const Jid &j, int len)
337
item_file_req *r = new item_file_req;
340
r->len = len < 1 ? 1: len;
341
r->id = genUniqueId();
344
QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
348
int EDBFlatFile::get(const Jid &j, const QString &id, int direction, int len)
350
item_file_req *r = new item_file_req;
353
r->len = len < 1 ? 1: len;
355
r->eventId = id.toInt();
356
r->id = genUniqueId();
359
QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
363
int EDBFlatFile::find(const QString &str, const Jid &j, const QString &id, int direction)
365
item_file_req *r = new item_file_req;
371
r->eventId = id.toInt();
372
r->id = genUniqueId();
375
QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
379
int EDBFlatFile::append(const Jid &j, PsiEvent *e)
381
item_file_req *r = new item_file_req;
384
if(e->type() == PsiEvent::Message)
385
r->event = new MessageEvent(*(MessageEvent *)e);
386
else if(e->type() == PsiEvent::Auth)
387
r->event = new AuthEvent(*(AuthEvent *)e);
388
r->id = genUniqueId();
391
QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
395
EDBFlatFile::File *EDBFlatFile::findFile(const Jid &j) const
397
QPtrListIterator<File> it(d->flist);
398
for(File *i; (i = it.current()); ++it) {
399
if(i->j.compare(j, false))
405
EDBFlatFile::File *EDBFlatFile::ensureFile(const Jid &j)
407
File *i = findFile(j);
409
i = new File(Jid(j.userHost()));
410
connect(i, SIGNAL(timeout()), SLOT(file_timeout()));
416
void EDBFlatFile::performRequests()
418
if(d->rlist.isEmpty())
421
item_file_req *r = d->rlist.first();
422
d->rlist.removeRef(r);
424
File *f = ensureFile(r->j);
426
if(type >= 0 && type <= 2) {
430
direction = Backward;
443
if(direction == Forward) {
444
if(id + r->len > f->total())
445
len = f->total() - id;
450
if((id+1) - r->len < 0)
456
EDBResult *result = new EDBResult;
457
result->setAutoDelete(true);
458
for(int n = 0; n < len; ++n) {
459
PsiEvent *e = f->get(id);
461
QString prevId, nextId;
463
prevId = QString::number(id-1);
464
if(id < f->total()-1)
465
nextId = QString::number(id+1);
466
EDBItem *ei = new EDBItem(e, QString::number(id), prevId, nextId);
470
if(direction == Forward)
475
resultReady(r->id, result);
478
writeFinished(r->id, f->append(r->event));
483
EDBResult *result = new EDBResult;
484
result->setAutoDelete(true);
486
PsiEvent *e = f->get(id);
490
QString prevId, nextId;
492
prevId = QString::number(id-1);
493
if(id < f->total()-1)
494
nextId = QString::number(id+1);
496
if(e->type() == PsiEvent::Message) {
497
MessageEvent *me = (MessageEvent *)e;
498
const Message &m = me->message();
499
if(m.body().find(r->findStr, 0, false) != -1) {
500
EDBItem *ei = new EDBItem(e, QString::number(id), prevId, nextId);
506
if(r->dir == Forward)
511
resultReady(r->id, result);
517
void EDBFlatFile::file_timeout()
519
File *i = (File *)sender();
525
//----------------------------------------------------------------------------
527
//----------------------------------------------------------------------------
528
class EDBFlatFile::File::Private
533
QValueVector<int> index;
536
EDBFlatFile::File::File(const Jid &_j)
542
t = new QTimer(this);
543
connect(t, SIGNAL(timeout()), SLOT(timer_timeout()));
545
//printf("[EDB opening -- %s]\n", j.full().latin1());
546
QString s = j.userHost();
547
fname = getHistoryDir() + "/" + qstrlower(jidEncode(s)) + ".history";
549
valid = f.open(IO_ReadWrite);
551
//printf(" file: %s\n", fname.latin1());
569
int oldsize = d->index.size();
570
d->index.resize(oldsize+1);
571
d->index[oldsize] = at;
575
//printf(" file: can't open\n");
578
//printf(" messages: %d\n\n", d->index.size());
582
EDBFlatFile::File::~File()
586
//printf("[EDB closing -- %s]\n", j.full().latin1());
591
int EDBFlatFile::File::total() const
593
return d->index.size();
596
void EDBFlatFile::File::touch()
601
void EDBFlatFile::File::timer_timeout()
606
PsiEvent *EDBFlatFile::File::get(int id)
613
if(id < 0 || id > (int)d->index.size())
620
t.setEncoding(QTextStream::UnicodeUTF8);
621
QString line = t.readLine();
624
return lineToEvent(line);
627
bool EDBFlatFile::File::append(PsiEvent *e)
634
QString line = eventToLine(e);
643
t.setEncoding(QTextStream::UnicodeUTF8);
648
int oldsize = d->index.size();
649
d->index.resize(oldsize+1);
650
d->index[oldsize] = at;
655
PsiEvent *EDBFlatFile::File::lineToEvent(const QString &line)
657
// -- read the line --
658
QString sTime, sType, sOrigin, sFlags, sText, sSubj, sUrl, sUrlDesc;
660
x1 = line.find('|') + 1;
662
x2 = line.find('|', x1);
663
sTime = line.mid(x1, x2-x1);
666
x2 = line.find('|', x1);
667
sType = line.mid(x1, x2-x1);
670
x2 = line.find('|', x1);
671
sOrigin = line.mid(x1, x2-x1);
674
x2 = line.find('|', x1);
675
sFlags = line.mid(x1, x2-x1);
678
// check for extra fields
679
if(sFlags[1] != '-') {
680
int subflags = hexChar2int(sFlags[1].latin1());
684
x2 = line.find('|', x1);
685
sSubj = line.mid(x1, x2-x1);
690
x2 = line.find('|', x1);
691
sUrl = line.mid(x1, x2-x1);
693
x2 = line.find('|', x1);
694
sUrlDesc = line.mid(x1, x2-x1);
700
sText = line.mid(x1);
704
int type = sType.toInt();
705
if(type == 0 || type == 1 || type == 4 || type == 5) {
707
m.setTimeStamp(QDateTime::fromString(sTime, Qt::ISODate));
713
m.setType("headline");
717
bool originLocal = (sOrigin == "to") ? true: false;
720
m.setBody(logdecode(sText));
722
m.setBody(logdecode(QString::fromUtf8(sText)));
723
m.setSubject(logdecode(sSubj));
725
QString url = logdecode(sUrl);
727
m.urlAdd(Url(url, logdecode(sUrlDesc)));
730
MessageEvent *me = new MessageEvent(m, 0);
731
me->setOriginLocal(originLocal);
735
else if(type == 2 || type == 3 || type == 6 || type == 7 || type == 8) {
736
QString subType = "subscribe";
738
// stupid "system message" from Psi <= 0.8.6
739
// try to figure out what kind it REALLY is based on the text
740
if(sText == tr("<big>[System Message]</big><br>You are now authorized."))
741
subType = "subscribed";
742
else if(sText == tr("<big>[System Message]</big><br>Your authorization has been removed!"))
743
subType = "unsubscribed";
746
subType = "subscribe";
748
subType = "subscribed";
750
subType = "unsubscribe";
752
subType = "unsubscribed";
754
AuthEvent *ae = new AuthEvent(j, subType, 0);
755
ae->setTimeStamp(QDateTime::fromString(sTime, Qt::ISODate));
762
QString EDBFlatFile::File::eventToLine(PsiEvent *e)
765
QString sTime, sType, sOrigin, sFlags;
767
if(e->type() == PsiEvent::Message) {
768
MessageEvent *me = (MessageEvent *)e;
769
const Message &m = me->message();
770
const UrlList urls = m.urlList();
772
if(!m.subject().isEmpty())
777
sTime = m.timeStamp().toString(Qt::ISODate);
779
if(m.type() == "chat")
781
else if(m.type() == "error")
783
else if(m.type() == "headline")
786
sOrigin = e->originLocal() ? "to": "from";
790
sFlags[1] = int2hexChar(subflags);
792
// | date | type | To/from | flags | text
793
QString line = "|" + sTime + "|" + sType + "|" + sOrigin + "|" + sFlags + "|";
796
line += logencode(m.subject()) + "|";
799
const Url &url = urls.first();
800
line += logencode(url.url()) + "|";
801
line += logencode(url.desc()) + "|";
803
line += logencode(m.body());
807
else if(e->type() == PsiEvent::Auth) {
808
AuthEvent *ae = (AuthEvent *)e;
809
sTime = ae->timeStamp().toString(Qt::ISODate);
810
QString subType = ae->authType();
812
if(subType == "subscribe")
814
else if(subType == "subscribed")
816
else if(subType == "unsubscribe")
818
else if(subType == "unsubscribed")
821
sOrigin = e->originLocal() ? "to": "from";
824
// | date | type | To/from | flags | text
825
QString line = "|" + sTime + "|" + sType + "|" + sOrigin + "|" + sFlags + "|";
826
line += logencode(subType);