2
* psiaccount.cpp - handles a Psi account
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
21
#include"psiaccount.h"
23
#include<qinputdialog.h>
27
#include<qmessagebox.h>
28
#include<qguardedptr.h>
29
#include<qapplication.h>
30
#include<qpushbutton.h>
32
#include<qobjectlist.h>
40
//#include"xmpp_client.h"
41
//#include"xmpp_stream.h"
42
//#include"xmpp_message.h"
43
#include"xmpp_tasks.h"
44
#include"xmpp_jidlink.h"
46
#include"filetransfer.h"
47
#include"accountdlg.h"
48
#include"changepwdlg.h"
49
#include"xmlconsole.h"
53
#include"groupchatdlg.h"
56
#include"adduserdlg.h"
57
#include"historydlg.h"
58
#include"servicesdlg.h"
62
#include"passphrasedlg.h"
63
#include"vcardfactory.h"
65
#include"sslcertdlg.h"
68
#include"fancylabel.h"
69
#include"iconwidget.h"
71
#include"filetransdlg.h"
77
typedef int socklen_t;
79
#include<sys/socket.h>
80
#include<netinet/in.h>
83
//----------------------------------------------------------------------------
85
//----------------------------------------------------------------------------
86
AccountLabel::AccountLabel(PsiAccount *_pa, QWidget *par, bool smode)
91
setFrameStyle( QFrame::Panel | QFrame::Sunken );
94
connect(pa, SIGNAL(updatedAccount()), this, SLOT(updateName()));
95
connect(pa, SIGNAL(destroyed()), this, SLOT(deleteMe()));
98
AccountLabel::~AccountLabel()
102
void AccountLabel::updateName()
104
setText(simpleMode ? pa->name() : pa->nameWithJid());
107
void AccountLabel::deleteMe()
113
//----------------------------------------------------------------------------
115
//----------------------------------------------------------------------------
116
static int transid = 0;
117
class PGPTransaction::Private
128
PGPTransaction::PGPTransaction(OpenPGP::Engine *pgp)
129
:OpenPGP::Request(pgp)
135
PGPTransaction::~PGPTransaction()
140
int PGPTransaction::id() const
145
void PGPTransaction::setMessage(const Message &m)
150
const Message & PGPTransaction::message() const
155
const QDomElement & PGPTransaction::xml() const
160
void PGPTransaction::setXml(const QDomElement &e)
165
Jid PGPTransaction::jid() const
170
void PGPTransaction::setJid(const Jid &j)
175
//----------------------------------------------------------------------------
176
// BlockTransportPopup -- blocks popups on transport status changes
177
//----------------------------------------------------------------------------
179
class BlockTransportPopupList;
181
class BlockTransportPopup : public QObject
185
BlockTransportPopup(QObject *, const Jid &);
195
friend class BlockTransportPopupList;
198
BlockTransportPopup::BlockTransportPopup(QObject *parent, const Jid &_j)
205
if ( j.host().left(3) == "icq" ) {
206
new BlockTransportPopup(parent, "sms." + j.host()); // sms.icq.host.com
207
new BlockTransportPopup(parent, "sms" + j.host().right(j.host().length() - 3)); // sms.host.com
210
QTimer::singleShot(15000, this, SLOT(timeout()));
213
void BlockTransportPopup::timeout()
215
if ( userCounter > 1 ) {
216
QTimer::singleShot(15000, this, SLOT(timeout()));
223
Jid BlockTransportPopup::jid() const
228
//----------------------------------------------------------------------------
229
// BlockTransportPopupList
230
//----------------------------------------------------------------------------
232
class BlockTransportPopupList : public QObject
236
BlockTransportPopupList();
238
bool find(const Jid &, bool online = false);
241
BlockTransportPopupList::BlockTransportPopupList()
246
bool BlockTransportPopupList::find(const Jid &j, bool online)
248
if ( j.user().isEmpty() ) // always show popups for transports
251
QObjectList *list = queryList("BlockTransportPopup");
252
QObjectListIt it( *list );
254
BlockTransportPopup *btp;
255
for ( ; it.current(); ++it) {
256
btp = (BlockTransportPopup *)it.current();
257
if ( j.host() == btp->jid().host() ) {
268
//----------------------------------------------------------------------------
270
//----------------------------------------------------------------------------
278
class PsiAccount::Private : public QObject
287
UserAccount acc, accnext;
290
QPtrList<item_dialog2> dialogList;
291
EventQueue *eventQueue;
292
XmlConsole *xmlConsole;
296
Status lastStatus, origStatus;
298
QString cur_pgpSecretKeyID;
299
QPtrList<Message> messageQueue;
300
BlockTransportPopupList *blockTransportPopupList;
304
//AvatarFactory* avatarFactory;
307
PassphraseDlg *ppdlg;
308
QGuardedPtr<OpenPGP::Request> ppreq;
311
QPtrList<GCContact> gcbank;
312
QStringList groupchats;
314
AdvancedConnector *conn;
315
ClientStream *stream;
317
QCATLSHandler *tlsHandler;
318
QPtrList<QCA::Cert> certList;
322
QHostAddress localAddress;
324
QString pathToProfileEvents()
326
return pathToProfile(activeProfile) + "/events-" + acc.name + ".xml";
332
eventQueue->toFile(pathToProfileEvents());
337
bool soundEnabled = useSound;
338
useSound = FALSE; // disable the sound and popups
341
QFileInfo fi( pathToProfileEvents() );
343
eventQueue->fromFile(pathToProfileEvents());
345
useSound = soundEnabled;
350
PsiAccount::PsiAccount(const UserAccount &acc, PsiCon *parent)
359
//d->avatarFactory = new AvatarFactory(this);
361
d->blockTransportPopupList = new BlockTransportPopupList();
365
isDisconnecting = false;
366
notifyOnlineOk = false;
368
usingAutoStatus = false;
369
presenceSent = false;
371
d->loginStatus = Status("", "");
373
d->lastStatus = Status("", "", 0, false);
375
d->dialogList.setAutoDelete(true);
376
d->eventQueue = new EventQueue(d->psi);
377
connect(d->eventQueue, SIGNAL(queueChanged()), SIGNAL(queueChanged()));
378
connect(d->eventQueue, SIGNAL(queueChanged()), d, SLOT(queueChanged()));
379
connect(d->eventQueue, SIGNAL(handleEvent(PsiEvent *)), SLOT(handleEvent(PsiEvent *)));
380
d->userList.setAutoDelete(true);
381
d->self = UserListItem(true);
382
d->self.setSubscription(Subscription::Both);
383
d->nickFromVCard = false;
386
//d->self.setAvatarFactory(d->avatarFactory);
388
d->messageQueue.setAutoDelete(true);
392
d->gcbank.setAutoDelete(true);
394
// we need to copy groupState, because later initialization will depend on that
395
d->acc.groupState = acc.groupState;
402
d->certList.setAutoDelete(true);
405
// create Jabber::Client
406
d->client = new Client;
407
d->client->setOSName(getOSName());
408
d->client->setTimeZone(getTZString(), getTZOffset());
409
d->client->setClientName(PROG_NAME);
410
d->client->setClientVersion(PROG_VERSION);
412
d->client->setFileTransferEnabled(true);
414
//connect(d->client, SIGNAL(connected()), SLOT(client_connected()));
415
//connect(d->client, SIGNAL(handshaken()), SLOT(client_handshaken()));
416
//connect(d->client, SIGNAL(error(const StreamError &)), SLOT(client_error(const StreamError &)));
417
//connect(d->client, SIGNAL(sslCertReady(const QSSLCert &)), SLOT(client_sslCertReady(const QSSLCert &)));
418
//connect(d->client, SIGNAL(closeFinished()), SLOT(client_closeFinished()));
419
//connect(d->client, SIGNAL(authFinished(bool, int, const QString &)), SLOT(client_authFinished(bool, int, const QString &)));
420
connect(d->client, SIGNAL(rosterRequestFinished(bool, int, const QString &)), SLOT(client_rosterRequestFinished(bool, int, const QString &)));
421
connect(d->client, SIGNAL(rosterItemAdded(const RosterItem &)), SLOT(client_rosterItemUpdated(const RosterItem &)));
422
connect(d->client, SIGNAL(rosterItemUpdated(const RosterItem &)), SLOT(client_rosterItemUpdated(const RosterItem &)));
423
connect(d->client, SIGNAL(rosterItemRemoved(const RosterItem &)), SLOT(client_rosterItemRemoved(const RosterItem &)));
424
connect(d->client, SIGNAL(resourceAvailable(const Jid &, const Resource &)), SLOT(client_resourceAvailable(const Jid &, const Resource &)));
425
connect(d->client, SIGNAL(resourceUnavailable(const Jid &, const Resource &)), SLOT(client_resourceUnavailable(const Jid &, const Resource &)));
426
connect(d->client, SIGNAL(presenceError(const Jid &, int, const QString &)), SLOT(client_presenceError(const Jid &, int, const QString &)));
427
connect(d->client, SIGNAL(messageReceived(const Message &)), SLOT(client_messageReceived(const Message &)));
428
connect(d->client, SIGNAL(subscription(const Jid &, const QString &)), SLOT(client_subscription(const Jid &, const QString &)));
429
connect(d->client, SIGNAL(debugText(const QString &)), SLOT(client_debugText(const QString &)));
430
connect(d->client, SIGNAL(groupChatJoined(const Jid &)), SLOT(client_groupChatJoined(const Jid &)));
431
connect(d->client, SIGNAL(groupChatLeft(const Jid &)), SLOT(client_groupChatLeft(const Jid &)));
432
connect(d->client, SIGNAL(groupChatPresence(const Jid &, const Status &)), SLOT(client_groupChatPresence(const Jid &, const Status &)));
433
connect(d->client, SIGNAL(groupChatError(const Jid &, int, const QString &)), SLOT(client_groupChatError(const Jid &, int, const QString &)));
434
connect(d->client, SIGNAL(incomingJidLink()), SLOT(client_incomingJidLink()));
435
connect(d->client->fileTransferManager(), SIGNAL(incomingReady()), SLOT(client_incomingFileTransfer()));
437
// contactprofile context
438
d->cp = new ContactProfile(this, acc.name, d->psi->contactView());
439
connect(d->cp, SIGNAL(actionDefault(const Jid &)),SLOT(actionDefault(const Jid &)));
440
connect(d->cp, SIGNAL(actionRecvEvent(const Jid &)),SLOT(actionRecvEvent(const Jid &)));
441
connect(d->cp, SIGNAL(actionSendMessage(const Jid &)),SLOT(actionSendMessage(const Jid &)));
442
connect(d->cp, SIGNAL(actionSendMessage(const JidList &)),SLOT(actionSendMessage(const JidList &)));
443
connect(d->cp, SIGNAL(actionSendUrl(const Jid &)),SLOT(actionSendUrl(const Jid &)));
444
connect(d->cp, SIGNAL(actionRemove(const Jid &)),SLOT(actionRemove(const Jid &)));
445
connect(d->cp, SIGNAL(actionRename(const Jid &, const QString &)),SLOT(actionRename(const Jid &, const QString &)));
446
connect(d->cp, SIGNAL(actionGroupRename(const QString &, const QString &)),SLOT(actionGroupRename(const QString &, const QString &)));
447
connect(d->cp, SIGNAL(actionHistory(const Jid &)),SLOT(actionHistory(const Jid &)));
448
connect(d->cp, SIGNAL(actionStatusShow(const Jid &)),SLOT(actionStatusShow(const Jid &)));
449
connect(d->cp, SIGNAL(actionOpenChat(const Jid &)),SLOT(actionOpenChat(const Jid &)));
450
connect(d->cp, SIGNAL(actionOpenChatSpecific(const Jid &)),SLOT(actionOpenChatSpecific(const Jid &)));
451
connect(d->cp, SIGNAL(actionAgentSetStatus(const Jid &, int)),SLOT(actionAgentSetStatus(const Jid &, int)));
452
connect(d->cp, SIGNAL(actionInfo(const Jid &)),SLOT(actionInfo(const Jid &)));
453
connect(d->cp, SIGNAL(actionAuth(const Jid &)),SLOT(actionAuth(const Jid &)));
454
connect(d->cp, SIGNAL(actionAuthRequest(const Jid &)),SLOT(actionAuthRequest(const Jid &)));
455
connect(d->cp, SIGNAL(actionAuthRemove(const Jid &)),SLOT(actionAuthRemove(const Jid &)));
456
connect(d->cp, SIGNAL(actionAdd(const Jid &)),SLOT(actionAdd(const Jid &)));
457
connect(d->cp, SIGNAL(actionGroupAdd(const Jid &, const QString &)),SLOT(actionGroupAdd(const Jid &, const QString &)));
458
connect(d->cp, SIGNAL(actionGroupRemove(const Jid &, const QString &)),SLOT(actionGroupRemove(const Jid &, const QString &)));
459
connect(d->cp, SIGNAL(actionTest(const Jid &)),SLOT(actionTest(const Jid &)));
460
connect(d->cp, SIGNAL(actionSendFile(const Jid &)),SLOT(actionSendFile(const Jid &)));
461
connect(d->cp, SIGNAL(actionSendFiles(const Jid &, const QStringList&)),SLOT(actionSendFiles(const Jid &, const QStringList&)));
462
connect(d->cp, SIGNAL(actionDisco(const Jid &, const QString &)),SLOT(actionDisco(const Jid &, const QString &)));
463
connect(d->cp, SIGNAL(actionInvite(const Jid &, const QString &)),SLOT(actionInvite(const Jid &, const QString &)));
464
connect(d->cp, SIGNAL(actionAssignKey(const Jid &)),SLOT(actionAssignKey(const Jid &)));
465
connect(d->cp, SIGNAL(actionUnassignKey(const Jid &)),SLOT(actionUnassignKey(const Jid &)));
467
// restore cached roster
468
for(Roster::ConstIterator it = acc.roster.begin(); it != acc.roster.end(); ++it)
469
client_rosterItemUpdated(*it);
471
// restore pgp key bindings
472
for(VarList::ConstIterator kit = acc.keybind.begin(); kit != acc.keybind.end(); ++kit) {
473
const VarListItem &i = *kit;
474
UserListItem *u = find(Jid(i.key()));
476
u->setPublicKeyID(i.data());
484
connect(d->psi, SIGNAL(emitOptionsUpdate()), SLOT(optionsUpdate()));
485
connect(d->psi, SIGNAL(pgpToggled(bool)), SLOT(pgpToggled(bool)));
486
connect(d->psi, SIGNAL(pgpKeysUpdated()), SLOT(pgpKeysUpdated()));
488
d->psi->setToggles(d->acc.tog_offline, d->acc.tog_away, d->acc.tog_agents, d->acc.tog_hidden,d->acc.tog_self);
490
setEnabled(d->acc.opt_enabled);
493
if(d->acc.opt_auto && d->acc.opt_enabled)
494
setStatus(Status("", "", d->acc.priority));
496
//printf("PsiAccount: [%s] loaded\n", name().latin1());
497
d->xmlConsole = new XmlConsole(this);
498
if(option.xmlConsoleOnLogin && d->acc.opt_enabled) {
499
this->showXmlConsole();
500
d->xmlConsole->enable();
503
// load event queue from disk
504
QTimer::singleShot(0, d, SLOT(loadQueue()));
507
PsiAccount::~PsiAccount()
510
QString str = name();
512
d->messageQueue.clear();
514
// nuke all related dialogs
517
d->psi->ftdlg()->killTransfers(this);
522
delete d->eventQueue;
524
delete d->blockTransportPopupList;
526
d->psi->unlink(this);
529
//printf("PsiAccount: [%s] unloaded\n", str.latin1());
532
void PsiAccount::cleanupStream()
547
d->localAddress = QHostAddress();
550
bool PsiAccount::enabled() const
552
return d->acc.opt_enabled;
555
void PsiAccount::setEnabled(bool e)
557
d->acc.opt_enabled = e;
558
d->psi->enableAccount(this, e);
559
d->psi->accountCountChanged();
560
d->cp->setEnabled(e);
565
bool PsiAccount::isActive() const
570
bool PsiAccount::isConnected() const
572
return (d->stream && d->stream->isAuthenticated());
575
const QString & PsiAccount::name() const
580
const UserAccount & PsiAccount::userAccount() const
582
d->psi->getToggles(&d->acc.tog_offline, &d->acc.tog_away, &d->acc.tog_agents, &d->acc.tog_hidden,&d->acc.tog_self);
584
// save the roster and pgp key bindings
585
d->acc.roster.clear();
586
d->acc.keybind.clear();
587
UserListIt it(d->userList);
588
for(UserListItem *u; (u = it.current()); ++it) {
592
if(!u->publicKeyID().isEmpty())
593
d->acc.keybind.set(u->jid().full(), u->publicKeyID());
599
UserList *PsiAccount::userList() const
604
Client *PsiAccount::client() const
609
ContactProfile *PsiAccount::contactProfile() const
614
EventQueue *PsiAccount::eventQueue() const
616
return d->eventQueue;
619
EDB *PsiAccount::edb() const
621
return d->psi->edb();
624
PsiCon *PsiAccount::psi() const
630
//AvatarFactory *PsiAccount::avatarFactory() const
632
// return d->avatarFactory;
635
QString PsiAccount::pgpKey() const
637
return d->cur_pgpSecretKeyID;
640
QHostAddress *PsiAccount::localAddress() const
642
QString s = d->localAddress.toString();
645
return &d->localAddress;
648
void PsiAccount::setUserAccount(const UserAccount &acc)
650
bool renamed = false;
652
if(d->acc.name != acc.name) {
654
oldfname = d->pathToProfileEvents();
659
// rename queue file?
661
QFileInfo oldfi(oldfname);
662
QFileInfo newfi(d->pathToProfileEvents());
664
QDir dir = oldfi.dir();
665
dir.rename(oldfi.fileName(), newfi.fileName());
670
if(d->acc.opt_keepAlive)
671
d->stream->setNoopTime(55000); // prevent NAT timeouts every minute
673
d->stream->setNoopTime(0);
676
d->cp->setName(d->acc.name);
683
d->cp->updateEntry(d->self);
685
if(!d->nickFromVCard)
688
d->self.setPublicKeyID(d->acc.pgpSecretKeyID);
690
if(d->acc.pgpSecretKeyID != d->cur_pgpSecretKeyID && loggedIn()) {
691
d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
692
d->loginStatus.setXSigned("");
693
if(d->acc.opt_passphrase)
694
d->passphrase = d->acc.pgpPassphrase;
697
setStatusDirect(d->loginStatus);
706
void PsiAccount::deleteQueueFile()
708
QFileInfo fi(d->pathToProfileEvents());
711
dir.remove(fi.fileName());
715
const Jid & PsiAccount::jid() const
720
QString PsiAccount::nameWithJid() const
722
return (name() + " (" + jid().full() + ')');
725
static QCA::Cert readCertXml(const QDomElement &e)
728
// there should be one child data tag
729
QDomElement data = e.elementsByTagName("data").item(0).toElement();
731
cert.fromDER(Base64::stringToArray(data.text()));
735
static QPtrList<QCA::Cert> getRootCerts(const QStringList &stores)
737
QPtrList<QCA::Cert> list;
739
for(QStringList::ConstIterator dit = stores.begin(); dit != stores.end(); ++dit) {
743
dir.setNameFilter("*.xml");
744
QStringList entries = dir.entryList();
745
for(QStringList::ConstIterator it = entries.begin(); it != entries.end(); ++it) {
746
QFile f(dir.filePath(*it));
747
if(!f.open(IO_ReadOnly))
750
bool ok = doc.setContent(&f);
755
QDomElement base = doc.documentElement();
756
if(base.tagName() != "store")
758
QDomNodeList cl = base.elementsByTagName("certificate");
761
for(int n = 0; n < (int)cl.count(); ++n) {
762
QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
777
// logs on with the active account settings
778
void PsiAccount::login()
780
if(isActive() && !doReconnect)
783
if(d->acc.opt_ssl && !QCA::isSupported(QCA::CAP_TLS)) {
784
QMessageBox::information(0, tr("%1: SSL Error").arg(name()), tr("Cannot login: SSL is enabled but no SSL/TLS (plugin) support is available."));
790
d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
795
isDisconnecting = false;
796
notifyOnlineOk = false;
798
presenceSent = false;
804
if(d->acc.opt_host) {
809
host = d->jid.host();
816
AdvancedConnector::Proxy p;
817
if(d->acc.proxy_index > 0) {
818
const ProxyItem &pi = d->psi->proxy()->getItem(d->acc.proxy_index-1);
819
if(pi.type == "http") // HTTP Connect
820
p.setHttpConnect(pi.settings.host, pi.settings.port);
821
else if(pi.type == "socks") // SOCKS
822
p.setSocks(pi.settings.host, pi.settings.port);
823
else if(pi.type == "poll") { // HTTP Poll
824
QUrl u = pi.settings.url;
825
if(u.query().isEmpty()) {
826
QString v = host + ':' + QString::number(port);
828
u.setQuery(QString("server=") + v);
830
p.setHttpPoll(pi.settings.host, pi.settings.port, u.toString());
831
p.setPollInterval(2);
834
if(pi.settings.useAuth)
835
p.setUserPass(pi.settings.user, pi.settings.pass);
839
d->conn = new AdvancedConnector;
841
QStringList certDirs;
842
certDirs += g.pathHome + "/certs";
843
certDirs += g.pathBase + "/certs";
844
d->certList = getRootCerts(certDirs);
846
d->tls = new QCA::TLS;
847
d->tls->setCertificateStore(d->certList);
848
d->tlsHandler = new QCATLSHandler(d->tls);
849
connect(d->tlsHandler, SIGNAL(tlsHandshaken()), SLOT(tls_handshaken()));
851
d->conn->setProxy(p);
852
d->conn->setOptHostPort(host, port);
853
d->conn->setOptSSL(d->acc.opt_ssl);
855
d->stream = new ClientStream(d->conn, d->tlsHandler);
856
d->stream->setOldOnly(true);
857
d->stream->setAllowPlain(d->acc.opt_plain);
858
if(d->acc.opt_keepAlive)
859
d->stream->setNoopTime(55000); // prevent NAT timeouts every minute
861
d->stream->setNoopTime(0);
862
connect(d->stream, SIGNAL(connected()), SLOT(cs_connected()));
863
connect(d->stream, SIGNAL(securityLayerActivated(int)), SLOT(cs_securityLayerActivated()));
864
connect(d->stream, SIGNAL(needAuthParams(bool, bool, bool)), SLOT(cs_needAuthParams(bool, bool, bool)));
865
connect(d->stream, SIGNAL(authenticated()), SLOT(cs_authenticated()));
866
connect(d->stream, SIGNAL(connectionClosed()), SLOT(cs_connectionClosed()));
867
connect(d->stream, SIGNAL(delayedCloseFinished()), SLOT(cs_delayedCloseFinished()));
868
connect(d->stream, SIGNAL(warning(int)), SLOT(cs_warning(int)));
869
connect(d->stream, SIGNAL(error(int)), SLOT(cs_error(int)));
871
Jid j = d->jid.withResource(d->acc.resource);
872
d->client->connectToServer(d->stream, j);
875
// disconnect or stop reconnecting
876
void PsiAccount::logout(bool fast, const Status &s)
885
// send logout status
886
d->client->setPresence(s);
889
isDisconnecting = true;
892
simulateRosterOffline();
896
QTimer::singleShot(0, this, SLOT(disconnect()));
899
// skz note: I had to split logout() because server seem to need some time to store status
900
// before stream is closed (weird, I know)
901
void PsiAccount::disconnect()
910
bool PsiAccount::loggedIn() const
912
return (v_isActive && presenceSent);
915
void PsiAccount::tls_handshaken()
917
QCA::Cert cert = d->tls->peerCertificate();
918
int r = d->tls->certificateValidityResult();
919
if(r != QCA::TLS::Valid && !d->acc.opt_ignoreSSLWarnings) {
920
QString str = resultToString(r);
922
int n = QMessageBox::warning(0,
923
tr("%1: Server Authentication").arg(name()),
924
tr("The %1 certificate failed the authenticity test.").arg(d->jid.host()) + '\n' + tr("Reason: %1").arg(str),
927
tr("&Cancel"), 0, 2);
929
SSLCertDlg::showCert(cert, r);
932
d->tlsHandler->continueAfterHandshake();
942
d->tlsHandler->continueAfterHandshake();
945
void PsiAccount::cs_connected()
948
ByteStream *bs = d->conn->stream();
949
if(bs->inherits("BSocket") || bs->inherits("XMPP::BSocket")) {
950
//#if QT_VERSION >= 0x030300
951
d->localAddress = ((BSocket *)bs)->address();
953
int s = ((BSocket *)bs)->socket();
954
struct sockaddr addr;
955
socklen_t size = sizeof(struct sockaddr);
956
if(!getsockname(s, &addr, &size)) {
958
struct sockaddr_in *in = (struct sockaddr_in *)&addr;
959
memcpy(&ipv4addr, &in->sin_addr.s_addr, 4);
960
ipv4addr = ntohl(ipv4addr);
961
d->localAddress = QHostAddress(ipv4addr);
967
void PsiAccount::cs_securityLayerActivated()
973
void PsiAccount::cs_needAuthParams(bool, bool pass, bool)
976
d->stream->setPassword(d->acc.pass);
977
d->stream->continueAfterParams();
980
void PsiAccount::cs_authenticated()
982
d->conn->changePollInterval(10); // for http poll, slow down after login
984
d->client->start(d->jid.host(), d->jid.user(), d->acc.pass, d->acc.resource);
986
//printf("PsiAccount: [%s] authenticated\n", name().latin1());
988
// flag roster for delete
989
UserListIt it(d->userList);
990
for(UserListItem *u; (u = it.current()); ++it) {
992
u->setFlagForDelete(true);
996
d->client->rosterRequest();
999
void PsiAccount::cs_connectionClosed()
1004
void PsiAccount::cs_delayedCloseFinished()
1006
//printf("PsiAccount: [%s] connection closed\n", name().latin1());
1009
void PsiAccount::cs_warning(int)
1011
d->stream->continueAfterWarning();
1014
void PsiAccount::getErrorInfo(int err, AdvancedConnector *conn, Stream *stream, QCATLSHandler *tlsHandler, QString *_str, bool *_reconn)
1017
bool reconn = false;
1020
str = tr("Disconnected");
1023
else if(err == XMPP::ClientStream::ErrParse) {
1024
str = tr("XML Parsing Error");
1027
else if(err == XMPP::ClientStream::ErrProtocol) {
1028
str = tr("XMPP Protocol Error");
1031
else if(err == XMPP::ClientStream::ErrStream) {
1032
int x = stream->errorCondition();
1035
if(x == XMPP::Stream::GenericStreamError)
1036
s = tr("Generic stream error");
1037
else if(x == XMPP::ClientStream::Conflict) {
1038
s = tr("Conflict (remote login replacing this one)");
1041
else if(x == XMPP::ClientStream::ConnectionTimeout)
1042
s = tr("Timed out from inactivity");
1043
else if(x == XMPP::ClientStream::InternalServerError)
1044
s = tr("Internal server error");
1045
else if(x == XMPP::ClientStream::InvalidXml)
1046
s = tr("Invalid XML");
1047
else if(x == XMPP::ClientStream::PolicyViolation) {
1048
s = tr("Policy violation");
1051
else if(x == XMPP::ClientStream::ResourceConstraint) {
1052
s = tr("Server out of resources");
1055
else if(x == XMPP::ClientStream::SystemShutdown)
1056
s = tr("Server is shutting down");
1057
str = tr("XMPP Stream Error: %1").arg(s);
1059
else if(err == XMPP::ClientStream::ErrConnection) {
1060
int x = conn->errorCode();
1063
if(x == XMPP::AdvancedConnector::ErrConnectionRefused)
1064
s = tr("Unable to connect to server");
1065
else if(x == XMPP::AdvancedConnector::ErrHostNotFound)
1066
s = tr("Host not found");
1067
else if(x == XMPP::AdvancedConnector::ErrProxyConnect)
1068
s = tr("Error connecting to proxy");
1069
else if(x == XMPP::AdvancedConnector::ErrProxyNeg)
1070
s = tr("Error during proxy negotiation");
1071
else if(x == XMPP::AdvancedConnector::ErrProxyAuth) {
1072
s = tr("Proxy authentication failed");
1075
else if(x == XMPP::AdvancedConnector::ErrStream)
1076
s = tr("Socket/stream error");
1077
str = tr("Connection Error: %1").arg(s);
1079
else if(err == XMPP::ClientStream::ErrNeg) {
1080
int x = stream->errorCondition();
1082
if(x == XMPP::ClientStream::HostGone)
1083
s = tr("Host no longer hosted");
1084
else if(x == XMPP::ClientStream::HostUnknown)
1085
s = tr("Host unknown");
1086
else if(x == XMPP::ClientStream::RemoteConnectionFailed) {
1087
s = tr("A required remote connection failed");
1090
else if(x == XMPP::ClientStream::SeeOtherHost)
1091
s = tr("See other host: %1").arg(stream->errorText());
1092
else if(x == XMPP::ClientStream::UnsupportedVersion)
1093
s = tr("Server does not support proper XMPP version");
1094
str = tr("Stream Negotiation Error: %1").arg(s);
1096
else if(err == XMPP::ClientStream::ErrTLS) {
1097
int x = stream->errorCondition();
1099
if(x == XMPP::ClientStream::TLSStart)
1100
s = tr("Server rejected STARTTLS");
1101
else if(x == XMPP::ClientStream::TLSFail) {
1102
int t = tlsHandler->tlsError();
1103
if(t == QCA::TLS::ErrHandshake)
1104
s = tr("TLS handshake error");
1106
s = tr("Broken security layer (TLS)");
1110
else if(err == XMPP::ClientStream::ErrAuth) {
1111
int x = stream->errorCondition();
1113
if(x == XMPP::ClientStream::GenericAuthError)
1114
s = tr("Unable to login");
1115
else if(x == XMPP::ClientStream::NoMech)
1116
s = tr("No appropriate mechanism available for given security settings");
1117
else if(x == XMPP::ClientStream::BadProto)
1118
s = tr("Bad server response");
1119
else if(x == XMPP::ClientStream::BadServ)
1120
s = tr("Server failed mutual authentication");
1121
else if(x == XMPP::ClientStream::EncryptionRequired)
1122
s = tr("Encryption required for chosen SASL mechanism");
1123
else if(x == XMPP::ClientStream::InvalidAuthzid)
1124
s = tr("Invalid account information");
1125
else if(x == XMPP::ClientStream::InvalidMech)
1126
s = tr("Invalid SASL mechanism");
1127
else if(x == XMPP::ClientStream::InvalidRealm)
1128
s = tr("Invalid realm");
1129
else if(x == XMPP::ClientStream::MechTooWeak)
1130
s = tr("SASL mechanism too weak for this account");
1131
else if(x == XMPP::ClientStream::NotAuthorized)
1132
s = tr("Not authorized");
1133
else if(x == XMPP::ClientStream::TemporaryAuthFailure)
1134
s = tr("Temporary auth failure");
1135
str = tr("Authentication error: %1").arg(s);
1137
else if(err == XMPP::ClientStream::ErrSecurityLayer)
1138
str = tr("Broken security layer (SASL)");
1141
//printf("str[%s], reconn=%d\n", str.latin1(), reconn);
1146
void PsiAccount::cs_error(int err)
1151
getErrorInfo(err, d->conn, d->stream, d->tlsHandler, &str, &reconn);
1156
//printf("Error: [%s]\n", str.latin1());
1158
isDisconnecting = true;
1160
if ( loggedIn() ) { // FIXME: is this condition okay?
1161
simulateRosterOffline();
1164
presenceSent = false; // this stops the idle detector?? (FIXME)
1167
if(d->acc.opt_reconn && reconn) {
1168
// reconnect in 5 seconds
1171
QTimer::singleShot(5000, this, SLOT(reconnect()));
1179
QMessageBox::critical(0, tr("%1: Server Error").arg(name()), tr("There was an error communicating with the Jabber server.\nDetails: %1").arg(str));
1182
void PsiAccount::client_rosterRequestFinished(bool success, int, const QString &)
1185
//printf("PsiAccount: [%s] roster retrieved ok. %d entries.\n", name().latin1(), d->client->roster().count());
1187
// delete flagged items
1188
UserListIt it(d->userList);
1189
for(UserListItem *u; (u = it.current());) {
1190
if(u->flagForDelete()) {
1191
//QMessageBox::information(0, "blah", QString("deleting: [%1]").arg(u->jid().full()));
1193
d->eventQueue->clear(u->jid());
1194
updateReadNext(u->jid());
1196
d->cp->removeEntry(u->jid());
1197
d->userList.removeRef(u);
1204
//printf("PsiAccount: [%s] error retrieving roster: [%d, %s]\n", name().latin1(), code, str.latin1());
1208
setStatusDirect(d->loginStatus);
1211
void PsiAccount::client_rosterItemUpdated(const RosterItem &r)
1213
// see if the item added is already in our local list
1214
UserListItem *u = d->userList.find(r.jid());
1216
u->setFlagForDelete(false);
1217
u->setRosterItem(r);
1220
// we don't have it at all, so add it
1221
u = new UserListItem;
1222
u->setRosterItem(r);
1223
//u->setAvatarFactory(d->avatarFactory); // Avatars
1224
d->userList.append(u);
1228
// TODO: auto info-grab (??)
1229
// a new entry that is "push"? [pushinfo]
1232
// if(readUserInfo(entry->jid, &info) && !info.field[vNickname].isEmpty()) {
1233
// item->nick = info.field[vNickname];
1236
// if(localStatus != STATUS_OFFLINE)
1237
// serv->getVCard(item->jid);
1241
d->cp->updateEntry(*u);
1244
void PsiAccount::client_rosterItemRemoved(const RosterItem &r)
1246
UserListItem *u = d->userList.find(r.jid());
1250
simulateContactOffline(u);
1252
// if the item has messages queued, then move them to 'not in list'
1253
if(d->eventQueue->count(r.jid()) > 0) {
1254
u->setInList(false);
1255
d->cp->updateEntry(*u);
1257
// else remove them for good!
1259
d->cp->removeEntry(u->jid());
1260
d->userList.removeRef(u);
1264
void PsiAccount::tryVerify(UserListItem *u, UserResource *ur)
1267
verifyStatus(u->jid().withResource(ur->name()), ur->status());
1270
void PsiAccount::client_resourceAvailable(const Jid &j, const Resource &r)
1274
PopupStatusChange = 1
1276
PopupType popupType = PopupOnline;
1278
if ( j.user().isEmpty() )
1279
new BlockTransportPopup(d->blockTransportPopupList, j);
1281
bool doSound = false;
1282
bool doPopup = false;
1283
QPtrList<UserListItem> list = findRelavent(j);
1284
QPtrListIterator<UserListItem> it(list);
1285
for(UserListItem *u; (u = it.current()); ++it) {
1286
bool doAnim = false;
1288
if(u->isSelf() && r.name() == d->client->resource())
1291
// add/update the resource
1292
QString oldStatus, oldKey;
1294
UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
1295
bool found = (rit == u->userResourceList().end()) ? false: true;
1297
popupType = PopupOnline;
1300
//ur.setSecurityEnabled(true);
1302
ur.setClient(PROG_NAME,PROG_VERSION,getOSName());
1303
rp = &(*u->userResourceList().append(ur));
1305
if(notifyOnlineOk && !local) {
1308
doPopup = option.ppOnline;
1311
if(!local && option.autoVersion && !status().isInvisible()) {
1312
// do a client-version request (too easy!)
1313
JT_ClientVersion *jcv = new JT_ClientVersion(d->client->rootTask());
1314
connect(jcv, SIGNAL(finished()), SLOT(slotClientVersionFinished()));
1321
popupType = PopupStatusChange;
1323
oldStatus = (*rit).status().status();
1324
oldKey = (*rit).status().keyID();
1327
(*rit).setResource(r);
1330
doPopup = option.ppStatus;
1333
rp->setPGPVerifyStatus(-1);
1334
if(!rp->status().xsigned().isEmpty())
1337
u->setPresenceError("");
1338
cpUpdate(*u, r.name(), true);
1340
if(doAnim && option.rosterAnim)
1341
d->cp->animateNick(u->jid());
1344
playSound(option.onevent[eOnline]);
1346
if ( popupType == PopupOnline && !option.ppOnline )
1348
if ( popupType == PopupStatusChange && !option.ppStatus )
1350
if(notifyOnlineOk && doPopup && d->doPopups && !d->blockTransportPopupList->find(j, popupType == PopupOnline)) {
1352
UserListItem *u = findFirstRelavent(j);
1354
PsiPopup::PopupType pt = PsiPopup::AlertNone;
1355
if ( popupType == PopupOnline )
1356
pt = PsiPopup::AlertOnline;
1357
else if ( popupType == PopupStatusChange )
1358
pt = PsiPopup::AlertStatusChange;
1359
PsiPopup *popup = new PsiPopup(pt, this);
1360
popup->setData(j, r, u);
1362
else if ( !notifyOnlineOk )
1366
void PsiAccount::client_resourceUnavailable(const Jid &j, const Resource &r)
1368
bool doSound = false;
1369
bool doPopup = false;
1371
if ( j.user().isEmpty() )
1372
new BlockTransportPopup(d->blockTransportPopupList, j);
1374
QPtrList<UserListItem> list = findRelavent(j);
1375
QPtrListIterator<UserListItem> it(list);
1376
for(UserListItem *u; (u = it.current()); ++it) {
1378
if(u->isSelf() && r.name() == d->client->resource())
1382
UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
1383
bool found = (rit == u->userResourceList().end()) ? false: true;
1385
u->setLastUnavailableStatus(r.status());
1386
u->userResourceList().remove(rit);
1388
if(!u->isAvailable())
1389
u->setLastAvailable(QDateTime::currentDateTime());
1391
if(!u->isAvailable() || u->isSelf()) {
1392
// don't sound for our own resource
1393
if(!isDisconnecting && !local) {
1395
doPopup = option.ppOffline;
1400
u->setPresenceError("");
1401
cpUpdate(*u, r.name(), true);
1404
playSound(option.onevent[eOffline]);
1406
if ( !option.ppOffline )
1408
if(doPopup && d->doPopups && !d->blockTransportPopupList->find(j)) {
1410
UserListItem *u = findFirstRelavent(j);
1412
PsiPopup *popup = new PsiPopup(PsiPopup::AlertOffline, this);
1413
popup->setData(j, r, u);
1417
void PsiAccount::client_presenceError(const Jid &j, int, const QString &str)
1419
QPtrList<UserListItem> list = findRelavent(j);
1420
QPtrListIterator<UserListItem> it(list);
1421
for(UserListItem *u; (u = it.current()); ++it) {
1422
simulateContactOffline(u);
1423
u->setPresenceError(str);
1424
cpUpdate(*u, j.resource(), false);
1428
void PsiAccount::client_messageReceived(const Message &_m)
1430
//check if it's a server message without a from, and set the from appropriately
1431
if (_m.from().isEmpty())
1433
_m.from()=jid().domain();
1436
// if the sender is already in the queue, then queue this message also
1437
QPtrListIterator<Message> it(d->messageQueue);
1438
for(Message *mi; (mi = it.current()); ++it) {
1439
if(mi->from().compare(_m.from())) {
1440
Message *m = new Message(_m);
1441
d->messageQueue.append(m);
1446
// encrypted message?
1447
if(!pgpKey().isEmpty() && !_m.xencrypted().isEmpty()) {
1448
Message *m = new Message(_m);
1449
d->messageQueue.append(m);
1450
processMessageQueue();
1454
processIncomingMessage(_m);
1457
void PsiAccount::processIncomingMessage(const Message &_m)
1459
// skip empty messages
1460
if(_m.body().isEmpty() && _m.urlList().isEmpty() && _m.invite().isEmpty() && !_m.containsEvents())
1464
if(_m.type() == "headline" && option.ignoreHeadline)
1467
//skip if on ignore list? KEVIN, must not forget
1469
if(_m.type() == "groupchat") {
1470
GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(_m.from().userHost()));
1476
// only toggle if not an invite or body is not empty
1477
if(_m.invite().isEmpty() && !_m.body().isEmpty())
1478
toggleSecurity(_m.from(), _m.wasEncrypted());
1480
UserListItem *u = findFirstRelavent(_m.from());
1482
if(_m.type() == "chat") u->setLastMessageType(1);
1483
else u->setLastMessageType(0);
1488
// smartchat: try to match up the incoming event to an existing chat
1489
// (prior to 0.9, m.from() always contained a resource)
1492
QPtrList<UserListItem> ul = findRelavent(m.from());
1494
// ignore events from non-roster JIDs?
1495
if (ul.isEmpty() && option.ignoreNonRoster)
1497
if (option.excludeGroupChatsFromIgnore)
1499
GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(_m.from().userHost()));
1513
j = m.from().userHost();
1515
j = ul.first()->jid();
1517
c = (ChatDlg *)dialogFind("ChatDlg", j);
1519
c = (ChatDlg *)dialogFind("ChatDlg", m.from().full());
1521
if(m.type() == "error")
1522
m.setBody(m.error().text + "\n------\n" + m.body());
1525
if(m.type() != "headline" && m.invite().isEmpty()) {
1526
if(option.incomingAs == 1)
1528
else if(option.incomingAs == 2)
1530
else if(option.incomingAs == 3) {
1531
if(c != NULL && !c->isHidden())
1538
// urls or subject on a chat message? convert back to regular message
1539
//if(m.type() == "chat" && (!m.urlList().isEmpty() || !m.subject().isEmpty()))
1542
MessageEvent *me = new MessageEvent(m, this);
1543
me->setOriginLocal(false);
1547
void PsiAccount::client_subscription(const Jid &j, const QString &str)
1549
// if they remove our subscription, then we lost presence
1550
if(str == "unsubscribed") {
1551
UserListItem *u = d->userList.find(j);
1553
simulateContactOffline(u);
1556
AuthEvent *ae = new AuthEvent(j, str, this);
1557
ae->setTimeStamp(QDateTime::currentDateTime());
1561
void PsiAccount::client_debugText(const QString &)
1563
//printf("%s", str.latin1());
1567
void PsiAccount::client_incomingJidLink()
1569
JidLink *jl = d->client->jidLinkManager()->takeIncoming();
1573
printf("[%s] -- Incoming JidLink request from [%s]\n", name().latin1(), jl->peer().full().latin1());
1574
JLTestDlg *w = new JLTestDlg(jl->peer(), jl, this);
1581
void PsiAccount::client_incomingFileTransfer()
1583
FileTransfer *ft = d->client->fileTransferManager()->takeIncoming();
1587
/*printf("psiaccount: incoming file transfer:\n");
1588
printf(" From: [%s]\n", ft->peer().full().latin1());
1589
printf(" Name: [%s]\n", ft->fileName().latin1());
1590
printf(" Size: %d bytes\n", ft->fileSize());*/
1592
FileEvent *fe = new FileEvent(ft->peer().full(), ft, this);
1593
fe->setTimeStamp(QDateTime::currentDateTime());
1597
void PsiAccount::reconnect()
1600
//printf("PsiAccount: [%s] reconnecting...\n", name().latin1());
1602
doReconnect = false;
1607
Status PsiAccount::status() const
1609
return d->loginStatus;
1612
void PsiAccount::setStatus(const Status &_s)
1614
// Block all transports' contacts' status change popups from popping
1616
Roster::ConstIterator rit = d->acc.roster.begin();
1617
for ( ; rit != d->acc.roster.end(); ++rit) {
1618
const RosterItem &i = *rit;
1619
if ( i.jid().user().isEmpty() /*&& i.jid().resource() == "registered"*/ ) // it is very likely then, that it's transport
1620
new BlockTransportPopup(d->blockTransportPopupList, i.jid());
1624
// cancel auto-status and reconnect
1625
usingAutoStatus = false;
1626
doReconnect = false;
1629
s.setPriority(d->acc.priority);
1633
if(s.isAvailable()) {
1634
// if client is not active then attempt to login
1637
if(d->acc.resource.isEmpty() || !j.isValid()) {
1638
QMessageBox::information(0, CAP(tr("Error")), tr("Unable to login. Ensure your account information is filled out."));
1642
if(!d->acc.opt_pass) {
1644
QString text = QInputDialog::getText(
1645
tr("Need Password"),
1646
tr("Please enter the password for %1:").arg(j.full()),
1647
QLineEdit::Password, QString::null, &ok, 0);
1648
if(ok && !text.isEmpty())
1655
if(d->acc.opt_passphrase)
1656
d->passphrase = d->acc.pgpPassphrase;
1667
if(s.isInvisible()) {//&&Pass invis to transports KEVIN
1668
//this is a nasty hack to let the transports know we're invisible, since they get an offline packet when we go invisible
1669
QPtrListIterator<UserListItem> it(d->userList);
1670
for(UserListItem *u; (u = it.current()); ++it) {
1671
if(u->isTransport()) {
1672
JT_Presence *j = new JT_Presence(d->client->rootTask());
1673
j->pres(u->jid(), s);
1686
void PsiAccount::setStatusDirect(const Status &_s)
1689
s.setPriority(d->acc.priority);
1691
//printf("setting status to [%s]\n", s.status().latin1());
1694
if(d->psi->pgp() && !d->cur_pgpSecretKeyID.isEmpty()) {
1701
/*if(d->psi->pgp() && !d->cur_pgpSecretKeyID.isEmpty())
1702
s.setKeyID(d->cur_pgpSecretKeyID);
1706
// send presence normally
1711
void PsiAccount::setStatusActual(const Status &s)
1715
d->client->setPresence(s);
1720
presenceSent = true;
1722
QTimer::singleShot(15000, this, SLOT(enableNotifyOnline()));
1724
const VCard *vcard = VCardFactory::vcard(d->jid);
1725
if ( option.autoVCardOnLogin || !vcard || vcard->isEmpty() || vcard->nickName().isEmpty() )
1726
VCardFactory::getVCard(d->jid, d->client->rootTask(), this, SLOT(slotCheckVCard()));
1728
d->nickFromVCard = true;
1729
setNick( vcard->nickName() );
1734
void PsiAccount::secondsIdle(int x)
1739
int lastIdle = d->lastIdle;
1740
Status lastStatus = d->lastStatus;
1742
Resource r = *(d->client->resourceList().find(d->client->resource()));
1743
Status ls = r.status();
1745
//must avoid status change when invisible KEVIN
1746
if(ls.isInvisible())
1749
if(ls.isAvailable()) {
1752
if(ls.isAway() && usingAutoStatus) {
1753
lastStatus = d->origStatus;
1754
setStatusDirect(lastStatus);
1755
usingAutoStatus = false;
1758
else if( !(ls.isAway() && !usingAutoStatus) ) {
1759
int minutes = x / 60;
1761
if(option.use_asOffline && option.asOffline > 0 && minutes >= option.asOffline) {
1762
lastStatus = Status("", "", d->acc.priority, false);
1763
usingAutoStatus = false;
1766
else if(option.use_asXa && option.asXa > 0 && minutes >= option.asXa) {
1767
if(ls.show() != "xa" && lastStatus.show() != "xa") {
1768
lastStatus = Status("xa", option.asMessage, d->acc.priority);
1769
if(!usingAutoStatus)
1770
d->origStatus = d->loginStatus;
1771
setStatusDirect(lastStatus);
1772
usingAutoStatus = true;
1775
else if(option.use_asAway && option.asAway > 0 && minutes >= option.asAway) {
1776
if(ls.show() != "away" && lastStatus.show() != "away") {
1777
lastStatus = Status("away", option.asMessage, d->acc.priority);
1778
if(!usingAutoStatus)
1779
d->origStatus = d->loginStatus;
1780
setStatusDirect(lastStatus);
1781
usingAutoStatus = true;
1788
d->lastStatus = lastStatus;
1791
void PsiAccount::playSound(const QString &str)
1796
int s = STATUS_OFFLINE;
1798
s = makeSTATUS(status());
1804
if(option.noAwaySound && (s == STATUS_AWAY || s == STATUS_XA))
1807
d->psi->playSound(str);
1810
QWidget *PsiAccount::dialogFind(const char *className, const Jid &j)
1812
QPtrListIterator<item_dialog2> it(d->dialogList);
1813
for(item_dialog2 *i; (i = it.current()); ++it) {
1814
// does the classname and jid match?
1815
if(i->className == className && i->jid.compare(j)) {
1822
void PsiAccount::dialogRegister(QWidget *w, const Jid &j)
1824
item_dialog2 *i = new item_dialog2;
1826
i->className = w->className();
1828
d->dialogList.append(i);
1831
void PsiAccount::dialogUnregister(QWidget *w)
1833
QPtrListIterator<item_dialog2> it(d->dialogList);
1834
for(item_dialog2 *i; (i = it.current()); ++it) {
1835
if(i->widget == w) {
1836
d->dialogList.removeRef(i);
1842
void PsiAccount::deleteAllDialogs()
1844
QPtrListIterator<item_dialog2> it(d->dialogList);
1845
for(item_dialog2 *i; (i = it.current());)
1847
d->dialogList.clear();
1850
bool PsiAccount::checkConnected(QWidget *par)
1853
QMessageBox::information(par, CAP(tr("Error")), tr("You must be connected to the server in order to do this."));
1860
void PsiAccount::modify()
1862
AccountModifyDlg *w = (AccountModifyDlg *)dialogFind("AccountModifyDlg");
1866
w = new AccountModifyDlg(this, 0);
1871
void PsiAccount::changeVCard()
1876
void PsiAccount::changePW()
1878
if(!checkConnected())
1881
ChangePasswordDlg *w = (ChangePasswordDlg *)dialogFind("ChangePasswordDlg");
1885
w = new ChangePasswordDlg(this);
1890
void PsiAccount::showXmlConsole()
1892
bringToFront(d->xmlConsole);
1895
void PsiAccount::openAddUserDlg()
1897
if(!checkConnected())
1900
AddUserDlg *w = (AddUserDlg *)dialogFind("AddUserDlg");
1904
QStringList gl, services, names;
1905
UserListIt it(d->userList);
1906
for(UserListItem *u; (u = it.current()); ++it) {
1907
if(u->isTransport()) {
1908
services += u->jid().full();
1909
names += jidnick(u->jid().full(), u->name());
1911
const QStringList &groups = u->groups();
1912
if(groups.isEmpty())
1914
for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) {
1915
if(qstringlistmatch(gl, *git) == -1)
1920
w = new AddUserDlg(services, names, gl, this);
1921
connect(w, SIGNAL(add(const Jid &, const QString &, const QStringList &, bool)), SLOT(dj_add(const Jid &, const QString &, const QStringList &, bool)));
1926
void PsiAccount::doDisco()
1928
actionDisco(d->jid.host(), "");
1931
void PsiAccount::actionDisco(const Jid &j, const QString &node)
1933
DiscoDlg *w = new DiscoDlg(this, j, node);
1934
connect(w, SIGNAL(featureActivated(QString, Jid, QString)), SLOT(featureActivated(QString, Jid, QString)));
1938
void PsiAccount::featureActivated(QString feature, Jid jid, QString node)
1940
Features f(feature);
1942
if ( f.canRegister() )
1943
actionRegister(jid);
1944
else if ( f.canSearch() )
1946
else if ( f.canGroupchat() )
1948
else if ( f.canDisco() )
1949
actionDisco(jid, node);
1950
else if ( f.isGateway() )
1952
else if ( f.haveVCard() )
1954
else if ( f.id() == Features::FID_Add ) {
1956
dj_add(jid, QString::null, sl, true);
1960
void PsiAccount::actionJoin(const Jid &j)
1962
GCJoinDlg *w = new GCJoinDlg(psi(), this);
1964
w->le_host->setText ( j.host() );
1965
w->le_room->setText ( j.user() );
1970
void PsiAccount::stateChanged()
1973
d->cp->setState(makeSTATUS(status()));
1976
d->cp->setState(-1);
1978
d->cp->setUsingSSL(true);
1980
d->cp->setUsingSSL(false);
1983
d->cp->setState(STATUS_OFFLINE);
1984
d->cp->setUsingSSL(false);
1991
void PsiAccount::simulateContactOffline(UserListItem *u)
1993
UserResourceList rl = u->userResourceList();
1994
u->setPresenceError("");
1996
for(UserResourceList::ConstIterator rit = rl.begin(); rit != rl.end(); ++rit) {
1997
const UserResource &r = *rit;
1999
if(u->jid().resource().isEmpty())
2000
j.setResource(r.name());
2001
client_resourceUnavailable(j, r);
2008
void PsiAccount::simulateRosterOffline()
2010
UserListIt it(d->userList);
2011
for(UserListItem *u; (u = it.current()); ++it)
2012
simulateContactOffline(u);
2016
UserListItem *u = &d->self;
2017
UserResourceList rl = u->userResourceList();
2018
for(UserResourceList::ConstIterator rit = rl.begin(); rit != rl.end(); ++rit) {
2020
if(u->jid().resource().isEmpty())
2021
j.setResource((*rit).name());
2022
u->setPresenceError("");
2023
client_resourceUnavailable(j, *rit);
2030
void PsiAccount::enableNotifyOnline()
2032
if ( d->userCounter > 1 ) {
2033
QTimer::singleShot(15000, this, SLOT(enableNotifyOnline()));
2037
notifyOnlineOk = true;
2040
void PsiAccount::slotClientVersionFinished()
2042
JT_ClientVersion *j = (JT_ClientVersion *)sender();
2044
QPtrList<UserListItem> list = findRelavent(j->jid());
2045
QPtrListIterator<UserListItem> it(list);
2046
for(UserListItem *u; (u = it.current()); ++it) {
2047
UserResourceList::Iterator rit = u->userResourceList().find(j->jid().resource());
2048
bool found = (rit == u->userResourceList().end()) ? false: true;
2052
(*rit).setClient(j->name(),j->version(),j->os());
2057
d->cp->updateSelf(*u);
2060
d->cp->updateEntry(*u);*/
2065
QPtrList<UserListItem> PsiAccount::findRelavent(const Jid &j) const
2067
QPtrList<UserListItem> list;
2070
if(j.compare(d->self.jid(), false))
2071
list.append(&d->self);
2073
QPtrListIterator<UserListItem> it(d->userList);
2074
for(UserListItem *u; (u = it.current()); ++it) {
2075
if(!u->jid().compare(j, false))
2078
if(!u->jid().resource().isEmpty()) {
2079
if(u->jid().resource() != j.resource())
2089
UserListItem *PsiAccount::findFirstRelavent(const Jid &j) const
2091
QPtrList<UserListItem> list = findRelavent(j);
2095
return list.first();
2098
UserListItem *PsiAccount::find(const Jid &j) const
2101
if(j.compare(d->self.jid()))
2104
u = d->userList.find(j);
2109
void PsiAccount::cpUpdate(const UserListItem &u, const QString &rname, bool fromPresence)
2111
PsiEvent *e = d->eventQueue->peek(u.jid());
2113
d->cp->updateEntry(u);
2116
d->cp->setAlert(u.jid(), is->event2icon(e));
2119
d->cp->clearAlert(u.jid());
2123
if(!rname.isEmpty())
2124
j.setResource(rname);
2126
updateContact(j, fromPresence);
2127
d->psi->updateContactGlobal(this, j);
2130
QLabel *PsiAccount::accountLabel(QWidget *par, bool simpleMode)
2132
AccountLabel *al = new AccountLabel(this, par, simpleMode);
2136
EventDlg *PsiAccount::ensureEventDlg(const Jid &j)
2138
EventDlg *w = (EventDlg *)dialogFind("EventDlg", j);
2140
w = new EventDlg(j, this, true);
2141
connect(w, SIGNAL(aReadNext(const Jid &)), SLOT(processReadNext(const Jid &)));
2142
connect(w, SIGNAL(aReply(const Jid &, const QString &, const QString &, const QString &)), SLOT(dj_composeMessage(const Jid &, const QString &, const QString &, const QString &)));
2143
connect(w, SIGNAL(aAuth(const Jid &)), SLOT(dj_addAuth(const Jid &)));
2144
connect(w, SIGNAL(aDeny(const Jid &)), SLOT(dj_deny(const Jid &)));
2145
connect(d->psi, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
2146
connect(this, SIGNAL(updateContact(const Jid &)), w, SLOT(updateContact(const Jid &)));
2152
ChatDlg *PsiAccount::ensureChatDlg(const Jid &j)
2154
ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
2156
// create the chatbox
2157
c = new ChatDlg(j, this);
2158
connect(c, SIGNAL(aSend(const Message &)), SLOT(dj_sendMessage(const Message &)));
2159
connect(c, SIGNAL(messagesRead(const Jid &)), SLOT(chatMessagesRead(const Jid &)));
2160
connect(c, SIGNAL(aInfo(const Jid &)), SLOT(actionInfo(const Jid &)));
2161
connect(c, SIGNAL(aHistory(const Jid &)), SLOT(actionHistory(const Jid &)));
2162
connect(c, SIGNAL(aFile(const Jid &)), SLOT(actionSendFile(const Jid &)));
2163
connect(d->psi, SIGNAL(emitOptionsUpdate()), c, SLOT(optionsUpdate()));
2164
connect(this, SIGNAL(updateContact(const Jid &, bool)), c, SLOT(updateContact(const Jid &, bool)));
2167
// on X11, do a special reparent to open on the right desktop
2170
const QPixmap *pp = c->icon();
2174
reparent_good(c, 0, false);
2184
void PsiAccount::changeStatus(int x)
2186
if(x == STATUS_OFFLINE && !option.askOffline) {
2187
setStatus(Status("","Logged out",0,false));
2190
if(x == STATUS_ONLINE && !option.askOnline) {
2191
setStatus(Status());
2193
else if(x == STATUS_INVISIBLE){
2194
Status s("","",0,true);
2195
s.setIsInvisible(true);
2199
StatusSetDlg *w = new StatusSetDlg(this, makeStatus(x, ""));
2200
connect(w, SIGNAL(set(const Status &)), SLOT(setStatus(const Status &)));
2206
void PsiAccount::actionTest(const Jid &j)
2208
UserListItem *u = find(j);
2212
if(j.resource().isEmpty()) {
2213
if(u->isAvailable())
2214
j2.setResource((*u->userResourceList().priority()).name());
2217
//S5BConnection *c = new S5BConnection(d->client->s5bManager());
2218
//c->connectToJid(j2, d->client->s5bManager()->genUniqueSID(j2));
2219
JLTestDlg *w = new JLTestDlg(j2, this);
2223
void PsiAccount::actionSendFile(const Jid &j)
2226
actionSendFiles(j, l);
2229
void PsiAccount::actionSendFiles(const Jid &j, const QStringList& l)
2232
if(j.resource().isEmpty()) {
2233
UserListItem *u = find(j);
2234
if(u && u->isAvailable())
2235
j2.setResource((*u->userResourceList().priority()).name());
2238
// Create a dialog for each file in the list. Once the xfer dialog itself
2239
// supports multiple files, only the 'else' branch needs to stay.
2241
for (QStringList::ConstIterator f = l.begin(); f != l.end(); ++f ) {
2243
FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, fl);
2248
FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, l);
2253
void PsiAccount::actionDefault(const Jid &j)
2255
UserListItem *u = find(j);
2259
if(d->eventQueue->count(u->jid()) > 0)
2262
if(option.defaultAction == 0)
2263
actionSendMessage(u->jid());
2265
actionOpenChat(u->jid());
2269
void PsiAccount::actionRecvEvent(const Jid &j)
2271
UserListItem *u = find(j);
2278
void PsiAccount::actionSendMessage(const Jid &j)
2280
EventDlg *w = d->psi->createEventDlg(j.full(), this);
2284
void PsiAccount::actionSendMessage(const JidList &j)
2288
for(QValueList<Jid>::ConstIterator it = j.begin(); it != j.end(); ++it) {
2293
str += (*it).full();
2296
EventDlg *w = d->psi->createEventDlg(str, this);
2300
void PsiAccount::actionSendUrl(const Jid &j)
2302
EventDlg *w = d->psi->createEventDlg(j.full(), this);
2307
void PsiAccount::actionRemove(const Jid &j)
2309
//avatarFactory()->removeManualAvatar(j); // Avatars
2313
void PsiAccount::actionRename(const Jid &j, const QString &name)
2318
void PsiAccount::actionGroupRename(const QString &oldname, const QString &newname)
2320
UserListIt it(d->userList);
2321
QPtrList<UserListItem> nu;
2322
for(UserListItem *u; (u = it.current()); ++it) {
2323
if(u->inGroup(oldname)) {
2324
u->removeGroup(oldname);
2325
u->addGroup(newname);
2333
JT_Roster *r = new JT_Roster(d->client->rootTask());
2335
QPtrListIterator<UserListItem> it(nu);
2336
for(UserListItem *u; (u = it.current()); ++it)
2337
r->set(u->jid(), u->name(), u->groups());
2343
void PsiAccount::actionHistory(const Jid &j)
2345
HistoryDlg *w = (HistoryDlg *)dialogFind("HistoryDlg", j);
2349
w = new HistoryDlg(j, this);
2350
connect(w, SIGNAL(openEvent(PsiEvent *)), SLOT(actionHistoryBox(PsiEvent *)));
2355
void PsiAccount::actionHistoryBox(PsiEvent *e)
2357
EventDlg *w = new EventDlg(e->from(), this, false);
2358
connect(w, SIGNAL(aReply(const Jid &, const QString &, const QString &, const QString &)), SLOT(dj_composeMessage(const Jid &, const QString &, const QString &, const QString &)));
2359
connect(w, SIGNAL(aAuth(const Jid &)), SLOT(dj_addAuth(const Jid &)));
2360
connect(w, SIGNAL(aDeny(const Jid &)), SLOT(dj_deny(const Jid &)));
2361
connect(d->psi, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
2362
connect(this, SIGNAL(updateContact(const Jid &)), w, SLOT(updateContact(const Jid &)));
2367
void PsiAccount::actionStatusShow(const Jid &j)
2369
UserListItem *u = find(j);
2373
StatusShowDlg *w = new StatusShowDlg(*u);
2377
void PsiAccount::actionOpenChat(const Jid &j)
2379
UserListItem *u = find(j);
2383
// if 'j' is bare, we might want to switch to a specific resource
2385
if(j.resource().isEmpty()) {
2386
// first, are there any queued chats?
2387
/*PsiEvent *e = d->eventQueue->peekFirstChat(j, false);
2389
res = e->from().resource();
2390
// if we have a bare chat, change to 'res'
2391
ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
2393
c->setJid(j.withResource(res));
2395
// else, is there a priority chat window available?
2396
else*/ if(u->isAvailable()) {
2397
QString pr = (*u->userResourceList().priority()).name();
2398
if(!pr.isEmpty() && dialogFind("ChatDlg", j.withResource(pr)))
2402
QStringList list = hiddenChats(j);
2409
openChat(j.withResource(res));
2414
void PsiAccount::actionOpenChatSpecific(const Jid &j)
2419
void PsiAccount::actionAgentSetStatus(const Jid &j, int x)
2421
if ( j.user().isEmpty() ) // add all transport popups to block list
2422
new BlockTransportPopup(d->blockTransportPopupList, j);
2424
JT_Presence *p = new JT_Presence(d->client->rootTask());
2425
p->pres(j, makeStatus(x, ""));
2429
void PsiAccount::actionInfo(const Jid &_j)
2431
bool useCache = true;
2433
if(findGCContact(_j)) {
2441
InfoDlg *w = (InfoDlg *)dialogFind("InfoDlg", j);
2445
const VCard *vcard = VCardFactory::vcard(j);
2450
w = new InfoDlg(j.compare(d->jid) ? InfoDlg::Self : InfoDlg::Contact, j, tmp, this, 0, 0, useCache);
2453
// automatically retrieve info if it doesn't exist
2454
if(!vcard && loggedIn())
2459
void PsiAccount::actionAuth(const Jid &j)
2464
void PsiAccount::actionAuthRequest(const Jid &j)
2469
void PsiAccount::actionAuthRemove(const Jid &j)
2474
void PsiAccount::actionAdd(const Jid &j)
2479
void PsiAccount::actionGroupAdd(const Jid &j, const QString &g)
2481
UserListItem *u = d->userList.find(j);
2489
JT_Roster *r = new JT_Roster(d->client->rootTask());
2490
r->set(u->jid(), u->name(), u->groups());
2494
void PsiAccount::actionGroupRemove(const Jid &j, const QString &g)
2496
UserListItem *u = d->userList.find(j);
2500
if(!u->removeGroup(g))
2504
JT_Roster *r = new JT_Roster(d->client->rootTask());
2505
r->set(u->jid(), u->name(), u->groups());
2509
void PsiAccount::actionRegister(const Jid &j)
2511
if(!checkConnected())
2514
RegistrationDlg *w = (RegistrationDlg *)dialogFind("RegistrationDlg", j);
2518
w = new RegistrationDlg(j, this);
2523
void PsiAccount::actionSearch(const Jid &j)
2525
if(!checkConnected())
2528
SearchDlg *w = (SearchDlg *)dialogFind("SearchDlg", j);
2532
w = new SearchDlg(j, this);
2533
connect(w, SIGNAL(add(const Jid &, const QString &, const QStringList &, bool)), SLOT(dj_add(const Jid &, const QString &, const QStringList &, bool)));
2534
connect(w, SIGNAL(aInfo(const Jid &)), SLOT(actionInfo(const Jid &)));
2539
void PsiAccount::actionInvite(const Jid &j, const QString &gc)
2544
m.setBody(tr("You have been invited to %1").arg(gc));
2545
m.setTimeStamp(QDateTime::currentDateTime());
2549
void PsiAccount::actionAssignKey(const Jid &j)
2552
UserListItem *u = findFirstRelavent(j);
2558
void PsiAccount::actionUnassignKey(const Jid &j)
2560
UserListItem *u = findFirstRelavent(j);
2562
u->setPublicKeyID("");
2567
void PsiAccount::dj_sendMessage(const Message &m, bool log)
2569
UserListItem *u = findFirstRelavent(m.to());
2572
if(option.incomingAs == 3) {
2574
switch(u->lastMessageType()) {
2575
case 0: nm.setType(""); break;
2576
case 1: nm.setType("chat"); break;
2581
d->client->sendMessage(nm);
2583
// only toggle if not an invite or body is not empty
2584
if(m.invite().isEmpty() && !m.body().isEmpty())
2585
toggleSecurity(m.to(), m.wasEncrypted());
2587
// don't log groupchat, private messages, or encrypted messages
2588
if(d->acc.opt_log && log) {
2589
if(m.type() != "groupchat" && m.xencrypted().isEmpty() && !findGCContact(m.to())) {
2590
MessageEvent *me = new MessageEvent(m, this);
2591
me->setOriginLocal(true);
2592
me->setTimeStamp(QDateTime::currentDateTime());
2593
logEvent(m.to(), me);
2598
// don't sound when sending groupchat messages or message events
2599
if(m.type() != "groupchat" && !m.body().isEmpty())
2600
playSound(option.onevent[eSend]);
2602
// auto close an open messagebox (if non-chat)
2603
if(m.type() != "chat") {
2604
UserListItem *u = findFirstRelavent(m.to());
2606
EventDlg *e = (EventDlg *)dialogFind("EventDlg", u->jid());
2608
e->closeAfterReply();
2613
void PsiAccount::dj_composeMessage(const Jid &jid, const QString &body, const QString &subject, const QString &thread)
2615
EventDlg *w = d->psi->createEventDlg(jid.full(), this);
2617
w->setText(qstrquote(body));
2619
if(!subject.isEmpty() && subject.left(3) != "Re:")
2620
w->setSubject("Re: " + subject);
2621
else if (subject.left(3) == "Re:")
2622
w->setSubject(subject);
2624
if(!thread.isEmpty())
2625
w->setThread(thread);
2630
void PsiAccount::dj_composeMessage(const Jid &j, const QString &body)
2632
dj_composeMessage(j, body, QString::null, QString::null);
2635
void PsiAccount::dj_addAuth(const Jid &j)
2639
UserListItem *u = d->userList.find(j);
2642
groups = u->groups();
2645
dj_add(j, name, groups, true);
2649
void PsiAccount::dj_add(const Jid &j, const QString &name, const QStringList &groups, bool authReq)
2651
JT_Roster *r = new JT_Roster(d->client->rootTask());
2652
r->set(j, name, groups);
2659
void PsiAccount::dj_authReq(const Jid &j)
2661
d->client->sendSubscription(j, "subscribe");
2664
void PsiAccount::dj_auth(const Jid &j)
2666
d->client->sendSubscription(j, "subscribed");
2669
void PsiAccount::dj_deny(const Jid &j)
2671
d->client->sendSubscription(j, "unsubscribed");
2674
void PsiAccount::dj_rename(const Jid &j, const QString &name)
2676
UserListItem *u = d->userList.find(j);
2681
if(name == u->jid().full())
2686
// strange workaround to avoid a null string ??
2688
if(u->name().isEmpty())
2700
JT_Roster *r = new JT_Roster(d->client->rootTask());
2701
r->set(u->jid(), u->name(), u->groups());
2706
void PsiAccount::dj_remove(const Jid &j)
2708
UserListItem *u = d->userList.find(j);
2712
// remove all events from the queue
2713
d->eventQueue->clear(j);
2716
// TODO: delete the item immediately (to simulate local change)
2718
//simulateContactOffline(u);
2719
d->userList.removeRef(u);
2722
JT_Roster *r = new JT_Roster(d->client->rootTask());
2726
// if it looks like a transport, unregister (but not if it is the server!!)
2727
if(u->isTransport() && !Jid(d->client->host()).compare(u->jid())) {
2728
JT_UnRegister *ju = new JT_UnRegister(d->client->rootTask());
2735
// handle an incoming event
2736
void PsiAccount::handleEvent(PsiEvent *e)
2741
bool doPopup = false;
2742
bool putToQueue = true;
2743
PsiPopup::PopupType popupType = PsiPopup::AlertNone;
2745
// find someone to accept the event
2747
QPtrList<UserListItem> ul = findRelavent(e->from());
2749
// if groupchat, then we want the full JID
2750
if(findGCContact(e->from())) {
2754
Jid bare = e->from().userHost();
2755
Jid reg = bare.withResource("registered");
2757
// see if we have a "registered" variant of the jid
2758
if(findFirstRelavent(reg)) {
2760
e->setFrom(reg); // HACK!!
2762
// retain full jid if sent to "registered"
2763
else if(e->from().resource() == "registered")
2765
// otherwise don't use the resource for new entries
2771
j = ul.first()->jid();
2774
if(d->acc.opt_log) {
2775
if(e->type() == PsiEvent::Message || e->type() == PsiEvent::Auth) {
2776
// don't log private messages
2777
if(!findGCContact(e->from()) && !(e->type() == PsiEvent::Message && ((MessageEvent *)e)->message().body().isEmpty()))
2778
logEvent(e->from(), e);
2782
if(e->type() == PsiEvent::Message) {
2783
MessageEvent *me = (MessageEvent *)e;
2784
const Message &m = me->message();
2786
// Pass message events to chat window
2787
if (m.containsEvents() && m.body().isEmpty()) {
2788
if (option.messageEvents) {
2789
ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", e->from());
2791
c = (ChatDlg *)dialogFind("ChatDlg", e->jid());
2793
c->incomingMessage(m);
2798
// pass chat messages directly to a chat window if possible (and deal with sound)
2799
if(m.type() == "chat") {
2800
ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", e->from());
2802
c = (ChatDlg *)dialogFind("ChatDlg", e->jid());
2805
c->setJid(e->from());
2807
if ( option.ppChat && ((c && !c->isActiveWindow()) || !c) ) {
2808
if (!c || c->isHidden() || option.alertOpenChats) {
2810
popupType = PsiPopup::AlertChat;
2814
if(c && !c->isHidden()) {
2815
c->incomingMessage(m);
2816
playSound(option.onevent[eChat2]);
2817
if(option.alertOpenChats && !c->isActiveWindow()) {
2818
// to alert the chat also, we put it in the queue
2819
me->setSentToChatWindow(true);
2825
bool firstChat = !d->eventQueue->hasChats(e->from());
2826
playSound(option.onevent[firstChat ? eChat1: eChat2]);
2829
else if (m.type() == "headline") {
2830
playSound(option.onevent[eHeadline]);
2831
if (option.ppHeadline) {
2833
popupType = PsiPopup::AlertHeadline;
2836
else if (m.type() == "") {
2837
playSound(option.onevent[eMessage]);
2838
if (option.ppMessage && m.type() == "") {
2840
popupType = PsiPopup::AlertMessage;
2844
playSound(option.onevent[eSystem]);
2846
if(m.type() == "error") {
2847
// FIXME: handle message errors
2848
//msg.text = QString(tr("<big>[Error Message]</big><br>%1").arg(plain2rich(msg.text)));
2851
else if(e->type() == PsiEvent::File) {
2852
playSound(option.onevent[eIncomingFT]);
2853
if (option.ppFile) {
2855
popupType = PsiPopup::AlertFile;
2859
playSound(option.onevent[eSystem]);
2861
AuthEvent *ae = (AuthEvent *)e;
2862
if(ae->authType() == "subscribe") {
2863
if(option.autoAuth) {
2864
dj_addAuth(ae->from());
2868
if(ae->authType() == "unsubscribe") {
2873
if ( popupType == PsiPopup::AlertChat && !option.ppChat )
2875
if ( popupType == PsiPopup::AlertMessage && !option.ppMessage )
2877
if ( popupType == PsiPopup::AlertHeadline && !option.ppHeadline )
2879
if ( popupType == PsiPopup::AlertFile && !option.ppFile )
2881
if ( doPopup && d->doPopups ) {
2884
UserListItem *u = findFirstRelavent(j);
2886
r = *(u->priority());
2888
PsiPopup *popup = new PsiPopup(popupType, this);
2889
popup->setData(j, r, u, e);
2898
// put an event into the event queue, and update the related alerts
2899
void PsiAccount::queueEvent(PsiEvent *e)
2901
// do we have roster item for this?
2902
UserListItem *u = find(e->jid());
2905
u = new UserListItem;
2906
u->setJid(e->jid());
2907
u->setInList(false);
2908
// u->setAvatarFactory(d->avatarFactory); // Avatars
2910
// is it a private groupchat?
2912
GCContact *c = findGCContact(j);
2914
u->setName(j.resource());
2915
u->setPrivate(true);
2917
// make a resource so the contact appears online
2919
ur.setName(j.resource());
2920
ur.setStatus(c->status);
2921
u->userResourceList().append(ur);
2924
// treat it like a push [pushinfo]
2926
//if(readUserInfo(item->jid, &info) && !info.field[vNickname].isEmpty())
2927
// item->nick = info.field[vNickname];
2929
// if(localStatus != STATUS_OFFLINE)
2930
// serv->getVCard(item->jid);
2933
d->userList.append(u);
2936
//printf("queuing message from [%s] for [%s].\n", e->from().full().latin1(), e->jid().full().latin1());
2937
d->eventQueue->enqueue(e);
2939
updateReadNext(e->jid());
2941
d->psi->raiseMainwin();
2943
// udpate the roster
2946
bool noPopup = false;
2947
if(d->loginStatus.isAvailable()) {
2948
QString show = d->loginStatus.show();
2951
else if((show == "away" || show == "xa") && option.noAwayPopup)
2956
bool doPopup = false;
2958
// Check to see if we need to popup
2959
if(e->type() == PsiEvent::Message) {
2960
MessageEvent *me = (MessageEvent *)e;
2961
const Message &m = me->message();
2962
if (m.type() == "chat")
2963
doPopup = option.popupChats;
2964
else if (m.type() == "headline")
2965
doPopup = option.popupHeadlines;
2967
doPopup = option.popupMsgs;
2968
} else if (e->type() == PsiEvent::File) {
2969
doPopup = option.popupFiles;
2972
doPopup = option.popupMsgs;
2976
UserListItem *u = find(e->jid());
2977
if (u && (!option.noUnlistedPopup || u->inList()))
2984
// take the next event from the queue and display it
2985
void PsiAccount::openNextEvent(const UserListItem &u)
2987
PsiEvent *e = d->eventQueue->peek(u.jid());
2991
psi()->processEvent(e);
2994
void PsiAccount::openNextEvent()
2996
PsiEvent *e = d->eventQueue->peekNext();
3000
if(e->type() == PsiEvent::PGP) {
3001
psi()->processEvent(e);
3005
UserListItem *u = find(e->jid());
3011
void PsiAccount::updateReadNext(const Jid &j)
3013
// update eventdlg's read-next
3014
EventDlg *w = (EventDlg *)dialogFind("EventDlg", j);
3017
int nextAmount = d->eventQueue->count(j);
3019
nextAnim = is->event2icon(d->eventQueue->peek(j));
3020
w->updateReadNext(nextAnim, nextAmount);
3026
void PsiAccount::processReadNext(const Jid &j)
3028
UserListItem *u = find(j);
3030
processReadNext(*u);
3033
void PsiAccount::processReadNext(const UserListItem &u)
3035
EventDlg *w = (EventDlg *)dialogFind("EventDlg", u.jid());
3037
// this should NEVER happen
3042
PsiEvent *e = d->eventQueue->peek(u.jid());
3046
bool isChat = false;
3047
if(e->type() == PsiEvent::Message) {
3048
MessageEvent *me = (MessageEvent *)e;
3049
const Message &m = me->message();
3050
if(m.type() == "chat")
3054
// if it's a chat message, just open the chat window. there is no need to do
3055
// further processing. the chat window will remove it from the queue, update
3056
// the cvlist, etc etc.
3058
openChat(e->from());
3062
// remove from queue
3063
e = d->eventQueue->dequeue(u.jid());
3065
// update the eventdlg
3069
// update the contact
3072
updateReadNext(u.jid());
3075
void PsiAccount::processChats(const Jid &j)
3077
//printf("processing chats for [%s]\n", j.full().latin1());
3078
ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
3082
// extract the chats
3083
QPtrList<PsiEvent> chatList;
3084
d->eventQueue->extractChats(&chatList, j);
3085
chatList.setAutoDelete(true);
3087
if(!chatList.isEmpty()) {
3088
// dump the chats into the chat window, and remove the related cvlist alerts
3089
QPtrListIterator<PsiEvent> it(chatList);
3090
for(PsiEvent *e; (e = it.current()); ++it) {
3091
MessageEvent *me = (MessageEvent *)e;
3092
const Message &m = me->message();
3094
// process the message
3095
if(!me->sentToChatWindow())
3096
c->incomingMessage(m);
3099
QPtrList<UserListItem> ul = findRelavent(j);
3101
UserListItem *u = ul.first();
3103
updateReadNext(u->jid());
3108
void PsiAccount::openChat(const Jid &j)
3110
ChatDlg *c = ensureChatDlg(j);
3115
void PsiAccount::chatMessagesRead(const Jid &j)
3117
if(option.alertOpenChats)
3121
void PsiAccount::logEvent(const Jid &j, PsiEvent *e)
3123
EDBHandle *h = new EDBHandle(d->psi->edb());
3124
connect(h, SIGNAL(finished()), SLOT(edb_finished()));
3128
void PsiAccount::edb_finished()
3130
EDBHandle *h = (EDBHandle *)sender();
3134
void PsiAccount::openGroupChat(const Jid &j)
3136
QString str = j.userHost();
3138
for(QStringList::ConstIterator it = d->groupchats.begin(); it != d->groupchats.end(); ++it) {
3145
d->groupchats += str;
3147
GCMainDlg *w = new GCMainDlg(this, j);
3148
connect(w, SIGNAL(aSend(const Message &)), SLOT(dj_sendMessage(const Message &)));
3149
connect(d->psi, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
3153
bool PsiAccount::groupChatJoin(const QString &host, const QString &room, const QString &nick)
3155
return d->client->groupChatJoin(host, room, nick);
3158
void PsiAccount::groupChatChangeNick(const QString &host, const QString &room, const QString& nick, const Status &s)
3160
d->client->groupChatChangeNick(host, room, nick, s);
3163
void PsiAccount::groupChatSetStatus(const QString &host, const QString &room, const Status &s)
3165
d->client->groupChatSetStatus(host, room, s);
3168
void PsiAccount::groupChatLeave(const QString &host, const QString &room)
3170
d->groupchats.remove(room + '@' + host);
3171
d->client->groupChatLeave(host, room);
3174
GCContact *PsiAccount::findGCContact(const Jid &j)
3176
QPtrListIterator<GCContact> it(d->gcbank);
3177
for(GCContact *c; (c = it.current()); ++it) {
3178
if(c->jid.compare(j))
3184
QStringList PsiAccount::groupchats() const
3186
return d->groupchats;
3189
void PsiAccount::client_groupChatJoined(const Jid &j)
3191
d->client->groupChatSetStatus(j.host(), j.user(), d->loginStatus);
3193
GCMainDlg *m = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
3198
GCJoinDlg *w = (GCJoinDlg *)dialogFind("GCJoinDlg", j);
3206
void PsiAccount::client_groupChatLeft(const Jid &j)
3208
// remove all associated groupchat contacts from the bank
3209
QPtrListIterator<GCContact> it(d->gcbank);
3210
for(GCContact *c; (c = it.current());) {
3211
// contact from this room?
3212
if(!c->jid.compare(j, false)) {
3216
UserListItem *u = find(c->jid);
3222
simulateContactOffline(u);
3223
d->gcbank.removeRef(c);
3227
void PsiAccount::client_groupChatPresence(const Jid &j, const Status &s)
3229
GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
3233
GCContact *c = findGCContact(j);
3238
d->gcbank.append(c);
3241
w->presence(j.resource(), s);
3243
// pass through the core presence handling also
3245
r.setName(j.resource());
3248
client_resourceAvailable(j, r);
3250
client_resourceUnavailable(j, j.resource());
3253
void PsiAccount::client_groupChatError(const Jid &j, int code, const QString &str)
3255
GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
3257
w->error(code, str);
3260
GCJoinDlg *w = (GCJoinDlg *)dialogFind("GCJoinDlg", j);
3262
w->error(code, str);
3267
QStringList PsiAccount::hiddenChats(const Jid &j) const
3271
QPtrListIterator<item_dialog2> it(d->dialogList);
3272
for(item_dialog2 *i; (i = it.current()); ++it) {
3273
if(i->className == "ChatDlg" && i->jid.compare(j, false))
3274
list += i->jid.resource();
3280
void PsiAccount::slotCheckVCard()
3282
JT_VCard *j = (JT_VCard *)sender();
3283
if(!j->success() && j->statusCode() == Task::ErrDisc) {
3284
setNick(d->jid.user());
3288
if(j->vcard().isEmpty()) {
3293
if(!j->vcard().nickName().isEmpty()) {
3294
d->nickFromVCard = true;
3295
setNick(j->vcard().nickName());
3298
setNick(d->jid.user());
3301
void PsiAccount::setNick(const QString &nick)
3303
d->self.setName(nick);
3308
QString PsiAccount::nick() const
3310
return d->self.name();
3313
void PsiAccount::pgpToggled(bool b)
3315
QString oldkey = d->cur_pgpSecretKeyID;
3319
d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
3322
d->acc.pgpPassphrase = ""; // clear account passphrase
3323
d->cur_pgpSecretKeyID = "";
3326
if(oldkey != d->cur_pgpSecretKeyID) {
3328
// resend status if online
3330
setStatusDirect(d->loginStatus);
3334
void PsiAccount::pgpKeysUpdated()
3336
OpenPGP::KeyList list = d->psi->pgp()->publicKeys();
3338
// are there any sigs that need verifying?
3339
QPtrListIterator<UserListItem> it(d->userList);
3340
for(UserListItem *u; (u = it.current()); ++it) {
3341
UserResourceList &rl = u->userResourceList();
3342
for(UserResourceList::Iterator rit = rl.begin(); rit != rl.end(); ++rit) {
3343
UserResource &r = *rit;
3344
if(!r.status().xsigned().isEmpty() && r.pgpVerifyStatus() == OpenPGP::VerifyNoKey) {
3345
bool haveKey = false;
3346
QString key = r.publicKeyID();
3347
for(OpenPGP::KeyList::ConstIterator kit = list.begin(); kit != list.end(); ++kit) {
3348
if((*kit).keyID() == key) {
3360
void PsiAccount::verifyStatus(const Jid &j, const Status &s)
3362
PGPTransaction *t = new PGPTransaction(d->psi->pgp());
3364
connect(t, SIGNAL(finished(bool)), SLOT(pgp_verifyFinished(bool)));
3365
QCString cs = s.status().utf8();
3366
QByteArray buf(cs.length());
3367
memcpy(buf.data(), cs.data(), buf.size());
3368
t->verify(buf, OpenPGP::addHeaderFooter(s.xsigned(), 1));
3370
//printf("%s: verifying\n", j.full().latin1());
3373
void PsiAccount::trySignPresence()
3375
OpenPGP::Request *r = new OpenPGP::Request(d->psi->pgp());
3376
connect(r, SIGNAL(finished(bool)), SLOT(pgp_signFinished(bool)));
3377
connect(r, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
3378
QCString cs = d->loginStatus.status().utf8();
3379
QByteArray buf(cs.length());
3380
memcpy(buf.data(), cs.data(), buf.size());
3381
r->sign(buf, d->cur_pgpSecretKeyID);
3384
void PsiAccount::pgp_needPassphrase()
3386
OpenPGP::Request *r = (OpenPGP::Request *)sender();
3389
if(!d->passphrase.isEmpty())
3390
r->submitPassphrase(d->passphrase);
3395
void PsiAccount::promptPassphrase()
3398
d->ppdlg->unblock();
3400
PassphraseDlg *w = new PassphraseDlg(0);
3401
connect(w, SIGNAL(submitPassphrase(const QString &)), SLOT(submitPassphrase(const QString &)));
3402
connect(w, SIGNAL(rejectPassphrase()), SLOT(rejectPassphrase()));
3403
w->setCaption(tr("%1: OpenPGP Passphrase").arg(name()));
3410
void PsiAccount::submitPassphrase(const QString &pp)
3412
d->ppreq->submitPassphrase(pp);
3415
void PsiAccount::rejectPassphrase()
3421
d->ppreq->deleteLater();
3426
void PsiAccount::pgp_signFinished(bool ok)
3428
OpenPGP::Request *r = (OpenPGP::Request *)sender();
3429
bool badPassphrase = r->badPassphrase();
3432
sig = r->signature();
3434
d->passphrase = d->ppdlg->passphrase();
3435
if(d->acc.opt_passphrase) {
3436
d->acc.pgpPassphrase = d->passphrase;
3437
// TODO: save account?
3443
Status s = d->loginStatus;
3444
s.setXSigned(OpenPGP::stripHeaderFooter(sig));
3450
QMessageBox::information(d->ppdlg ? d->ppdlg : 0, CAP(tr("Error")), tr("You entered a bad passphrase. Please try again."));
3451
QTimer::singleShot(0, this, SLOT(trySignPresence()));
3455
QMessageBox::information(d->ppdlg ? d->ppdlg : 0, CAP(tr("Error")), tr("There was an error during OpenPGP processing. Check your settings and try again."));
3460
// remove the dialog if it is there
3462
d->ppdlg->deleteLater();
3467
void PsiAccount::pgp_verifyFinished(bool b)
3469
PGPTransaction *t = (PGPTransaction *)sender();
3472
//printf("%s: verify complete\n", j.full().latin1());
3473
QPtrList<UserListItem> list = findRelavent(j);
3474
QPtrListIterator<UserListItem> it(list);
3475
for(UserListItem *u; (u = it.current()); ++it) {
3476
UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
3477
bool found = (rit == u->userResourceList().end()) ? false: true;
3480
UserResource &ur = *rit;
3483
//printf("vergood\n");
3484
ur.setPublicKeyID(t->keyID());
3485
ur.setPGPVerifyStatus(t->verifyResult());
3486
ur.setSigTimestamp(t->timestamp());
3488
// if the key doesn't match the assigned key, unassign it
3489
if(t->keyID() != u->publicKeyID())
3490
u->setPublicKeyID("");
3493
//QMessageBox::information(0, "sig verify error", QString("error verifying [%1]").arg(u->jid().full()));
3494
ur.setPGPVerifyStatus(OpenPGP::VerifyError);
3497
//printf("updating [%s]\n", u->jid().full().latin1());
3504
int PsiAccount::sendMessageEncrypted(const Message &_m)
3506
if(!ensureKey(_m.to()))
3508
QString key = findFirstRelavent(_m.to())->publicKeyID();
3510
PGPTransaction *pt = new PGPTransaction(d->psi->pgp());
3512
pt->setMessage(m); // keep a copy
3513
//QByteArray a = m.generateEncryptablePayload(d->client->doc());
3514
QCString cs = m.body().utf8();
3515
QByteArray a(cs.length());
3516
memcpy(a.data(), cs.data(), a.size());
3521
connect(pt, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
3522
pt->encrypt(a, rcpt);
3527
void PsiAccount::pgp_finished(bool b)
3529
PGPTransaction *pt = (PGPTransaction *)sender();
3532
if(pt->type() == OpenPGP::Encrypt) {
3534
Message m = pt->message();
3535
// log the message here, before we encrypt it
3536
if(d->acc.opt_log) {
3537
MessageEvent *me = new MessageEvent(m, this);
3538
me->setOriginLocal(true);
3539
me->setTimeStamp(QDateTime::currentDateTime());
3540
logEvent(m.to(), me);
3545
mwrap.setTo(m.to());
3546
mwrap.setType(m.type());
3547
QString enc = OpenPGP::stripHeaderFooter(pt->encrypted());
3548
mwrap.setBody(tr("[This message is encrypted.]"));
3549
mwrap.setXEncrypted(enc);
3550
mwrap.setWasEncrypted(true);
3551
// FIXME: Should be done cleaner, with an extra method in Iris
3552
if (m.containsEvent(OfflineEvent)) mwrap.addEvent(OfflineEvent);
3553
if (m.containsEvent(DeliveredEvent)) mwrap.addEvent(DeliveredEvent);
3554
if (m.containsEvent(DisplayedEvent)) mwrap.addEvent(DisplayedEvent);
3555
if (m.containsEvent(ComposingEvent)) mwrap.addEvent(ComposingEvent);
3556
if (m.containsEvent(CancelEvent)) mwrap.addEvent(CancelEvent);
3557
dj_sendMessage(mwrap);
3559
encryptedMessageSent(x, b);
3565
void PsiAccount::pgp_decryptFinished(bool b)
3567
PGPTransaction *pt = (PGPTransaction *)sender();
3569
bool tryAgain = false;
3571
Message m = pt->message();
3572
//if(m.applyDecryptedPayload(pt->decrypted(), d->client->doc()))
3573
QByteArray buf = pt->decrypted();
3574
QCString cs(buf.size()+1);
3575
memcpy(cs.data(), buf.data(), buf.size());
3576
QString str = QString::fromUtf8(cs);
3578
m.setXEncrypted("");
3579
m.setWasEncrypted(true);
3580
processIncomingMessage(m);
3583
// QMessageBox::information(0, CAP(tr("Error")), tr("A successful decryption operation resulted in an invalid message, so it has been ignored."));
3588
m.setTo(pt->message().from());
3590
m.setBody(pt->message().body());
3592
err.condition = 500;
3593
err.text = "Unable to decrypt";
3595
d->client->sendMessage(m);
3602
processEncryptedMessageNext();
3605
processEncryptedMessageDone();
3609
void PsiAccount::processEncryptedMessage(const Message &m)
3612
PGPTransaction *t = new PGPTransaction(d->psi->pgp());
3613
t->setMessage(m); // keep a copy
3614
connect(t, SIGNAL(needPassphrase()), SLOT(pgp_needPassphrase()));
3615
connect(t, SIGNAL(finished(bool)), SLOT(pgp_decryptFinished(bool)));
3616
QString str = OpenPGP::addHeaderFooter(m.xencrypted(), 0);
3620
void PsiAccount::processMessageQueue()
3622
while(!d->messageQueue.isEmpty()) {
3623
Message *mp = d->messageQueue.getFirst();
3626
if(d->psi->pgp() && !mp->xencrypted().isEmpty()) {
3627
processEncryptedMessageNext();
3631
processIncomingMessage(*mp);
3632
d->messageQueue.removeRef(mp);
3636
void PsiAccount::processEncryptedMessageNext()
3638
// 'peek' and try to process it
3639
Message *mp = d->messageQueue.getFirst();
3640
processEncryptedMessage(*mp);
3643
void PsiAccount::processEncryptedMessageDone()
3645
// 'pop' the message
3646
Message *mp = d->messageQueue.getFirst();
3647
d->messageQueue.removeRef(mp);
3649
// do the rest of the queue
3650
processMessageQueue();
3653
void PsiAccount::optionsUpdate()
3655
d->cp->updateEntry(d->self);
3658
QString PsiAccount::resultToString(int result)
3662
case QCA::TLS::NoCert:
3663
s = tr("The server did not present a certificate.");
3665
case QCA::TLS::Valid:
3666
s = tr("Certificate is valid.");
3668
case QCA::TLS::HostMismatch:
3669
s = tr("The hostname does not match the one the certificate was issued to.");
3671
case QCA::TLS::Rejected:
3672
s = tr("Root CA is marked to reject the specified purpose.");
3674
case QCA::TLS::Untrusted:
3675
s = tr("Certificate not trusted for the required purpose.");
3677
case QCA::TLS::SignatureFailed:
3678
s = tr("Invalid signature.");
3680
case QCA::TLS::InvalidCA:
3681
s = tr("Invalid CA certificate.");
3683
case QCA::TLS::InvalidPurpose:
3684
s = tr("Invalid certificate purpose.");
3686
case QCA::TLS::SelfSigned:
3687
s = tr("Certificate is self-signed.");
3689
case QCA::TLS::Revoked:
3690
s = tr("Certificate has been revoked.");
3692
case QCA::TLS::PathLengthExceeded:
3693
s = tr("Maximum certificate chain length exceeded.");
3695
case QCA::TLS::Expired:
3696
s = tr("Certificate has expired.");
3698
case QCA::TLS::Unknown:
3700
s = tr("General certificate validation error.");
3706
void PsiAccount::invokeGCMessage(const Jid &j)
3708
GCContact *c = findGCContact(j);
3712
// create dummy item, open chat, then destroy item. HORRIBLE HACK!
3713
UserListItem *u = new UserListItem;
3715
u->setInList(false);
3716
u->setName(j.resource());
3717
u->setPrivate(true);
3718
//u->setAvatarFactory(d->avatarFactory); // Avatars
3720
// make a resource so the contact appears online
3722
ur.setName(j.resource());
3723
ur.setStatus(c->status);
3724
u->userResourceList().append(ur);
3726
d->userList.append(u);
3727
actionSendMessage(j);
3728
d->userList.remove(u);
3731
void PsiAccount::invokeGCChat(const Jid &j)
3733
GCContact *c = findGCContact(j);
3737
// create dummy item, open chat, then destroy item. HORRIBLE HACK!
3738
UserListItem *u = new UserListItem;
3740
u->setInList(false);
3741
u->setName(j.resource());
3742
u->setPrivate(true);
3743
//u->setAvatarFactory(d->avatarFactory); // Avatars
3745
// make a resource so the contact appears online
3747
ur.setName(j.resource());
3748
ur.setStatus(c->status);
3749
u->userResourceList().append(ur);
3751
d->userList.append(u);
3753
d->userList.remove(u);
3756
void PsiAccount::invokeGCInfo(const Jid &j)
3761
void PsiAccount::invokeGCFile(const Jid &j)
3766
void PsiAccount::toggleSecurity(const Jid &j, bool b)
3768
UserListItem *u = findFirstRelavent(j);
3772
bool isBare = j.resource().isEmpty();
3773
bool isPriority = false;
3775
// sick sick sick sick sick sick
3776
UserResource *r1, *r2;
3778
UserResourceList::Iterator it1 = u->userResourceList().find(j.resource());
3779
UserResourceList::Iterator it2 = u->userResourceList().priority();
3780
r1 = (it1 != u->userResourceList().end() ? &(*it1) : 0);
3781
r2 = (it2 != u->userResourceList().end() ? &(*it2) : 0);
3782
if(r1 && (r1 == r2))
3785
bool needUpdate = false;
3786
bool sec = u->isSecure(j.resource());
3788
u->setSecure(j.resource(), b);
3792
sec = u->isSecure(r2->name());
3794
u->setSecure(r2->name(), b);
3799
sec = u->isSecure("");
3801
u->setSecure("", b);
3810
bool PsiAccount::ensureKey(const Jid &j)
3815
UserListItem *u = findFirstRelavent(j);
3820
if(u->publicKeyID().isEmpty()) {
3821
// does the user have any presence signed with a key?
3823
const UserResourceList &rl = u->userResourceList();
3824
for(UserResourceList::ConstIterator it = rl.begin(); it != rl.end(); ++it) {
3825
const UserResource &r = *it;
3826
if(!r.publicKeyID().isEmpty()) {
3827
akey = r.publicKeyID();
3832
bool inList = false;
3833
OpenPGP::KeyList list = d->psi->pgp()->publicKeys();
3834
for(OpenPGP::KeyList::ConstIterator lit = list.begin(); lit != list.end(); ++lit) {
3835
const OpenPGP::Key &k = *lit;
3836
if(k.keyID() == akey) {
3842
if(akey.isEmpty() || !inList) {
3843
int n = QMessageBox::information(0, CAP(tr("No key")), tr(
3844
"<p>Psi was unable to locate the OpenPGP key to use for <b>%1</b>.<br>"
3846
"This can happen if you do not have the key that the contact is advertising "
3847
"via signed presence, or if the contact is not advertising any key at all.</p>"
3848
).arg(u->jid().full()), tr("&Choose key manually"), tr("Do ¬hing"));
3853
PGPKeyDlg *w = new PGPKeyDlg(list, akey, 0);
3854
w->setCaption(tr("Public Key: %1").arg(j.full()));
3857
if(r == QDialog::Accepted)
3862
u->setPublicKeyID(key);
3870
//----------------------------------------------------------------------------
3872
//----------------------------------------------------------------------------
3873
class KeyViewItem : public QListViewItem
3876
KeyViewItem(const QString &_keyID, QListView *par)
3885
class PGPKeyDlg::Private
3894
PGPKeyDlg::PGPKeyDlg(const OpenPGP::KeyList &list, const QString &choose, QWidget *parent, const char *name)
3895
:PGPKeyUI(parent, name, true)
3899
connect(lv_keys, SIGNAL(doubleClicked(QListViewItem *)), SLOT(qlv_doubleClicked(QListViewItem *)));
3900
connect(pb_ok, SIGNAL(clicked()), SLOT(do_accept()));
3901
connect(pb_cancel, SIGNAL(clicked()), SLOT(reject()));
3903
QListViewItem *isel = 0;
3904
for(OpenPGP::KeyList::ConstIterator it = list.begin(); it != list.end(); ++it) {
3905
const OpenPGP::Key &k = *it;
3906
KeyViewItem *i = new KeyViewItem(k.keyID(), lv_keys);
3907
i->setPixmap(0, IconsetFactory::icon("psi/keySingle"));
3908
i->setText(0, k.keyID().right(8));
3909
i->setText(1, k.userID());
3911
if(!choose.isEmpty() && k.keyID() == choose) {
3912
lv_keys->setSelected(i, true);
3916
if(lv_keys->childCount() > 0 && !isel)
3917
lv_keys->setSelected(lv_keys->firstChild(), true);
3919
lv_keys->ensureItemVisible(isel);
3922
PGPKeyDlg::~PGPKeyDlg()
3927
QString PGPKeyDlg::keyID() const
3932
QString PGPKeyDlg::userID() const
3937
void PGPKeyDlg::qlv_doubleClicked(QListViewItem *i)
3939
lv_keys->setSelected(i, true);
3943
void PGPKeyDlg::do_accept()
3945
KeyViewItem *i = (KeyViewItem *)lv_keys->selectedItem();
3947
QMessageBox::information(this, tr("Error"), tr("Please select a key."));
3950
d->keyID = i->keyID;
3951
d->userID = i->text(1);
3955
#include "psiaccount.moc"