source: psi/trunk/src/eventdb.cpp@ 143

Last change on this file since 143 was 2, checked in by dmik, 19 years ago

Imported original Psi 0.10 sources from Affinix

File size: 16.6 KB
Line 
1/*
2 * eventdb.cpp - asynchronous I/O event database
3 * Copyright (C) 2001, 2002 Justin Karneges
4 *
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.
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 library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 *
19 */
20
21#include"eventdb.h"
22
23#define FAKEDELAY 0
24
25#include<qfile.h>
26#include<qptrlist.h>
27#include<qtimer.h>
28#include<qtextstream.h>
29#include<qvaluevector.h>
30#include"common.h"
31#include"profiles.h"
32
33
34//----------------------------------------------------------------------------
35// EDBItem
36//----------------------------------------------------------------------------
37EDBItem::EDBItem(PsiEvent *event, const QString &id, const QString &prevId, const QString &nextId)
38{
39 e = event;
40 v_id = id;
41 v_prevId = prevId;
42 v_nextId = nextId;
43}
44
45EDBItem::~EDBItem()
46{
47 delete e;
48}
49
50PsiEvent *EDBItem::event() const
51{
52 return e;
53}
54
55const QString & EDBItem::id() const
56{
57 return v_id;
58}
59
60const QString & EDBItem::nextId() const
61{
62 return v_nextId;
63}
64
65const QString & EDBItem::prevId() const
66{
67 return v_prevId;
68}
69
70
71//----------------------------------------------------------------------------
72// EDBHandle
73//----------------------------------------------------------------------------
74class EDBHandle::Private
75{
76public:
77 Private() {}
78
79 EDB *edb;
80 EDBResult *r;
81 bool busy;
82 bool writeSuccess;
83 int listeningFor;
84 int lastRequestType;
85};
86
87EDBHandle::EDBHandle(EDB *edb)
88:QObject(0)
89{
90 d = new Private;
91 d->edb = edb;
92 d->r = 0;
93 d->busy = false;
94 d->writeSuccess = false;
95 d->listeningFor = -1;
96 d->lastRequestType = Read;
97
98 d->edb->reg(this);
99}
100
101EDBHandle::~EDBHandle()
102{
103 d->edb->unreg(this);
104
105 delete d->r;
106 delete d;
107}
108
109void EDBHandle::getLatest(const Jid &j, int len)
110{
111 d->busy = true;
112 d->lastRequestType = Read;
113 d->listeningFor = d->edb->op_getLatest(j, len);
114}
115
116void EDBHandle::getOldest(const Jid &j, int len)
117{
118 d->busy = true;
119 d->lastRequestType = Read;
120 d->listeningFor = d->edb->op_getOldest(j, len);
121}
122
123void EDBHandle::get(const Jid &j, const QString &id, int direction, int len)
124{
125 d->busy = true;
126 d->lastRequestType = Read;
127 d->listeningFor = d->edb->op_get(j, id, direction, len);
128}
129
130void EDBHandle::find(const QString &str, const Jid &j, const QString &id, int direction)
131{
132 d->busy = true;
133 d->lastRequestType = Read;
134 d->listeningFor = d->edb->op_find(str, j, id, direction);
135}
136
137void EDBHandle::append(const Jid &j, PsiEvent *e)
138{
139 d->busy = true;
140 d->lastRequestType = Write;
141 d->listeningFor = d->edb->op_append(j, e);
142}
143
144bool EDBHandle::busy() const
145{
146 return d->busy;
147}
148
149const EDBResult *EDBHandle::result() const
150{
151 return d->r;
152}
153
154bool EDBHandle::writeSuccess() const
155{
156 return d->writeSuccess;
157}
158
159void EDBHandle::edb_resultReady(EDBResult *r)
160{
161 d->busy = false;
162 if(r == 0) {
163 delete d->r;
164 d->r = 0;
165 }
166 else {
167 d->r = r;
168 }
169 d->listeningFor = -1;
170 finished();
171}
172
173void EDBHandle::edb_writeFinished(bool b)
174{
175 d->busy = false;
176 d->writeSuccess = b;
177 d->listeningFor = -1;
178 finished();
179}
180
181int EDBHandle::listeningFor() const
182{
183 return d->listeningFor;
184}
185
186int EDBHandle::lastRequestType() const
187{
188 return d->lastRequestType;
189}
190
191
192//----------------------------------------------------------------------------
193// EDB
194//----------------------------------------------------------------------------
195class EDB::Private
196{
197public:
198 Private() {}
199
200 QPtrList<EDBHandle> list;
201 int reqid_base;
202};
203
204EDB::EDB()
205{
206 d = new Private;
207 d->reqid_base = 0;
208}
209
210EDB::~EDB()
211{
212 d->list.setAutoDelete(true);
213 d->list.clear();
214 delete d;
215}
216
217int EDB::genUniqueId() const
218{
219 return d->reqid_base++;
220}
221
222void EDB::reg(EDBHandle *h)
223{
224 d->list.append(h);
225}
226
227void EDB::unreg(EDBHandle *h)
228{
229 d->list.removeRef(h);
230}
231
232int EDB::op_getLatest(const Jid &j, int len)
233{
234 return getLatest(j, len);
235}
236
237int EDB::op_getOldest(const Jid &j, int len)
238{
239 return getOldest(j, len);
240}
241
242int EDB::op_get(const Jid &jid, const QString &id, int direction, int len)
243{
244 return get(jid, id, direction, len);
245}
246
247int EDB::op_find(const QString &str, const Jid &j, const QString &id, int direction)
248{
249 return find(str, j, id, direction);
250}
251
252int EDB::op_append(const Jid &j, PsiEvent *e)
253{
254 return append(j, e);
255}
256
257void EDB::resultReady(int req, EDBResult *r)
258{
259 // deliver
260 QPtrListIterator<EDBHandle> it(d->list);
261 for(EDBHandle *h; (h = it.current()); ++it) {
262 if(h->listeningFor() == req) {
263 h->edb_resultReady(r);
264 return;
265 }
266 }
267 delete r;
268}
269
270void EDB::writeFinished(int req, bool b)
271{
272 // deliver
273 QPtrListIterator<EDBHandle> it(d->list);
274 for(EDBHandle *h; (h = it.current()); ++it) {
275 if(h->listeningFor() == req) {
276 h->edb_writeFinished(b);
277 return;
278 }
279 }
280}
281
282
283//----------------------------------------------------------------------------
284// EDBFlatFile
285//----------------------------------------------------------------------------
286struct item_file_req
287{
288 Jid j;
289 int type; // 0 = latest, 1 = oldest, 2 = random, 3 = write
290 int len;
291 int dir;
292 int id;
293 int eventId;
294 QString findStr;
295 PsiEvent *event;
296
297 typedef enum Type {
298 Type_getLatest = 0,
299 Type_getOldest,
300 Type_get,
301 Type_append,
302 Type_find
303 };
304};
305
306class EDBFlatFile::Private
307{
308public:
309 Private() {}
310
311 QPtrList<File> flist;
312 QPtrList<item_file_req> rlist;
313};
314
315EDBFlatFile::EDBFlatFile()
316:EDB()
317{
318 d = new Private;
319}
320
321EDBFlatFile::~EDBFlatFile()
322{
323 d->rlist.setAutoDelete(true);
324 d->flist.setAutoDelete(true);
325 d->flist.clear();
326
327 delete d;
328}
329
330int EDBFlatFile::getLatest(const Jid &j, int len)
331{
332 item_file_req *r = new item_file_req;
333 r->j = j;
334 r->type = item_file_req::Type_getLatest;
335 r->len = len < 1 ? 1: len;
336 r->id = genUniqueId();
337 d->rlist.append(r);
338
339 QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
340 return r->id;
341}
342
343int EDBFlatFile::getOldest(const Jid &j, int len)
344{
345 item_file_req *r = new item_file_req;
346 r->j = j;
347 r->type = item_file_req::Type_getOldest;
348 r->len = len < 1 ? 1: len;
349 r->id = genUniqueId();
350 d->rlist.append(r);
351
352 QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
353 return r->id;
354}
355
356int EDBFlatFile::get(const Jid &j, const QString &id, int direction, int len)
357{
358 item_file_req *r = new item_file_req;
359 r->j = j;
360 r->type = item_file_req::Type_get;
361 r->len = len < 1 ? 1: len;
362 r->dir = direction;
363 r->eventId = id.toInt();
364 r->id = genUniqueId();
365 d->rlist.append(r);
366
367 QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
368 return r->id;
369}
370
371int EDBFlatFile::find(const QString &str, const Jid &j, const QString &id, int direction)
372{
373 item_file_req *r = new item_file_req;
374 r->j = j;
375 r->type = item_file_req::Type_find;
376 r->len = 1;
377 r->dir = direction;
378 r->findStr = str;
379 r->eventId = id.toInt();
380 r->id = genUniqueId();
381 d->rlist.append(r);
382
383 QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
384 return r->id;
385}
386
387int EDBFlatFile::append(const Jid &j, PsiEvent *e)
388{
389 item_file_req *r = new item_file_req;
390 r->j = j;
391 r->type = item_file_req::Type_append;
392 r->event = e->copy();
393 if ( !r->event ) {
394 qWarning("EDBFlatFile::append(): Attempted to append incompatible type.");
395 delete r;
396 return 0;
397 }
398 r->id = genUniqueId();
399 d->rlist.append(r);
400
401 QTimer::singleShot(FAKEDELAY, this, SLOT(performRequests()));
402 return r->id;
403}
404
405EDBFlatFile::File *EDBFlatFile::findFile(const Jid &j) const
406{
407 QPtrListIterator<File> it(d->flist);
408 for(File *i; (i = it.current()); ++it) {
409 if(i->j.compare(j, false))
410 return i;
411 }
412 return 0;
413}
414
415EDBFlatFile::File *EDBFlatFile::ensureFile(const Jid &j)
416{
417 File *i = findFile(j);
418 if(!i) {
419 i = new File(Jid(j.userHost()));
420 connect(i, SIGNAL(timeout()), SLOT(file_timeout()));
421 d->flist.append(i);
422 }
423 return i;
424}
425
426void EDBFlatFile::performRequests()
427{
428 if(d->rlist.isEmpty())
429 return;
430
431 item_file_req *r = d->rlist.first();
432 d->rlist.removeRef(r);
433
434 File *f = ensureFile(r->j);
435 int type = r->type;
436 if(type >= item_file_req::Type_getLatest && type <= item_file_req::Type_get) {
437 int id, direction;
438
439 if(type == item_file_req::Type_getLatest) {
440 direction = Backward;
441 id = f->total()-1;
442 }
443 else if(type == item_file_req::Type_getOldest) {
444 direction = Forward;
445 id = 0;
446 }
447 else if(type == item_file_req::Type_get) {
448 direction = r->dir;
449 id = r->eventId;
450 }
451 else {
452 qWarning("EDBFlatFile::performRequests(): Invalid type.");
453 return;
454 }
455
456 int len;
457 if(direction == Forward) {
458 if(id + r->len > f->total())
459 len = f->total() - id;
460 else
461 len = r->len;
462 }
463 else {
464 if((id+1) - r->len < 0)
465 len = id+1;
466 else
467 len = r->len;
468 }
469
470 EDBResult *result = new EDBResult;
471 result->setAutoDelete(true);
472 for(int n = 0; n < len; ++n) {
473 PsiEvent *e = f->get(id);
474 if(e) {
475 QString prevId, nextId;
476 if(id > 0)
477 prevId = QString::number(id-1);
478 if(id < f->total()-1)
479 nextId = QString::number(id+1);
480 EDBItem *ei = new EDBItem(e, QString::number(id), prevId, nextId);
481 result->append(ei);
482 }
483
484 if(direction == Forward)
485 ++id;
486 else
487 --id;
488 }
489 resultReady(r->id, result);
490 }
491 else if(type == item_file_req::Type_append) {
492 writeFinished(r->id, f->append(r->event));
493 delete r->event;
494 }
495 else if(type == item_file_req::Type_find) {
496 int id = r->eventId;
497 EDBResult *result = new EDBResult;
498 result->setAutoDelete(true);
499 while(1) {
500 PsiEvent *e = f->get(id);
501 if(!e)
502 break;
503
504 QString prevId, nextId;
505 if(id > 0)
506 prevId = QString::number(id-1);
507 if(id < f->total()-1)
508 nextId = QString::number(id+1);
509
510 if(e->type() == PsiEvent::Message) {
511 MessageEvent *me = (MessageEvent *)e;
512 const Message &m = me->message();
513 if(m.body().find(r->findStr, 0, false) != -1) {
514 EDBItem *ei = new EDBItem(e, QString::number(id), prevId, nextId);
515 result->append(ei);
516 break;
517 }
518 }
519
520 if(r->dir == Forward)
521 ++id;
522 else
523 --id;
524 }
525 resultReady(r->id, result);
526 }
527
528 delete r;
529}
530
531void EDBFlatFile::file_timeout()
532{
533 File *i = (File *)sender();
534 d->flist.remove(i);
535 delete i;
536}
537
538
539//----------------------------------------------------------------------------
540// EDBFlatFile::File
541//----------------------------------------------------------------------------
542class EDBFlatFile::File::Private
543{
544public:
545 Private() {}
546
547 QValueVector<int> index;
548 bool indexed;
549};
550
551EDBFlatFile::File::File(const Jid &_j)
552{
553 d = new Private;
554 d->indexed = false;
555
556 j = _j;
557 valid = false;
558 t = new QTimer(this);
559 connect(t, SIGNAL(timeout()), SLOT(timer_timeout()));
560
561 //printf("[EDB opening -- %s]\n", j.full().latin1());
562 QString s = j.userHost();
563 fname = getHistoryDir() + "/" + qstrlower(jidEncode(s)) + ".history";
564 f.setName(fname);
565 valid = f.open(IO_ReadWrite);
566
567 touch();
568}
569
570EDBFlatFile::File::~File()
571{
572 if(valid)
573 f.close();
574 //printf("[EDB closing -- %s]\n", j.full().latin1());
575
576 delete d;
577}
578
579void EDBFlatFile::File::ensureIndex()
580{
581 if ( valid && !d->indexed ) {
582 f.reset(); // go to beginning
583 d->index.clear();
584
585 //printf(" file: %s\n", fname.latin1());
586 // build index
587 while(1) {
588 int at = f.at();
589
590 // locate a newline
591 bool found = false;
592 do {
593 int c = f.getch();
594 if(c == '\n') {
595 found = true;
596 break;
597 }
598 } while(!f.atEnd());
599
600 if(!found)
601 break;
602
603 int oldsize = d->index.size();
604 d->index.resize(oldsize+1);
605 d->index[oldsize] = at;
606 }
607
608 d->indexed = true;
609 }
610 else {
611 //printf(" file: can't open\n");
612 }
613
614 //printf(" messages: %d\n\n", d->index.size());
615}
616
617int EDBFlatFile::File::total() const
618{
619 ((EDBFlatFile::File *)this)->ensureIndex();
620 return d->index.size();
621}
622
623void EDBFlatFile::File::touch()
624{
625 t->start(30000);
626}
627
628void EDBFlatFile::File::timer_timeout()
629{
630 timeout();
631}
632
633PsiEvent *EDBFlatFile::File::get(int id)
634{
635 touch();
636
637 if(!valid)
638 return 0;
639
640 ensureIndex();
641 if(id < 0 || id > (int)d->index.size())
642 return 0;
643
644 f.at(d->index[id]);
645
646 QTextStream t;
647 t.setDevice(&f);
648 t.setEncoding(QTextStream::UnicodeUTF8);
649 QString line = t.readLine();
650 t.unsetDevice();
651
652 return lineToEvent(line);
653}
654
655bool EDBFlatFile::File::append(PsiEvent *e)
656{
657 touch();
658
659 if(!valid)
660 return false;
661
662 QString line = eventToLine(e);
663 if(line.isEmpty())
664 return false;
665
666 f.at(f.size());
667 int at = f.at();
668
669 QTextStream t;
670 t.setDevice(&f);
671 t.setEncoding(QTextStream::UnicodeUTF8);
672 t << line << endl;
673 t.unsetDevice();
674 f.flush();
675
676 if ( d->indexed ) {
677 int oldsize = d->index.size();
678 d->index.resize(oldsize+1);
679 d->index[oldsize] = at;
680 }
681
682 return true;
683}
684
685PsiEvent *EDBFlatFile::File::lineToEvent(const QString &line)
686{
687 // -- read the line --
688 QString sTime, sType, sOrigin, sFlags, sText, sSubj, sUrl, sUrlDesc;
689 int x1, x2;
690 x1 = line.find('|') + 1;
691
692 x2 = line.find('|', x1);
693 sTime = line.mid(x1, x2-x1);
694 x1 = x2 + 1;
695
696 x2 = line.find('|', x1);
697 sType = line.mid(x1, x2-x1);
698 x1 = x2 + 1;
699
700 x2 = line.find('|', x1);
701 sOrigin = line.mid(x1, x2-x1);
702 x1 = x2 + 1;
703
704 x2 = line.find('|', x1);
705 sFlags = line.mid(x1, x2-x1);
706 x1 = x2 + 1;
707
708 // check for extra fields
709 if(sFlags[1] != '-') {
710 int subflags = hexChar2int(sFlags[1].latin1());
711
712 // have subject?
713 if(subflags & 1) {
714 x2 = line.find('|', x1);
715 sSubj = line.mid(x1, x2-x1);
716 x1 = x2 + 1;
717 }
718 // have url?
719 if(subflags & 2) {
720 x2 = line.find('|', x1);
721 sUrl = line.mid(x1, x2-x1);
722 x1 = x2 + 1;
723 x2 = line.find('|', x1);
724 sUrlDesc = line.mid(x1, x2-x1);
725 x1 = x2 + 1;
726 }
727 }
728
729 // body text is last
730 sText = line.mid(x1);
731
732 // -- read end --
733
734 int type = sType.toInt();
735 if(type == 0 || type == 1 || type == 4 || type == 5) {
736 Message m;
737 m.setTimeStamp(QDateTime::fromString(sTime, Qt::ISODate));
738 if(type == 1)
739 m.setType("chat");
740 else if(type == 4)
741 m.setType("error");
742 else if(type == 5)
743 m.setType("headline");
744 else
745 m.setType("");
746
747 bool originLocal = (sOrigin == "to") ? true: false;
748 m.setFrom(j);
749 if(sFlags[0] == 'N')
750 m.setBody(logdecode(sText));
751 else
752 m.setBody(logdecode(QString::fromUtf8(sText)));
753 m.setSubject(logdecode(sSubj));
754
755 QString url = logdecode(sUrl);
756 if(!url.isEmpty())
757 m.urlAdd(Url(url, logdecode(sUrlDesc)));
758 m.setSpooled(true);
759
760 MessageEvent *me = new MessageEvent(m, 0);
761 me->setOriginLocal(originLocal);
762
763 return me;
764 }
765 else if(type == 2 || type == 3 || type == 6 || type == 7 || type == 8) {
766 QString subType = "subscribe";
767 if(type == 2) {
768 // stupid "system message" from Psi <= 0.8.6
769 // try to figure out what kind it REALLY is based on the text
770 if(sText == tr("<big>[System Message]</big><br>You are now authorized."))
771 subType = "subscribed";
772 else if(sText == tr("<big>[System Message]</big><br>Your authorization has been removed!"))
773 subType = "unsubscribed";
774 }
775 else if(type == 3)
776 subType = "subscribe";
777 else if(type == 6)
778 subType = "subscribed";
779 else if(type == 7)
780 subType = "unsubscribe";
781 else if(type == 8)
782 subType = "unsubscribed";
783
784 AuthEvent *ae = new AuthEvent(j, subType, 0);
785 ae->setTimeStamp(QDateTime::fromString(sTime, Qt::ISODate));
786 return ae;
787 }
788 else
789 return 0;
790}
791
792QString EDBFlatFile::File::eventToLine(PsiEvent *e)
793{
794 int subflags = 0;
795 QString sTime, sType, sOrigin, sFlags;
796
797 if(e->type() == PsiEvent::Message) {
798 MessageEvent *me = (MessageEvent *)e;
799 const Message &m = me->message();
800 const UrlList urls = m.urlList();
801
802 if(!m.subject().isEmpty())
803 subflags |= 1;
804 if(!urls.isEmpty())
805 subflags |= 2;
806
807 sTime = m.timeStamp().toString(Qt::ISODate);
808 int n = 0;
809 if(m.type() == "chat")
810 n = 1;
811 else if(m.type() == "error")
812 n = 4;
813 else if(m.type() == "headline")
814 n = 5;
815 sType.setNum(n);
816 sOrigin = e->originLocal() ? "to": "from";
817 sFlags = "N---";
818
819 if(subflags != 0)
820 sFlags[1] = int2hexChar(subflags);
821
822 // | date | type | To/from | flags | text
823 QString line = "|" + sTime + "|" + sType + "|" + sOrigin + "|" + sFlags + "|";
824
825 if(subflags & 1) {
826 line += logencode(m.subject()) + "|";
827 }
828 if(subflags & 2) {
829 const Url &url = urls.first();
830 line += logencode(url.url()) + "|";
831 line += logencode(url.desc()) + "|";
832 }
833 line += logencode(m.body());
834
835 return line;
836 }
837 else if(e->type() == PsiEvent::Auth) {
838 AuthEvent *ae = (AuthEvent *)e;
839 sTime = ae->timeStamp().toString(Qt::ISODate);
840 QString subType = ae->authType();
841 int n = 0;
842 if(subType == "subscribe")
843 n = 3;
844 else if(subType == "subscribed")
845 n = 6;
846 else if(subType == "unsubscribe")
847 n = 7;
848 else if(subType == "unsubscribed")
849 n = 8;
850 sType.setNum(n);
851 sOrigin = e->originLocal() ? "to": "from";
852 sFlags = "N---";
853
854 // | date | type | To/from | flags | text
855 QString line = "|" + sTime + "|" + sType + "|" + sOrigin + "|" + sFlags + "|";
856 line += logencode(subType);
857
858 return line;
859 }
860 else
861 return "";
862}
Note: See TracBrowser for help on using the repository browser.