/* * psievent.h - events * Copyright (C) 2001, 2002 Justin Karneges * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include "psievent.h" #include #include "psicon.h" #include "psiaccount.h" #include "xmpp_xmlcommon.h" #include "filetransfer.h" using namespace XMLHelper; static PsiEvent *copyPsiEvent(PsiEvent *fe) { PsiEvent *e = 0; if ( fe->inherits("MessageEvent") ) e = new MessageEvent( *((MessageEvent *)fe) ); else if ( fe->inherits("AuthEvent") ) e = new AuthEvent( *((AuthEvent *)fe) ); else if ( fe->inherits("PGPEvent") ) e = new PGPEvent( *((PGPEvent *)fe) ); else qWarning("copyPsiEvent(): Failed: unknown event type: %s", fe->className()); return 0; } //---------------------------------------------------------------------------- // DummyStream //---------------------------------------------------------------------------- class DummyStream : public Stream { public: QDomDocument & doc() const { return v_doc; } QString baseNS() const { return QString::null; } bool old() const { return false; } void close() { } bool stanzaAvailable() const { return false; } Stanza read() { return Stanza(); } void write(const Stanza &) { } int errorCondition() const { return 0; } QString errorText() const { return QString::null; } QDomElement errorAppSpec() const { return v_doc.documentElement(); } private: static QDomDocument v_doc; }; QDomDocument DummyStream::v_doc; //---------------------------------------------------------------------------- // PsiEvent //---------------------------------------------------------------------------- PsiEvent::PsiEvent(PsiAccount *acc) { v_originLocal = false; v_late = false; v_account = acc; } PsiEvent::PsiEvent(const PsiEvent &from) : QObject() { v_originLocal = from.v_originLocal; v_late = from.v_late; v_ts = from.v_ts; v_jid = from.v_jid; v_account = from.v_account; } PsiEvent::~PsiEvent() { } XMPP::Jid PsiEvent::jid() const { return v_jid; } void PsiEvent::setJid(const XMPP::Jid &j) { v_jid = j; } PsiAccount *PsiEvent::account() const { return v_account; } bool PsiEvent::originLocal() const { return v_originLocal; } bool PsiEvent::late() const { return v_late; } QDateTime PsiEvent::timeStamp() const { return v_ts; } void PsiEvent::setOriginLocal(bool b) { v_originLocal = b; } void PsiEvent::setLate(bool b) { v_late = b; } void PsiEvent::setTimeStamp(const QDateTime &t) { v_ts = t; } QDomElement PsiEvent::toXml(QDomDocument *doc) const { QDomElement e = doc->createElement("event"); e.setAttribute("type", className()); e.appendChild( textTag(*doc, "originLocal", v_originLocal) ); e.appendChild( textTag(*doc, "late", v_late) ); e.appendChild( textTag(*doc, "ts", v_ts.toString( ISODate )) ); if ( !v_jid.full().isEmpty() ) e.appendChild( textTag(*doc, "jid", v_jid.full()) ); if ( v_account ) e.appendChild( textTag(*doc, "account", v_account->name()) ); return e; } bool PsiEvent::fromXml(PsiCon *psi, PsiAccount *account, const QDomElement *e) { if ( e->tagName() != "event" ) return false; if ( e->attribute("type") != className() ) return false; readBoolEntry(*e, "originLocal", &v_originLocal); readBoolEntry(*e, "late", &v_late); v_ts = QDateTime::fromString(subTagText(*e, "ts"), ISODate); v_jid = Jid( subTagText(*e, "jid") ); if ( account ) { v_account = account; } else if ( hasSubTag(*e, "account") ) { PsiAccountList list = psi->accountList(); PsiAccountListIt it(list); QString accName = subTagText(*e, "account"); for ( ; it.current(); ++it) { if ( it.current()->name() == accName ) { v_account = it.current(); break; } } } return true; } int PsiEvent::priority() const { return Options::EventPriorityDontCare; } PsiEvent *PsiEvent::copy() const { return 0; } //---------------------------------------------------------------------------- // MessageEvent //---------------------------------------------------------------------------- MessageEvent::MessageEvent(PsiAccount *acc) : PsiEvent(acc) { v_sentToChatWindow = false; } MessageEvent::MessageEvent(const XMPP::Message &m, PsiAccount *acc) : PsiEvent(acc) { v_sentToChatWindow = false; setMessage(m); } MessageEvent::MessageEvent(const MessageEvent &from) : PsiEvent(from), v_m(from.v_m), v_sentToChatWindow(from.v_sentToChatWindow) { } MessageEvent::~MessageEvent() { } int MessageEvent::type() const { return Message; } Jid MessageEvent::from() const { return v_m.from(); } void MessageEvent::setFrom(const Jid &j) { v_m.setFrom(j); } bool MessageEvent::sentToChatWindow() const { return v_sentToChatWindow; } const XMPP::Message & MessageEvent::message() const { return v_m; } void MessageEvent::setSentToChatWindow(bool b) { v_sentToChatWindow = b; } void MessageEvent::setMessage(const XMPP::Message &m) { v_m = m; setTimeStamp ( v_m.timeStamp() ); setLate ( v_m.spooled() ); } QDomElement MessageEvent::toXml(QDomDocument *doc) const { QDomElement e = PsiEvent::toXml(doc); DummyStream stream; Stanza s = v_m.toStanza(&stream); e.appendChild( s.element() ); return e; } bool MessageEvent::fromXml(PsiCon *psi, PsiAccount *account, const QDomElement *e) { if ( !PsiEvent::fromXml(psi, account, e) ) return false; bool found = false; QDomElement msg = findSubTag(*e, "message", &found); if ( found ) { DummyStream stream; Stanza s = stream.createStanza(msg); v_m.fromStanza(s, 0); // FIXME: fix tzoffset? return true; } return false; } int MessageEvent::priority() const { if ( v_m.type() == "headline" ) return option.eventPriorityHeadline; else if ( v_m.type() == "chat" ) return option.eventPriorityChat; return option.eventPriorityMessage; } PsiEvent *MessageEvent::copy() const { return new MessageEvent( *this ); } //---------------------------------------------------------------------------- // AuthEvent //---------------------------------------------------------------------------- AuthEvent::AuthEvent(const Jid &j, const QString &authType, PsiAccount *acc) : PsiEvent(acc) { v_from = j; v_at = authType; } AuthEvent::AuthEvent(const AuthEvent &from) : PsiEvent(from), v_from(from.v_from), v_at(from.v_at) { } AuthEvent::~AuthEvent() { } int AuthEvent::type() const { return Auth; } Jid AuthEvent::from() const { return v_from; } void AuthEvent::setFrom(const Jid &j) { v_from = j; } QString AuthEvent::authType() const { return v_at; } QDomElement AuthEvent::toXml(QDomDocument *doc) const { QDomElement e = PsiEvent::toXml(doc); e.appendChild( textTag(*doc, "from", v_from.full()) ); e.appendChild( textTag(*doc, "authType", v_at) ); return e; } bool AuthEvent::fromXml(PsiCon *psi, PsiAccount *account, const QDomElement *e) { if ( !PsiEvent::fromXml(psi, account, e) ) return false; v_from = Jid( subTagText(*e, "from") ); v_at = subTagText(*e, "authType"); return false; } int AuthEvent::priority() const { return option.eventPriorityAuth; } PsiEvent *AuthEvent::copy() const { return new AuthEvent( *this ); } //---------------------------------------------------------------------------- // FileEvent //---------------------------------------------------------------------------- FileEvent::FileEvent(const Jid &j, FileTransfer *_ft, PsiAccount *acc) :PsiEvent(acc) { v_from = j; ft = _ft; } FileEvent::~FileEvent() { delete ft; } int FileEvent::priority() const { return option.eventPriorityFile; } Jid FileEvent::from() const { return v_from; } void FileEvent::setFrom(const Jid &j) { v_from = j; } FileTransfer *FileEvent::takeFileTransfer() { FileTransfer *_ft = ft; ft = 0; return _ft; } //---------------------------------------------------------------------------- // EventQueue //---------------------------------------------------------------------------- class EventItem { public: EventItem(PsiEvent *_e, int i) { e = _e; v_id = i; } EventItem(const EventItem &from) { e = copyPsiEvent(from.e); v_id = from.v_id; } ~EventItem() { } int id() const { return v_id; } PsiEvent *event() const { return e; } private: PsiEvent *e; int v_id; }; class EventQueue::Private { public: Private() { list.setAutoDelete(true); } QPtrList list; PsiCon *psi; PsiAccount *account; }; EventQueue::EventQueue(PsiAccount *account) { d = new Private(); d->account = account; d->psi = account->psi(); } EventQueue::EventQueue(const EventQueue &from) : QObject() { d = new Private(); *this = from; } EventQueue::~EventQueue() { delete d; } int EventQueue::nextId() const { QPtrListIterator it(d->list); EventItem *i = it.current(); if(!i) return -1; return i->id(); } int EventQueue::count() const { return d->list.count(); } int EventQueue::count(const Jid &j, bool compareRes) const { int total = 0; QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++it) { Jid j2(i->event()->jid()); if(j.compare(j2, compareRes)) ++total; } return total; } void EventQueue::enqueue(PsiEvent *e) { EventItem *i = new EventItem(e, d->psi->getId()); int prior = e->priority(); bool found = false; // skip all with higher or equal priority for ( EventItem *ei = d->list.first(); ei; ei = d->list.next() ) { if ( ei && ei->event()->priority() < prior ) { d->list.insert(d->list.find(ei), i); found = true; break; } } // everything else if ( !found ) d->list.append(i); emit queueChanged(); } void EventQueue::dequeue(PsiEvent *e) { if ( !e ) return; QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++it) { if ( e == i->event() ) { d->list.remove(i); return; } } emit queueChanged(); } PsiEvent *EventQueue::dequeue(const Jid &j, bool compareRes) { QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++it) { PsiEvent *e = i->event(); Jid j2(e->jid()); if(j.compare(j2, compareRes)) { d->list.removeRef(i); emit queueChanged(); return e; } } return 0; } PsiEvent *EventQueue::peek(const Jid &j, bool compareRes) const { QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++it) { PsiEvent *e = i->event(); Jid j2(e->jid()); if(j.compare(j2, compareRes)) { return e; } } return 0; } PsiEvent *EventQueue::dequeueNext() { QPtrListIterator it(d->list); EventItem *i = it.current(); if(!i) return 0; PsiEvent *e = i->event(); d->list.remove(it); emit queueChanged(); return e; } PsiEvent *EventQueue::peekNext() const { QPtrListIterator it(d->list); EventItem *i = it.current(); if(!i) return 0; return i->event(); } PsiEvent *EventQueue::peekFirstChat(const Jid &j, bool compareRes) const { QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++it) { PsiEvent *e = i->event(); if(e->type() == PsiEvent::Message) { MessageEvent *me = (MessageEvent *)e; if(j.compare(me->from(), compareRes) && me->message().type() == "chat") return e; } } return 0; } bool EventQueue::hasChats(const Jid &j, bool compareRes) const { return (peekFirstChat(j, compareRes) ? true: false); } // this function extracts all chats from the queue, and returns a list of queue positions void EventQueue::extractChats(QPtrList *el, const Jid &j, bool compareRes) { bool changed = false; QPtrListIterator it(d->list); for(EventItem *i; (i = it.current());) { PsiEvent *e = i->event(); if(e->type() == PsiEvent::Message) { MessageEvent *me = (MessageEvent *)e; if(j.compare(me->from(), compareRes) && me->message().type() == "chat") { el->append(me); d->list.remove(it); changed = true; continue; } } ++it; } if ( changed ) emit queueChanged(); } void EventQueue::printContent() const { QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++it) { PsiEvent *e = i->event(); printf(" %d: (%d) from=[%s] jid=[%s]\n", i->id(), e->type(), e->from().full().latin1(), e->jid().full().latin1()); } } void EventQueue::clear() { d->list.clear(); emit queueChanged(); } // this function removes all events associated with the input jid void EventQueue::clear(const Jid &j, bool compareRes) { bool changed = false; QPtrListIterator it(d->list); for(EventItem *i; (i = it.current());) { PsiEvent *e = i->event(); Jid j2(e->jid()); if(j.compare(j2, compareRes)) { d->list.removeRef(i); changed = true; } else ++it; } if ( changed ) emit queueChanged(); } EventQueue &EventQueue::operator= (const EventQueue &from) { d->list.clear(); d->psi = from.d->psi; QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++i) { PsiEvent *e = i->event(); enqueue( copyPsiEvent(e) ); } return *this; } QDomElement EventQueue::toXml(QDomDocument *doc) const { QDomElement e = doc->createElement("eventQueue"); e.setAttribute("version", "1.0"); e.appendChild(textTag(doc, "progver", PROG_VERSION)); QPtrListIterator it(d->list); for(EventItem *i; (i = it.current()); ++it) { QDomElement event = (*it)->event()->toXml(doc); e.appendChild( event ); } return e; } bool EventQueue::fromXml(const QDomElement *q) { if ( !q ) return false; if ( q->tagName() != "eventQueue" ) return false; if ( q->attribute("version") != "1.0" ) return false; QString progver = subTagText(*q, "progver"); for(QDomNode n = q->firstChild(); !n.isNull(); n = n.nextSibling()) { QDomElement e = n.toElement(); if( e.isNull() ) continue; if ( e.tagName() != "event" ) continue; PsiEvent *event = 0; QString eventType = e.attribute("type"); if ( eventType == "MessageEvent" ) { event = new MessageEvent(0); if ( !event->fromXml(d->psi, d->account, &e) ) { delete event; event = 0; } } else if ( eventType == "AuthEvent" ) { event = new AuthEvent("", "", 0); if ( !event->fromXml(d->psi, d->account, &e) ) { delete event; event = 0; } } if ( event ) emit handleEvent( event ); } return true; } bool EventQueue::toFile(const QString &fname) { QDomDocument doc; QDomElement element = toXml(&doc); doc.appendChild(element); QFile f( fname ); if( !f.open(IO_WriteOnly) ) return FALSE; QTextStream t; t.setDevice( &f ); t.setEncoding( QTextStream::UnicodeUTF8 ); t << doc.toString(4); t.unsetDevice(); f.close(); return TRUE; } bool EventQueue::fromFile(const QString &fname) { QString confver; QDomDocument doc; QFile f(fname); if(!f.open(IO_ReadOnly)) return FALSE; if(!doc.setContent(&f, true)) return FALSE; f.close(); QDomElement base = doc.documentElement(); return fromXml(&base); }