Fixed IBB and partially S5B in MUCs
[psi:psi.git] / src / psicon.cpp
1 /*
2  * psicon.cpp - core of Psi
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 "psicon.h"
22
23 #include <QApplication>
24 #include <QDesktopWidget>
25 #include <QMenuBar>
26 #include <QPointer>
27 #include <QIcon>
28 #include <QColor>
29 #include <QImage>
30 #include <QPixmapCache>
31 #include <QFile>
32 #include <QPixmap>
33 #include <QList>
34 #include <QImageReader>
35 #include <QMessageBox>
36 #include <QDir>
37
38 #include "s5b.h"
39 #include "psiaccount.h"
40 #include "activeprofiles.h"
41 #include "accountadddlg.h"
42 #include "psiiconset.h"
43 #include "contactview.h"
44 #include "psievent.h"
45 #include "passphrasedlg.h"
46 #include "common.h"
47 #include "mainwin.h"
48 #include "idle/idle.h"
49 #include "accountmanagedlg.h"
50 #include "statusdlg.h"
51 #include "options/optionsdlg.h"
52 #include "options/opt_toolbars.h"
53 #include "accountregdlg.h"
54 #include "combinedtunecontroller.h"
55 #include "mucjoindlg.h"
56 #include "userlist.h"
57 #include "eventdlg.h"
58 #ifdef HAVE_PGPUTIL
59 #include "pgputil.h"
60 #endif
61 #include "eventdb.h"
62 #include "proxy.h"
63 #ifdef PSIMNG
64 #include "psimng.h"
65 #endif
66 #include "alerticon.h"
67 #include "iconselect.h"
68 #include "psitoolbar.h"
69 #ifdef FILETRANSFER
70 #include "filetransfer.h"
71 #include "filetransdlg.h"
72 #endif
73 #include "accountmodifydlg.h"
74 #include "psiactionlist.h"
75 #include "applicationinfo.h"
76 #include "jidutil.h"
77 #include "systemwatch/systemwatch.h"
78 #include "accountscombobox.h"
79 #include "tabdlg.h"
80 #include "chatdlg.h"
81 #include "capsregistry.h"
82 #include "urlobject.h"
83 #include "anim.h"
84 #include "psioptions.h"
85 #include "psirichtext.h"
86 #ifdef PSI_PLUGINS
87 #include "pluginmanager.h"
88 #endif
89 #include "psicontactlist.h"
90 #include "dbus.h"
91 #include "tipdlg.h"
92 #include "shortcutmanager.h"
93 #include "globalshortcut/globalshortcutmanager.h"
94 #include "desktoputil.h"
95 #include "tabmanager.h"
96 #include "xmpp_xmlcommon.h"
97 #include "psicontact.h"
98 #include "contactupdatesmanager.h"
99 #include "capsmanager.h"
100 #include "avcall/avcall.h"
101 #include "avcall/calldlg.h"
102 #include "alertmanager.h"
103 #include "bosskey.h"
104
105 #include "AutoUpdater/AutoUpdater.h"
106 #ifdef HAVE_SPARKLE
107 #include "AutoUpdater/SparkleAutoUpdater.h"
108 #endif
109
110 #ifdef Q_WS_MAC
111 #include "mac_dock/mac_dock.h"
112 #endif
113
114 // from opt_avcall.cpp
115 extern void options_avcall_update();
116
117 //----------------------------------------------------------------------------
118 // PsiConObject
119 //----------------------------------------------------------------------------
120 class PsiConObject : public QObject
121 {
122         Q_OBJECT
123 public:
124         PsiConObject(QObject *parent)
125         : QObject(parent)
126         {
127                 QDir p(ApplicationInfo::homeDir());
128                 QDir v(ApplicationInfo::homeDir() + "/tmp-sounds");
129                 if(!v.exists())
130                         p.mkdir("tmp-sounds");
131                 Iconset::setSoundPrefs(v.absolutePath(), this, SLOT(playSound(QString)));
132                 connect(URLObject::getInstance(), SIGNAL(openURL(QString)), SLOT(openURL(QString)));
133         }
134
135         ~PsiConObject()
136         {
137                 // removing temp dirs
138                 QDir p(ApplicationInfo::homeDir());
139                 QDir v(ApplicationInfo::homeDir() + "/tmp-sounds");
140                 folderRemove(v);
141         }
142
143 public slots:
144         void playSound(QString file)
145         {
146                 if ( file.isEmpty() || !PsiOptions::instance()->getOption("options.ui.notifications.sounds.enable").toBool() )
147                         return;
148
149                 soundPlay(file);
150         }
151
152         void openURL(QString url)
153         {
154                 DesktopUtil::openUrl(url);
155         }
156
157 private:
158         // ripped from profiles.cpp
159         bool folderRemove(const QDir &_d)
160         {
161                 QDir d = _d;
162
163                 QStringList entries = d.entryList();
164                 for(QStringList::Iterator it = entries.begin(); it != entries.end(); ++it) {
165                         if(*it == "." || *it == "..")
166                                 continue;
167                         QFileInfo info(d, *it);
168                         if(info.isDir()) {
169                                 if(!folderRemove(QDir(info.filePath())))
170                                         return false;
171                         }
172                         else {
173                                 //printf("deleting [%s]\n", info.filePath().latin1());
174                                 d.remove(info.fileName());
175                         }
176                 }
177                 QString name = d.dirName();
178                 if(!d.cdUp())
179                         return false;
180                 //printf("removing folder [%s]\n", d.filePath(name).latin1());
181                 d.rmdir(name);
182
183                 return true;
184         }
185 };
186
187 //----------------------------------------------------------------------------
188 // PsiCon::Private
189 //----------------------------------------------------------------------------
190
191 struct item_dialog
192 {
193         QWidget *widget;
194         QString className;
195 };
196
197 class PsiCon::Private : public QObject
198 {
199         Q_OBJECT
200 public:
201         Private(PsiCon *parent)
202                 : QObject(parent)
203                 , contactList(0)
204                 , iconSelect(0)
205                 , quitting(false)
206                 , alertManager(parent)
207                 , bossKey(0)
208         {
209                 psi = parent;
210         }
211
212         ~Private()
213         {
214                 if ( iconSelect )
215                         delete iconSelect;
216         }
217
218         void saveProfile(UserAccountList acc)
219         {
220                 // clear it
221                 accountTree.removeOption("accounts", true);
222                 // save accounts with known base
223                 QSet<QString> cbases;
224                 foreach(UserAccount ua, acc) {
225                         if (!ua.optionsBase.isEmpty()) {
226                                 ua.toOptions(&accountTree);
227                                 cbases += ua.optionsBase;
228                         }
229                 }
230                 // save new accounts
231                 int idx = 0;
232                 foreach(UserAccount ua, acc) {
233                         if (ua.optionsBase.isEmpty()) {
234                                 QString base;
235                                 do {
236                                         base = "accounts.a"+QString::number(idx++);
237                                 } while (cbases.contains(base));
238                                 cbases += base;
239                                 ua.toOptions(&accountTree, base);
240                         }
241                 }
242                 QFile accountsFile(pathToProfile( activeProfile ) + "/accounts.xml");   
243                 accountTree.saveOptions(accountsFile.fileName(), "accounts", ApplicationInfo::optionsNS(), ApplicationInfo::version());;
244                 
245         }
246
247 private slots:
248         void updateIconSelect()
249         {
250                 Iconset iss;
251                 foreach(Iconset* iconset, PsiIconset::instance()->emoticons) {
252                         iss += *iconset;
253                 }
254
255                 iconSelect->setIconset(iss);
256                 QPixmapCache::clear();
257         }
258
259 public:
260         PsiCon* psi;
261         PsiContactList* contactList;
262         OptionsMigration optionsMigration;
263         OptionsTree accountTree;
264         MainWin *mainwin;
265         Idle idle;
266         QList<item_dialog*> dialogList;
267         int eventId;
268         QStringList recentNodeList; // FIXME move this to options system?
269         EDB *edb;
270         S5BServer *s5bServer;
271         ProxyManager *proxy;
272         IconSelectPopup *iconSelect;
273 #ifdef FILETRANSFER
274         FileTransDlg *ftwin;
275 #endif
276         PsiActionList *actionList;
277         //GlobalAccelManager *globalAccelManager;
278         TuneController* tuneController;
279         QMenuBar* defaultMenuBar;
280         CapsRegistry* capsRegistry;
281         TabManager *tabManager;
282         bool quitting;
283         QTimer* updatedAccountTimer_;
284         AutoUpdater *autoUpdater;
285         AlertManager alertManager;
286         BossKey *bossKey;
287 };
288
289 //----------------------------------------------------------------------------
290 // PsiCon
291 //----------------------------------------------------------------------------
292
293 PsiCon::PsiCon()
294         : QObject(0)
295 {
296         //pdb(DEBUG_JABCON, QString("%1 v%2\n By Justin Karneges\n    infiniti@affinix.com\n\n").arg(PROG_NAME).arg(PROG_VERSION));
297         d = new Private(this);
298         d->tabManager = new TabManager(this);
299         connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), SLOT(aboutToQuit()));
300
301
302         d->mainwin = 0;
303 #ifdef FILETRANSFER
304         d->ftwin = 0;
305 #endif
306
307         d->edb = new EDBFlatFile;
308
309         d->s5bServer = 0;
310         d->proxy = 0;
311         d->tuneController = 0;
312         d->autoUpdater = 0;
313
314         d->actionList = 0;
315         d->defaultMenuBar = new QMenuBar(0);
316
317         d->capsRegistry = new CapsRegistry();
318         connect(d->capsRegistry, SIGNAL(registered(const CapsSpec&)), SLOT(saveCapabilities()));
319 }
320
321 PsiCon::~PsiCon()
322 {
323         deinit();
324
325         saveCapabilities();
326         delete d->capsRegistry;
327
328         delete d->autoUpdater;
329         delete d->actionList;
330         delete d->edb;
331         delete d->defaultMenuBar;
332         delete d->tabManager;
333         delete d;
334 }
335
336 bool PsiCon::init()
337 {
338         // check active profiles
339         if (!ActiveProfiles::instance()->setThisProfile(activeProfile))
340                 return false;
341
342         connect(qApp, SIGNAL(forceSavePreferences()), SLOT(forceSavePreferences()));
343
344 #ifdef HAVE_PGPUTIL
345         // PGP initialization (needs to be before any gpg usage!)
346         PGPUtil::instance();
347 #endif
348
349         d->contactList = new PsiContactList(this);
350
351         connect(d->contactList, SIGNAL(accountAdded(PsiAccount*)), SIGNAL(accountAdded(PsiAccount*)));
352         connect(d->contactList, SIGNAL(accountRemoved(PsiAccount*)), SIGNAL(accountRemoved(PsiAccount*)));
353         connect(d->contactList, SIGNAL(accountCountChanged()), SIGNAL(accountCountChanged()));
354         connect(d->contactList, SIGNAL(accountActivityChanged()), SIGNAL(accountActivityChanged()));
355         connect(d->contactList, SIGNAL(saveAccounts()), SLOT(saveAccounts()));
356         connect(d->contactList, SIGNAL(queueChanged()), SLOT(queueChanged()));
357
358         d->updatedAccountTimer_ = new QTimer(this);
359         d->updatedAccountTimer_->setSingleShot(true);
360         d->updatedAccountTimer_->setInterval(1000);
361         connect(d->updatedAccountTimer_, SIGNAL(timeout()), SLOT(saveAccounts()));
362
363         // do some backuping in case we are about to start migration from config.xml+options.xml
364         // to options.xml only.
365         QString backupfile = optionsFile() + "-preOptionsMigration";
366         if (QFile::exists(pathToProfileConfig(activeProfile))
367                 && PsiOptions::exists(optionsFile())
368                 && !QFile::exists(backupfile)) {
369                 QFile::copy(optionsFile(), backupfile);
370         }
371         
372         // advanced widget
373         GAdvancedWidget::setStickEnabled( false ); //until this is bugless
374         GAdvancedWidget::setStickToWindows( false ); //again
375         GAdvancedWidget::setStickAt( 5 );
376         
377         PsiRichText::setAllowedImageDirs(QStringList()
378                                                                          << ApplicationInfo::resourcesDir()
379                                                                          << ApplicationInfo::homeDir());
380         
381         // To allow us to upgrade from old hardcoded options gracefully, be careful about the order here
382         PsiOptions *options=PsiOptions::instance();
383         //load the system-wide defaults, if they exist
384         QString systemDefaults=ApplicationInfo::resourcesDir();
385         systemDefaults += "/options-default.xml";
386         //qWarning(qPrintable(QString("Loading system defaults from %1").arg(systemDefaults)));
387         options->load(systemDefaults);
388
389         if (!PsiOptions::exists(optionsFile())) {
390                 if (!options->newProfile()) {
391                         qWarning("ERROR: Failed to new profile default options");
392                 }
393         }
394         
395         // load the old profile
396         d->optionsMigration.fromFile(pathToProfileConfig(activeProfile));
397         
398         //load the new profile
399         options->load(optionsFile());
400         //Save every time an option is changed
401         options->autoSave(true, optionsFile());
402
403         //just set a dummy option to trigger saving
404         options->setOption("trigger-save",false);
405         options->setOption("trigger-save",true);
406         
407         // do some late migration work
408         d->optionsMigration.lateMigration();
409
410 #ifdef USE_PEP
411         // Create the tune controller
412         d->tuneController = new CombinedTuneController();
413 #endif
414
415         // Auto updater initialization
416 #ifdef HAVE_SPARKLE
417         d->autoUpdater = new SparkleAutoUpdater(ApplicationInfo::getAppCastURL());
418 #endif
419         if (d->autoUpdater && PsiOptions::instance()->getOption("options.auto-update.check-on-startup").toBool()) {
420                 d->autoUpdater->checkForUpdates();
421         }
422
423         // calculate the small font size
424         const int minimumFontSize = 7;
425         common_smallFontSize = qApp->font().pointSize();
426         common_smallFontSize -= 2;
427         if ( common_smallFontSize < minimumFontSize )
428                 common_smallFontSize = minimumFontSize;
429         FancyLabel::setSmallFontSize( common_smallFontSize );
430         
431         QFile accountsFile(pathToProfile( activeProfile ) + "/accounts.xml");
432         bool accountMigration = false;  
433         if (!accountsFile.exists()) {
434                 accountMigration = true;
435                 int idx = 0;
436                 foreach(UserAccount a, d->optionsMigration.accMigration) {
437                         QString base = "accounts.a"+QString::number(idx++);
438                         a.toOptions(&d->accountTree, base);
439                 }
440         } else {
441                 d->accountTree.loadOptions(accountsFile.fileName(), "accounts", ApplicationInfo::optionsNS());
442         }
443
444         // proxy
445         d->proxy = new ProxyManager(&d->accountTree, this);
446         if (accountMigration) d->proxy->migrateItemList(d->optionsMigration.proxyMigration);
447         connect(d->proxy, SIGNAL(settingsChanged()), SLOT(proxy_settingsChanged()));
448         
449         connect(options, SIGNAL(optionChanged(const QString&)), SLOT(optionChanged(const QString&)));
450         
451
452         contactUpdatesManager_ = new ContactUpdatesManager(this);
453
454         QDir profileDir( pathToProfile( activeProfile ) );
455         profileDir.rmdir( "info" ); // remove unused dir
456
457         d->iconSelect = new IconSelectPopup(0);
458         connect(PsiIconset::instance(), SIGNAL(emoticonsChanged()), d, SLOT(updateIconSelect()));
459
460         // first thing, try to load the iconset
461         bool result = true;;
462         if( !PsiIconset::instance()->loadAll() ) {
463                 //LEGOPTS.iconset = "stellar";
464                 //if(!is.load(LEGOPTS.iconset)) {
465                         QMessageBox::critical(0, tr("Error"), tr("Unable to load iconset!  Please make sure Psi is properly installed."));
466                         result = false;
467                 //}
468         }
469
470         if ( !d->actionList )
471                 d->actionList = new PsiActionList( this );
472
473         PsiConObject* psiConObject = new PsiConObject(this);
474                 
475         Anim::setMainThread(QThread::currentThread());
476
477         // setup the main window
478         d->mainwin = new MainWin(PsiOptions::instance()->getOption("options.ui.contactlist.always-on-top").toBool(), (PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool() && PsiOptions::instance()->getOption("options.contactlist.use-toolwindow").toBool()), this);
479         d->mainwin->setUseDock(PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool());
480         d->bossKey = new BossKey(d->mainwin);
481
482         Q_UNUSED(psiConObject);
483
484         connect(d->mainwin, SIGNAL(closeProgram()), SLOT(closeProgram()));
485         connect(d->mainwin, SIGNAL(changeProfile()), SLOT(changeProfile()));
486         connect(d->mainwin, SIGNAL(doManageAccounts()), SLOT(doManageAccounts()));
487         connect(d->mainwin, SIGNAL(doGroupChat()), SLOT(doGroupChat()));
488         connect(d->mainwin, SIGNAL(blankMessage()), SLOT(doNewBlankMessage()));
489         connect(d->mainwin, SIGNAL(statusChanged(int)), SLOT(statusMenuChanged(int)));
490         connect(d->mainwin, SIGNAL(doOptions()), SLOT(doOptions()));
491         connect(d->mainwin, SIGNAL(doToolbars()), SLOT(doToolbars()));
492         connect(d->mainwin, SIGNAL(doFileTransDlg()), SLOT(doFileTransDlg()));
493         connect(d->mainwin, SIGNAL(recvNextEvent()), SLOT(recvNextEvent()));
494         connect(this, SIGNAL(emitOptionsUpdate()), d->mainwin, SLOT(optionsUpdate()));
495
496 #ifndef NEWCONTACTLIST
497         connect(this, SIGNAL(emitOptionsUpdate()), d->mainwin->cvlist, SLOT(optionsUpdate()));
498 #endif
499
500
501         d->mainwin->setGeometryOptionPath("options.ui.contactlist.saved-window-geometry");
502
503         if (result &&
504             !(PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool() &&
505               PsiOptions::instance()->getOption("options.contactlist.hide-on-start").toBool()))
506         {
507                 d->mainwin->show();
508         }
509
510 #ifdef FILETRANSFER
511         d->ftwin = new FileTransDlg(this);
512 #endif
513
514         d->idle.start();
515
516         // S5B
517         d->s5bServer = new S5BServer;
518         s5b_init();
519
520         // Connect to the system monitor
521         SystemWatch* sw = SystemWatch::instance();
522         connect(sw, SIGNAL(sleep()), this, SLOT(doSleep()));
523         connect(sw, SIGNAL(wakeup()), this, SLOT(doWakeup()));
524
525 #ifdef PSI_PLUGINS
526         // Plugin Manager
527         PluginManager::instance();
528 #endif
529
530         // Global shortcuts
531         setShortcuts();
532
533         // FIXME
534 #ifdef __GNUC__
535 #warning "Temporary hard-coding caps registration of own version"
536 #endif
537         // client()->identity()
538
539         registerCaps(ApplicationInfo::capsVersion(), QStringList()
540 #ifdef FILETRANSFER
541                      << "http://jabber.org/protocol/bytestreams"
542                      << "http://jabber.org/protocol/ibb"
543                      << "http://jabber.org/protocol/si"
544                      << "http://jabber.org/protocol/si/profile/file-transfer"
545 #endif
546                      << "http://jabber.org/protocol/disco#info"
547                      << "http://jabber.org/protocol/commands"
548                      << "http://jabber.org/protocol/rosterx"
549 #ifdef GROUPCHAT
550                      << "http://jabber.org/protocol/muc"
551 #endif
552                      << "jabber:x:data"
553                     );
554
555         registerCaps("ep", QStringList()
556                      << "http://jabber.org/protocol/mood"
557                      << "http://jabber.org/protocol/tune"
558                      << "http://jabber.org/protocol/physloc"
559                      << "http://jabber.org/protocol/geoloc"
560                      << "urn:xmpp:avatar:data"
561                      << "urn:xmpp:avatar:metadata"
562                     );
563
564         registerCaps("ep-notify-2", QStringList()
565                      << "http://jabber.org/protocol/mood+notify"
566                      << "http://jabber.org/protocol/tune+notify"
567                      << "http://jabber.org/protocol/physloc+notify"
568                      << "http://jabber.org/protocol/geoloc+notify"
569                      << "urn:xmpp:avatar:metadata+notify"
570                     );
571
572         registerCaps("html", QStringList("http://jabber.org/protocol/xhtml-im"));
573         registerCaps("cs", QStringList("http://jabber.org/protocol/chatstates"));
574 #ifdef YAPSI
575         registerCaps("mr", QStringList("urn:xmpp:receipts"));
576 #endif
577
578         // load accounts
579         {
580                 QList<UserAccount> accs;
581                 QStringList bases = d->accountTree.getChildOptionNames("accounts", true, true);
582                 foreach (QString base, bases) {
583                         UserAccount ua;
584                         ua.fromOptions(&d->accountTree, base);
585                         accs += ua;
586                 }
587                 
588                 // Disable accounts if necessary, and overwrite locked properties
589                 if (PsiOptions::instance()->getOption("options.ui.account.single").toBool() || !PsiOptions::instance()->getOption("options.account.domain").toString().isEmpty()) {
590                         bool haveEnabled = false;
591                         for(UserAccountList::Iterator it = accs.begin(); it != accs.end(); ++it) {
592                                 // With single accounts, only modify the first account
593                                 if (PsiOptions::instance()->getOption("options.ui.account.single").toBool()) {
594                                         if (!haveEnabled) {
595                                                 haveEnabled = it->opt_enabled;
596                                                 if (it->opt_enabled) {
597                                                         if (!PsiOptions::instance()->getOption("options.account.domain").toString().isEmpty())
598                                                                 it->jid = JIDUtil::accountFromString(Jid(it->jid).node()).bare();
599                                                 }
600                                         }
601                                         else
602                                                 it->opt_enabled = false;
603                                 }
604                                 else {
605                                         // Overwirte locked properties
606                                         if (!PsiOptions::instance()->getOption("options.account.domain").toString().isEmpty())
607                                                 it->jid = JIDUtil::accountFromString(Jid(it->jid).node()).bare();
608                                 }
609                         }
610                 }
611                 
612                 d->contactList->loadAccounts(accs);
613         }       
614         
615         checkAccountsEmpty();
616         
617         // try autologin if needed
618         foreach(PsiAccount* account, d->contactList->accounts()) {
619                 account->autoLogin();
620         }
621
622         // show tip of the day
623         if ( PsiOptions::instance()->getOption("options.ui.tip.show").toBool() ) {
624                 TipDlg::show(this);
625         }
626
627 #ifdef USE_DBUS
628         addPsiConAdapter(this);
629 #endif
630
631         connect(ActiveProfiles::instance(), SIGNAL(setStatusRequested(const QString &, const QString &)), SLOT(setStatusFromCommandline(const QString &, const QString &)));
632         connect(ActiveProfiles::instance(), SIGNAL(openUriRequested(const QString &)), SLOT(openUri(const QString &)));
633         connect(ActiveProfiles::instance(), SIGNAL(raiseRequested()), SLOT(raiseMainwin()));
634
635
636         DesktopUtil::setUrlHandler("xmpp", this, "openUri");
637         DesktopUtil::setUrlHandler("x-psi-atstyle", this, "openAtStyleUri");
638
639         if(AvCallManager::isSupported()) {
640                 options_avcall_update();
641                 AvCallManager::setAudioOutDevice(PsiOptions::instance()->getOption("options.media.devices.audio-output").toString());
642                 AvCallManager::setAudioInDevice(PsiOptions::instance()->getOption("options.media.devices.audio-input").toString());
643                 AvCallManager::setVideoInDevice(PsiOptions::instance()->getOption("options.media.devices.video-input").toString());
644                 AvCallManager::setBasePort(PsiOptions::instance()->getOption("options.p2p.bytestreams.listen-port").toInt());
645                 AvCallManager::setExternalAddress(PsiOptions::instance()->getOption("options.p2p.bytestreams.external-address").toString());
646         }
647
648         return result;
649 }
650
651 bool PsiCon::haveAutoUpdater() const
652 {
653         return d->autoUpdater != 0;
654 }
655
656 void PsiCon::registerCaps(const QString& ext, const QStringList& features)
657 {
658         DiscoItem::Identity identity = { "client", ApplicationInfo::name(), "pc" };
659         DiscoItem::Identities identities;
660         identities += identity;
661
662         d->capsRegistry->registerCaps(CapsSpec(ApplicationInfo::capsNode(),
663                                                ApplicationInfo::capsVersion(), ext),
664                                       identities,
665                                       Features(features));
666 }
667
668 void PsiCon::deinit()
669 {
670         // this deletes all dialogs except for mainwin
671         deleteAllDialogs();
672
673         d->idle.stop();
674
675         // shut down all accounts
676         UserAccountList acc;
677         if(d->contactList) {
678                 acc = d->contactList->getUserAccountList();
679                 delete d->contactList;
680         }
681
682         // delete s5b server
683         delete d->s5bServer;
684
685 #ifdef FILETRANSFER
686         delete d->ftwin;
687 #endif
688
689         if(d->mainwin) {
690                 delete d->mainwin;
691                 d->mainwin = 0;
692         }
693
694         // TuneController
695         delete d->tuneController;
696
697         // save profile
698         if(d->contactList)
699                 d->saveProfile(acc);
700
701         GlobalShortcutManager::clear();
702
703         DesktopUtil::unsetUrlHandler("xmpp");
704 }
705
706
707 void PsiCon::setShortcuts()
708 {
709         // FIX-ME: GlobalShortcutManager::clear() is one big hack,
710         // but people wanted to change global hotkeys without restarting in 0.11
711         GlobalShortcutManager::clear();
712         ShortcutManager::connect("global.event", this, SLOT(recvNextEvent()));
713         ShortcutManager::connect("global.toggle-visibility", d->mainwin, SLOT(toggleVisible()));
714         ShortcutManager::connect("global.bring-to-front", d->mainwin, SLOT(trayShow()));
715         ShortcutManager::connect("global.new-blank-message", this, SLOT(doNewBlankMessage()));
716         ShortcutManager::connect("global.boss-key", d->bossKey, SLOT(shortCutActivated()));
717 #ifdef YAPSI
718         ShortcutManager::connect("global.filter-contacts", d->mainwin, SLOT(filterContacts()));
719 #endif
720 }
721
722 PsiContactList* PsiCon::contactList() const
723 {
724         return d->contactList;
725 }
726
727 EDB *PsiCon::edb() const
728 {
729         return d->edb;
730 }
731
732 ProxyManager *PsiCon::proxy() const
733 {
734         return d->proxy;
735 }
736
737 FileTransDlg *PsiCon::ftdlg() const
738 {
739 #ifdef FILETRANSFER
740         return d->ftwin;
741 #else
742         return 0;
743 #endif
744 }
745
746 TabManager *PsiCon::tabManager() const
747 {
748         return d->tabManager;
749 }
750
751 TuneController *PsiCon::tuneController() const
752 {
753         return d->tuneController;
754 }
755
756 AlertManager *PsiCon::alertManager() const {
757         return &(d->alertManager);
758 }
759
760
761 void PsiCon::closeProgram()
762 {
763         doQuit(QuitProgram);
764 }
765
766 void PsiCon::changeProfile()
767 {
768         ActiveProfiles::instance()->unsetThisProfile();
769         if(d->contactList->haveActiveAccounts()) {
770                 QMessageBox messageBox(QMessageBox::Information, CAP(tr("Error")), tr("Please disconnect before changing the profile."));
771                 QPushButton* cancel = messageBox.addButton(QMessageBox::Cancel);
772                 QPushButton* disconnect = messageBox.addButton(tr("&Disconnect"), QMessageBox::AcceptRole);
773                 messageBox.setDefaultButton(disconnect);
774                 messageBox.exec();
775                 if (messageBox.clickedButton() == cancel)
776                         return;
777
778                 setStatusFromDialog(XMPP::Status::Offline, false, true);
779         }
780
781         doQuit(QuitProfile);
782 }
783
784 void PsiCon::doManageAccounts()
785 {
786         if (!PsiOptions::instance()->getOption("options.ui.account.single").toBool()) {
787                 AccountManageDlg *w = (AccountManageDlg *)dialogFind("AccountManageDlg");
788                 if(w)
789                         bringToFront(w);
790                 else {
791                         w = new AccountManageDlg(this);
792                         w->show();
793                 }
794         }
795         else {
796                 PsiAccount *account = d->contactList->defaultAccount();
797                 if(account) {
798                         account->modify();
799                 }
800                 else {
801                         promptUserToCreateAccount();
802                 }
803         }
804 }
805
806 void PsiCon::doGroupChat()
807 {
808 #ifdef GROUPCHAT
809         PsiAccount *account = d->contactList->defaultAccount();
810         if(!account)
811                 return;
812
813         MUCJoinDlg *w = new MUCJoinDlg(this, account);
814         w->show();
815 #endif
816 }
817
818 void PsiCon::doNewBlankMessage()
819 {
820         PsiAccount *account = d->contactList->defaultAccount();
821         if(!account)
822                 return;
823
824         EventDlg *w = createEventDlg("", account);
825         if (!w)
826                 return;
827
828         w->show();
829 }
830
831 // FIXME: smells fishy. Refactor! Probably create a common class for all dialogs and 
832 // call optionsUpdate() automatically.
833 EventDlg *PsiCon::createEventDlg(const QString &to, PsiAccount *pa)
834 {
835         if (!EventDlg::messagingEnabled())
836                 return 0;
837
838         EventDlg *w = new EventDlg(to, this, pa);
839         connect(this, SIGNAL(emitOptionsUpdate()), w, SLOT(optionsUpdate()));
840         return w;
841 }
842
843 // FIXME: WTF? Refactor! Refactor!
844 void PsiCon::updateContactGlobal(PsiAccount *pa, const Jid &j)
845 {
846         foreach(item_dialog* i, d->dialogList) {
847                 if(i->className == "EventDlg") {
848                         EventDlg *e = (EventDlg *)i->widget;
849                         if(e->psiAccount() == pa)
850                                 e->updateContact(j);
851                 }
852         }
853 }
854
855 // FIXME: make it work like QObject::findChildren<ChildName>()
856 QWidget *PsiCon::dialogFind(const char *className)
857 {
858         foreach(item_dialog *i, d->dialogList) {
859                 // does the classname and jid match?
860                 if(i->className == className) {
861                         return i->widget;
862                 }
863         }
864         return 0;
865 }
866
867 QMenuBar* PsiCon::defaultMenuBar() const
868 {
869         return d->defaultMenuBar;
870 }
871
872 void PsiCon::dialogRegister(QWidget *w)
873 {
874         item_dialog *i = new item_dialog;
875         i->widget = w;
876         i->className = w->metaObject()->className();
877         d->dialogList.append(i);
878 }
879
880 void PsiCon::dialogUnregister(QWidget *w)
881 {
882         for (QList<item_dialog*>::Iterator it = d->dialogList.begin(); it != d->dialogList.end(); ) {
883                 item_dialog* i = *it;
884                 if(i->widget == w) {
885                         it = d->dialogList.erase(it);
886                         delete i;
887                 }
888                 else
889                         ++it;
890         }
891 }
892
893 void PsiCon::deleteAllDialogs()
894 {
895         while(!d->dialogList.isEmpty()) {
896                 item_dialog* i = d->dialogList.takeFirst();
897                 delete i->widget;
898                 delete i;
899         }
900         d->tabManager->deleteAll();
901 }
902
903 AccountsComboBox *PsiCon::accountsComboBox(QWidget *parent, bool online_only)
904 {
905         AccountsComboBox* acb = new AccountsComboBox(parent);
906         acb->setController(this);
907         acb->setOnlineOnly(online_only);
908         return acb;
909 }
910
911 PsiAccount* PsiCon::createAccount(const QString &name, const Jid &j, const QString &pass, bool opt_host, const QString &host, int port, bool legacy_ssl_probe, UserAccount::SSLFlag ssl, QString proxy, const QString &tlsOverrideDomain, const QByteArray &tlsOverrideCert)
912 {
913         return d->contactList->createAccount(name, j, pass, opt_host, host, port, legacy_ssl_probe, ssl, proxy, tlsOverrideDomain, tlsOverrideCert);
914 }
915
916 PsiAccount *PsiCon::createAccount(const UserAccount& _acc)
917 {
918         UserAccount acc = _acc;
919         PsiAccount *pa = new PsiAccount(acc, d->contactList, d->capsRegistry, d->tabManager);
920         connect(&d->idle, SIGNAL(secondsIdle(int)), pa, SLOT(secondsIdle(int)));
921         connect(pa, SIGNAL(updatedActivity()), SLOT(pa_updatedActivity()));
922         connect(pa, SIGNAL(updatedAccount()), SLOT(pa_updatedAccount()));
923         connect(pa, SIGNAL(queueChanged()), SLOT(queueChanged()));
924         connect(pa, SIGNAL(startBounce()), SLOT(startBounce()));
925         if (d->s5bServer) {
926                 pa->client()->s5bManager()->setServer(d->s5bServer);
927         }
928         return pa;
929 }
930
931 void PsiCon::removeAccount(PsiAccount *pa)
932 {
933         d->contactList->removeAccount(pa);
934 }
935
936 void PsiCon::statusMenuChanged(int x)
937 {
938 #ifndef YAPSI
939         if(x == STATUS_OFFLINE && !PsiOptions::instance()->getOption("options.status.ask-for-message-on-offline").toBool()) {
940                 setGlobalStatus(PsiAccount::loggedOutStatus(), false, true);
941                 if(PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool() == true)
942                         d->mainwin->setTrayToolTip(Status(Status::Offline, "", 0));
943         }
944         else {
945                 if(x == STATUS_ONLINE && !PsiOptions::instance()->getOption("options.status.ask-for-message-on-online").toBool()) {
946                         setGlobalStatus(Status(), false, true);
947                         if(PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool() == true)
948                                 d->mainwin->setTrayToolTip(Status());
949                 }
950                 else if(x == STATUS_INVISIBLE){
951                         Status s("","",0,true);
952                         s.setIsInvisible(true);
953                         setGlobalStatus(s, false, true);
954                         if(PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool() == true)
955                                 d->mainwin->setTrayToolTip(s);
956                 }
957                 else {
958                         // Create a dialog with the last status message
959                         StatusSetDlg *w = new StatusSetDlg(this, makeStatus(x, currentStatusMessage()));
960                         connect(w, SIGNAL(set(const XMPP::Status &, bool, bool)), SLOT(setStatusFromDialog(const XMPP::Status &, bool, bool)));
961                         connect(w, SIGNAL(cancelled()), SLOT(updateMainwinStatus()));
962                         if(PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool() == true)
963                                 connect(w, SIGNAL(set(const XMPP::Status &, bool, bool)), d->mainwin, SLOT(setTrayToolTip(const XMPP::Status &, bool, bool)));
964                         w->show();
965                 }
966         }
967 #else
968         setGlobalStatus(makeStatus(x, currentStatusMessage()), false, true);
969 #endif
970 }
971
972 XMPP::Status::Type PsiCon::currentStatusType() const
973 {
974 #ifdef YAPSI
975         if (!d->mainwin)
976                 return XMPP::Status::Offline;
977         return d->mainwin->statusType();
978 #else
979         bool active = false;
980         bool loggedIn = false;
981         XMPP::Status::Type state = XMPP::Status::Online;
982         foreach(PsiAccount* account, d->contactList->enabledAccounts()) {
983                 if(account->isActive())
984                         active = true;
985                 if(account->loggedIn()) {
986                         loggedIn = true;
987                         state = account->status().type();
988                 }
989         }
990
991         if (!loggedIn) {
992                 state = XMPP::Status::Offline;
993         }
994
995         return state;
996 #endif
997 }
998
999 XMPP::Status::Type PsiCon::lastLoggedInStatusType() const
1000 {
1001 #ifdef YAPSI
1002         return d->mainwin->lastLoggedInStatusType();
1003 #else
1004 // FIXME: Has to use status type from global status type selector
1005         return XMPP::Status::Online;
1006 #endif
1007 }
1008
1009 QString PsiCon::currentStatusMessage() const
1010 {
1011 #ifdef YAPSI
1012         if (!d->mainwin)
1013                 return QString();
1014         return d->mainwin->statusMessage();
1015 #else
1016         return PsiOptions::instance()->getOption("options.status.last-message").toString();
1017 #endif
1018 }
1019
1020 void PsiCon::setStatusFromDialog(const XMPP::Status &s, bool withPriority, bool isManualStatus)
1021 {
1022         if (isManualStatus) {
1023                 PsiOptions::instance()->setOption("options.status.last-message", s.status());
1024         }
1025         setGlobalStatus(s, withPriority, isManualStatus);
1026 }
1027
1028 void PsiCon::setStatusFromCommandline(const QString &status, const QString &message)
1029 {
1030         bool isManualStatus = true; // presume that this will always be a manual user action
1031         if (isManualStatus) {
1032                 PsiOptions::instance()->setOption("options.status.last-message", message);
1033         }
1034         XMPP::Status s;
1035         s.setType(status);
1036         s.setStatus(message);   // yes, a bit different naming convention..
1037         setGlobalStatus(s, false, isManualStatus);
1038 }
1039
1040 void PsiCon::setGlobalStatus(const Status &s, bool withPriority, bool isManualStatus)
1041 {
1042         // Check whether all accounts are logged off
1043         bool allOffline = true;
1044 #ifndef YAPSI
1045         foreach(PsiAccount* account, d->contactList->enabledAccounts()) {
1046                 if ( account->isActive() ) {
1047                         allOffline = false;
1048                         break;
1049                 }
1050         }
1051 #endif
1052
1053         // globally set each account which is logged in
1054         foreach(PsiAccount* account, d->contactList->enabledAccounts())
1055                 if (allOffline || account->isActive())
1056                         account->setStatus(s, withPriority, isManualStatus);
1057 }
1058
1059 void PsiCon::pa_updatedActivity()
1060 {
1061         PsiAccount *pa = (PsiAccount *)sender();
1062         emit accountUpdated(pa);
1063
1064         // update s5b server
1065         updateS5BServerAddresses();
1066
1067         updateMainwinStatus();
1068 }
1069
1070 void PsiCon::pa_updatedAccount()
1071 {
1072         PsiAccount *pa = (PsiAccount *)sender();
1073         emit accountUpdated(pa);
1074
1075         d->updatedAccountTimer_->start();
1076 }
1077
1078 void PsiCon::saveAccounts()
1079 {
1080         d->updatedAccountTimer_->stop();
1081
1082         UserAccountList acc = d->contactList->getUserAccountList();
1083         d->saveProfile(acc);
1084 }
1085
1086 void PsiCon::saveCapabilities()
1087 {
1088         QFile file(ApplicationInfo::homeDir() + "/caps.xml");
1089         d->capsRegistry->save(file);
1090 }
1091
1092 void PsiCon::updateMainwinStatus()
1093 {
1094         bool active = false;
1095         bool loggedIn = false;
1096         int state = STATUS_ONLINE;
1097         foreach(PsiAccount* account, d->contactList->enabledAccounts()) {
1098                 if(account->isActive())
1099                         active = true;
1100                 if(account->loggedIn()) {
1101                         loggedIn = true;
1102                         state = makeSTATUS(account->status());
1103                 }
1104         }
1105         if(loggedIn)
1106                 d->mainwin->decorateButton(state);
1107         else {
1108                 if(active)
1109                         d->mainwin->decorateButton(-1);
1110                 else
1111                         d->mainwin->decorateButton(STATUS_OFFLINE);
1112         }
1113 }
1114
1115 void PsiCon::doOptions()
1116 {
1117         OptionsDlg *w = (OptionsDlg *)dialogFind("OptionsDlg");
1118         if(w)
1119                 bringToFront(w);
1120         else {
1121                 w = new OptionsDlg(this);
1122                 connect(w, SIGNAL(applyOptions()), SLOT(slotApplyOptions()));
1123                 w->show();
1124         }
1125 }
1126
1127 void PsiCon::doFileTransDlg()
1128 {
1129 #ifdef FILETRANSFER
1130         bringToFront(d->ftwin);
1131 #endif
1132 }
1133
1134 void PsiCon::checkAccountsEmpty()
1135 {
1136         if (d->contactList->accounts().count() == 0) {
1137 #ifndef YAPSI
1138                 promptUserToCreateAccount();
1139 #endif
1140         }
1141 }
1142
1143 void PsiCon::openUri(const QString &uri)
1144 {
1145         QUrl url;
1146         url.setEncodedUrl(uri.toLatin1());
1147         openUri(url);
1148 }
1149
1150 void PsiCon::openUri(const QUrl &uri)
1151 {
1152         //qDebug() << "uri:  " << uri.toString();
1153
1154         // scheme
1155         if (uri.scheme() != "xmpp") {
1156                 QMessageBox::critical(0, tr("Unsupported URI type"), QString("URI (link) type \"%1\" is not supported.").arg(uri.scheme()));
1157                 return;
1158         }
1159
1160         // authority
1161         PsiAccount *pa = 0;
1162         //if (uri.authority().isEmpty()) {
1163                 pa = d->contactList->defaultAccount();
1164                 if (!pa) {
1165                         QMessageBox::critical(0, tr("Error"), QString("You need to have an account configured and enabled to open URIs (links)."));
1166                         return;
1167                 }
1168         //
1169         // TODO: finish authority component handling
1170         //
1171         //} else {
1172         //      qDebug() << "uri auth: [" << uri.authority() << "]");
1173
1174         //      // is there such account ready to use?
1175         //      Jid authJid = JIDUtil::fromString(uri.authority());
1176         //      foreach (PsiAccount* acc, d->contactList->enabledAccounts()) {
1177         //              if (acc->jid().compare(authJid, false)) {
1178         //                      pa = acc;
1179         //              }
1180         //      }
1181
1182         //      // or maybe it is configured but not enabled?
1183         //      if (!pa) {
1184         //              foreach (PsiAccount* acc, d->contactList->accounts()) {
1185         //                      if (acc->jid().compare(authJid, false)) {
1186         //                              QMessageBox::error(0, tr("Error"), QString("The account for %1 JID is disabled right now.").arg(authJid.bare()));
1187         //                              return; // TODO: Should suggest enabling it now
1188         //                      }
1189         //              }
1190         //      }
1191
1192         //      // nope..
1193         //      if (!pa) {
1194         //              QMessageBox::error(0, tr("Error"), QString("You don't have an account for %1.").arg(authJid.bare()));
1195         //              return;
1196         //      }
1197         //}
1198
1199         pa->openUri(uri);
1200
1201
1202 }
1203
1204 void PsiCon::openAtStyleUri(const QUrl &uri)
1205 {
1206         QUrl u(uri);
1207         u.setScheme("mailto");
1208         DesktopUtil::openUrl(u);
1209 }
1210
1211 void PsiCon::doToolbars()
1212 {
1213         OptionsDlg *w = (OptionsDlg *)dialogFind("OptionsDlg");
1214         if (w) {
1215                 w->openTab("toolbars");
1216                 bringToFront(w);
1217         }
1218         else {
1219                 w = new OptionsDlg(this);
1220                 connect(w, SIGNAL(applyOptions()), SLOT(slotApplyOptions()));
1221                 w->openTab("toolbars");
1222                 w->show();
1223         }
1224 }
1225
1226 void PsiCon::optionChanged(const QString& option)
1227 {
1228         bool notifyRestart = true;
1229
1230         // Global shortcuts
1231         setShortcuts();
1232
1233         if (option == "options.ui.notifications.alert-style") {
1234                 alertIconUpdateAlertStyle();
1235         }
1236
1237         if (option == "options.ui.tabs.use-tabs") {
1238                 QMessageBox::information(0, tr("Information"), tr("Some of the options you changed will only have full effect upon restart."));
1239                 notifyRestart = false;
1240         }
1241
1242         // update s5b
1243         if (option == "options.p2p.bytestreams.listen-port") {
1244                 s5b_init();
1245         }
1246 }
1247
1248 void PsiCon::slotApplyOptions()
1249 {
1250         PsiIconset::instance()->reloadRoster();
1251
1252 #ifndef Q_WS_MAC
1253         PsiOptions *o = PsiOptions::instance();
1254         if (!PsiOptions::instance()->getOption("options.ui.contactlist.show-menubar").toBool()) {
1255                 // check if all toolbars are disabled
1256                 bool toolbarsVisible = false;
1257                 foreach(QString base, o->getChildOptionNames("options.ui.contactlist.toolbars", true, true)) {
1258                         if (o->getOption( base + ".visible").toBool()) {
1259                                 toolbarsVisible = true;
1260                                 break;
1261                         }
1262                 }
1263
1264                 // Check whether it is legal to disable the menubar
1265                 if ( !toolbarsVisible ) {
1266                         QMessageBox::warning(0, tr("Warning"),
1267                                 tr("You can not disable <i>all</i> toolbars <i>and</i> the menubar. If you do so, you will be unable to enable them back, when you'll change your mind."),
1268                                 tr("I understand"));
1269                         PsiOptions::instance()->setOption("options.ui.contactlist.show-menubar", true);
1270                 }
1271         }
1272 #endif
1273
1274         updateS5BServerAddresses();
1275
1276         if(AvCallManager::isSupported()) {
1277                 AvCallManager::setAudioOutDevice(PsiOptions::instance()->getOption("options.media.devices.audio-output").toString());
1278                 AvCallManager::setAudioInDevice(PsiOptions::instance()->getOption("options.media.devices.audio-input").toString());
1279                 AvCallManager::setVideoInDevice(PsiOptions::instance()->getOption("options.media.devices.video-input").toString());
1280                 AvCallManager::setBasePort(PsiOptions::instance()->getOption("options.p2p.bytestreams.listen-port").toInt());
1281                 AvCallManager::setExternalAddress(PsiOptions::instance()->getOption("options.p2p.bytestreams.external-address").toString());
1282         }
1283
1284         // mainwin stuff
1285         d->mainwin->setWindowOpts(PsiOptions::instance()->getOption("options.ui.contactlist.always-on-top").toBool(), (PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool() && PsiOptions::instance()->getOption("options.contactlist.use-toolwindow").toBool()));
1286         d->mainwin->setUseDock(PsiOptions::instance()->getOption("options.ui.systemtray.enable").toBool());
1287         d->mainwin->buildToolbars();
1288
1289         // notify about options change
1290         emit emitOptionsUpdate();
1291 }
1292
1293 void PsiCon::queueChanged()
1294 {
1295         PsiIcon *nextAnim = 0;
1296         int nextAmount = d->contactList->queueCount();
1297         PsiAccount *pa = d->contactList->queueLowestEventId();
1298         if(pa)
1299                 nextAnim = PsiIconset::instance()->event2icon(pa->eventQueue()->peekNext());
1300
1301 #ifdef Q_WS_MAC
1302         {
1303                 // Update the event count
1304                 MacDock::overlay(nextAmount ? QString::number(nextAmount) : QString());
1305
1306                 if (!nextAmount) {
1307                         MacDock::stopBounce();
1308                 }
1309         }
1310 #endif
1311
1312         d->mainwin->updateReadNext(nextAnim, nextAmount);
1313 }
1314
1315 void PsiCon::startBounce()
1316 {
1317 #ifdef Q_WS_MAC
1318         if (PsiOptions::instance()->getOption("options.ui.notifications.bounce-dock").toString() != "never") {
1319                 MacDock::startBounce();
1320                 if (PsiOptions::instance()->getOption("options.ui.notifications.bounce-dock").toString() == "once") {
1321                         MacDock::stopBounce();
1322                 }
1323         }
1324 #endif
1325 }
1326
1327 void PsiCon::recvNextEvent()
1328 {
1329         /*printf("--- Queue Content: ---\n");
1330         PsiAccountListIt it(d->list);
1331         for(PsiAccount *pa; (pa = it.current()); ++it) {
1332                 printf(" Account: [%s]\n", pa->name().latin1());
1333                 pa->eventQueue()->printContent();
1334         }*/
1335         PsiAccount *pa = d->contactList->queueLowestEventId();
1336         if(pa)
1337                 pa->openNextEvent(UserAction);
1338 }
1339
1340 void PsiCon::playSound(const QString &str)
1341 {
1342         if(str.isEmpty() || !PsiOptions::instance()->getOption("options.ui.notifications.sounds.enable").toBool())
1343                 return;
1344
1345         soundPlay(str);
1346 }
1347
1348 void PsiCon::raiseMainwin()
1349 {
1350         d->mainwin->showNoFocus();
1351 }
1352
1353 bool PsiCon::mainWinVisible() const
1354 {
1355         return d->mainwin->isVisible();
1356 }
1357
1358 QStringList PsiCon::recentGCList() const
1359 {
1360         return PsiOptions::instance()->getOption("options.muc.recent-joins.jids").toStringList();
1361 }
1362
1363 void PsiCon::recentGCAdd(const QString &str)
1364 {
1365         QStringList recentList = recentGCList();
1366         // remove it if we have it
1367         foreach(const QString& s, recentList) {
1368                 if(s == str) {
1369                         recentList.removeAll(s);
1370                         break;
1371                 }
1372         }
1373
1374         // put it in the front
1375         recentList.prepend(str);
1376
1377         // trim the list if bigger than 10
1378         while(recentList.count() > PsiOptions::instance()->getOption("options.muc.recent-joins.maximum").toInt()) {
1379                 recentList.takeLast();
1380         }
1381         
1382         PsiOptions::instance()->setOption("options.muc.recent-joins.jids", recentList);
1383 }
1384
1385 QStringList PsiCon::recentBrowseList() const
1386 {
1387         return PsiOptions::instance()->getOption("options.ui.service-discovery.recent-jids").toStringList();
1388 }
1389
1390 void PsiCon::recentBrowseAdd(const QString &str)
1391 {
1392         QStringList recentList = recentBrowseList();
1393         // remove it if we have it
1394         foreach(const QString& s, recentList) {
1395                 if(s == str) {
1396                         recentList.removeAll(s);
1397                         break;
1398                 }
1399         }
1400
1401         // put it in the front
1402         recentList.prepend(str);
1403
1404         // trim the list if bigger than 10
1405         while(recentList.count() > 10) {
1406                 recentList.takeLast();
1407         }
1408         
1409         PsiOptions::instance()->setOption("options.ui.service-discovery.recent-jids", recentList);
1410 }
1411
1412 const QStringList & PsiCon::recentNodeList() const
1413 {
1414         return d->recentNodeList;
1415 }
1416
1417 void PsiCon::recentNodeAdd(const QString &str)
1418 {
1419         // remove it if we have it
1420         foreach(const QString& s, d->recentNodeList) {
1421                 if(s == str) {
1422                         d->recentNodeList.removeAll(s);
1423                         break;
1424                 }
1425         }
1426
1427         // put it in the front
1428         d->recentNodeList.prepend(str);
1429
1430         // trim the list if bigger than 10
1431         while(d->recentNodeList.count() > 10)
1432                 d->recentNodeList.takeLast();
1433 }
1434
1435 void PsiCon::proxy_settingsChanged()
1436 {
1437         saveAccounts();
1438 }
1439
1440 IconSelectPopup *PsiCon::iconSelectPopup() const
1441 {
1442         return d->iconSelect;
1443 }
1444
1445 bool PsiCon::filterEvent(const PsiAccount* acc, const PsiEvent* e) const
1446 {
1447         Q_UNUSED(acc);
1448         Q_UNUSED(e);
1449         return false;
1450 }
1451
1452 void PsiCon::processEvent(PsiEvent *e, ActivationType activationType)
1453 {
1454         if ( e->type() == PsiEvent::PGP ) {
1455                 e->account()->eventQueue()->dequeue(e);
1456                 e->account()->queueChanged();
1457                 return;
1458         }
1459
1460         if ( !e->account() )
1461                 return;
1462
1463         UserListItem *u = e->account()->find(e->jid());
1464         if ( !u ) {
1465                 qWarning("SYSTEM MESSAGE: Bug #1. Contact the developers and tell them what you did to make this message appear. Thank you.");
1466                 e->account()->eventQueue()->dequeue(e);
1467                 e->account()->queueChanged();
1468                 return;
1469         }
1470
1471 #ifdef FILETRANSFER
1472         if( e->type() == PsiEvent::File ) {
1473                 FileEvent *fe = (FileEvent *)e;
1474                 FileTransfer *ft = fe->takeFileTransfer();
1475                 e->account()->eventQueue()->dequeue(e);
1476                 e->account()->queueChanged();
1477                 e->account()->cpUpdate(*u);
1478                 if(ft) {
1479                         FileRequestDlg *w = new FileRequestDlg(fe->timeStamp(), ft, e->account());
1480                         bringToFront(w);
1481                 }
1482                 return;
1483         }
1484 #endif
1485
1486         if(e->type() == PsiEvent::AvCallType) {
1487                 AvCallEvent *ae = (AvCallEvent *)e;
1488                 AvCall *sess = ae->takeAvCall();
1489                 e->account()->eventQueue()->dequeue(e);
1490                 e->account()->queueChanged();
1491                 e->account()->cpUpdate(*u);
1492                 if(sess) {
1493                         if(!sess->jid().isEmpty()) {
1494                                 CallDlg *w = new CallDlg(e->account(), 0);
1495                                 w->setAttribute(Qt::WA_DeleteOnClose);
1496                                 w->setIncoming(sess);
1497                                 bringToFront(w);
1498                         }
1499                         else {
1500                                 QMessageBox::information(0, tr("Call ended"), tr("Other party canceled call."));
1501                                 delete sess;
1502                         }
1503                 }
1504                 return;
1505         }
1506
1507         bool isChat = false;
1508         bool sentToChatWindow = false;
1509         if ( e->type() == PsiEvent::Message ) {
1510                 MessageEvent *me = (MessageEvent *)e;
1511                 const Message &m = me->message();
1512                 bool emptyForm = m.getForm().fields().empty();
1513                 // FIXME: Refactor this, PsiAccount and PsiEvent out
1514                 if ((m.type() == "chat" && emptyForm)
1515                     || !EventDlg::messagingEnabled()) {
1516                         isChat = true;
1517                         sentToChatWindow = me->sentToChatWindow();
1518                 }
1519         }
1520
1521         if (e->type() == PsiEvent::Auth && !EventDlg::messagingEnabled()) {
1522                 if (dynamic_cast<AuthEvent*>(e)->authType() == "subscribe") {
1523 #ifdef YAPSI
1524                         bringToFront(d->mainwin);
1525                         return;
1526 #else
1527                         e->account()->dj_addAuth(e->jid());
1528 #endif
1529                 }
1530                 e->account()->eventQueue()->dequeue(e);
1531                 delete e;
1532                 return;
1533         }
1534
1535         if ( isChat ) {
1536                 PsiAccount* account = e->account();
1537                 XMPP::Jid from = e->from();
1538
1539                 if ( PsiOptions::instance()->getOption("options.ui.chat.alert-for-already-open-chats").toBool() && sentToChatWindow ) {
1540                         // Message already displayed, need only to pop up chat dialog, so that
1541                         // it will be read (or marked as read)
1542                         ChatDlg *c = account->findChatDialog(from);
1543                         if(!c)
1544                                 c = account->findChatDialog(e->jid());
1545                         if(!c)
1546                                 return; // should never happen
1547
1548                         account->processChats(from); // this will delete all events, corresponding to that chat dialog
1549                 }
1550
1551                 // as the event could be deleted just above, we're using cached account and from values
1552                 account->openChat(from, activationType);
1553         }
1554         else {
1555                 // search for an already opened eventdlg
1556                 EventDlg *w = e->account()->findDialog<EventDlg*>(u->jid());
1557
1558                 if ( !w ) {
1559                         // create the eventdlg
1560                         w = e->account()->ensureEventDlg(u->jid());
1561
1562                         // load next message
1563                         e->account()->processReadNext(*u);
1564                 }
1565
1566                 if (w)
1567                         bringToFront(w);
1568         }
1569 }
1570
1571 void PsiCon::updateS5BServerAddresses()
1572 {
1573         if(!d->s5bServer)
1574                 return;
1575
1576         QList<QHostAddress> list;
1577
1578         // grab all IP addresses
1579         foreach(PsiAccount* account, d->contactList->accounts()) {
1580                 QHostAddress *a = account->localAddress();
1581                 if(!a)
1582                         continue;
1583
1584                 // don't take dups
1585                 bool found = false;
1586                 for(QList<QHostAddress>::ConstIterator hit = list.begin(); hit != list.end(); ++hit) {
1587                         const QHostAddress &ha = *hit;
1588                         if(ha == (*a)) {
1589                                 found = true;
1590                                 break;
1591                         }
1592                 }
1593                 if(!found)
1594                         list += (*a);
1595         }
1596
1597         // convert to stringlist
1598         QStringList slist;
1599         for(QList<QHostAddress>::ConstIterator hit = list.begin(); hit != list.end(); ++hit)
1600                 slist += (*hit).toString();
1601
1602         // add external
1603         if(!PsiOptions::instance()->getOption("options.p2p.bytestreams.external-address").toString().isEmpty()) {
1604                 bool found = false;
1605                 for(QStringList::ConstIterator sit = slist.begin(); sit != slist.end(); ++sit) {
1606                         const QString &s = *sit;
1607                         if(s == PsiOptions::instance()->getOption("options.p2p.bytestreams.external-address").toString()) {
1608                                 found = true;
1609                                 break;
1610                         }
1611                 }
1612                 if(!found)
1613                         slist += PsiOptions::instance()->getOption("options.p2p.bytestreams.external-address").toString();
1614         }
1615
1616         // set up the server
1617         d->s5bServer->setHostList(slist);
1618 }
1619
1620 void PsiCon::s5b_init()
1621 {
1622         if(d->s5bServer->isActive())
1623                 d->s5bServer->stop();
1624
1625         if (PsiOptions::instance()->getOption("options.p2p.bytestreams.listen-port").toInt()) {
1626                 if(!d->s5bServer->start(PsiOptions::instance()->getOption("options.p2p.bytestreams.listen-port").toInt())) {
1627                         QMessageBox::warning(0, tr("Warning"), tr("Unable to bind to port %1 for Data Transfer.\nThis may mean you are already running another instance of Psi. You may experience problems sending and/or receiving files.").arg(PsiOptions::instance()->getOption("options.p2p.bytestreams.listen-port").toInt()));
1628                 }
1629         }
1630 }
1631
1632 void PsiCon::doSleep()
1633 {
1634         setGlobalStatus(Status(Status::Offline, tr("Computer went to sleep"), 0));
1635 }
1636
1637 void PsiCon::doWakeup()
1638 {
1639         // TODO: Restore the status from before the log out. Make it an (hidden) option for people with a bad wireless connection.
1640         //setGlobalStatus(Status());
1641
1642         foreach(PsiAccount* account, d->contactList->enabledAccounts()) {
1643                 if (account->userAccount().opt_connectAfterSleep) {
1644                         // Should we do this when the network comes up ?
1645                         account->setStatus(Status("", "", account->userAccount().priority));
1646                 }
1647         }
1648 }
1649
1650 PsiActionList *PsiCon::actionList() const
1651 {
1652         return d->actionList;
1653 }
1654
1655 /**
1656  * Prompts user to create new account, if none are currently present in system.
1657  */
1658 void PsiCon::promptUserToCreateAccount()
1659 {
1660         QMessageBox msgBox(QMessageBox::Question,tr("Account setup"),tr("You need to set up an account to start. Would you like to register a new account, or use an existing account?"));
1661         QPushButton *registerButton = msgBox.addButton(tr("Register new account"), QMessageBox::AcceptRole);
1662         QPushButton *existingButton = msgBox.addButton(tr("Use existing account"),QMessageBox::AcceptRole);
1663         msgBox.addButton(QMessageBox::Cancel);
1664         msgBox.exec();
1665         if (msgBox.clickedButton() ==  existingButton) {
1666                 AccountModifyDlg w(this);
1667                 w.exec();
1668         }
1669         else if (msgBox.clickedButton() ==  registerButton) {
1670                 AccountRegDlg w(proxy());
1671                 int n = w.exec();
1672                 if (n == QDialog::Accepted) {
1673                         contactList()->createAccount(w.jid().node(),w.jid(),w.pass(),w.useHost(),w.host(),w.port(),w.legacySSLProbe(),w.ssl(),w.proxy(),w.tlsOverrideDomain(), w.tlsOverrideCert());
1674                 }
1675         }
1676 }
1677
1678 QString PsiCon::optionsFile() const
1679 {
1680         return pathToProfile(activeProfile) + "/options.xml";
1681 }
1682
1683 void PsiCon::forceSavePreferences()
1684 {
1685         PsiOptions::instance()->save(optionsFile());
1686 }
1687
1688 void PsiCon::doQuit(int quitCode)
1689 {
1690         d->quitting = true;
1691         emit quit(quitCode);
1692 }
1693
1694 void PsiCon::aboutToQuit()
1695 {
1696         doQuit(QuitProgram);
1697 }
1698
1699 ContactUpdatesManager* PsiCon::contactUpdatesManager() const
1700 {
1701         return contactUpdatesManager_;
1702 }
1703
1704 #ifndef NEWCONTACTLIST
1705 ContactView* PsiCon::contactView() const
1706 {
1707         if(d->mainwin)
1708                 return d->mainwin->cvlist;
1709         else
1710                 return 0;
1711 }
1712 #endif
1713
1714 #include "psicon.moc"