~ubuntu-branches/ubuntu/oneiric/psi/oneiric

« back to all changes in this revision

Viewing changes to src/psiaccount.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jan Niehusmann
  • Date: 2005-01-10 17:41:43 UTC
  • mfrom: (1.2.1 upstream) (2.1.2 hoary)
  • Revision ID: james.westby@ubuntu.com-20050110174143-ltocv5zapl6blf5d
Tags: 0.9.3-1
* New upstream release
* Cleaned up debian/rules (some things are done by upstream Makefiles now)
* Fixed some lintian warnings:
  - removed executable bit from some .png files
  - moved psi.desktop to /usr/share/applications
* Updated menu files

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * psiaccount.cpp - handles a Psi account
 
3
 * Copyright (C) 2001, 2002  Justin Karneges
 
4
 *
 
5
 * This program is free software; you can redistribute it and/or
 
6
 * modify it under the terms of the GNU General Public License
 
7
 * as published by the Free Software Foundation; either version 2
 
8
 * of the License, or (at your option) any later version.
 
9
 *
 
10
 * This program is distributed in the hope that it will be useful,
 
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
13
 * GNU General Public License for more details.
 
14
 *
 
15
 * You should have received a copy of the GNU General Public License
 
16
 * along with this library; if not, write to the Free Software
 
17
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 *
 
19
 */
 
20
 
 
21
#include"psiaccount.h"
 
22
 
 
23
#include<qinputdialog.h>
 
24
#include<qptrlist.h>
 
25
#include<qcstring.h>
 
26
#include<qtimer.h>
 
27
#include<qmessagebox.h>
 
28
#include<qguardedptr.h>
 
29
#include<qapplication.h>
 
30
#include<qpushbutton.h>
 
31
#include<qlayout.h>
 
32
#include<qobjectlist.h>
 
33
#include<qurl.h>
 
34
#include<qca.h>
 
35
#include<qfileinfo.h>
 
36
 
 
37
#include"psicon.h"
 
38
#include"profiles.h"
 
39
#include"im.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"
 
45
#include"s5b.h"
 
46
#include"filetransfer.h"
 
47
#include"accountdlg.h"
 
48
#include"changepwdlg.h"
 
49
#include"xmlconsole.h"
 
50
#include"userlist.h"
 
51
#include"eventdlg.h"
 
52
#include"chatdlg.h"
 
53
#include"groupchatdlg.h"
 
54
#include"statusdlg.h"
 
55
#include"infodlg.h"
 
56
#include"adduserdlg.h"
 
57
#include"historydlg.h"
 
58
#include"servicesdlg.h"
 
59
#include"discodlg.h"
 
60
#include"eventdb.h"
 
61
#include"jltest.h"
 
62
#include"passphrasedlg.h"
 
63
#include"vcardfactory.h"
 
64
//#include"qssl.h"
 
65
#include"sslcertdlg.h"
 
66
#include"qwextend.h"
 
67
#include"psipopup.h"
 
68
#include"fancylabel.h"
 
69
#include"iconwidget.h"
 
70
#include"base64.h"
 
71
#include"filetransdlg.h"
 
72
//#include"avatars.h"
 
73
 
 
74
#include"bsocket.h"
 
75
/*#ifdef Q_WS_WIN
 
76
#include<windows.h>
 
77
typedef int socklen_t;
 
78
#else
 
79
#include<sys/socket.h>
 
80
#include<netinet/in.h>
 
81
#endif*/
 
82
 
 
83
//----------------------------------------------------------------------------
 
84
// AccountLabel
 
85
//----------------------------------------------------------------------------
 
86
AccountLabel::AccountLabel(PsiAccount *_pa, QWidget *par, bool smode)
 
87
:QLabel(par)
 
88
{
 
89
        pa = _pa;
 
90
        simpleMode = smode;
 
91
        setFrameStyle( QFrame::Panel | QFrame::Sunken );
 
92
 
 
93
        updateName();
 
94
        connect(pa, SIGNAL(updatedAccount()), this, SLOT(updateName()));
 
95
        connect(pa, SIGNAL(destroyed()), this, SLOT(deleteMe()));
 
96
}
 
97
 
 
98
AccountLabel::~AccountLabel()
 
99
{
 
100
}
 
101
 
 
102
void AccountLabel::updateName()
 
103
{
 
104
        setText(simpleMode ? pa->name() : pa->nameWithJid());
 
105
}
 
106
 
 
107
void AccountLabel::deleteMe()
 
108
{
 
109
        delete this;
 
110
}
 
111
 
 
112
 
 
113
//----------------------------------------------------------------------------
 
114
// PGPTransaction
 
115
//----------------------------------------------------------------------------
 
116
static int transid = 0;
 
117
class PGPTransaction::Private
 
118
{
 
119
public:
 
120
        Private() {}
 
121
 
 
122
        int id;
 
123
        Message m;
 
124
        QDomElement e;
 
125
        Jid j;
 
126
};
 
127
 
 
128
PGPTransaction::PGPTransaction(OpenPGP::Engine *pgp)
 
129
:OpenPGP::Request(pgp)
 
130
{
 
131
        d = new Private;
 
132
        d->id = transid++;
 
133
}
 
134
 
 
135
PGPTransaction::~PGPTransaction()
 
136
{
 
137
        delete d;
 
138
}
 
139
 
 
140
int PGPTransaction::id() const
 
141
{
 
142
        return d->id;
 
143
}
 
144
 
 
145
void PGPTransaction::setMessage(const Message &m)
 
146
{
 
147
        d->m = m;
 
148
}
 
149
 
 
150
const Message & PGPTransaction::message() const
 
151
{
 
152
        return d->m;
 
153
}
 
154
 
 
155
const QDomElement & PGPTransaction::xml() const
 
156
{
 
157
        return d->e;
 
158
}
 
159
 
 
160
void PGPTransaction::setXml(const QDomElement &e)
 
161
{
 
162
        d->e = e;
 
163
}
 
164
 
 
165
Jid PGPTransaction::jid() const
 
166
{
 
167
        return d->j;
 
168
}
 
169
 
 
170
void PGPTransaction::setJid(const Jid &j)
 
171
{
 
172
        d->j = j;
 
173
}
 
174
 
 
175
//----------------------------------------------------------------------------
 
176
// BlockTransportPopup -- blocks popups on transport status changes
 
177
//----------------------------------------------------------------------------
 
178
 
 
179
class BlockTransportPopupList;
 
180
 
 
181
class BlockTransportPopup : public QObject
 
182
{
 
183
        Q_OBJECT
 
184
public:
 
185
        BlockTransportPopup(QObject *, const Jid &);
 
186
 
 
187
        Jid jid() const;
 
188
 
 
189
private slots:
 
190
        void timeout();
 
191
 
 
192
private:
 
193
        Jid j;
 
194
        int userCounter;
 
195
        friend class BlockTransportPopupList;
 
196
};
 
197
 
 
198
BlockTransportPopup::BlockTransportPopup(QObject *parent, const Jid &_j)
 
199
: QObject(parent)
 
200
{
 
201
        j = _j;
 
202
        userCounter = 0;
 
203
 
 
204
        // Hack for ICQ SMS
 
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
 
208
        }
 
209
 
 
210
        QTimer::singleShot(15000, this, SLOT(timeout()));
 
211
}
 
212
 
 
213
void BlockTransportPopup::timeout()
 
214
{
 
215
        if ( userCounter > 1 ) {
 
216
                QTimer::singleShot(15000, this, SLOT(timeout()));
 
217
                userCounter = 0;
 
218
        }
 
219
        else
 
220
                deleteLater();
 
221
}
 
222
 
 
223
Jid BlockTransportPopup::jid() const
 
224
{
 
225
        return j;
 
226
}
 
227
 
 
228
//----------------------------------------------------------------------------
 
229
// BlockTransportPopupList
 
230
//----------------------------------------------------------------------------
 
231
 
 
232
class BlockTransportPopupList : public QObject
 
233
{
 
234
        Q_OBJECT
 
235
public:
 
236
        BlockTransportPopupList();
 
237
 
 
238
        bool find(const Jid &, bool online = false);
 
239
};
 
240
 
 
241
BlockTransportPopupList::BlockTransportPopupList()
 
242
: QObject()
 
243
{
 
244
}
 
245
 
 
246
bool BlockTransportPopupList::find(const Jid &j, bool online)
 
247
{
 
248
        if ( j.user().isEmpty() ) // always show popups for transports
 
249
                return false;
 
250
 
 
251
        QObjectList *list = queryList("BlockTransportPopup");
 
252
        QObjectListIt it( *list );
 
253
 
 
254
        BlockTransportPopup *btp;
 
255
        for ( ; it.current(); ++it) {
 
256
                btp = (BlockTransportPopup *)it.current();
 
257
                if ( j.host() == btp->jid().host() ) {
 
258
                        if ( online )
 
259
                                btp->userCounter++;
 
260
                        return true;
 
261
                }
 
262
        }
 
263
        delete list;
 
264
 
 
265
        return false;
 
266
}
 
267
 
 
268
//----------------------------------------------------------------------------
 
269
// PsiAccount
 
270
//----------------------------------------------------------------------------
 
271
struct item_dialog2
 
272
{
 
273
        QWidget *widget;
 
274
        QString className;
 
275
        Jid jid;
 
276
};
 
277
 
 
278
class PsiAccount::Private : public QObject
 
279
{
 
280
        Q_OBJECT
 
281
public:
 
282
        Private() {}
 
283
 
 
284
        PsiCon *psi;
 
285
        Client *client;
 
286
        ContactProfile *cp;
 
287
        UserAccount acc, accnext;
 
288
        Jid jid, nextJid;
 
289
        Status loginStatus;
 
290
        QPtrList<item_dialog2> dialogList;
 
291
        EventQueue *eventQueue;
 
292
        XmlConsole *xmlConsole;
 
293
        UserList userList;
 
294
        UserListItem self;
 
295
        int lastIdle;
 
296
        Status lastStatus, origStatus;
 
297
        bool nickFromVCard;
 
298
        QString cur_pgpSecretKeyID;
 
299
        QPtrList<Message> messageQueue;
 
300
        BlockTransportPopupList *blockTransportPopupList;
 
301
        int userCounter;
 
302
 
 
303
        // Avatars
 
304
        //AvatarFactory* avatarFactory;
 
305
 
 
306
        // pgp related
 
307
        PassphraseDlg *ppdlg;
 
308
        QGuardedPtr<OpenPGP::Request> ppreq;
 
309
        QString passphrase;
 
310
 
 
311
        QPtrList<GCContact> gcbank;
 
312
        QStringList groupchats;
 
313
 
 
314
        AdvancedConnector *conn;
 
315
        ClientStream *stream;
 
316
        QCA::TLS *tls;
 
317
        QCATLSHandler *tlsHandler;
 
318
        QPtrList<QCA::Cert> certList;
 
319
        bool usingSSL;
 
320
        bool doPopups;
 
321
 
 
322
        QHostAddress localAddress;
 
323
 
 
324
        QString pathToProfileEvents()
 
325
        {
 
326
                return pathToProfile(activeProfile) + "/events-" + acc.name + ".xml";
 
327
        }
 
328
 
 
329
public slots:
 
330
        void queueChanged()
 
331
        {
 
332
                eventQueue->toFile(pathToProfileEvents());
 
333
        }
 
334
 
 
335
        void loadQueue()
 
336
        {
 
337
                bool soundEnabled = useSound;
 
338
                useSound = FALSE; // disable the sound and popups
 
339
                doPopups = FALSE;
 
340
 
 
341
                QFileInfo fi( pathToProfileEvents() );
 
342
                if ( fi.exists() )
 
343
                        eventQueue->fromFile(pathToProfileEvents());
 
344
 
 
345
                useSound = soundEnabled;
 
346
                doPopups = TRUE;
 
347
        }
 
348
};
 
349
 
 
350
PsiAccount::PsiAccount(const UserAccount &acc, PsiCon *parent)
 
351
:QObject(0)
 
352
{
 
353
        d = new Private;
 
354
        d->psi = parent;
 
355
        d->client = 0;
 
356
        d->cp = 0;
 
357
 
 
358
        // Avatars 
 
359
        //d->avatarFactory = new AvatarFactory(this);
 
360
 
 
361
        d->blockTransportPopupList = new BlockTransportPopupList();
 
362
 
 
363
        d->doPopups = true;
 
364
        v_isActive = false;
 
365
        isDisconnecting = false;
 
366
        notifyOnlineOk = false;
 
367
        doReconnect = false;
 
368
        usingAutoStatus = false;
 
369
        presenceSent = false;
 
370
 
 
371
        d->loginStatus = Status("", "");
 
372
        d->lastIdle = 0;
 
373
        d->lastStatus = Status("", "", 0, false);
 
374
 
 
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;
 
384
 
 
385
        // Avatars
 
386
        //d->self.setAvatarFactory(d->avatarFactory);
 
387
 
 
388
        d->messageQueue.setAutoDelete(true);
 
389
        d->ppdlg = 0;
 
390
        d->ppreq = 0;
 
391
 
 
392
        d->gcbank.setAutoDelete(true);
 
393
 
 
394
        // we need to copy groupState, because later initialization will depend on that
 
395
        d->acc.groupState = acc.groupState;
 
396
 
 
397
        // stream
 
398
        d->conn = 0;
 
399
        d->tls = 0;
 
400
        d->tlsHandler = 0;
 
401
        d->stream = 0;
 
402
        d->certList.setAutoDelete(true);
 
403
        d->usingSSL = false;
 
404
 
 
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);
 
411
 
 
412
        d->client->setFileTransferEnabled(true);
 
413
 
 
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()));
 
436
 
 
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 &)));
 
466
 
 
467
        // restore cached roster
 
468
        for(Roster::ConstIterator it = acc.roster.begin(); it != acc.roster.end(); ++it)
 
469
                client_rosterItemUpdated(*it);
 
470
 
 
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()));
 
475
                if(u) {
 
476
                        u->setPublicKeyID(i.data());
 
477
                        cpUpdate(*u);
 
478
                }
 
479
        }
 
480
 
 
481
        setUserAccount(acc);
 
482
 
 
483
        d->psi->link(this);
 
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()));
 
487
 
 
488
        d->psi->setToggles(d->acc.tog_offline, d->acc.tog_away, d->acc.tog_agents, d->acc.tog_hidden,d->acc.tog_self);
 
489
 
 
490
        setEnabled(d->acc.opt_enabled);
 
491
 
 
492
        // auto-login ?
 
493
        if(d->acc.opt_auto && d->acc.opt_enabled)
 
494
                setStatus(Status("", "", d->acc.priority));
 
495
 
 
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();
 
501
        }
 
502
 
 
503
        // load event queue from disk
 
504
        QTimer::singleShot(0, d, SLOT(loadQueue()));
 
505
}
 
506
 
 
507
PsiAccount::~PsiAccount()
 
508
{
 
509
        logout(true);
 
510
        QString str = name();
 
511
 
 
512
        d->messageQueue.clear();
 
513
 
 
514
        // nuke all related dialogs
 
515
        deleteAllDialogs();
 
516
 
 
517
        d->psi->ftdlg()->killTransfers(this);
 
518
 
 
519
        delete d->cp;
 
520
        delete d->client;
 
521
        cleanupStream();
 
522
        delete d->eventQueue;
 
523
 
 
524
        delete d->blockTransportPopupList;
 
525
 
 
526
        d->psi->unlink(this);
 
527
        delete d;
 
528
 
 
529
        //printf("PsiAccount: [%s] unloaded\n", str.latin1());
 
530
}
 
531
 
 
532
void PsiAccount::cleanupStream()
 
533
{
 
534
        delete d->stream;
 
535
        d->stream = 0;
 
536
 
 
537
        delete d->tls;
 
538
        d->tls = 0;
 
539
        d->tlsHandler = 0;
 
540
 
 
541
        delete d->conn;
 
542
        d->conn = 0;
 
543
 
 
544
        d->certList.clear();
 
545
        d->usingSSL = false;
 
546
 
 
547
        d->localAddress = QHostAddress();
 
548
}
 
549
 
 
550
bool PsiAccount::enabled() const
 
551
{
 
552
        return d->acc.opt_enabled;
 
553
}
 
554
 
 
555
void PsiAccount::setEnabled(bool e)
 
556
{
 
557
        d->acc.opt_enabled = e;
 
558
        d->psi->enableAccount(this, e);
 
559
        d->psi->accountCountChanged();
 
560
        d->cp->setEnabled(e);
 
561
        cpUpdate(d->self);
 
562
        updatedAccount();
 
563
}
 
564
 
 
565
bool PsiAccount::isActive() const
 
566
{
 
567
        return v_isActive;
 
568
}
 
569
 
 
570
bool PsiAccount::isConnected() const
 
571
{
 
572
        return (d->stream && d->stream->isAuthenticated());
 
573
}
 
574
 
 
575
const QString & PsiAccount::name() const
 
576
{
 
577
        return d->acc.name;
 
578
}
 
579
 
 
580
const UserAccount & PsiAccount::userAccount() const
 
581
{
 
582
        d->psi->getToggles(&d->acc.tog_offline, &d->acc.tog_away, &d->acc.tog_agents, &d->acc.tog_hidden,&d->acc.tog_self);
 
583
 
 
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) {
 
589
                if(u->inList())
 
590
                        d->acc.roster += *u;
 
591
 
 
592
                if(!u->publicKeyID().isEmpty())
 
593
                        d->acc.keybind.set(u->jid().full(), u->publicKeyID());
 
594
        }
 
595
 
 
596
        return d->acc;
 
597
}
 
598
 
 
599
UserList *PsiAccount::userList() const
 
600
{
 
601
        return &d->userList;
 
602
}
 
603
 
 
604
Client *PsiAccount::client() const
 
605
{
 
606
        return d->client;
 
607
}
 
608
 
 
609
ContactProfile *PsiAccount::contactProfile() const
 
610
{
 
611
        return d->cp;
 
612
}
 
613
 
 
614
EventQueue *PsiAccount::eventQueue() const
 
615
{
 
616
        return d->eventQueue;
 
617
}
 
618
 
 
619
EDB *PsiAccount::edb() const
 
620
{
 
621
        return d->psi->edb();
 
622
}
 
623
 
 
624
PsiCon *PsiAccount::psi() const
 
625
{
 
626
        return d->psi;
 
627
}
 
628
 
 
629
// Avatars
 
630
//AvatarFactory *PsiAccount::avatarFactory() const
 
631
//{
 
632
//      return d->avatarFactory;
 
633
//}
 
634
 
 
635
QString PsiAccount::pgpKey() const
 
636
{
 
637
        return d->cur_pgpSecretKeyID;
 
638
}
 
639
 
 
640
QHostAddress *PsiAccount::localAddress() const
 
641
{
 
642
        QString s = d->localAddress.toString();
 
643
        if(s == "0.0.0.0")
 
644
                return 0;
 
645
        return &d->localAddress;
 
646
}
 
647
 
 
648
void PsiAccount::setUserAccount(const UserAccount &acc)
 
649
{
 
650
        bool renamed = false;
 
651
        QString oldfname;
 
652
        if(d->acc.name != acc.name) {
 
653
                renamed = true;
 
654
                oldfname = d->pathToProfileEvents();
 
655
        }
 
656
 
 
657
        d->acc = acc;
 
658
 
 
659
        // rename queue file?
 
660
        if(renamed) {
 
661
                QFileInfo oldfi(oldfname);
 
662
                QFileInfo newfi(d->pathToProfileEvents());
 
663
                if(oldfi.exists()) {
 
664
                        QDir dir = oldfi.dir();
 
665
                        dir.rename(oldfi.fileName(), newfi.fileName());
 
666
                }
 
667
        }
 
668
 
 
669
        if(d->stream) {
 
670
                if(d->acc.opt_keepAlive)
 
671
                        d->stream->setNoopTime(55000);  // prevent NAT timeouts every minute
 
672
                else
 
673
                        d->stream->setNoopTime(0);
 
674
        }
 
675
 
 
676
        d->cp->setName(d->acc.name);
 
677
 
 
678
        Jid j = acc.jid;
 
679
        d->nextJid = j;
 
680
        if(!isActive()) {
 
681
                d->jid = j;
 
682
                d->self.setJid(j);
 
683
                d->cp->updateEntry(d->self);
 
684
        }
 
685
        if(!d->nickFromVCard)
 
686
                setNick(j.user());
 
687
 
 
688
        d->self.setPublicKeyID(d->acc.pgpSecretKeyID);
 
689
        if(d->psi->pgp()) {
 
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;
 
695
                        else
 
696
                                d->passphrase = "";
 
697
                        setStatusDirect(d->loginStatus);
 
698
                        pgpKeyChanged();
 
699
                }
 
700
        }
 
701
 
 
702
        cpUpdate(d->self);
 
703
        updatedAccount();
 
704
}
 
705
 
 
706
void PsiAccount::deleteQueueFile()
 
707
{
 
708
        QFileInfo fi(d->pathToProfileEvents());
 
709
        if(fi.exists()) {
 
710
                QDir dir = fi.dir();
 
711
                dir.remove(fi.fileName());
 
712
        }
 
713
}
 
714
 
 
715
const Jid & PsiAccount::jid() const
 
716
{
 
717
        return d->jid;
 
718
}
 
719
 
 
720
QString PsiAccount::nameWithJid() const
 
721
{
 
722
        return (name() + " (" + jid().full() + ')');
 
723
}
 
724
 
 
725
static QCA::Cert readCertXml(const QDomElement &e)
 
726
{
 
727
        QCA::Cert cert;
 
728
        // there should be one child data tag
 
729
        QDomElement data = e.elementsByTagName("data").item(0).toElement();
 
730
        if(!data.isNull())
 
731
                cert.fromDER(Base64::stringToArray(data.text()));
 
732
        return cert;
 
733
}
 
734
 
 
735
static QPtrList<QCA::Cert> getRootCerts(const QStringList &stores)
 
736
{
 
737
        QPtrList<QCA::Cert> list;
 
738
 
 
739
        for(QStringList::ConstIterator dit = stores.begin(); dit != stores.end(); ++dit) {
 
740
                QDir dir(*dit);
 
741
                if(!dir.exists())
 
742
                        continue;
 
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))
 
748
                                continue;
 
749
                        QDomDocument doc;
 
750
                        bool ok = doc.setContent(&f);
 
751
                        f.close();
 
752
                        if(!ok)
 
753
                                continue;
 
754
 
 
755
                        QDomElement base = doc.documentElement();
 
756
                        if(base.tagName() != "store")
 
757
                                continue;
 
758
                        QDomNodeList cl = base.elementsByTagName("certificate");
 
759
 
 
760
                        int num = 0;
 
761
                        for(int n = 0; n < (int)cl.count(); ++n) {
 
762
                                QCA::Cert *cert = new QCA::Cert(readCertXml(cl.item(n).toElement()));
 
763
                                if(cert->isNull()) {
 
764
                                        delete cert;
 
765
                                        continue;
 
766
                                }
 
767
 
 
768
                                ++num;
 
769
                                list.append(cert);
 
770
                        }
 
771
                }
 
772
        }
 
773
 
 
774
        return list;
 
775
}
 
776
 
 
777
// logs on with the active account settings
 
778
void PsiAccount::login()
 
779
{
 
780
        if(isActive() && !doReconnect)
 
781
                return;
 
782
 
 
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."));
 
785
                return;
 
786
        }
 
787
 
 
788
        d->jid = d->nextJid;
 
789
        if(d->psi->pgp()) {
 
790
                d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
 
791
                pgpKeyChanged();
 
792
        }
 
793
 
 
794
        v_isActive = true;
 
795
        isDisconnecting = false;
 
796
        notifyOnlineOk = false;
 
797
        rosterDone = false;
 
798
        presenceSent = false;
 
799
 
 
800
        stateChanged();
 
801
 
 
802
        QString host;
 
803
        int port;
 
804
        if(d->acc.opt_host) {
 
805
                host = d->acc.host;
 
806
                port = d->acc.port;
 
807
        }
 
808
        else {
 
809
                host = d->jid.host();
 
810
                if(d->acc.opt_ssl)
 
811
                        port = 5223;
 
812
                else
 
813
                        port = 5222;
 
814
        }
 
815
 
 
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);
 
827
                                QUrl::encode(v);
 
828
                                u.setQuery(QString("server=") + v);
 
829
                        }
 
830
                        p.setHttpPoll(pi.settings.host, pi.settings.port, u.toString());
 
831
                        p.setPollInterval(2);
 
832
                }
 
833
 
 
834
                if(pi.settings.useAuth)
 
835
                        p.setUserPass(pi.settings.user, pi.settings.pass);
 
836
        }
 
837
 
 
838
        // stream
 
839
        d->conn = new AdvancedConnector;
 
840
        if(d->acc.opt_ssl) {
 
841
                QStringList certDirs;
 
842
                certDirs += g.pathHome + "/certs";
 
843
                certDirs += g.pathBase + "/certs";
 
844
                d->certList = getRootCerts(certDirs);
 
845
 
 
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()));
 
850
        }
 
851
        d->conn->setProxy(p);
 
852
        d->conn->setOptHostPort(host, port);
 
853
        d->conn->setOptSSL(d->acc.opt_ssl);
 
854
 
 
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
 
860
        else
 
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)));
 
870
 
 
871
        Jid j = d->jid.withResource(d->acc.resource);
 
872
        d->client->connectToServer(d->stream, j);
 
873
}
 
874
 
 
875
// disconnect or stop reconnecting
 
876
void PsiAccount::logout(bool fast, const Status &s)
 
877
{
 
878
        if(!isActive())
 
879
                return;
 
880
 
 
881
        // cancel reconnect
 
882
        doReconnect = false;
 
883
 
 
884
        if(loggedIn()) {
 
885
                // send logout status
 
886
                d->client->setPresence(s);
 
887
        }
 
888
 
 
889
        isDisconnecting = true;
 
890
 
 
891
        if(!fast)
 
892
                simulateRosterOffline();
 
893
        v_isActive = false;
 
894
        stateChanged();
 
895
 
 
896
        QTimer::singleShot(0, this, SLOT(disconnect()));
 
897
}
 
898
 
 
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()
 
902
{
 
903
        // disconnect
 
904
        d->client->close();
 
905
        cleanupStream();
 
906
 
 
907
        disconnected();
 
908
}
 
909
 
 
910
bool PsiAccount::loggedIn() const
 
911
{
 
912
        return (v_isActive && presenceSent);
 
913
}
 
914
 
 
915
void PsiAccount::tls_handshaken()
 
916
{
 
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);
 
921
                while(1) {
 
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),
 
925
                                tr("&Details..."),
 
926
                                tr("Co&ntinue"),
 
927
                                tr("&Cancel"), 0, 2);
 
928
                        if(n == 0) {
 
929
                                SSLCertDlg::showCert(cert, r);
 
930
                        }
 
931
                        else if(n == 1) {
 
932
                                d->tlsHandler->continueAfterHandshake();
 
933
                                break;
 
934
                        }
 
935
                        else if(n == 2) {
 
936
                                logout();
 
937
                                break;
 
938
                        }
 
939
                }
 
940
        }
 
941
        else
 
942
                d->tlsHandler->continueAfterHandshake();
 
943
}
 
944
 
 
945
void PsiAccount::cs_connected()
 
946
{
 
947
        // get IP address
 
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();
 
952
/*#else
 
953
                int s = ((BSocket *)bs)->socket();
 
954
                struct sockaddr addr;
 
955
                socklen_t size = sizeof(struct sockaddr);
 
956
                if(!getsockname(s, &addr, &size)) {
 
957
                        Q_UINT32 ipv4addr;
 
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);
 
962
                }
 
963
#endif*/
 
964
        }
 
965
}
 
966
 
 
967
void PsiAccount::cs_securityLayerActivated()
 
968
{
 
969
        d->usingSSL = true;
 
970
        stateChanged();
 
971
}
 
972
 
 
973
void PsiAccount::cs_needAuthParams(bool, bool pass, bool)
 
974
{
 
975
        if(pass)
 
976
                d->stream->setPassword(d->acc.pass);
 
977
        d->stream->continueAfterParams();
 
978
}
 
979
 
 
980
void PsiAccount::cs_authenticated()
 
981
{
 
982
        d->conn->changePollInterval(10); // for http poll, slow down after login
 
983
 
 
984
        d->client->start(d->jid.host(), d->jid.user(), d->acc.pass, d->acc.resource);
 
985
 
 
986
        //printf("PsiAccount: [%s] authenticated\n", name().latin1());
 
987
 
 
988
        // flag roster for delete
 
989
        UserListIt it(d->userList);
 
990
        for(UserListItem *u; (u = it.current()); ++it) {
 
991
                if(u->inList())
 
992
                        u->setFlagForDelete(true);
 
993
        }
 
994
 
 
995
        // ask for roster
 
996
        d->client->rosterRequest();
 
997
}
 
998
 
 
999
void PsiAccount::cs_connectionClosed()
 
1000
{
 
1001
        cs_error(-1);
 
1002
}
 
1003
 
 
1004
void PsiAccount::cs_delayedCloseFinished()
 
1005
{
 
1006
        //printf("PsiAccount: [%s] connection closed\n", name().latin1());
 
1007
}
 
1008
 
 
1009
void PsiAccount::cs_warning(int)
 
1010
{
 
1011
        d->stream->continueAfterWarning();
 
1012
}
 
1013
 
 
1014
void PsiAccount::getErrorInfo(int err, AdvancedConnector *conn, Stream *stream, QCATLSHandler *tlsHandler, QString *_str, bool *_reconn)
 
1015
{
 
1016
        QString str;
 
1017
        bool reconn = false;
 
1018
 
 
1019
        if(err == -1) {
 
1020
                str = tr("Disconnected");
 
1021
                reconn = true;
 
1022
        }
 
1023
        else if(err == XMPP::ClientStream::ErrParse) {
 
1024
                str = tr("XML Parsing Error");
 
1025
                reconn = true;
 
1026
        }
 
1027
        else if(err == XMPP::ClientStream::ErrProtocol) {
 
1028
                str = tr("XMPP Protocol Error");
 
1029
                reconn = true;
 
1030
        }
 
1031
        else if(err == XMPP::ClientStream::ErrStream) {
 
1032
                int x = stream->errorCondition();
 
1033
                QString s;
 
1034
                reconn = true;
 
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)");
 
1039
                        reconn = false;
 
1040
                }
 
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");
 
1049
                        reconn = false;
 
1050
                }
 
1051
                else if(x == XMPP::ClientStream::ResourceConstraint) {
 
1052
                        s = tr("Server out of resources");
 
1053
                        reconn = false;
 
1054
                }
 
1055
                else if(x == XMPP::ClientStream::SystemShutdown)
 
1056
                        s = tr("Server is shutting down");
 
1057
                str = tr("XMPP Stream Error: %1").arg(s);
 
1058
        }
 
1059
        else if(err == XMPP::ClientStream::ErrConnection) {
 
1060
                int x = conn->errorCode();
 
1061
                QString s;
 
1062
                reconn = true;
 
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");
 
1073
                        reconn = false;
 
1074
                }
 
1075
                else if(x == XMPP::AdvancedConnector::ErrStream)
 
1076
                        s = tr("Socket/stream error");
 
1077
                str = tr("Connection Error: %1").arg(s);
 
1078
        }
 
1079
        else if(err == XMPP::ClientStream::ErrNeg) {
 
1080
                int x = stream->errorCondition();
 
1081
                QString s;
 
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");
 
1088
                        reconn = true;
 
1089
                }
 
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);
 
1095
        }
 
1096
        else if(err == XMPP::ClientStream::ErrTLS) {
 
1097
                int x = stream->errorCondition();
 
1098
                QString s;
 
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");
 
1105
                        else
 
1106
                                s = tr("Broken security layer (TLS)");
 
1107
                }
 
1108
                str = s;
 
1109
        }
 
1110
        else if(err == XMPP::ClientStream::ErrAuth) {
 
1111
                int x = stream->errorCondition();
 
1112
                QString s;
 
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);
 
1136
        }
 
1137
        else if(err == XMPP::ClientStream::ErrSecurityLayer)
 
1138
                str = tr("Broken security layer (SASL)");
 
1139
        else
 
1140
                str = tr("None");
 
1141
        //printf("str[%s], reconn=%d\n", str.latin1(), reconn);
 
1142
        *_str = str;
 
1143
        *_reconn = reconn;
 
1144
}
 
1145
 
 
1146
void PsiAccount::cs_error(int err)
 
1147
{
 
1148
        QString str;
 
1149
        bool reconn;
 
1150
 
 
1151
        getErrorInfo(err, d->conn, d->stream, d->tlsHandler, &str, &reconn);
 
1152
 
 
1153
        d->client->close();
 
1154
        cleanupStream();
 
1155
 
 
1156
        //printf("Error: [%s]\n", str.latin1());
 
1157
 
 
1158
        isDisconnecting = true;
 
1159
 
 
1160
        if ( loggedIn() ) { // FIXME: is this condition okay?
 
1161
                simulateRosterOffline();
 
1162
        }
 
1163
 
 
1164
        presenceSent = false; // this stops the idle detector?? (FIXME)
 
1165
 
 
1166
        // Auto-Reconnect?
 
1167
        if(d->acc.opt_reconn && reconn) {
 
1168
                // reconnect in 5 seconds
 
1169
                doReconnect = true;
 
1170
                stateChanged();
 
1171
                QTimer::singleShot(5000, this, SLOT(reconnect()));
 
1172
                return;
 
1173
        }
 
1174
 
 
1175
        v_isActive = false;
 
1176
        stateChanged();
 
1177
        disconnected();
 
1178
 
 
1179
        QMessageBox::critical(0, tr("%1: Server Error").arg(name()), tr("There was an error communicating with the Jabber server.\nDetails: %1").arg(str));
 
1180
}
 
1181
 
 
1182
void PsiAccount::client_rosterRequestFinished(bool success, int, const QString &)
 
1183
{
 
1184
        if(success) {
 
1185
                //printf("PsiAccount: [%s] roster retrieved ok.  %d entries.\n", name().latin1(), d->client->roster().count());
 
1186
 
 
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()));
 
1192
 
 
1193
                                d->eventQueue->clear(u->jid());
 
1194
                                updateReadNext(u->jid());
 
1195
 
 
1196
                                d->cp->removeEntry(u->jid());
 
1197
                                d->userList.removeRef(u);
 
1198
                        }
 
1199
                        else
 
1200
                                ++it;
 
1201
                }
 
1202
        }
 
1203
        else {
 
1204
                //printf("PsiAccount: [%s] error retrieving roster: [%d, %s]\n", name().latin1(), code, str.latin1());
 
1205
        }
 
1206
 
 
1207
        rosterDone = true;
 
1208
        setStatusDirect(d->loginStatus);
 
1209
}
 
1210
 
 
1211
void PsiAccount::client_rosterItemUpdated(const RosterItem &r)
 
1212
{
 
1213
        // see if the item added is already in our local list
 
1214
        UserListItem *u = d->userList.find(r.jid());
 
1215
        if(u) {
 
1216
                u->setFlagForDelete(false);
 
1217
                u->setRosterItem(r);
 
1218
        }
 
1219
        else {
 
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);
 
1225
        }
 
1226
        u->setInList(true);
 
1227
 
 
1228
        // TODO: auto info-grab (??)
 
1229
        // a new entry that is "push"?  [pushinfo]
 
1230
        //if(entry->push) {
 
1231
        //      VCard info;
 
1232
        //      if(readUserInfo(entry->jid, &info) && !info.field[vNickname].isEmpty()) {
 
1233
        //              item->nick = info.field[vNickname];
 
1234
        //      }
 
1235
        //      else {
 
1236
        //              if(localStatus != STATUS_OFFLINE)
 
1237
        //                      serv->getVCard(item->jid);
 
1238
        //      }
 
1239
        //}
 
1240
 
 
1241
        d->cp->updateEntry(*u);
 
1242
}
 
1243
 
 
1244
void PsiAccount::client_rosterItemRemoved(const RosterItem &r)
 
1245
{
 
1246
        UserListItem *u = d->userList.find(r.jid());
 
1247
        if(!u)
 
1248
                return;
 
1249
 
 
1250
        simulateContactOffline(u);
 
1251
 
 
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);
 
1256
        }
 
1257
        // else remove them for good!
 
1258
        else {
 
1259
                d->cp->removeEntry(u->jid());
 
1260
                d->userList.removeRef(u);
 
1261
        }
 
1262
}
 
1263
 
 
1264
void PsiAccount::tryVerify(UserListItem *u, UserResource *ur)
 
1265
{
 
1266
        if(d->psi->pgp())
 
1267
                verifyStatus(u->jid().withResource(ur->name()), ur->status());
 
1268
}
 
1269
 
 
1270
void PsiAccount::client_resourceAvailable(const Jid &j, const Resource &r)
 
1271
{
 
1272
        enum PopupType {
 
1273
                PopupOnline = 0,
 
1274
                PopupStatusChange = 1
 
1275
        };
 
1276
        PopupType popupType = PopupOnline;
 
1277
 
 
1278
        if ( j.user().isEmpty() )
 
1279
                new BlockTransportPopup(d->blockTransportPopupList, j);
 
1280
 
 
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;
 
1287
                bool local = false;
 
1288
                if(u->isSelf() && r.name() == d->client->resource())
 
1289
                        local = true;
 
1290
 
 
1291
                // add/update the resource
 
1292
                QString oldStatus, oldKey;
 
1293
                UserResource *rp;
 
1294
                UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
 
1295
                bool found = (rit == u->userResourceList().end()) ? false: true;
 
1296
                if(!found) {
 
1297
                        popupType = PopupOnline;
 
1298
 
 
1299
                        UserResource ur(r);
 
1300
                        //ur.setSecurityEnabled(true);
 
1301
                        if(local)
 
1302
                                ur.setClient(PROG_NAME,PROG_VERSION,getOSName());
 
1303
                        rp = &(*u->userResourceList().append(ur));
 
1304
 
 
1305
                        if(notifyOnlineOk && !local) {
 
1306
                                doSound = true;
 
1307
                                doAnim = true;
 
1308
                                doPopup = option.ppOnline;
 
1309
                        }
 
1310
 
 
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()));
 
1315
                                jcv->get(j);
 
1316
                                jcv->go(true);
 
1317
                        }
 
1318
                }
 
1319
                else {
 
1320
                        if ( !doPopup )
 
1321
                                popupType = PopupStatusChange;
 
1322
 
 
1323
                        oldStatus = (*rit).status().status();
 
1324
                        oldKey = (*rit).status().keyID();
 
1325
                        rp = &(*rit);
 
1326
 
 
1327
                        (*rit).setResource(r);
 
1328
 
 
1329
                        if (!local)
 
1330
                                doPopup = option.ppStatus;
 
1331
                }
 
1332
 
 
1333
                rp->setPGPVerifyStatus(-1);
 
1334
                if(!rp->status().xsigned().isEmpty())
 
1335
                        tryVerify(u, rp);
 
1336
 
 
1337
                u->setPresenceError("");
 
1338
                cpUpdate(*u, r.name(), true);
 
1339
 
 
1340
                if(doAnim && option.rosterAnim)
 
1341
                        d->cp->animateNick(u->jid());
 
1342
        }
 
1343
        if(doSound)
 
1344
                playSound(option.onevent[eOnline]);
 
1345
 
 
1346
        if ( popupType == PopupOnline && !option.ppOnline )
 
1347
                doPopup = false;
 
1348
        if ( popupType == PopupStatusChange && !option.ppStatus )
 
1349
                doPopup = false;
 
1350
        if(notifyOnlineOk && doPopup && d->doPopups && !d->blockTransportPopupList->find(j, popupType == PopupOnline)) {
 
1351
                QString name;
 
1352
                UserListItem *u = findFirstRelavent(j);
 
1353
 
 
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);
 
1361
        }
 
1362
        else if ( !notifyOnlineOk )
 
1363
                d->userCounter++;
 
1364
}
 
1365
 
 
1366
void PsiAccount::client_resourceUnavailable(const Jid &j, const Resource &r)
 
1367
{
 
1368
        bool doSound = false;
 
1369
        bool doPopup = false;
 
1370
 
 
1371
        if ( j.user().isEmpty() )
 
1372
                new BlockTransportPopup(d->blockTransportPopupList, j);
 
1373
 
 
1374
        QPtrList<UserListItem> list = findRelavent(j);
 
1375
        QPtrListIterator<UserListItem> it(list);
 
1376
        for(UserListItem *u; (u = it.current()); ++it) {
 
1377
                bool local = false;
 
1378
                if(u->isSelf() && r.name() == d->client->resource())
 
1379
                        local = true;
 
1380
 
 
1381
                // remove resource
 
1382
                UserResourceList::Iterator rit = u->userResourceList().find(j.resource());
 
1383
                bool found = (rit == u->userResourceList().end()) ? false: true;
 
1384
                if(found) {
 
1385
                        u->setLastUnavailableStatus(r.status());
 
1386
                        u->userResourceList().remove(rit);
 
1387
 
 
1388
                        if(!u->isAvailable())
 
1389
                                u->setLastAvailable(QDateTime::currentDateTime());
 
1390
 
 
1391
                        if(!u->isAvailable() || u->isSelf()) {
 
1392
                                // don't sound for our own resource
 
1393
                                if(!isDisconnecting && !local) {
 
1394
                                        doSound = true;
 
1395
                                        doPopup = option.ppOffline;
 
1396
                                }
 
1397
                        }
 
1398
                }
 
1399
 
 
1400
                u->setPresenceError("");
 
1401
                cpUpdate(*u, r.name(), true);
 
1402
        }
 
1403
        if(doSound)
 
1404
                playSound(option.onevent[eOffline]);
 
1405
 
 
1406
        if ( !option.ppOffline )
 
1407
                doPopup = false;
 
1408
        if(doPopup && d->doPopups && !d->blockTransportPopupList->find(j)) {
 
1409
                QString name;
 
1410
                UserListItem *u = findFirstRelavent(j);
 
1411
 
 
1412
                PsiPopup *popup = new PsiPopup(PsiPopup::AlertOffline, this);
 
1413
                popup->setData(j, r, u);
 
1414
        }
 
1415
}
 
1416
 
 
1417
void PsiAccount::client_presenceError(const Jid &j, int, const QString &str)
 
1418
{
 
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);
 
1425
        }
 
1426
}
 
1427
 
 
1428
void PsiAccount::client_messageReceived(const Message &_m)
 
1429
{
 
1430
        //check if it's a server message without a from, and set the from appropriately
 
1431
        if (_m.from().isEmpty())
 
1432
        {
 
1433
                _m.from()=jid().domain();
 
1434
        }       
 
1435
 
 
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);
 
1442
                        return;
 
1443
                }
 
1444
        }
 
1445
 
 
1446
        // encrypted message?
 
1447
        if(!pgpKey().isEmpty() && !_m.xencrypted().isEmpty()) {
 
1448
                Message *m = new Message(_m);
 
1449
                d->messageQueue.append(m);
 
1450
                processMessageQueue();
 
1451
                return;
 
1452
        }
 
1453
 
 
1454
        processIncomingMessage(_m);
 
1455
}
 
1456
 
 
1457
void PsiAccount::processIncomingMessage(const Message &_m)
 
1458
{
 
1459
        // skip empty messages
 
1460
        if(_m.body().isEmpty() && _m.urlList().isEmpty() && _m.invite().isEmpty() && !_m.containsEvents())
 
1461
                return;
 
1462
 
 
1463
        // skip headlines?
 
1464
        if(_m.type() == "headline" && option.ignoreHeadline)
 
1465
                return;
 
1466
 
 
1467
        //skip if on ignore list? KEVIN, must not forget
 
1468
 
 
1469
        if(_m.type() == "groupchat") {
 
1470
                GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(_m.from().userHost()));
 
1471
                if(w)
 
1472
                        w->message(_m);
 
1473
                return;
 
1474
        }
 
1475
 
 
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());
 
1479
 
 
1480
        UserListItem *u = findFirstRelavent(_m.from());
 
1481
        if(u) {
 
1482
                if(_m.type() == "chat") u->setLastMessageType(1);
 
1483
                else u->setLastMessageType(0);
 
1484
        }
 
1485
 
 
1486
        Message m = _m;
 
1487
 
 
1488
        // smartchat: try to match up the incoming event to an existing chat
 
1489
        // (prior to 0.9, m.from() always contained a resource)
 
1490
        Jid j;
 
1491
        ChatDlg *c;
 
1492
        QPtrList<UserListItem> ul = findRelavent(m.from());
 
1493
 
 
1494
        // ignore events from non-roster JIDs?
 
1495
        if (ul.isEmpty() && option.ignoreNonRoster)
 
1496
        {
 
1497
                if (option.excludeGroupChatsFromIgnore)
 
1498
                {
 
1499
                        GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(_m.from().userHost()));
 
1500
                        if(!w)
 
1501
                        {
 
1502
                                return;
 
1503
                        }
 
1504
                                
 
1505
                }
 
1506
                else
 
1507
                {
 
1508
                        return;
 
1509
                }
 
1510
        }
 
1511
 
 
1512
        if(ul.isEmpty())
 
1513
                j = m.from().userHost();
 
1514
        else
 
1515
                j = ul.first()->jid();
 
1516
 
 
1517
        c = (ChatDlg *)dialogFind("ChatDlg", j);
 
1518
        if(!c)
 
1519
                c = (ChatDlg *)dialogFind("ChatDlg", m.from().full());
 
1520
 
 
1521
        if(m.type() == "error")
 
1522
                m.setBody(m.error().text + "\n------\n" + m.body());
 
1523
 
 
1524
        // change the type?
 
1525
        if(m.type() != "headline" && m.invite().isEmpty()) {
 
1526
                if(option.incomingAs == 1)
 
1527
                        m.setType("");
 
1528
                else if(option.incomingAs == 2)
 
1529
                        m.setType("chat");
 
1530
                else if(option.incomingAs == 3) {
 
1531
                        if(c != NULL && !c->isHidden())
 
1532
                                m.setType("chat");
 
1533
                        else
 
1534
                                m.setType("");
 
1535
                        }
 
1536
        }
 
1537
 
 
1538
        // urls or subject on a chat message?  convert back to regular message
 
1539
        //if(m.type() == "chat" && (!m.urlList().isEmpty() || !m.subject().isEmpty()))
 
1540
        //      m.setType("");
 
1541
 
 
1542
        MessageEvent *me = new MessageEvent(m, this);
 
1543
        me->setOriginLocal(false);
 
1544
        handleEvent(me);
 
1545
}
 
1546
 
 
1547
void PsiAccount::client_subscription(const Jid &j, const QString &str)
 
1548
{
 
1549
        // if they remove our subscription, then we lost presence
 
1550
        if(str == "unsubscribed") {
 
1551
                UserListItem *u = d->userList.find(j);
 
1552
                if(u)
 
1553
                        simulateContactOffline(u);
 
1554
        }
 
1555
 
 
1556
        AuthEvent *ae = new AuthEvent(j, str, this);
 
1557
        ae->setTimeStamp(QDateTime::currentDateTime());
 
1558
        handleEvent(ae);
 
1559
}
 
1560
 
 
1561
void PsiAccount::client_debugText(const QString &)
 
1562
{
 
1563
        //printf("%s", str.latin1());
 
1564
        //fflush(stdout);
 
1565
}
 
1566
 
 
1567
void PsiAccount::client_incomingJidLink()
 
1568
{
 
1569
        JidLink *jl = d->client->jidLinkManager()->takeIncoming();
 
1570
        if(!jl)
 
1571
                return;
 
1572
        if(link_test) {
 
1573
                printf("[%s] -- Incoming JidLink request from [%s]\n", name().latin1(), jl->peer().full().latin1());
 
1574
                JLTestDlg *w = new JLTestDlg(jl->peer(), jl, this);
 
1575
                w->show();
 
1576
        }
 
1577
        else
 
1578
                jl->deleteLater();
 
1579
}
 
1580
 
 
1581
void PsiAccount::client_incomingFileTransfer()
 
1582
{
 
1583
        FileTransfer *ft = d->client->fileTransferManager()->takeIncoming();
 
1584
        if(!ft)
 
1585
                return;
 
1586
 
 
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());*/
 
1591
 
 
1592
        FileEvent *fe = new FileEvent(ft->peer().full(), ft, this);
 
1593
        fe->setTimeStamp(QDateTime::currentDateTime());
 
1594
        handleEvent(fe);
 
1595
}
 
1596
 
 
1597
void PsiAccount::reconnect()
 
1598
{
 
1599
        if(doReconnect) {
 
1600
                //printf("PsiAccount: [%s] reconnecting...\n", name().latin1());
 
1601
                v_isActive = false;
 
1602
                doReconnect = false;
 
1603
                login();
 
1604
        }
 
1605
}
 
1606
 
 
1607
Status PsiAccount::status() const
 
1608
{
 
1609
        return d->loginStatus;
 
1610
}
 
1611
 
 
1612
void PsiAccount::setStatus(const Status &_s)
 
1613
{
 
1614
        // Block all transports' contacts' status change popups from popping
 
1615
        {
 
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());
 
1621
                }
 
1622
        }
 
1623
 
 
1624
        // cancel auto-status and reconnect
 
1625
        usingAutoStatus = false;
 
1626
        doReconnect = false;
 
1627
 
 
1628
        Status s = _s;
 
1629
        s.setPriority(d->acc.priority);
 
1630
 
 
1631
        d->loginStatus = s;
 
1632
 
 
1633
        if(s.isAvailable()) {
 
1634
                // if client is not active then attempt to login
 
1635
                if(!isActive()) {
 
1636
                        Jid j = d->jid;
 
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."));
 
1639
                                modify();
 
1640
                                return;
 
1641
                        }
 
1642
                        if(!d->acc.opt_pass) {
 
1643
                                bool ok = false;
 
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())
 
1649
                                        d->acc.pass = text;
 
1650
                                else
 
1651
                                        return;
 
1652
                        }
 
1653
 
 
1654
                        // set passphrase
 
1655
                        if(d->acc.opt_passphrase)
 
1656
                                d->passphrase = d->acc.pgpPassphrase;
 
1657
                        else
 
1658
                                d->passphrase = "";
 
1659
 
 
1660
                        login();
 
1661
                }
 
1662
                // change status
 
1663
                else {
 
1664
                        if(rosterDone)
 
1665
                                setStatusDirect(s);
 
1666
 
 
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);
 
1674
                                                j->go(true);
 
1675
                                        }
 
1676
                                }
 
1677
                        }
 
1678
                }
 
1679
        }
 
1680
        else {
 
1681
                if(isActive())
 
1682
                        logout(false, s);
 
1683
        }
 
1684
}
 
1685
 
 
1686
void PsiAccount::setStatusDirect(const Status &_s)
 
1687
{
 
1688
        Status s = _s;
 
1689
        s.setPriority(d->acc.priority);
 
1690
 
 
1691
        //printf("setting status to [%s]\n", s.status().latin1());
 
1692
 
 
1693
        // using pgp?
 
1694
        if(d->psi->pgp() && !d->cur_pgpSecretKeyID.isEmpty()) {
 
1695
                d->loginStatus = s;
 
1696
 
 
1697
                // sign presence
 
1698
                trySignPresence();
 
1699
        }
 
1700
        else {
 
1701
                /*if(d->psi->pgp() && !d->cur_pgpSecretKeyID.isEmpty())
 
1702
                        s.setKeyID(d->cur_pgpSecretKeyID);
 
1703
                else
 
1704
                        s.setKeyID("");*/
 
1705
 
 
1706
                // send presence normally
 
1707
                setStatusActual(s);
 
1708
        }
 
1709
}
 
1710
 
 
1711
void PsiAccount::setStatusActual(const Status &s)
 
1712
{
 
1713
        d->loginStatus = s;
 
1714
 
 
1715
        d->client->setPresence(s);
 
1716
        if(presenceSent) {
 
1717
                stateChanged();
 
1718
        }
 
1719
        else {
 
1720
                presenceSent = true;
 
1721
                stateChanged();
 
1722
                QTimer::singleShot(15000, this, SLOT(enableNotifyOnline()));
 
1723
 
 
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()));
 
1727
                else {
 
1728
                        d->nickFromVCard = true;
 
1729
                        setNick( vcard->nickName() );
 
1730
                }
 
1731
        }
 
1732
}
 
1733
 
 
1734
void PsiAccount::secondsIdle(int x)
 
1735
{
 
1736
        if(!loggedIn())
 
1737
                return;
 
1738
 
 
1739
        int lastIdle = d->lastIdle;
 
1740
        Status lastStatus = d->lastStatus;
 
1741
 
 
1742
        Resource r = *(d->client->resourceList().find(d->client->resource()));
 
1743
        Status ls = r.status();
 
1744
 
 
1745
        //must avoid status change when invisible KEVIN
 
1746
        if(ls.isInvisible())
 
1747
                return;
 
1748
 
 
1749
        if(ls.isAvailable()) {
 
1750
                // no longer idle?
 
1751
                if(lastIdle > x) {
 
1752
                        if(ls.isAway() && usingAutoStatus) {
 
1753
                                lastStatus = d->origStatus;
 
1754
                                setStatusDirect(lastStatus);
 
1755
                                usingAutoStatus = false;
 
1756
                        }
 
1757
                }
 
1758
                else if( !(ls.isAway() && !usingAutoStatus) ) {
 
1759
                        int minutes = x / 60;
 
1760
 
 
1761
                        if(option.use_asOffline && option.asOffline > 0 && minutes >= option.asOffline) {
 
1762
                                lastStatus = Status("", "", d->acc.priority, false);
 
1763
                                usingAutoStatus = false;
 
1764
                                logout();
 
1765
                        }
 
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;
 
1773
                                }
 
1774
                        }
 
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;
 
1782
                                }
 
1783
                        }
 
1784
                }
 
1785
        }
 
1786
 
 
1787
        d->lastIdle = x;
 
1788
        d->lastStatus = lastStatus;
 
1789
}
 
1790
 
 
1791
void PsiAccount::playSound(const QString &str)
 
1792
{
 
1793
        if(str.isEmpty())
 
1794
                return;
 
1795
 
 
1796
        int s = STATUS_OFFLINE;
 
1797
        if(loggedIn())
 
1798
                s = makeSTATUS(status());
 
1799
 
 
1800
        if(s == STATUS_DND)
 
1801
                return;
 
1802
 
 
1803
        // no away sounds?
 
1804
        if(option.noAwaySound && (s == STATUS_AWAY || s == STATUS_XA))
 
1805
                return;
 
1806
 
 
1807
        d->psi->playSound(str);
 
1808
}
 
1809
 
 
1810
QWidget *PsiAccount::dialogFind(const char *className, const Jid &j)
 
1811
{
 
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)) {
 
1816
                        return i->widget;
 
1817
                }
 
1818
        }
 
1819
        return 0;
 
1820
}
 
1821
 
 
1822
void PsiAccount::dialogRegister(QWidget *w, const Jid &j)
 
1823
{
 
1824
        item_dialog2 *i = new item_dialog2;
 
1825
        i->widget = w;
 
1826
        i->className = w->className();
 
1827
        i->jid = j;
 
1828
        d->dialogList.append(i);
 
1829
}
 
1830
 
 
1831
void PsiAccount::dialogUnregister(QWidget *w)
 
1832
{
 
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);
 
1837
                        return;
 
1838
                }
 
1839
        }
 
1840
}
 
1841
 
 
1842
void PsiAccount::deleteAllDialogs()
 
1843
{
 
1844
        QPtrListIterator<item_dialog2> it(d->dialogList);
 
1845
        for(item_dialog2 *i; (i = it.current());)
 
1846
                delete i->widget;
 
1847
        d->dialogList.clear();
 
1848
}
 
1849
 
 
1850
bool PsiAccount::checkConnected(QWidget *par)
 
1851
{
 
1852
        if(!loggedIn()) {
 
1853
                QMessageBox::information(par, CAP(tr("Error")), tr("You must be connected to the server in order to do this."));
 
1854
                return false;
 
1855
        }
 
1856
 
 
1857
        return true;
 
1858
}
 
1859
 
 
1860
void PsiAccount::modify()
 
1861
{
 
1862
        AccountModifyDlg *w = (AccountModifyDlg *)dialogFind("AccountModifyDlg");
 
1863
        if(w)
 
1864
                bringToFront(w);
 
1865
        else {
 
1866
                w = new AccountModifyDlg(this, 0);
 
1867
                w->show();
 
1868
        }
 
1869
}
 
1870
 
 
1871
void PsiAccount::changeVCard()
 
1872
{
 
1873
        actionInfo(d->jid);
 
1874
}
 
1875
 
 
1876
void PsiAccount::changePW()
 
1877
{
 
1878
        if(!checkConnected())
 
1879
                return;
 
1880
 
 
1881
        ChangePasswordDlg *w = (ChangePasswordDlg *)dialogFind("ChangePasswordDlg");
 
1882
        if(w)
 
1883
                bringToFront(w);
 
1884
        else {
 
1885
                w = new ChangePasswordDlg(this);
 
1886
                w->show();
 
1887
        }
 
1888
}
 
1889
 
 
1890
void PsiAccount::showXmlConsole()
 
1891
{
 
1892
        bringToFront(d->xmlConsole);
 
1893
}
 
1894
 
 
1895
void PsiAccount::openAddUserDlg()
 
1896
{
 
1897
        if(!checkConnected())
 
1898
                return;
 
1899
 
 
1900
        AddUserDlg *w = (AddUserDlg *)dialogFind("AddUserDlg");
 
1901
        if(w)
 
1902
                bringToFront(w);
 
1903
        else {
 
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());
 
1910
                        }
 
1911
                        const QStringList &groups = u->groups();
 
1912
                        if(groups.isEmpty())
 
1913
                                continue;
 
1914
                        for(QStringList::ConstIterator git = groups.begin(); git != groups.end(); ++git) {
 
1915
                                if(qstringlistmatch(gl, *git) == -1)
 
1916
                                        gl.append(*git);
 
1917
                        }
 
1918
                }
 
1919
 
 
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)));
 
1922
                w->show();
 
1923
        }
 
1924
}
 
1925
 
 
1926
void PsiAccount::doDisco()
 
1927
{
 
1928
        actionDisco(d->jid.host(), "");
 
1929
}
 
1930
 
 
1931
void PsiAccount::actionDisco(const Jid &j, const QString &node)
 
1932
{
 
1933
        DiscoDlg *w = new DiscoDlg(this, j, node);
 
1934
        connect(w, SIGNAL(featureActivated(QString, Jid, QString)), SLOT(featureActivated(QString, Jid, QString)));
 
1935
        w->show();
 
1936
}
 
1937
 
 
1938
void PsiAccount::featureActivated(QString feature, Jid jid, QString node)
 
1939
{
 
1940
        Features f(feature);
 
1941
 
 
1942
        if ( f.canRegister() )
 
1943
                actionRegister(jid);
 
1944
        else if ( f.canSearch() )
 
1945
                actionSearch(jid);
 
1946
        else if ( f.canGroupchat() )
 
1947
                actionJoin(jid);
 
1948
        else if ( f.canDisco() )
 
1949
                actionDisco(jid, node);
 
1950
        else if ( f.isGateway() )
 
1951
                ; // TODO
 
1952
        else if ( f.haveVCard() )
 
1953
                actionInfo(jid);
 
1954
        else if ( f.id() == Features::FID_Add ) {
 
1955
                QStringList sl;
 
1956
                dj_add(jid, QString::null, sl, true);
 
1957
        }
 
1958
}
 
1959
 
 
1960
void PsiAccount::actionJoin(const Jid &j)
 
1961
{
 
1962
        GCJoinDlg *w = new GCJoinDlg(psi(), this);
 
1963
 
 
1964
        w->le_host->setText ( j.host() );
 
1965
        w->le_room->setText ( j.user() );
 
1966
 
 
1967
        w->show();
 
1968
}
 
1969
 
 
1970
void PsiAccount::stateChanged()
 
1971
{
 
1972
        if(loggedIn())
 
1973
                d->cp->setState(makeSTATUS(status()));
 
1974
        else {
 
1975
                if(isActive()) {
 
1976
                        d->cp->setState(-1);
 
1977
                        if(d->usingSSL)
 
1978
                                d->cp->setUsingSSL(true);
 
1979
                        else
 
1980
                                d->cp->setUsingSSL(false);
 
1981
                }
 
1982
                else {
 
1983
                        d->cp->setState(STATUS_OFFLINE);
 
1984
                        d->cp->setUsingSSL(false);
 
1985
                }
 
1986
        }
 
1987
 
 
1988
        updatedActivity();
 
1989
}
 
1990
 
 
1991
void PsiAccount::simulateContactOffline(UserListItem *u)
 
1992
{
 
1993
        UserResourceList rl = u->userResourceList();
 
1994
        u->setPresenceError("");
 
1995
        if(!rl.isEmpty()) {
 
1996
                for(UserResourceList::ConstIterator rit = rl.begin(); rit != rl.end(); ++rit) {
 
1997
                        const UserResource &r = *rit;
 
1998
                        Jid j = u->jid();
 
1999
                        if(u->jid().resource().isEmpty())
 
2000
                                j.setResource(r.name());
 
2001
                        client_resourceUnavailable(j, r);
 
2002
                }
 
2003
        }
 
2004
        else
 
2005
                cpUpdate(*u);
 
2006
}
 
2007
 
 
2008
void PsiAccount::simulateRosterOffline()
 
2009
{
 
2010
        UserListIt it(d->userList);
 
2011
        for(UserListItem *u; (u = it.current()); ++it)
 
2012
                simulateContactOffline(u);
 
2013
 
 
2014
        // self
 
2015
        {
 
2016
                UserListItem *u = &d->self;
 
2017
                UserResourceList rl = u->userResourceList();
 
2018
                for(UserResourceList::ConstIterator rit = rl.begin(); rit != rl.end(); ++rit) {
 
2019
                        Jid j = u->jid();
 
2020
                        if(u->jid().resource().isEmpty())
 
2021
                                j.setResource((*rit).name());
 
2022
                        u->setPresenceError("");
 
2023
                        client_resourceUnavailable(j, *rit);
 
2024
                }
 
2025
        }
 
2026
 
 
2027
        d->gcbank.clear();
 
2028
}
 
2029
 
 
2030
void PsiAccount::enableNotifyOnline()
 
2031
{
 
2032
        if ( d->userCounter > 1 ) {
 
2033
                QTimer::singleShot(15000, this, SLOT(enableNotifyOnline()));
 
2034
                d->userCounter = 0;
 
2035
        }
 
2036
        else
 
2037
                notifyOnlineOk = true;
 
2038
}
 
2039
 
 
2040
void PsiAccount::slotClientVersionFinished()
 
2041
{
 
2042
        JT_ClientVersion *j = (JT_ClientVersion *)sender();
 
2043
        if(j->success()) {
 
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;
 
2049
                        if(!found)
 
2050
                                continue;
 
2051
 
 
2052
                        (*rit).setClient(j->name(),j->version(),j->os());
 
2053
 
 
2054
                        cpUpdate(*u);
 
2055
                        /*if(u->isSelf()) {
 
2056
                                if(d->cp->self())
 
2057
                                        d->cp->updateSelf(*u);
 
2058
                        }
 
2059
                        else
 
2060
                                d->cp->updateEntry(*u);*/
 
2061
                }
 
2062
        }
 
2063
}
 
2064
 
 
2065
QPtrList<UserListItem> PsiAccount::findRelavent(const Jid &j) const
 
2066
{
 
2067
        QPtrList<UserListItem> list;
 
2068
 
 
2069
        // self?
 
2070
        if(j.compare(d->self.jid(), false))
 
2071
                list.append(&d->self);
 
2072
        else {
 
2073
                QPtrListIterator<UserListItem> it(d->userList);
 
2074
                for(UserListItem *u; (u = it.current()); ++it) {
 
2075
                        if(!u->jid().compare(j, false))
 
2076
                                continue;
 
2077
 
 
2078
                        if(!u->jid().resource().isEmpty()) {
 
2079
                                if(u->jid().resource() != j.resource())
 
2080
                                        continue;
 
2081
                        }
 
2082
                        list.append(u);
 
2083
                }
 
2084
        }
 
2085
 
 
2086
        return list;
 
2087
}
 
2088
 
 
2089
UserListItem *PsiAccount::findFirstRelavent(const Jid &j) const
 
2090
{
 
2091
        QPtrList<UserListItem> list = findRelavent(j);
 
2092
        if(list.isEmpty())
 
2093
                return 0;
 
2094
        else
 
2095
                return list.first();
 
2096
}
 
2097
 
 
2098
UserListItem *PsiAccount::find(const Jid &j) const
 
2099
{
 
2100
        UserListItem *u;
 
2101
        if(j.compare(d->self.jid()))
 
2102
                u = &d->self;
 
2103
        else
 
2104
                u = d->userList.find(j);
 
2105
 
 
2106
        return u;
 
2107
}
 
2108
 
 
2109
void PsiAccount::cpUpdate(const UserListItem &u, const QString &rname, bool fromPresence)
 
2110
{
 
2111
        PsiEvent *e = d->eventQueue->peek(u.jid());
 
2112
 
 
2113
        d->cp->updateEntry(u);
 
2114
 
 
2115
        if(e) {
 
2116
                d->cp->setAlert(u.jid(), is->event2icon(e));
 
2117
        }
 
2118
        else
 
2119
                d->cp->clearAlert(u.jid());
 
2120
 
 
2121
        updateContact(u);
 
2122
        Jid j = u.jid();
 
2123
        if(!rname.isEmpty())
 
2124
                j.setResource(rname);
 
2125
        updateContact(j);
 
2126
        updateContact(j, fromPresence);
 
2127
        d->psi->updateContactGlobal(this, j);
 
2128
}
 
2129
 
 
2130
QLabel *PsiAccount::accountLabel(QWidget *par, bool simpleMode)
 
2131
{
 
2132
        AccountLabel *al = new AccountLabel(this, par, simpleMode);
 
2133
        return al;
 
2134
}
 
2135
 
 
2136
EventDlg *PsiAccount::ensureEventDlg(const Jid &j)
 
2137
{
 
2138
        EventDlg *w = (EventDlg *)dialogFind("EventDlg", j);
 
2139
        if(!w) {
 
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 &)));
 
2147
        }
 
2148
 
 
2149
        return w;
 
2150
}
 
2151
 
 
2152
ChatDlg *PsiAccount::ensureChatDlg(const Jid &j)
 
2153
{
 
2154
        ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
 
2155
        if(!c) {
 
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)));
 
2165
        }
 
2166
        else {
 
2167
                // on X11, do a special reparent to open on the right desktop
 
2168
#ifdef Q_WS_X11
 
2169
                if(c->isHidden()) {
 
2170
                        const QPixmap *pp = c->icon();
 
2171
                        QPixmap p;
 
2172
                        if(pp)
 
2173
                                p = *pp;
 
2174
                        reparent_good(c, 0, false);
 
2175
                        if(!p.isNull())
 
2176
                                c->setIcon(p);
 
2177
                }
 
2178
#endif
 
2179
        }
 
2180
 
 
2181
        return c;
 
2182
}
 
2183
 
 
2184
void PsiAccount::changeStatus(int x)
 
2185
{
 
2186
        if(x == STATUS_OFFLINE && !option.askOffline) {
 
2187
                setStatus(Status("","Logged out",0,false));
 
2188
        }
 
2189
        else {
 
2190
                if(x == STATUS_ONLINE && !option.askOnline) {
 
2191
                        setStatus(Status());
 
2192
                }
 
2193
                else if(x == STATUS_INVISIBLE){
 
2194
                        Status s("","",0,true);
 
2195
                        s.setIsInvisible(true);
 
2196
                        setStatus(s);
 
2197
                }
 
2198
                else {
 
2199
                        StatusSetDlg *w = new StatusSetDlg(this, makeStatus(x, ""));
 
2200
                        connect(w, SIGNAL(set(const Status &)), SLOT(setStatus(const Status &)));
 
2201
                        w->show();
 
2202
                }
 
2203
        }
 
2204
}
 
2205
 
 
2206
void PsiAccount::actionTest(const Jid &j)
 
2207
{
 
2208
        UserListItem *u = find(j);
 
2209
        if(!u)
 
2210
                return;
 
2211
        Jid j2 = j;
 
2212
        if(j.resource().isEmpty()) {
 
2213
                if(u->isAvailable())
 
2214
                        j2.setResource((*u->userResourceList().priority()).name());
 
2215
        }
 
2216
 
 
2217
        //S5BConnection *c = new S5BConnection(d->client->s5bManager());
 
2218
        //c->connectToJid(j2, d->client->s5bManager()->genUniqueSID(j2));
 
2219
        JLTestDlg *w = new JLTestDlg(j2, this);
 
2220
        w->show();
 
2221
}
 
2222
 
 
2223
void PsiAccount::actionSendFile(const Jid &j)
 
2224
{
 
2225
        QStringList l;
 
2226
        actionSendFiles(j, l);
 
2227
}
 
2228
 
 
2229
void PsiAccount::actionSendFiles(const Jid &j, const QStringList& l)
 
2230
{
 
2231
        Jid j2 = j;
 
2232
        if(j.resource().isEmpty()) {
 
2233
                UserListItem *u = find(j);
 
2234
                if(u && u->isAvailable())
 
2235
                        j2.setResource((*u->userResourceList().priority()).name());
 
2236
        }
 
2237
 
 
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.
 
2240
        if (!l.isEmpty()) {
 
2241
                for (QStringList::ConstIterator f = l.begin(); f != l.end(); ++f ) {
 
2242
                        QStringList fl(*f);
 
2243
                        FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, fl);
 
2244
                        w->show();
 
2245
                }
 
2246
        }
 
2247
        else {
 
2248
                FileRequestDlg *w = new FileRequestDlg(j2, d->psi, this, l);
 
2249
                w->show();
 
2250
        }
 
2251
}
 
2252
 
 
2253
void PsiAccount::actionDefault(const Jid &j)
 
2254
{
 
2255
        UserListItem *u = find(j);
 
2256
        if(!u)
 
2257
                return;
 
2258
 
 
2259
        if(d->eventQueue->count(u->jid()) > 0)
 
2260
                openNextEvent(*u);
 
2261
        else {
 
2262
                if(option.defaultAction == 0)
 
2263
                        actionSendMessage(u->jid());
 
2264
                else
 
2265
                        actionOpenChat(u->jid());
 
2266
        }
 
2267
}
 
2268
 
 
2269
void PsiAccount::actionRecvEvent(const Jid &j)
 
2270
{
 
2271
        UserListItem *u = find(j);
 
2272
        if(!u)
 
2273
                return;
 
2274
 
 
2275
        openNextEvent(*u);
 
2276
}
 
2277
 
 
2278
void PsiAccount::actionSendMessage(const Jid &j)
 
2279
{
 
2280
        EventDlg *w = d->psi->createEventDlg(j.full(), this);
 
2281
        w->show();
 
2282
}
 
2283
 
 
2284
void PsiAccount::actionSendMessage(const JidList &j)
 
2285
{
 
2286
        QString str;
 
2287
        bool first = true;
 
2288
        for(QValueList<Jid>::ConstIterator it = j.begin(); it != j.end(); ++it) {
 
2289
                if(!first)
 
2290
                        str += ", ";
 
2291
                first = false;
 
2292
 
 
2293
                str += (*it).full();
 
2294
        }
 
2295
 
 
2296
        EventDlg *w = d->psi->createEventDlg(str, this);
 
2297
        w->show();
 
2298
}
 
2299
 
 
2300
void PsiAccount::actionSendUrl(const Jid &j)
 
2301
{
 
2302
        EventDlg *w = d->psi->createEventDlg(j.full(), this);
 
2303
        w->setUrlOnShow();
 
2304
        w->show();
 
2305
}
 
2306
 
 
2307
void PsiAccount::actionRemove(const Jid &j)
 
2308
{
 
2309
        //avatarFactory()->removeManualAvatar(j); // Avatars
 
2310
        dj_remove(j);
 
2311
}
 
2312
 
 
2313
void PsiAccount::actionRename(const Jid &j, const QString &name)
 
2314
{
 
2315
        dj_rename(j, name);
 
2316
}
 
2317
 
 
2318
void PsiAccount::actionGroupRename(const QString &oldname, const QString &newname)
 
2319
{
 
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);
 
2326
                        cpUpdate(*u);
 
2327
                        if(u->inList())
 
2328
                                nu.append(u);
 
2329
                }
 
2330
        }
 
2331
 
 
2332
        if(!nu.isEmpty()) {
 
2333
                JT_Roster *r = new JT_Roster(d->client->rootTask());
 
2334
 
 
2335
                QPtrListIterator<UserListItem> it(nu);
 
2336
                for(UserListItem *u; (u = it.current()); ++it)
 
2337
                        r->set(u->jid(), u->name(), u->groups());
 
2338
 
 
2339
                r->go(true);
 
2340
        }
 
2341
}
 
2342
 
 
2343
void PsiAccount::actionHistory(const Jid &j)
 
2344
{
 
2345
        HistoryDlg *w = (HistoryDlg *)dialogFind("HistoryDlg", j);
 
2346
        if(w)
 
2347
                bringToFront(w);
 
2348
        else {
 
2349
                w = new HistoryDlg(j, this);
 
2350
                connect(w, SIGNAL(openEvent(PsiEvent *)), SLOT(actionHistoryBox(PsiEvent *)));
 
2351
                w->show();
 
2352
        }
 
2353
}
 
2354
 
 
2355
void PsiAccount::actionHistoryBox(PsiEvent *e)
 
2356
{
 
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 &)));
 
2363
        w->updateEvent(e);
 
2364
        w->show();
 
2365
}
 
2366
 
 
2367
void PsiAccount::actionStatusShow(const Jid &j)
 
2368
{
 
2369
        UserListItem *u = find(j);
 
2370
        if(!u)
 
2371
                return;
 
2372
 
 
2373
        StatusShowDlg *w = new StatusShowDlg(*u);
 
2374
        w->show();
 
2375
}
 
2376
 
 
2377
void PsiAccount::actionOpenChat(const Jid &j)
 
2378
{
 
2379
        UserListItem *u = find(j);
 
2380
        if(!u)
 
2381
                return;
 
2382
 
 
2383
        // if 'j' is bare, we might want to switch to a specific resource
 
2384
        QString res;
 
2385
        if(j.resource().isEmpty()) {
 
2386
                // first, are there any queued chats?
 
2387
                /*PsiEvent *e = d->eventQueue->peekFirstChat(j, false);
 
2388
                if(e) {
 
2389
                        res = e->from().resource();
 
2390
                        // if we have a bare chat, change to 'res'
 
2391
                        ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
 
2392
                        if(c)
 
2393
                                c->setJid(j.withResource(res));
 
2394
                }
 
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)))
 
2399
                                res = pr;
 
2400
                }
 
2401
                else {
 
2402
                        QStringList list = hiddenChats(j);
 
2403
                        if(!list.isEmpty())
 
2404
                                res = list.first();
 
2405
                }
 
2406
        }
 
2407
 
 
2408
        if(!res.isEmpty())
 
2409
                openChat(j.withResource(res));
 
2410
        else
 
2411
                openChat(j);
 
2412
}
 
2413
 
 
2414
void PsiAccount::actionOpenChatSpecific(const Jid &j)
 
2415
{
 
2416
        openChat(j);
 
2417
}
 
2418
 
 
2419
void PsiAccount::actionAgentSetStatus(const Jid &j, int x)
 
2420
{
 
2421
        if ( j.user().isEmpty() ) // add all transport popups to block list
 
2422
                new BlockTransportPopup(d->blockTransportPopupList, j);
 
2423
 
 
2424
        JT_Presence *p = new JT_Presence(d->client->rootTask());
 
2425
        p->pres(j, makeStatus(x, ""));
 
2426
        p->go(true);
 
2427
}
 
2428
 
 
2429
void PsiAccount::actionInfo(const Jid &_j)
 
2430
{
 
2431
        bool useCache = true;
 
2432
        Jid j;
 
2433
        if(findGCContact(_j)) {
 
2434
                useCache = false;
 
2435
                j = _j;
 
2436
        }
 
2437
        else {
 
2438
                j = _j.userHost();
 
2439
        }
 
2440
 
 
2441
        InfoDlg *w = (InfoDlg *)dialogFind("InfoDlg", j);
 
2442
        if(w)
 
2443
                bringToFront(w);
 
2444
        else {
 
2445
                const VCard *vcard = VCardFactory::vcard(j);
 
2446
 
 
2447
                VCard tmp;
 
2448
                if ( vcard )
 
2449
                        tmp = *vcard;
 
2450
                w = new InfoDlg(j.compare(d->jid) ? InfoDlg::Self : InfoDlg::Contact, j, tmp, this, 0, 0, useCache);
 
2451
                w->show();
 
2452
 
 
2453
                // automatically retrieve info if it doesn't exist
 
2454
                if(!vcard && loggedIn())
 
2455
                        w->doRefresh();
 
2456
        }
 
2457
}
 
2458
 
 
2459
void PsiAccount::actionAuth(const Jid &j)
 
2460
{
 
2461
        dj_auth(j);
 
2462
}
 
2463
 
 
2464
void PsiAccount::actionAuthRequest(const Jid &j)
 
2465
{
 
2466
        dj_authReq(j);
 
2467
}
 
2468
 
 
2469
void PsiAccount::actionAuthRemove(const Jid &j)
 
2470
{
 
2471
        dj_deny(j);
 
2472
}
 
2473
 
 
2474
void PsiAccount::actionAdd(const Jid &j)
 
2475
{
 
2476
        dj_addAuth(j);
 
2477
}
 
2478
 
 
2479
void PsiAccount::actionGroupAdd(const Jid &j, const QString &g)
 
2480
{
 
2481
        UserListItem *u = d->userList.find(j);
 
2482
        if(!u)
 
2483
                return;
 
2484
 
 
2485
        if(!u->addGroup(g))
 
2486
                return;
 
2487
        cpUpdate(*u);
 
2488
 
 
2489
        JT_Roster *r = new JT_Roster(d->client->rootTask());
 
2490
        r->set(u->jid(), u->name(), u->groups());
 
2491
        r->go(true);
 
2492
}
 
2493
 
 
2494
void PsiAccount::actionGroupRemove(const Jid &j, const QString &g)
 
2495
{
 
2496
        UserListItem *u = d->userList.find(j);
 
2497
        if(!u)
 
2498
                return;
 
2499
 
 
2500
        if(!u->removeGroup(g))
 
2501
                return;
 
2502
        cpUpdate(*u);
 
2503
 
 
2504
        JT_Roster *r = new JT_Roster(d->client->rootTask());
 
2505
        r->set(u->jid(), u->name(), u->groups());
 
2506
        r->go(true);
 
2507
}
 
2508
 
 
2509
void PsiAccount::actionRegister(const Jid &j)
 
2510
{
 
2511
        if(!checkConnected())
 
2512
                return;
 
2513
 
 
2514
        RegistrationDlg *w = (RegistrationDlg *)dialogFind("RegistrationDlg", j);
 
2515
        if(w)
 
2516
                bringToFront(w);
 
2517
        else {
 
2518
                w = new RegistrationDlg(j, this);
 
2519
                w->show();
 
2520
        }
 
2521
}
 
2522
 
 
2523
void PsiAccount::actionSearch(const Jid &j)
 
2524
{
 
2525
        if(!checkConnected())
 
2526
                return;
 
2527
 
 
2528
        SearchDlg *w = (SearchDlg *)dialogFind("SearchDlg", j);
 
2529
        if(w)
 
2530
                bringToFront(w);
 
2531
        else {
 
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 &)));
 
2535
                w->show();
 
2536
        }
 
2537
}
 
2538
 
 
2539
void PsiAccount::actionInvite(const Jid &j, const QString &gc)
 
2540
{
 
2541
        Message m;
 
2542
        m.setTo(j);
 
2543
        m.setInvite(gc);
 
2544
        m.setBody(tr("You have been invited to %1").arg(gc));
 
2545
        m.setTimeStamp(QDateTime::currentDateTime());
 
2546
        dj_sendMessage(m);
 
2547
}
 
2548
 
 
2549
void PsiAccount::actionAssignKey(const Jid &j)
 
2550
{
 
2551
        if(ensureKey(j)) {
 
2552
                UserListItem *u = findFirstRelavent(j);
 
2553
                if(u)
 
2554
                        cpUpdate(*u);
 
2555
        }
 
2556
}
 
2557
 
 
2558
void PsiAccount::actionUnassignKey(const Jid &j)
 
2559
{
 
2560
        UserListItem *u = findFirstRelavent(j);
 
2561
        if(u) {
 
2562
                u->setPublicKeyID("");
 
2563
                cpUpdate(*u);
 
2564
        }
 
2565
}
 
2566
 
 
2567
void PsiAccount::dj_sendMessage(const Message &m, bool log)
 
2568
{
 
2569
        UserListItem *u = findFirstRelavent(m.to());
 
2570
        Message nm = m;
 
2571
 
 
2572
        if(option.incomingAs == 3) {
 
2573
                if(u) {
 
2574
                        switch(u->lastMessageType()) {
 
2575
                                case 0: nm.setType(""); break;
 
2576
                                case 1: nm.setType("chat"); break;
 
2577
                        }
 
2578
                }
 
2579
        }
 
2580
 
 
2581
        d->client->sendMessage(nm);
 
2582
 
 
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());
 
2586
 
 
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);
 
2594
                        delete me;
 
2595
                }
 
2596
        }
 
2597
 
 
2598
        // don't sound when sending groupchat messages or message events
 
2599
        if(m.type() != "groupchat" && !m.body().isEmpty())
 
2600
                playSound(option.onevent[eSend]);
 
2601
 
 
2602
        // auto close an open messagebox (if non-chat)
 
2603
        if(m.type() != "chat") {
 
2604
                UserListItem *u = findFirstRelavent(m.to());
 
2605
                if(u) {
 
2606
                        EventDlg *e = (EventDlg *)dialogFind("EventDlg", u->jid());
 
2607
                        if(e)
 
2608
                                e->closeAfterReply();
 
2609
                }
 
2610
        }
 
2611
}
 
2612
 
 
2613
void PsiAccount::dj_composeMessage(const Jid &jid, const QString &body, const QString &subject, const QString &thread)
 
2614
{
 
2615
        EventDlg *w = d->psi->createEventDlg(jid.full(), this);
 
2616
        if(!body.isEmpty())
 
2617
                w->setText(qstrquote(body));
 
2618
 
 
2619
        if(!subject.isEmpty() && subject.left(3) != "Re:")
 
2620
                w->setSubject("Re: " + subject);
 
2621
        else if (subject.left(3) == "Re:")
 
2622
                w->setSubject(subject);
 
2623
 
 
2624
        if(!thread.isEmpty())
 
2625
                w->setThread(thread);
 
2626
 
 
2627
        w->show();
 
2628
}
 
2629
 
 
2630
void PsiAccount::dj_composeMessage(const Jid &j, const QString &body)
 
2631
{
 
2632
        dj_composeMessage(j, body, QString::null, QString::null);
 
2633
}
 
2634
 
 
2635
void PsiAccount::dj_addAuth(const Jid &j)
 
2636
{
 
2637
        QString name;
 
2638
        QStringList groups;
 
2639
        UserListItem *u = d->userList.find(j);
 
2640
        if(u) {
 
2641
                name = u->name();
 
2642
                groups = u->groups();
 
2643
        }
 
2644
 
 
2645
        dj_add(j, name, groups, true);
 
2646
        dj_auth(j);
 
2647
}
 
2648
 
 
2649
void PsiAccount::dj_add(const Jid &j, const QString &name, const QStringList &groups, bool authReq)
 
2650
{
 
2651
        JT_Roster *r = new JT_Roster(d->client->rootTask());
 
2652
        r->set(j, name, groups);
 
2653
        r->go(true);
 
2654
 
 
2655
        if(authReq)
 
2656
                dj_authReq(j);
 
2657
}
 
2658
 
 
2659
void PsiAccount::dj_authReq(const Jid &j)
 
2660
{
 
2661
        d->client->sendSubscription(j, "subscribe");
 
2662
}
 
2663
 
 
2664
void PsiAccount::dj_auth(const Jid &j)
 
2665
{
 
2666
        d->client->sendSubscription(j, "subscribed");
 
2667
}
 
2668
 
 
2669
void PsiAccount::dj_deny(const Jid &j)
 
2670
{
 
2671
        d->client->sendSubscription(j, "unsubscribed");
 
2672
}
 
2673
 
 
2674
void PsiAccount::dj_rename(const Jid &j, const QString &name)
 
2675
{
 
2676
        UserListItem *u = d->userList.find(j);
 
2677
        if(!u)
 
2678
                return;
 
2679
 
 
2680
        QString str;
 
2681
        if(name == u->jid().full())
 
2682
                str = "";
 
2683
        else
 
2684
                str = name;
 
2685
 
 
2686
        // strange workaround to avoid a null string ??
 
2687
        QString uname;
 
2688
        if(u->name().isEmpty())
 
2689
                uname = "";
 
2690
        else
 
2691
                uname = u->name();
 
2692
 
 
2693
        if(uname == str)
 
2694
                return;
 
2695
        u->setName(str);
 
2696
 
 
2697
        cpUpdate(*u);
 
2698
 
 
2699
        if(u->inList()) {
 
2700
                JT_Roster *r = new JT_Roster(d->client->rootTask());
 
2701
                r->set(u->jid(), u->name(), u->groups());
 
2702
                r->go(true);
 
2703
        }
 
2704
}
 
2705
 
 
2706
void PsiAccount::dj_remove(const Jid &j)
 
2707
{
 
2708
        UserListItem *u = d->userList.find(j);
 
2709
        if(!u)
 
2710
                return;
 
2711
 
 
2712
        // remove all events from the queue
 
2713
        d->eventQueue->clear(j);
 
2714
        updateReadNext(j);
 
2715
 
 
2716
        // TODO: delete the item immediately (to simulate local change)
 
2717
        if(!u->inList()) {
 
2718
                //simulateContactOffline(u);
 
2719
                d->userList.removeRef(u);
 
2720
        }
 
2721
        else {
 
2722
                JT_Roster *r = new JT_Roster(d->client->rootTask());
 
2723
                r->remove(j);
 
2724
                r->go(true);
 
2725
 
 
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());
 
2729
                        ju->unreg(j);
 
2730
                        ju->go(true);
 
2731
                }
 
2732
        }
 
2733
}
 
2734
 
 
2735
// handle an incoming event
 
2736
void PsiAccount::handleEvent(PsiEvent *e)
 
2737
{
 
2738
        if ( e )
 
2739
                setEnabled();
 
2740
 
 
2741
        bool doPopup    = false;
 
2742
        bool putToQueue = true;
 
2743
        PsiPopup::PopupType popupType = PsiPopup::AlertNone;
 
2744
 
 
2745
        // find someone to accept the event
 
2746
        Jid j;
 
2747
        QPtrList<UserListItem> ul = findRelavent(e->from());
 
2748
        if(ul.isEmpty()) {
 
2749
                // if groupchat, then we want the full JID
 
2750
                if(findGCContact(e->from())) {
 
2751
                        j = e->from();
 
2752
                }
 
2753
                else {
 
2754
                        Jid bare = e->from().userHost();
 
2755
                        Jid reg = bare.withResource("registered");
 
2756
 
 
2757
                        // see if we have a "registered" variant of the jid
 
2758
                        if(findFirstRelavent(reg)) {
 
2759
                                j = reg;
 
2760
                                e->setFrom(reg); // HACK!!
 
2761
                        }
 
2762
                        // retain full jid if sent to "registered"
 
2763
                        else if(e->from().resource() == "registered")
 
2764
                                j = e->from();
 
2765
                        // otherwise don't use the resource for new entries
 
2766
                        else
 
2767
                                j = bare;
 
2768
                }
 
2769
        }
 
2770
        else
 
2771
                j = ul.first()->jid();
 
2772
        e->setJid(j);
 
2773
 
 
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);
 
2779
                }
 
2780
        }
 
2781
 
 
2782
        if(e->type() == PsiEvent::Message) {
 
2783
                MessageEvent *me = (MessageEvent *)e;
 
2784
                const Message &m = me->message();
 
2785
 
 
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());
 
2790
                                if(!c)
 
2791
                                        c = (ChatDlg *)dialogFind("ChatDlg", e->jid());
 
2792
                                if (c) 
 
2793
                                        c->incomingMessage(m);
 
2794
                        }
 
2795
                        return;
 
2796
                }
 
2797
 
 
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());
 
2801
                        if(!c)
 
2802
                                c = (ChatDlg *)dialogFind("ChatDlg", e->jid());
 
2803
 
 
2804
                        if(c)
 
2805
                                c->setJid(e->from());
 
2806
 
 
2807
                        if ( option.ppChat && ((c && !c->isActiveWindow()) || !c) ) {
 
2808
                                if (!c || c->isHidden() || option.alertOpenChats) {
 
2809
                                        doPopup = true;
 
2810
                                        popupType = PsiPopup::AlertChat;
 
2811
                                }
 
2812
                        }
 
2813
 
 
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);
 
2820
                                }
 
2821
                                else
 
2822
                                        putToQueue = false;
 
2823
                        }
 
2824
                        else {
 
2825
                                bool firstChat = !d->eventQueue->hasChats(e->from());
 
2826
                                playSound(option.onevent[firstChat ? eChat1: eChat2]);
 
2827
                        }
 
2828
                }
 
2829
                else if (m.type() == "headline") {
 
2830
                        playSound(option.onevent[eHeadline]);
 
2831
                        if (option.ppHeadline) {
 
2832
                                doPopup = true;
 
2833
                                popupType = PsiPopup::AlertHeadline;
 
2834
                        }
 
2835
                }
 
2836
                else if (m.type() == "") {
 
2837
                        playSound(option.onevent[eMessage]);
 
2838
                        if (option.ppMessage && m.type() == "") {
 
2839
                                doPopup = true;
 
2840
                                popupType = PsiPopup::AlertMessage;
 
2841
                        }
 
2842
                }
 
2843
                else
 
2844
                        playSound(option.onevent[eSystem]);
 
2845
 
 
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)));
 
2849
                }
 
2850
        }
 
2851
        else if(e->type() == PsiEvent::File) {
 
2852
                playSound(option.onevent[eIncomingFT]);
 
2853
                if (option.ppFile) {
 
2854
                        doPopup = true;
 
2855
                        popupType = PsiPopup::AlertFile;
 
2856
                }
 
2857
        }
 
2858
        else {
 
2859
                playSound(option.onevent[eSystem]);
 
2860
 
 
2861
                AuthEvent *ae = (AuthEvent *)e;
 
2862
                if(ae->authType() == "subscribe") {
 
2863
                        if(option.autoAuth) {
 
2864
                                dj_addAuth(ae->from());
 
2865
                                putToQueue = false;
 
2866
                        }
 
2867
                }
 
2868
                if(ae->authType() == "unsubscribe") {
 
2869
                        putToQueue = false;
 
2870
                }
 
2871
        }
 
2872
 
 
2873
        if ( popupType == PsiPopup::AlertChat && !option.ppChat )
 
2874
                doPopup = false;
 
2875
        if ( popupType == PsiPopup::AlertMessage && !option.ppMessage )
 
2876
                doPopup = false;
 
2877
        if ( popupType == PsiPopup::AlertHeadline && !option.ppHeadline )
 
2878
                doPopup = false;
 
2879
        if ( popupType == PsiPopup::AlertFile && !option.ppFile )
 
2880
                doPopup = false;
 
2881
        if ( doPopup && d->doPopups ) {
 
2882
 
 
2883
                Resource r;
 
2884
                UserListItem *u = findFirstRelavent(j);
 
2885
                if ( u )
 
2886
                        r = *(u->priority());
 
2887
 
 
2888
                PsiPopup *popup = new PsiPopup(popupType, this);
 
2889
                popup->setData(j, r, u, e);
 
2890
        }
 
2891
 
 
2892
        if ( putToQueue )
 
2893
                queueEvent(e);
 
2894
        else
 
2895
                delete e;
 
2896
}
 
2897
 
 
2898
// put an event into the event queue, and update the related alerts
 
2899
void PsiAccount::queueEvent(PsiEvent *e)
 
2900
{
 
2901
        // do we have roster item for this?
 
2902
        UserListItem *u = find(e->jid());
 
2903
        if(!u) {
 
2904
                // create item
 
2905
                u = new UserListItem;
 
2906
                u->setJid(e->jid());
 
2907
                u->setInList(false);
 
2908
                // u->setAvatarFactory(d->avatarFactory); // Avatars
 
2909
 
 
2910
                // is it a private groupchat?
 
2911
                Jid j = u->jid();
 
2912
                GCContact *c = findGCContact(j);
 
2913
                if(c) {
 
2914
                        u->setName(j.resource());
 
2915
                        u->setPrivate(true);
 
2916
 
 
2917
                        // make a resource so the contact appears online
 
2918
                        UserResource ur;
 
2919
                        ur.setName(j.resource());
 
2920
                        ur.setStatus(c->status);
 
2921
                        u->userResourceList().append(ur);
 
2922
                }
 
2923
 
 
2924
                // treat it like a push  [pushinfo]
 
2925
                //VCard info;
 
2926
                //if(readUserInfo(item->jid, &info) && !info.field[vNickname].isEmpty())
 
2927
                //      item->nick = info.field[vNickname];
 
2928
                //else {
 
2929
                //      if(localStatus != STATUS_OFFLINE)
 
2930
                //              serv->getVCard(item->jid);
 
2931
                //}
 
2932
 
 
2933
                d->userList.append(u);
 
2934
        }
 
2935
 
 
2936
        //printf("queuing message from [%s] for [%s].\n", e->from().full().latin1(), e->jid().full().latin1());
 
2937
        d->eventQueue->enqueue(e);
 
2938
 
 
2939
        updateReadNext(e->jid());
 
2940
        if(option.raise)
 
2941
                d->psi->raiseMainwin();
 
2942
 
 
2943
        // udpate the roster
 
2944
        cpUpdate(*u);
 
2945
 
 
2946
        bool noPopup = false;
 
2947
        if(d->loginStatus.isAvailable()) {
 
2948
                QString show = d->loginStatus.show();
 
2949
                if(show == "dnd")
 
2950
                        noPopup = true;
 
2951
                else if((show == "away" || show == "xa") && option.noAwayPopup)
 
2952
                        noPopup = true;
 
2953
        }
 
2954
 
 
2955
         if (!noPopup) {
 
2956
                bool doPopup = false;
 
2957
 
 
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;
 
2966
                        else
 
2967
                                doPopup = option.popupMsgs;
 
2968
                } else if (e->type() == PsiEvent::File) {
 
2969
                        doPopup = option.popupFiles;
 
2970
                }
 
2971
                else
 
2972
                        doPopup = option.popupMsgs;
 
2973
 
 
2974
                // Popup
 
2975
                if (doPopup) {
 
2976
                        UserListItem *u = find(e->jid());
 
2977
                        if (u && (!option.noUnlistedPopup || u->inList()))
 
2978
                                openNextEvent(*u);
 
2979
                }
 
2980
 
 
2981
        }
 
2982
}
 
2983
 
 
2984
// take the next event from the queue and display it
 
2985
void PsiAccount::openNextEvent(const UserListItem &u)
 
2986
{
 
2987
        PsiEvent *e = d->eventQueue->peek(u.jid());
 
2988
        if(!e)
 
2989
                return;
 
2990
 
 
2991
        psi()->processEvent(e);
 
2992
}
 
2993
 
 
2994
void PsiAccount::openNextEvent()
 
2995
{
 
2996
        PsiEvent *e = d->eventQueue->peekNext();
 
2997
        if(!e)
 
2998
                return;
 
2999
 
 
3000
        if(e->type() == PsiEvent::PGP) {
 
3001
                psi()->processEvent(e);
 
3002
                return;
 
3003
        }
 
3004
 
 
3005
        UserListItem *u = find(e->jid());
 
3006
        if(!u)
 
3007
                return;
 
3008
        openNextEvent(*u);
 
3009
}
 
3010
 
 
3011
void PsiAccount::updateReadNext(const Jid &j)
 
3012
{
 
3013
        // update eventdlg's read-next
 
3014
        EventDlg *w = (EventDlg *)dialogFind("EventDlg", j);
 
3015
        if(w) {
 
3016
                Icon *nextAnim = 0;
 
3017
                int nextAmount = d->eventQueue->count(j);
 
3018
                if(nextAmount > 0)
 
3019
                        nextAnim = is->event2icon(d->eventQueue->peek(j));
 
3020
                w->updateReadNext(nextAnim, nextAmount);
 
3021
        }
 
3022
 
 
3023
        queueChanged();
 
3024
}
 
3025
 
 
3026
void PsiAccount::processReadNext(const Jid &j)
 
3027
{
 
3028
        UserListItem *u = find(j);
 
3029
        if(u)
 
3030
                processReadNext(*u);
 
3031
}
 
3032
 
 
3033
void PsiAccount::processReadNext(const UserListItem &u)
 
3034
{
 
3035
        EventDlg *w = (EventDlg *)dialogFind("EventDlg", u.jid());
 
3036
        if(!w) {
 
3037
                // this should NEVER happen
 
3038
                return;
 
3039
        }
 
3040
 
 
3041
        // peek the event
 
3042
        PsiEvent *e = d->eventQueue->peek(u.jid());
 
3043
        if(!e)
 
3044
                return;
 
3045
 
 
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")
 
3051
                        isChat = true;
 
3052
        }
 
3053
 
 
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.
 
3057
        if(isChat) {
 
3058
                openChat(e->from());
 
3059
                return;
 
3060
        }
 
3061
 
 
3062
        // remove from queue
 
3063
        e = d->eventQueue->dequeue(u.jid());
 
3064
 
 
3065
        // update the eventdlg
 
3066
        w->updateEvent(e);
 
3067
        delete e;
 
3068
 
 
3069
        // update the contact
 
3070
        cpUpdate(u);
 
3071
 
 
3072
        updateReadNext(u.jid());
 
3073
}
 
3074
 
 
3075
void PsiAccount::processChats(const Jid &j)
 
3076
{
 
3077
        //printf("processing chats for [%s]\n", j.full().latin1());
 
3078
        ChatDlg *c = (ChatDlg *)dialogFind("ChatDlg", j);
 
3079
        if(!c)
 
3080
                return;
 
3081
 
 
3082
        // extract the chats
 
3083
        QPtrList<PsiEvent> chatList;
 
3084
        d->eventQueue->extractChats(&chatList, j);
 
3085
        chatList.setAutoDelete(true);
 
3086
 
 
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();
 
3093
 
 
3094
                        // process the message
 
3095
                        if(!me->sentToChatWindow())
 
3096
                                c->incomingMessage(m);
 
3097
                }
 
3098
 
 
3099
                QPtrList<UserListItem> ul = findRelavent(j);
 
3100
                if(!ul.isEmpty()) {
 
3101
                        UserListItem *u = ul.first();
 
3102
                        cpUpdate(*u);
 
3103
                        updateReadNext(u->jid());
 
3104
                }
 
3105
        }
 
3106
}
 
3107
 
 
3108
void PsiAccount::openChat(const Jid &j)
 
3109
{
 
3110
        ChatDlg *c = ensureChatDlg(j);
 
3111
        processChats(j);
 
3112
        bringToFront(c);
 
3113
}
 
3114
 
 
3115
void PsiAccount::chatMessagesRead(const Jid &j)
 
3116
{
 
3117
        if(option.alertOpenChats)
 
3118
                processChats(j);
 
3119
}
 
3120
 
 
3121
void PsiAccount::logEvent(const Jid &j, PsiEvent *e)
 
3122
{
 
3123
        EDBHandle *h = new EDBHandle(d->psi->edb());
 
3124
        connect(h, SIGNAL(finished()), SLOT(edb_finished()));
 
3125
        h->append(j, e);
 
3126
}
 
3127
 
 
3128
void PsiAccount::edb_finished()
 
3129
{
 
3130
        EDBHandle *h = (EDBHandle *)sender();
 
3131
        delete h;
 
3132
}
 
3133
 
 
3134
void PsiAccount::openGroupChat(const Jid &j)
 
3135
{
 
3136
        QString str = j.userHost();
 
3137
        bool found = false;
 
3138
        for(QStringList::ConstIterator it = d->groupchats.begin(); it != d->groupchats.end(); ++it) {
 
3139
                if((*it) == str) {
 
3140
                        found = true;
 
3141
                        break;
 
3142
                }
 
3143
        }
 
3144
        if(!found)
 
3145
                d->groupchats += str;
 
3146
 
 
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()));
 
3150
        w->show();
 
3151
}
 
3152
 
 
3153
bool PsiAccount::groupChatJoin(const QString &host, const QString &room, const QString &nick)
 
3154
{
 
3155
        return d->client->groupChatJoin(host, room, nick);
 
3156
}
 
3157
 
 
3158
void PsiAccount::groupChatChangeNick(const QString &host, const QString &room, const QString& nick, const Status &s)
 
3159
{
 
3160
        d->client->groupChatChangeNick(host, room, nick, s);
 
3161
}
 
3162
 
 
3163
void PsiAccount::groupChatSetStatus(const QString &host, const QString &room, const Status &s)
 
3164
{
 
3165
        d->client->groupChatSetStatus(host, room, s);
 
3166
}
 
3167
 
 
3168
void PsiAccount::groupChatLeave(const QString &host, const QString &room)
 
3169
{
 
3170
        d->groupchats.remove(room + '@' + host);
 
3171
        d->client->groupChatLeave(host, room);
 
3172
}
 
3173
 
 
3174
GCContact *PsiAccount::findGCContact(const Jid &j)
 
3175
{
 
3176
        QPtrListIterator<GCContact> it(d->gcbank);
 
3177
        for(GCContact *c; (c = it.current()); ++it) {
 
3178
                if(c->jid.compare(j))
 
3179
                        return c;
 
3180
        }
 
3181
        return 0;
 
3182
}
 
3183
 
 
3184
QStringList PsiAccount::groupchats() const
 
3185
{
 
3186
        return d->groupchats;
 
3187
}
 
3188
 
 
3189
void PsiAccount::client_groupChatJoined(const Jid &j)
 
3190
{
 
3191
        d->client->groupChatSetStatus(j.host(), j.user(), d->loginStatus);
 
3192
 
 
3193
        GCMainDlg *m = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
 
3194
        if(m) {
 
3195
                m->joined();
 
3196
                return;
 
3197
        }
 
3198
        GCJoinDlg *w = (GCJoinDlg *)dialogFind("GCJoinDlg", j);
 
3199
        if(!w)
 
3200
                return;
 
3201
        w->joined();
 
3202
 
 
3203
        openGroupChat(j);
 
3204
}
 
3205
 
 
3206
void PsiAccount::client_groupChatLeft(const Jid &j)
 
3207
{
 
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)) {
 
3213
                        ++it;
 
3214
                        continue;
 
3215
                }
 
3216
                UserListItem *u = find(c->jid);
 
3217
                if(!u) {
 
3218
                        ++it;
 
3219
                        continue;
 
3220
                }
 
3221
 
 
3222
                simulateContactOffline(u);
 
3223
                d->gcbank.removeRef(c);
 
3224
        }
 
3225
}
 
3226
 
 
3227
void PsiAccount::client_groupChatPresence(const Jid &j, const Status &s)
 
3228
{
 
3229
        GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
 
3230
        if(!w)
 
3231
                return;
 
3232
 
 
3233
        GCContact *c = findGCContact(j);
 
3234
        if(!c) {
 
3235
                c = new GCContact;
 
3236
                c->jid = j;
 
3237
                c->status = s;
 
3238
                d->gcbank.append(c);
 
3239
        }
 
3240
 
 
3241
        w->presence(j.resource(), s);
 
3242
 
 
3243
        // pass through the core presence handling also
 
3244
        Resource r;
 
3245
        r.setName(j.resource());
 
3246
        r.setStatus(s);
 
3247
        if(s.isAvailable())
 
3248
                client_resourceAvailable(j, r);
 
3249
        else
 
3250
                client_resourceUnavailable(j, j.resource());
 
3251
}
 
3252
 
 
3253
void PsiAccount::client_groupChatError(const Jid &j, int code, const QString &str)
 
3254
{
 
3255
        GCMainDlg *w = (GCMainDlg *)dialogFind("GCMainDlg", Jid(j.userHost()));
 
3256
        if(w) {
 
3257
                w->error(code, str);
 
3258
        }
 
3259
        else {
 
3260
                GCJoinDlg *w = (GCJoinDlg *)dialogFind("GCJoinDlg", j);
 
3261
                if(w) {
 
3262
                        w->error(code, str);
 
3263
                }
 
3264
        }
 
3265
}
 
3266
 
 
3267
QStringList PsiAccount::hiddenChats(const Jid &j) const
 
3268
{
 
3269
        QStringList list;
 
3270
 
 
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();
 
3275
        }
 
3276
 
 
3277
        return list;
 
3278
}
 
3279
 
 
3280
void PsiAccount::slotCheckVCard()
 
3281
{
 
3282
        JT_VCard *j = (JT_VCard *)sender();
 
3283
        if(!j->success() && j->statusCode() == Task::ErrDisc) {
 
3284
                setNick(d->jid.user());
 
3285
                return;
 
3286
        }
 
3287
 
 
3288
        if(j->vcard().isEmpty()) {
 
3289
                changeVCard();
 
3290
                return;
 
3291
        }
 
3292
 
 
3293
        if(!j->vcard().nickName().isEmpty()) {
 
3294
                d->nickFromVCard = true;
 
3295
                setNick(j->vcard().nickName());
 
3296
        }
 
3297
        else
 
3298
                setNick(d->jid.user());
 
3299
}
 
3300
 
 
3301
void PsiAccount::setNick(const QString &nick)
 
3302
{
 
3303
        d->self.setName(nick);
 
3304
        cpUpdate(d->self);
 
3305
        nickChanged();
 
3306
}
 
3307
 
 
3308
QString PsiAccount::nick() const
 
3309
{
 
3310
        return d->self.name();
 
3311
}
 
3312
 
 
3313
void PsiAccount::pgpToggled(bool b)
 
3314
{
 
3315
        QString oldkey = d->cur_pgpSecretKeyID;
 
3316
 
 
3317
        // gaining pgp?
 
3318
        if(b)
 
3319
                d->cur_pgpSecretKeyID = d->acc.pgpSecretKeyID;
 
3320
        // losing it?
 
3321
        else {
 
3322
                d->acc.pgpPassphrase = ""; // clear account passphrase
 
3323
                d->cur_pgpSecretKeyID = "";
 
3324
        }
 
3325
 
 
3326
        if(oldkey != d->cur_pgpSecretKeyID) {
 
3327
                pgpKeyChanged();
 
3328
                // resend status if online
 
3329
                if(loggedIn())
 
3330
                        setStatusDirect(d->loginStatus);
 
3331
        }
 
3332
}
 
3333
 
 
3334
void PsiAccount::pgpKeysUpdated()
 
3335
{
 
3336
        OpenPGP::KeyList list = d->psi->pgp()->publicKeys();
 
3337
 
 
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) {
 
3349
                                                haveKey = true;
 
3350
                                                break;
 
3351
                                        }
 
3352
                                }
 
3353
                                if(haveKey)
 
3354
                                        tryVerify(u, &r);
 
3355
                        }
 
3356
                }
 
3357
        }
 
3358
}
 
3359
 
 
3360
void PsiAccount::verifyStatus(const Jid &j, const Status &s)
 
3361
{
 
3362
        PGPTransaction *t = new PGPTransaction(d->psi->pgp());
 
3363
        t->setJid(j);
 
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));
 
3369
 
 
3370
        //printf("%s: verifying\n", j.full().latin1());
 
3371
}
 
3372
 
 
3373
void PsiAccount::trySignPresence()
 
3374
{
 
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);
 
3382
}
 
3383
 
 
3384
void PsiAccount::pgp_needPassphrase()
 
3385
{
 
3386
        OpenPGP::Request *r = (OpenPGP::Request *)sender();
 
3387
        d->ppreq = r;
 
3388
 
 
3389
        if(!d->passphrase.isEmpty())
 
3390
                r->submitPassphrase(d->passphrase);
 
3391
        else
 
3392
                promptPassphrase();
 
3393
}
 
3394
 
 
3395
void PsiAccount::promptPassphrase()
 
3396
{
 
3397
        if(d->ppdlg)
 
3398
                d->ppdlg->unblock();
 
3399
        else {
 
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()));
 
3404
                w->show();
 
3405
 
 
3406
                d->ppdlg = w;
 
3407
        }
 
3408
}
 
3409
 
 
3410
void PsiAccount::submitPassphrase(const QString &pp)
 
3411
{
 
3412
        d->ppreq->submitPassphrase(pp);
 
3413
}
 
3414
 
 
3415
void PsiAccount::rejectPassphrase()
 
3416
{
 
3417
        d->ppdlg = 0;
 
3418
 
 
3419
        // cancel request
 
3420
        if(d->ppreq)
 
3421
                d->ppreq->deleteLater();
 
3422
 
 
3423
        logout();
 
3424
}
 
3425
 
 
3426
void PsiAccount::pgp_signFinished(bool ok)
 
3427
{
 
3428
        OpenPGP::Request *r = (OpenPGP::Request *)sender();
 
3429
        bool badPassphrase = r->badPassphrase();
 
3430
        QString sig;
 
3431
        if(ok)
 
3432
                sig = r->signature();
 
3433
        if(d->ppdlg) {
 
3434
                d->passphrase = d->ppdlg->passphrase();
 
3435
                if(d->acc.opt_passphrase) {
 
3436
                        d->acc.pgpPassphrase = d->passphrase;
 
3437
                        // TODO: save account?
 
3438
                }
 
3439
        }
 
3440
        r->deleteLater();
 
3441
 
 
3442
        if(ok) {
 
3443
                Status s = d->loginStatus;
 
3444
                s.setXSigned(OpenPGP::stripHeaderFooter(sig));
 
3445
                setStatusActual(s);
 
3446
        }
 
3447
        else {
 
3448
                if(badPassphrase) {
 
3449
                        d->passphrase = "";
 
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()));
 
3452
                        return;
 
3453
                }
 
3454
                else {
 
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."));
 
3456
                        logout();
 
3457
                }
 
3458
        }
 
3459
 
 
3460
        // remove the dialog if it is there
 
3461
        if(d->ppdlg) {
 
3462
                d->ppdlg->deleteLater();
 
3463
                d->ppdlg = 0;
 
3464
        }
 
3465
}
 
3466
 
 
3467
void PsiAccount::pgp_verifyFinished(bool b)
 
3468
{
 
3469
        PGPTransaction *t = (PGPTransaction *)sender();
 
3470
 
 
3471
        Jid j = t->jid();
 
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;
 
3478
                if(!found)
 
3479
                        continue;
 
3480
                UserResource &ur = *rit;
 
3481
 
 
3482
                if(b) {
 
3483
                        //printf("vergood\n");
 
3484
                        ur.setPublicKeyID(t->keyID());
 
3485
                        ur.setPGPVerifyStatus(t->verifyResult());
 
3486
                        ur.setSigTimestamp(t->timestamp());
 
3487
 
 
3488
                        // if the key doesn't match the assigned key, unassign it
 
3489
                        if(t->keyID() != u->publicKeyID())
 
3490
                                u->setPublicKeyID("");
 
3491
                }
 
3492
                else {
 
3493
                        //QMessageBox::information(0, "sig verify error", QString("error verifying [%1]").arg(u->jid().full()));
 
3494
                        ur.setPGPVerifyStatus(OpenPGP::VerifyError);
 
3495
                }
 
3496
 
 
3497
                //printf("updating [%s]\n", u->jid().full().latin1());
 
3498
                cpUpdate(*u);
 
3499
        }
 
3500
 
 
3501
        t->deleteLater();
 
3502
}
 
3503
 
 
3504
int PsiAccount::sendMessageEncrypted(const Message &_m)
 
3505
{
 
3506
        if(!ensureKey(_m.to()))
 
3507
                return -1;
 
3508
        QString key = findFirstRelavent(_m.to())->publicKeyID();
 
3509
 
 
3510
        PGPTransaction *pt = new PGPTransaction(d->psi->pgp());
 
3511
        Message m = _m;
 
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());
 
3517
 
 
3518
        // encrypt
 
3519
        QStringList rcpt;
 
3520
        rcpt += key;
 
3521
        connect(pt, SIGNAL(finished(bool)), SLOT(pgp_finished(bool)));
 
3522
        pt->encrypt(a, rcpt);
 
3523
 
 
3524
        return pt->id();
 
3525
}
 
3526
 
 
3527
void PsiAccount::pgp_finished(bool b)
 
3528
{
 
3529
        PGPTransaction *pt = (PGPTransaction *)sender();
 
3530
 
 
3531
        int x = pt->id();
 
3532
        if(pt->type() == OpenPGP::Encrypt) {
 
3533
                if(b) {
 
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);
 
3541
                                delete me;
 
3542
                        }
 
3543
 
 
3544
                        Message mwrap;
 
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);
 
3558
                }
 
3559
                encryptedMessageSent(x, b);
 
3560
        }
 
3561
 
 
3562
        pt->deleteLater();
 
3563
}
 
3564
 
 
3565
void PsiAccount::pgp_decryptFinished(bool b)
 
3566
{
 
3567
        PGPTransaction *pt = (PGPTransaction *)sender();
 
3568
 
 
3569
        bool tryAgain = false;
 
3570
        if(b) {
 
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);
 
3577
                m.setBody(str);
 
3578
                m.setXEncrypted("");
 
3579
                m.setWasEncrypted(true);
 
3580
                processIncomingMessage(m);
 
3581
 
 
3582
                //else
 
3583
                //      QMessageBox::information(0, CAP(tr("Error")), tr("A successful decryption operation resulted in an invalid message, so it has been ignored."));
 
3584
        }
 
3585
        else {
 
3586
                if(loggedIn()) {
 
3587
                        Message m;
 
3588
                        m.setTo(pt->message().from());
 
3589
                        m.setType("error");
 
3590
                        m.setBody(pt->message().body());
 
3591
                        Stanza::Error err;
 
3592
                        err.condition = 500;
 
3593
                        err.text = "Unable to decrypt";
 
3594
                        m.setError(err);
 
3595
                        d->client->sendMessage(m);
 
3596
                }
 
3597
        }
 
3598
 
 
3599
        pt->deleteLater();
 
3600
 
 
3601
        if(tryAgain) {
 
3602
                processEncryptedMessageNext();
 
3603
        }
 
3604
        else {
 
3605
                processEncryptedMessageDone();
 
3606
        }
 
3607
}
 
3608
 
 
3609
void PsiAccount::processEncryptedMessage(const Message &m)
 
3610
{
 
3611
        // decrypt
 
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);
 
3617
        t->decrypt(str);
 
3618
}
 
3619
 
 
3620
void PsiAccount::processMessageQueue()
 
3621
{
 
3622
        while(!d->messageQueue.isEmpty()) {
 
3623
                Message *mp = d->messageQueue.getFirst();
 
3624
 
 
3625
                // encrypted?
 
3626
                if(d->psi->pgp() && !mp->xencrypted().isEmpty()) {
 
3627
                        processEncryptedMessageNext();
 
3628
                        break;
 
3629
                }
 
3630
 
 
3631
                processIncomingMessage(*mp);
 
3632
                d->messageQueue.removeRef(mp);
 
3633
        }
 
3634
}
 
3635
 
 
3636
void PsiAccount::processEncryptedMessageNext()
 
3637
{
 
3638
        // 'peek' and try to process it
 
3639
        Message *mp = d->messageQueue.getFirst();
 
3640
        processEncryptedMessage(*mp);
 
3641
}
 
3642
 
 
3643
void PsiAccount::processEncryptedMessageDone()
 
3644
{
 
3645
        // 'pop' the message
 
3646
        Message *mp = d->messageQueue.getFirst();
 
3647
        d->messageQueue.removeRef(mp);
 
3648
 
 
3649
        // do the rest of the queue
 
3650
        processMessageQueue();
 
3651
}
 
3652
 
 
3653
void PsiAccount::optionsUpdate()
 
3654
{
 
3655
        d->cp->updateEntry(d->self);
 
3656
}
 
3657
 
 
3658
QString PsiAccount::resultToString(int result)
 
3659
{
 
3660
        QString s;
 
3661
        switch(result) {
 
3662
                case QCA::TLS::NoCert:
 
3663
                        s = tr("The server did not present a certificate.");
 
3664
                        break;
 
3665
                case QCA::TLS::Valid:
 
3666
                        s = tr("Certificate is valid.");
 
3667
                        break;
 
3668
                case QCA::TLS::HostMismatch:
 
3669
                        s = tr("The hostname does not match the one the certificate was issued to.");
 
3670
                        break;
 
3671
                case QCA::TLS::Rejected:
 
3672
                        s = tr("Root CA is marked to reject the specified purpose.");
 
3673
                        break;
 
3674
                case QCA::TLS::Untrusted:
 
3675
                        s = tr("Certificate not trusted for the required purpose.");
 
3676
                        break;
 
3677
                case QCA::TLS::SignatureFailed:
 
3678
                        s = tr("Invalid signature.");
 
3679
                        break;
 
3680
                case QCA::TLS::InvalidCA:
 
3681
                        s = tr("Invalid CA certificate.");
 
3682
                        break;
 
3683
                case QCA::TLS::InvalidPurpose:
 
3684
                        s = tr("Invalid certificate purpose.");
 
3685
                        break;
 
3686
                case QCA::TLS::SelfSigned:
 
3687
                        s = tr("Certificate is self-signed.");
 
3688
                        break;
 
3689
                case QCA::TLS::Revoked:
 
3690
                        s = tr("Certificate has been revoked.");
 
3691
                        break;
 
3692
                case QCA::TLS::PathLengthExceeded:
 
3693
                        s = tr("Maximum certificate chain length exceeded.");
 
3694
                        break;
 
3695
                case QCA::TLS::Expired:
 
3696
                        s = tr("Certificate has expired.");
 
3697
                        break;
 
3698
                case QCA::TLS::Unknown:
 
3699
                default:
 
3700
                        s = tr("General certificate validation error.");
 
3701
                        break;
 
3702
        }
 
3703
        return s;
 
3704
}
 
3705
 
 
3706
void PsiAccount::invokeGCMessage(const Jid &j)
 
3707
{
 
3708
        GCContact *c = findGCContact(j);
 
3709
        if(!c)
 
3710
                return;
 
3711
 
 
3712
        // create dummy item, open chat, then destroy item.  HORRIBLE HACK!
 
3713
        UserListItem *u = new UserListItem;
 
3714
        u->setJid(j);
 
3715
        u->setInList(false);
 
3716
        u->setName(j.resource());
 
3717
        u->setPrivate(true);
 
3718
        //u->setAvatarFactory(d->avatarFactory); // Avatars
 
3719
 
 
3720
        // make a resource so the contact appears online
 
3721
        UserResource ur;
 
3722
        ur.setName(j.resource());
 
3723
        ur.setStatus(c->status);
 
3724
        u->userResourceList().append(ur);
 
3725
 
 
3726
        d->userList.append(u);
 
3727
        actionSendMessage(j);
 
3728
        d->userList.remove(u);
 
3729
}
 
3730
 
 
3731
void PsiAccount::invokeGCChat(const Jid &j)
 
3732
{
 
3733
        GCContact *c = findGCContact(j);
 
3734
        if(!c)
 
3735
                return;
 
3736
 
 
3737
        // create dummy item, open chat, then destroy item.  HORRIBLE HACK!
 
3738
        UserListItem *u = new UserListItem;
 
3739
        u->setJid(j);
 
3740
        u->setInList(false);
 
3741
        u->setName(j.resource());
 
3742
        u->setPrivate(true);
 
3743
        //u->setAvatarFactory(d->avatarFactory); // Avatars
 
3744
 
 
3745
        // make a resource so the contact appears online
 
3746
        UserResource ur;
 
3747
        ur.setName(j.resource());
 
3748
        ur.setStatus(c->status);
 
3749
        u->userResourceList().append(ur);
 
3750
 
 
3751
        d->userList.append(u);
 
3752
        actionOpenChat(j);
 
3753
        d->userList.remove(u);
 
3754
}
 
3755
 
 
3756
void PsiAccount::invokeGCInfo(const Jid &j)
 
3757
{
 
3758
        actionInfo(j);
 
3759
}
 
3760
 
 
3761
void PsiAccount::invokeGCFile(const Jid &j)
 
3762
{
 
3763
        actionSendFile(j);
 
3764
}
 
3765
 
 
3766
void PsiAccount::toggleSecurity(const Jid &j, bool b)
 
3767
{
 
3768
        UserListItem *u = findFirstRelavent(j);
 
3769
        if(!u)
 
3770
                return;
 
3771
 
 
3772
        bool isBare = j.resource().isEmpty();
 
3773
        bool isPriority = false;
 
3774
 
 
3775
        // sick sick sick sick sick sick
 
3776
        UserResource *r1, *r2;
 
3777
        r1 = r2 = 0;
 
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))
 
3783
                isPriority = true;
 
3784
 
 
3785
        bool needUpdate = false;
 
3786
        bool sec = u->isSecure(j.resource());
 
3787
        if(sec != b) {
 
3788
                u->setSecure(j.resource(), b);
 
3789
                needUpdate = true;
 
3790
        }
 
3791
        if(isBare && r2) {
 
3792
                sec = u->isSecure(r2->name());
 
3793
                if(sec != b) {
 
3794
                        u->setSecure(r2->name(), b);
 
3795
                        needUpdate = true;
 
3796
                }
 
3797
        }
 
3798
        if(isPriority) {
 
3799
                sec = u->isSecure("");
 
3800
                if(sec != b) {
 
3801
                        u->setSecure("", b);
 
3802
                        needUpdate = true;
 
3803
                }
 
3804
        }
 
3805
 
 
3806
        if(needUpdate)
 
3807
                cpUpdate(*u);
 
3808
}
 
3809
 
 
3810
bool PsiAccount::ensureKey(const Jid &j)
 
3811
{
 
3812
        if(!d->psi->pgp())
 
3813
                return false;
 
3814
 
 
3815
        UserListItem *u = findFirstRelavent(j);
 
3816
        if(!u)
 
3817
                return false;
 
3818
 
 
3819
        // no key?
 
3820
        if(u->publicKeyID().isEmpty()) {
 
3821
                // does the user have any presence signed with a key?
 
3822
                QString akey;
 
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();
 
3828
                                break;
 
3829
                        }
 
3830
                }
 
3831
 
 
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) {
 
3837
                                inList = true;
 
3838
                                break;
 
3839
                        }
 
3840
                }
 
3841
 
 
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>"
 
3845
                                "<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 &nothing"));
 
3849
                        if(n != 0)
 
3850
                                return false;
 
3851
                }
 
3852
 
 
3853
                PGPKeyDlg *w = new PGPKeyDlg(list, akey, 0);
 
3854
                w->setCaption(tr("Public Key: %1").arg(j.full()));
 
3855
                int r = w->exec();
 
3856
                QString key;
 
3857
                if(r == QDialog::Accepted)
 
3858
                        key = w->keyID();
 
3859
                delete w;
 
3860
                if(key.isEmpty())
 
3861
                        return false;
 
3862
                u->setPublicKeyID(key);
 
3863
                cpUpdate(*u);
 
3864
        }
 
3865
 
 
3866
        return true;
 
3867
}
 
3868
 
 
3869
 
 
3870
//----------------------------------------------------------------------------
 
3871
// PGPKeyDlg
 
3872
//----------------------------------------------------------------------------
 
3873
class KeyViewItem : public QListViewItem
 
3874
{
 
3875
public:
 
3876
        KeyViewItem(const QString &_keyID, QListView *par)
 
3877
        :QListViewItem(par)
 
3878
        {
 
3879
                keyID = _keyID;
 
3880
        }
 
3881
 
 
3882
        QString keyID;
 
3883
};
 
3884
 
 
3885
class PGPKeyDlg::Private
 
3886
{
 
3887
public:
 
3888
        Private() {}
 
3889
 
 
3890
        QString keyID;
 
3891
        QString userID;
 
3892
};
 
3893
 
 
3894
PGPKeyDlg::PGPKeyDlg(const OpenPGP::KeyList &list, const QString &choose, QWidget *parent, const char *name)
 
3895
:PGPKeyUI(parent, name, true)
 
3896
{
 
3897
        d = new Private;
 
3898
 
 
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()));
 
3902
 
 
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());
 
3910
 
 
3911
                if(!choose.isEmpty() && k.keyID() == choose) {
 
3912
                        lv_keys->setSelected(i, true);
 
3913
                        isel = i;
 
3914
                }
 
3915
        }
 
3916
        if(lv_keys->childCount() > 0 && !isel)
 
3917
                lv_keys->setSelected(lv_keys->firstChild(), true);
 
3918
        else if(isel)
 
3919
                lv_keys->ensureItemVisible(isel);
 
3920
}
 
3921
 
 
3922
PGPKeyDlg::~PGPKeyDlg()
 
3923
{
 
3924
        delete d;
 
3925
}
 
3926
 
 
3927
QString PGPKeyDlg::keyID() const
 
3928
{
 
3929
        return d->keyID;
 
3930
}
 
3931
 
 
3932
QString PGPKeyDlg::userID() const
 
3933
{
 
3934
        return d->userID;
 
3935
}
 
3936
 
 
3937
void PGPKeyDlg::qlv_doubleClicked(QListViewItem *i)
 
3938
{
 
3939
        lv_keys->setSelected(i, true);
 
3940
        do_accept();
 
3941
}
 
3942
 
 
3943
void PGPKeyDlg::do_accept()
 
3944
{
 
3945
        KeyViewItem *i = (KeyViewItem *)lv_keys->selectedItem();
 
3946
        if(!i) {
 
3947
                QMessageBox::information(this, tr("Error"), tr("Please select a key."));
 
3948
                return;
 
3949
        }
 
3950
        d->keyID = i->keyID;
 
3951
        d->userID = i->text(1);
 
3952
        accept();
 
3953
}
 
3954
 
 
3955
#include "psiaccount.moc"