initial commit

This commit is contained in:
mikhail "synzr" 2025-12-25 01:37:49 +05:00
commit 9d20827c46
2469 changed files with 470994 additions and 0 deletions

16
src/.gitignore vendored Normal file
View file

@ -0,0 +1,16 @@
/yachat
/config.h
/Makefile.Debug
/Makefile.Release
/debug
/release
/mocinclude.tmp
/object_script.*
/_rcc
/_obj
/vc70.pdb
/vc70.idb
/yachat.ncb
/yachat.sln
/yachat.suo
/yachat.vcproj

312
src/about.ui Normal file
View file

@ -0,0 +1,312 @@
<ui version="4.0" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>AboutDlg</class>
<widget class="QDialog" name="AboutDlg" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>504</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle" >
<string>About Psi</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="IconLabel" name="lb_icon" >
<property name="frameShape" >
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/logo_48</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lb_name" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QTabWidget" name="tw_tabs" >
<widget class="QWidget" name="tab_about" >
<attribute name="title" >
<string>About</string>
</attribute>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="lb_about" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>A cross-platform Jabber client designed for the Jabber power user.&lt;br>
&lt;br>
Copyright © 2001 - 2007 by Justin Karneges&lt;br>
</string>
</property>
<property name="textFormat" >
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item>
<widget class="URLLabel" name="uRLLabel1" >
<property name="alignment" >
<set>Qt::AlignCenter</set>
</property>
<property name="url" >
<string>http://psi-im.org</string>
</property>
<property name="title" >
<string>http://psi-im.org</string>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_authors" >
<attribute name="title" >
<string>Authors</string>
</attribute>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="PsiTextView" name="te_authors" />
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_thanks" >
<attribute name="title" >
<string>Thanks To</string>
</attribute>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="PsiTextView" name="te_thanks" />
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_translation" >
<attribute name="title" >
<string>Translation</string>
</attribute>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="PsiTextView" name="te_translation" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="tab_license" >
<attribute name="title" >
<string>License</string>
</attribute>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QTextEdit" name="te_license" >
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>418</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="IconButton" name="pb_close" >
<property name="text" >
<string>&amp;Close</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<customwidgets>
<customwidget>
<class>IconButton</class>
<extends></extends>
<header>iconbutton.h</header>
<container>0</container>
<pixmap></pixmap>
</customwidget>
<customwidget>
<class>IconLabel</class>
<extends></extends>
<header>iconlabel.h</header>
<container>0</container>
<pixmap></pixmap>
</customwidget>
<customwidget>
<class>PsiTextView</class>
<extends></extends>
<header>psitextview.h</header>
<container>0</container>
<pixmap></pixmap>
</customwidget>
<customwidget>
<class>URLLabel</class>
<extends></extends>
<header>urllabel.h</header>
<container>0</container>
<pixmap></pixmap>
</customwidget>
</customwidgets>
<resources/>
<connections>
<connection>
<sender>pb_close</sender>
<signal>clicked(bool)</signal>
<receiver>AboutDlg</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>436</x>
<y>295</y>
</hint>
<hint type="destinationlabel" >
<x>369</x>
<y>299</y>
</hint>
</hints>
</connection>
</connections>
</ui>

141
src/aboutdlg.cpp Normal file
View file

@ -0,0 +1,141 @@
/*
* aboutdlg.cpp
* Copyright (C) 2001-2003 Justin Karneges, Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "applicationinfo.h"
#include "aboutdlg.h"
#include "textutil.h"
AboutDlg::AboutDlg(QWidget* parent)
: QDialog(parent)
{
setAttribute(Qt::WA_DeleteOnClose);
ui_.setupUi(this);
setModal(false);
ui_.lb_name->setText ( QString("<h3><b>%1 v%2</b></h3>").arg(ApplicationInfo::name()).arg(ApplicationInfo::version()) );
ui_.te_license->setText ( loadText(":/COPYING") );
QString lang_name = qApp->translate( "@default", "language_name" );
if ( lang_name == "language_name" ) // remove the translation tab, if no translation is used
ui_.tw_tabs->removePage ( ui_.tw_tabs->page(3) );
// fill in Authors tab...
QString authors;
authors += details(QString::fromUtf8("Justin Karneges"),
"justin@affinix.com", "", "",
tr("Founder and Original Author"));
authors += details(QString::fromUtf8("Kevin Smith"),
"kismith@psi-im.org", "", "",
tr("Project Lead/Maintainer"));
authors += details(QString::fromUtf8("Remko Tronçon"),
"remko@psi-im.org", "", "",
tr("Lead Developer"));
authors += details(QString::fromUtf8("Michail Pishchagin"),
"mblsha@psi-im.org", "", "",
tr("Lead Widget Developer"));
authors += details(QString::fromUtf8("Maciej Niedzielski"),
"machekku@psi-im.org", "", "",
tr("Developer"));
authors += details(QString::fromUtf8("Martin Hostettler"),
"martin@psi-im.org", "", "",
tr("Developer"));
authors += details(QString::fromUtf8("Akito Nozaki"),
"anpluto@usa.net", "", "",
tr("Miscellaneous Developer"));
ui_.te_authors->setText( authors );
// fill in Thanks To tab...
QString thanks;
thanks += details(QString::fromUtf8("Jan Niehusmann"),
"jan@gondor.com", "", "",
tr("Build setup, miscellaneous assistance"));
thanks += details(QString::fromUtf8("Everaldo Coelho"),
"", "", "http://www.everaldo.com",
tr("Many icons are from his Crystal icon theme"));
thanks += details(QString::fromUtf8("Jason Kim"),
"", "", "",
tr("Graphics"));
thanks += details(QString::fromUtf8("Hideaki Omuro"),
"", "", "",
tr("Graphics"));
thanks += details(QString::fromUtf8("Bill Myers"),
"", "", "",
tr("Original Mac Port"));
thanks += details(QString::fromUtf8("Eric Smith (Tarkvara Design, Inc.)"),
"eric@tarkvara.org", "", "",
tr("Mac OS X Port"));
thanks += details(QString::fromUtf8("Tony Collins"),
"", "", "",
tr("Original End User Documentation"));
thanks += details(QString::fromUtf8("Hal Rottenberg"),
"", "", "",
tr("Webmaster, Marketing"));
thanks += details(QString::fromUtf8("Mircea Bardac"),
"", "", "",
tr("Bug Tracker Management"));
thanks += details(QString::fromUtf8("Jacek Tomasiak"),
"", "", "",
tr("Patches"));
//thanks += tr("Thanks to many others.\n"
// "The above list only reflects the contributors I managed to keep track of.\n"
// "If you're not included but you think that you must be in the list, contact the developers.");
ui_.te_thanks->setText( thanks );
QString translation = tr(
"I. M. Anonymous <note text=\"replace with your real name\"><br>\n"
"&nbsp;&nbsp;<a href=\"http://me.com\">http://me.com</a><br>\n"
"&nbsp;&nbsp;Jabber: <a href=\"xmpp:me@me.com\">me@me.com</a><br>\n"
"&nbsp;&nbsp;<a href=\"mailto:me@me.com\">me@me.com</a><br>\n"
"&nbsp;&nbsp;Translator<br>\n"
"<br>\n"
"Join the translation team today! Go to \n"
"<a href=\"http://forum.psi-im.org/forum/14\">\n"
"http://forum.psi-im.org/forum/14</a> for further details!"
);
ui_.te_translation->appendText(translation);
}
QString AboutDlg::loadText( const QString & fileName )
{
return TextUtil::loadText(fileName);
}
QString AboutDlg::details( QString name, QString email, QString jabber, QString www, QString desc )
{
QString ret;
const QString nbsp = "&nbsp;&nbsp;";
ret += name + "<br>\n";
if ( !email.isEmpty() )
ret += nbsp + "E-mail: " + "<a href=\"mailto:" + email + "\">" + email + "</a><br>\n";
if ( !jabber.isEmpty() )
ret += nbsp + "Jabber: " + "<a href=\"jabber:" + jabber + "\">" + jabber + "</a><br>\n";
if ( !www.isEmpty() )
ret += nbsp + "WWW: " + "<a href=\"" + www + "\">" + www + "</a><br>\n";
if ( !desc.isEmpty() )
ret += nbsp + desc + "<br>\n";
ret += "<br>\n";
return ret;
}

44
src/aboutdlg.h Normal file
View file

@ -0,0 +1,44 @@
/*
* aboutdlg.h
* Copyright (C) 2001-2003 Justin Karneges, Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ABOUTDLG_H
#define ABOUTDLG_H
#include <QDialog>
#include "ui_about.h"
class AboutDlg : public QDialog
{
Q_OBJECT
public:
AboutDlg(QWidget* parent = NULL);
static QString loadText( const QString & fileName );
protected:
QString details( QString name, QString email, QString jabber, QString www, QString desc );
private:
Ui::AboutDlg ui_;
};
#endif

167
src/accountadd.ui Normal file
View file

@ -0,0 +1,167 @@
<ui version="4.0" stdsetdef="1" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>AccountAdd</class>
<widget class="QDialog" name="AccountAdd" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>465</width>
<height>252</height>
</rect>
</property>
<property name="windowTitle" >
<string>Add Account</string>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QTextEdit" name="TextEdit1" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>200</width>
<height>32767</height>
</size>
</property>
<property name="text" >
<string>&lt;qt>Please choose a friendly &lt;b>Name&lt;/b> that Psi can use to refer to this account.&lt;br>
&lt;br>
Click the &lt;b>Register New Account&lt;/b> checkbox if you want Psi to try and create an account for you on a remote server. If you are adding an existing Jabber account then leave this box unchecked.</string>
</property>
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="lb_name" >
<property name="text" >
<string>Name:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_name" />
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="ck_reg" >
<property name="text" >
<string>Register new account</string>
</property>
</widget>
</item>
<item>
<spacer name="Spacer20" >
<property name="sizeHint" >
<size>
<width>20</width>
<height>130</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Vertical</enum>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="Line3" >
<property name="frameShape" >
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<spacer name="Spacer19" >
<property name="sizeHint" >
<size>
<width>118</width>
<height>20</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="IconButton" name="pb_close" >
<property name="text" >
<string>&amp;Close</string>
</property>
</widget>
</item>
<item>
<widget class="IconButton" name="pb_add" >
<property name="text" >
<string>&amp;Add</string>
</property>
<property name="shortcut" >
<string>Alt+A</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/addContact</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
</ui>

125
src/accountadddlg.cpp Normal file
View file

@ -0,0 +1,125 @@
/*
* accountadddlg.cpp - dialogs for manipulating PsiAccounts
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "accountadddlg.h"
#include "psicon.h"
#include "psioptions.h"
#include "psiaccount.h"
#include "accountregdlg.h"
#include "psicontactlist.h"
AccountAddDlg::AccountAddDlg(PsiCon *_psi, QWidget *parent)
:QDialog(parent)
{
setupUi(this);
setModal(false);
psi = _psi;
psi->dialogRegister(this);
setWindowTitle(CAP(caption()));
connect(pb_close, SIGNAL(clicked()), SLOT(close()));
connect(pb_add, SIGNAL(clicked()), SLOT(add()));
connect(le_name, SIGNAL(textChanged(const QString &)), SLOT(setAddButton(const QString &)));
ck_reg->setWhatsThis(
tr("Check this option if you don't yet have a Jabber account "
"and you want to register one. Note that this will only work "
"on servers that allow anonymous registration."));
QString aname = createNewAccountName(tr("Default"));
if (PsiOptions::instance()->getOption("options.ui.account.single").toBool()) {
le_name->setText("account");
lb_name->hide();
le_name->hide();
}
else {
le_name->setText(aname);
le_name->setFocus();
}
}
AccountAddDlg::~AccountAddDlg()
{
psi->dialogUnregister(this);
}
QString AccountAddDlg::createNewAccountName(QString def)
{
QString aname = def;
int n = 0;
while(1) {
bool taken = false;
foreach(PsiAccount* pa, psi->contactList()->accounts()) {
if(aname == pa->name()) {
taken = true;
break;
}
}
if(!taken)
break;
aname = def + '_' + QString::number(++n);
}
return aname;
}
void AccountAddDlg::add()
{
QString aname = createNewAccountName(le_name->text());
le_name->setText( aname );
if(ck_reg->isChecked()) {
AccountRegDlg *w = new AccountRegDlg(psi->proxy(), this);
int n = w->exec();
if(n != QDialog::Accepted) {
delete w;
return;
}
Jid jid = w->jid();
QString pass = w->pass();
bool opt_host = w->useHost();
QString host = w->host();
int port = w->port();
bool legacy_ssl_probe = w->legacySSLProbe();
UserAccount::SSLFlag ssl = w->ssl();
int proxy = w->proxy();
delete w;
psi->createAccount(le_name->text(), jid, pass, opt_host, host, port, legacy_ssl_probe, ssl, proxy);
}
else {
psi->createAccount(le_name->text());
}
close();
}
void AccountAddDlg::setAddButton(const QString &s)
{
pb_add->setEnabled(!s.isEmpty());
}

49
src/accountadddlg.h Normal file
View file

@ -0,0 +1,49 @@
/*
* accountadddlg.h - dialogs for manipulating PsiAccounts
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACCOUNTADDDLG_H
#define ACCOUNTADDDLG_H
#include <QDialog>
#include "ui_accountadd.h"
class PsiCon;
class QWidget;
class QString;
class AccountAddDlg : public QDialog, public Ui::AccountAdd
{
Q_OBJECT
public:
AccountAddDlg(PsiCon *, QWidget *parent=0);
~AccountAddDlg();
private slots:
void add();
void setAddButton(const QString &);
private:
PsiCon *psi;
QString createNewAccountName(QString def);
};
#endif

71
src/accountlabel.cpp Normal file
View file

@ -0,0 +1,71 @@
/*
* accountlabel.cpp - simple label to display account name currently in use
* Copyright (C) 2006-2007 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "accountlabel.h"
#include "psiaccount.h"
AccountLabel::AccountLabel(QWidget* parent)
: QLabel(parent)
, showJid_(true)
{
setFrameStyle(QFrame::Panel | QFrame::Sunken);
}
AccountLabel::~AccountLabel()
{
}
PsiAccount* AccountLabel::account() const
{
return account_;
}
bool AccountLabel::showJid() const
{
return showJid_;
}
void AccountLabel::setAccount(PsiAccount* account)
{
account_ = account;
if (account) {
connect(account, SIGNAL(updatedAccount()), SLOT(updateName()));
connect(account, SIGNAL(destroyed()), SLOT(deleteLater()));
}
updateName();
}
void AccountLabel::setShowJid(bool showJid)
{
showJid_ = showJid;
updateName();
}
void AccountLabel::updateName()
{
QString text = "...";
if (!account_.isNull()) {
if (showJid_)
text = account_->nameWithJid();
else
text = account_->name();
}
setText(text);
}

50
src/accountlabel.h Normal file
View file

@ -0,0 +1,50 @@
/*
* accountlabel.h - simple label to display account name currently in use
* Copyright (C) 2006-2007 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACCOUNTLABEL_H
#define ACCOUNTLABEL_H
#include <QLabel>
#include <QPointer>
class PsiAccount;
class AccountLabel : public QLabel
{
Q_OBJECT
Q_PROPERTY(bool showJid READ showJid WRITE setShowJid)
public:
AccountLabel(QWidget* parent);
~AccountLabel();
PsiAccount* account() const;
bool showJid() const;
void setAccount(PsiAccount* account);
void setShowJid(bool showJid);
private slots:
void updateName();
private:
QPointer<PsiAccount> account_;
bool showJid_;
};
#endif

169
src/accountmanage.ui Normal file
View file

@ -0,0 +1,169 @@
<ui version="4.0" stdsetdef="1" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>AccountManage</class>
<widget class="QDialog" name="AccountManage" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>481</width>
<height>282</height>
</rect>
</property>
<property name="windowTitle" >
<string>Jabber Accounts</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="Q3ListView" name="lv_accs" >
<column>
<property name="text" >
<string>Name</string>
</property>
<property name="clickable" >
<bool>true</bool>
</property>
<property name="resizable" >
<bool>true</bool>
</property>
</column>
<column>
<property name="text" >
<string>Server</string>
</property>
<property name="clickable" >
<bool>true</bool>
</property>
<property name="resizable" >
<bool>true</bool>
</property>
</column>
<column>
<property name="text" >
<string>Status</string>
</property>
<property name="clickable" >
<bool>true</bool>
</property>
<property name="resizable" >
<bool>true</bool>
</property>
</column>
</widget>
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="IconButton" name="pb_add" >
<property name="text" >
<string>&amp;Add</string>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/addContact</string>
</property>
</widget>
</item>
<item>
<widget class="IconButton" name="pb_modify" >
<property name="text" >
<string>&amp;Modify</string>
</property>
</widget>
</item>
<item>
<widget class="IconButton" name="pb_remove" >
<property name="text" >
<string>Rem&amp;ove</string>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/remove</string>
</property>
</widget>
</item>
<item>
<spacer name="Spacer12" >
<property name="sizeHint" >
<size>
<width>20</width>
<height>127</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Vertical</enum>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="Line1" >
<property name="frameShape" >
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<spacer name="Spacer11" >
<property name="sizeHint" >
<size>
<width>395</width>
<height>20</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="IconButton" name="pb_close" >
<property name="text" >
<string>&amp;Close</string>
</property>
<property name="shortcut" >
<string>Alt+C</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
</ui>

394
src/accountmanagedlg.cpp Normal file
View file

@ -0,0 +1,394 @@
/*
* accountmanagedlg.cpp - dialogs for manipulating PsiAccounts
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QtCrypto>
#include <QInputDialog>
#include <QMessageBox>
#include <QPushButton>
#include <QLineEdit>
#include <QLabel>
#include <q3listview.h>
#include <q3buttongroup.h>
#include "psicon.h"
#include "psiaccount.h"
#include "common.h"
#include "xmpp_tasks.h"
#include "proxy.h"
#include "miniclient.h"
#include "accountadddlg.h"
#include "accountmanagedlg.h"
#include "ui_accountremove.h"
#include "psicontactlist.h"
using namespace XMPP;
//----------------------------------------------------------------------------
// AccountRemoveDlg
//----------------------------------------------------------------------------
class AccountRemoveDlg : public QDialog, public Ui::AccountRemove
{
Q_OBJECT
public:
AccountRemoveDlg(ProxyManager *, const UserAccount &, QWidget *parent=0);
~AccountRemoveDlg();
protected:
// reimplemented
//void closeEvent(QCloseEvent *);
public slots:
void done(int);
private slots:
void remove();
void bg_clicked(int);
void client_handshaken();
void client_error();
void client_disconnected();
void unreg_finished();
private:
class Private;
Private *d;
MiniClient *client;
};
class AccountRemoveDlg::Private
{
public:
Private() {}
UserAccount acc;
QButtonGroup *bg;
ProxyManager *proxyman;
};
AccountRemoveDlg::AccountRemoveDlg(ProxyManager *proxyman, const UserAccount &acc, QWidget *parent)
:QDialog(parent)
{
setupUi(this);
setModal(false);
d = new Private;
d->acc = acc;
d->proxyman = proxyman;
setWindowTitle(CAP(caption()));
connect(pb_close, SIGNAL(clicked()), SLOT(close()));
connect(pb_remove, SIGNAL(clicked()), SLOT(remove()));
d->bg = new QButtonGroup(0);
d->bg->addButton(rb_remove, 0);
d->bg->addButton(rb_removeAndUnreg, 1);
connect(d->bg, SIGNAL(buttonClicked(int)), SLOT(bg_clicked(int)));
rb_remove->setChecked(true);
bg_clicked(0);
pb_close->setFocus();
client = new MiniClient;
connect(client, SIGNAL(handshaken()), SLOT(client_handshaken()));
connect(client, SIGNAL(error()), SLOT(client_error()));
connect(client, SIGNAL(disconnected()), SLOT(client_disconnected()));
}
AccountRemoveDlg::~AccountRemoveDlg()
{
delete client;
delete d->bg;
delete d;
}
/*void AccountRemoveDlg::closeEvent(QCloseEvent *e)
{
e->ignore();
reject();
}*/
void AccountRemoveDlg::done(int r)
{
if(busy->isActive()) {
int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the unregistration?"), tr("&Yes"), tr("&No"));
if(n != 0)
return;
}
QDialog::done(r);
}
void AccountRemoveDlg::bg_clicked(int x)
{
if(x == 0) {
lb_pass->setEnabled(false);
le_pass->setEnabled(false);
}
else if(x == 1) {
lb_pass->setEnabled(true);
le_pass->setEnabled(true);
le_pass->setFocus();
}
}
void AccountRemoveDlg::remove()
{
bool unreg = rb_removeAndUnreg->isChecked();
if(unreg) {
if(!d->acc.pass.isEmpty() && le_pass->text() != d->acc.pass) {
QMessageBox::information(this, tr("Error"), tr("Password does not match account. Please try again."));
le_pass->setFocus();
return;
}
}
int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to remove <b>%1</b> ?").arg(d->acc.name), tr("&Yes"), tr("&No"));
if(n != 0)
return;
if(!unreg) {
accept();
return;
}
busy->start();
gb_account->setEnabled(false);
pb_remove->setEnabled(false);
Jid j = d->acc.jid;
QString pass = le_pass->text();
j.setResource(d->acc.resource);
client->connectToServer(j, d->acc.legacy_ssl_probe, d->acc.ssl == UserAccount::SSL_Legacy, d->acc.ssl == UserAccount::SSL_Yes, d->acc.opt_host ? d->acc.host : QString(), d->acc.port, d->proxyman, d->acc.proxy_index, &pass);
}
void AccountRemoveDlg::client_handshaken()
{
// Workaround for servers that do not send a response to the remove request
client->setErrorOnDisconnect(false);
// try to unregister an account
JT_Register *reg = new JT_Register(client->client()->rootTask());
connect(reg, SIGNAL(finished()), SLOT(unreg_finished()));
reg->unreg();
reg->go(true);
}
void AccountRemoveDlg::client_error()
{
busy->stop();
gb_account->setEnabled(true);
pb_remove->setEnabled(true);
}
void AccountRemoveDlg::unreg_finished()
{
JT_Register *reg = (JT_Register *)sender();
client->close();
busy->stop();
if(reg->success()) {
QMessageBox::information(this, tr("Success"), tr("The account was unregistered successfully."));
accept();
return;
}
else if(reg->statusCode() != Task::ErrDisc) {
gb_account->setEnabled(true);
pb_remove->setEnabled(true);
QMessageBox::critical(this, tr("Error"), QString(tr("There was an error unregistering the account.\nReason: %1")).arg(reg->statusString()));
}
}
void AccountRemoveDlg::client_disconnected()
{
// Workaround for servers that do not send a response to the remove request
busy->stop();
QMessageBox::information(this, tr("Success"), tr("The account was unregistered successfully."));
accept();
}
//----------------------------------------------------------------------------
// AccountManageDlg
//----------------------------------------------------------------------------
class AccountManageItem : public Q3CheckListItem
{
public:
AccountManageItem(Q3ListView *par, PsiAccount *_pa)
:Q3CheckListItem(par,"",CheckBox)
{
pa = _pa;
updateInfo();
}
void updateInfo()
{
const UserAccount &acc = pa->userAccount();
setText(0, pa->name());
//setPixmap(0, IconsetFactory::icon("psi/account"));
Jid j = acc.jid;
setText(1, acc.opt_host && acc.host.length() ? acc.host : j.host());
setText(2, pa->isActive() ? AccountManageDlg::tr("Active") : AccountManageDlg::tr("Not active"));
setOn(pa->enabled());
}
void stateChange( bool s)
{
if (pa->enabled()!=s)
pa->setEnabled(s);
updateInfo();
}
int rtti() const
{
return 8109;
}
PsiAccount *pa;
};
AccountManageDlg::AccountManageDlg(PsiCon *_psi)
:QDialog(0)
{
setupUi(this);
setModal(false);
psi = _psi;
psi->dialogRegister(this);
setWindowTitle(CAP(caption()));
// setup signals
connect(pb_add, SIGNAL(clicked()), SLOT(add()));
connect(pb_modify, SIGNAL(clicked()), SLOT(modify()));
connect(pb_remove, SIGNAL(clicked()), SLOT(remove()));
connect(pb_close, SIGNAL(clicked()), SLOT(close()));
connect(lv_accs, SIGNAL(doubleClicked(Q3ListViewItem *)), SLOT(modify(Q3ListViewItem *)));
connect(lv_accs, SIGNAL(selectionChanged(Q3ListViewItem *)), SLOT(qlv_selectionChanged(Q3ListViewItem *)));
connect(psi, SIGNAL(accountAdded(PsiAccount *)), SLOT(accountAdded(PsiAccount *)));
connect(psi, SIGNAL(accountUpdated(PsiAccount *)), SLOT(accountUpdated(PsiAccount *)));
connect(psi, SIGNAL(accountRemoved(PsiAccount *)), SLOT(accountRemoved(PsiAccount *)));
lv_accs->setAllColumnsShowFocus(true);
lv_accs->setResizeMode(Q3ListView::LastColumn);
foreach(PsiAccount* pa, psi->contactList()->accounts())
new AccountManageItem(lv_accs, pa);
if(lv_accs->childCount() > 0)
lv_accs->setSelected(lv_accs->firstChild(), true);
else
qlv_selectionChanged(0);
}
AccountManageDlg::~AccountManageDlg()
{
psi->dialogUnregister(this);
}
void AccountManageDlg::qlv_selectionChanged(Q3ListViewItem *lvi)
{
AccountManageItem *i = (AccountManageItem *)lvi;
bool ok = i ? true: false;
pb_modify->setEnabled(ok);
pb_remove->setEnabled(ok);
}
void AccountManageDlg::add()
{
AccountAddDlg *w = new AccountAddDlg(psi, 0);
w->show();
}
void AccountManageDlg::modify()
{
modify(lv_accs->currentItem());
}
void AccountManageDlg::modify(Q3ListViewItem *lvi)
{
AccountManageItem *i = (AccountManageItem *)lvi;
if(!i)
return;
i->pa->modify();
}
void AccountManageDlg::remove()
{
AccountManageItem *i = (AccountManageItem *)lv_accs->currentItem();
if(!i)
return;
if(i->pa->isActive()) {
QMessageBox::information(this, tr("Error"), tr("Unable to remove the account, as it is currently active."));
return;
}
AccountRemoveDlg *w = new AccountRemoveDlg(psi->proxy(), i->pa->userAccount());
int n = w->exec();
if(n != QDialog::Accepted) {
delete w;
return;
}
delete w;
psi->removeAccount(i->pa);
}
void AccountManageDlg::accountAdded(PsiAccount *pa)
{
new AccountManageItem(lv_accs, pa);
}
void AccountManageDlg::accountUpdated(PsiAccount *pa)
{
AccountManageItem *i;
Q3ListViewItemIterator it(lv_accs);
for(; (i = (AccountManageItem *)it.current()) ; ++it) {
if(i->pa == pa)
break;
}
if(!i)
return;
i->updateInfo();
}
void AccountManageDlg::accountRemoved(PsiAccount *pa)
{
AccountManageItem *i;
Q3ListViewItemIterator it(lv_accs);
for(; (i = (AccountManageItem *)it.current()) ; ++it) {
if(i->pa == pa)
break;
}
if(!i)
return;
delete i;
qlv_selectionChanged(lv_accs->currentItem());
}
#include "accountmanagedlg.moc"

58
src/accountmanagedlg.h Normal file
View file

@ -0,0 +1,58 @@
/*
* accountmanagedlg.h - dialogs for manipulating PsiAccounts
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACCOUNTMANAGEDLG_H
#define ACCOUNTMANAGEDLG_H
#include "ui_accountmanage.h"
namespace XMPP
{
class Jid;
class Client;
}
class PsiCon;
class PsiAccount;
class Q3ListViewItem;
class AccountManageDlg : public QDialog, public Ui::AccountManage
{
Q_OBJECT
public:
AccountManageDlg(PsiCon *);
~AccountManageDlg();
private slots:
void qlv_selectionChanged(Q3ListViewItem *);
void add();
void modify();
void modify(Q3ListViewItem *);
void remove();
void accountAdded(PsiAccount *);
void accountUpdated(PsiAccount *);
void accountRemoved(PsiAccount *);
private:
PsiCon *psi;
};
#endif

1121
src/accountmodify.ui Normal file

File diff suppressed because it is too large Load diff

637
src/accountmodifydlg.cpp Normal file
View file

@ -0,0 +1,637 @@
/*
* accountmodifydlg.cpp
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QInputDialog>
#include <QMessageBox>
#include "accountmodifydlg.h"
#include "psiaccount.h"
#include "iconset.h"
#include "psioptions.h"
#include "jidutil.h"
#include "profiles.h"
#include "psicon.h"
#include "proxy.h"
#include "privacymanager.h"
#include "privacydlg.h"
#include "psicontactlist.h"
#ifdef HAVE_PGPUTIL
#include "pgpkeydlg.h"
#include "pgputil.h"
#endif
AccountModifyDlg::AccountModifyDlg(PsiCon *_psi, QWidget *parent)
:QDialog(parent)
{
acc.name = "";
setupUi(this);
setModal(true);
pa = NULL;
psi = _psi;
init();
}
AccountModifyDlg::AccountModifyDlg(PsiAccount *_pa, QWidget *parent)
:QDialog(parent)
{
setupUi(this);
setModal(false);
setAttribute(Qt::WA_DeleteOnClose);
pa = _pa;
psi = pa->psi();
acc = pa->userAccount();
init();
}
void AccountModifyDlg::init()
{
//connect(pa->psi(), SIGNAL(pgpToggled(bool)), SLOT(pgpToggled(bool)));
if (pa)
pa->dialogRegister(this);
setWindowTitle(CAP(caption()));
#ifndef Q_WS_MAC
setWindowIcon(IconsetFactory::icon("psi/account").icon());
#endif
le_pass->setEnabled(true);
le_host->setEnabled(false);
lb_host->setEnabled(false);
le_port->setEnabled(false);
lb_port->setEnabled(false);
// FIXME: Temporarily removing security level settings
ck_req_mutual->hide();
cb_security_level->hide();
lb_security_level->hide();
connect(pb_close, SIGNAL(clicked()), SLOT(reject()));
connect(ck_host, SIGNAL(toggled(bool)), SLOT(hostToggled(bool)));
connect(pb_key, SIGNAL(clicked()), SLOT(chooseKey()));
connect(pb_keyclear, SIGNAL(clicked()), SLOT(clearKey()));
connect(pb_save, SIGNAL(clicked()), SLOT(save()));
connect(ck_automatic_resource, SIGNAL(toggled(bool)), le_resource, SLOT(setDisabled(bool)));
connect(ck_automatic_resource, SIGNAL(toggled(bool)), lb_resource, SLOT(setDisabled(bool)));
gb_pgp->setEnabled(false);
if (pa) {
connect(pb_vcard, SIGNAL(clicked()), SLOT(detailsVCard()));
connect(pb_changepw, SIGNAL(clicked()), SLOT(detailsChangePW()));
}
else {
pb_vcard->setEnabled(false);
pb_changepw->setEnabled(false);
}
// Hide the name if necessary
le_name->setText(acc.name);
le_jid->setText(JIDUtil::accountToString(acc.jid,false));
cb_ssl->addItem(tr("Always"),UserAccount::SSL_Yes);
cb_ssl->addItem(tr("When available"),UserAccount::SSL_Auto);
cb_ssl->addItem(tr("Never"), UserAccount::SSL_No);
cb_ssl->addItem(tr("Legacy SSL"), UserAccount::SSL_Legacy);
cb_ssl->setCurrentIndex(cb_ssl->findData(acc.ssl));
connect(cb_ssl, SIGNAL(activated(int)), SLOT(sslActivated(int)));
cb_plain->addItem(tr("Always"),ClientStream::AllowPlain);
cb_plain->addItem(tr("Over encrypted connection"), ClientStream::AllowPlainOverTLS);
cb_plain->addItem(tr("Never"), ClientStream::NoAllowPlain);
cb_plain->setCurrentIndex(cb_plain->findData(acc.allow_plain));
if (acc.opt_pass)
le_pass->setText(acc.pass);
ck_host->setChecked(acc.opt_host);
le_host->setText(acc.host);
le_port->setText(QString::number(acc.port));
ck_req_mutual->setChecked(acc.req_mutual_auth);
ck_legacy_ssl_probe->setChecked(acc.legacy_ssl_probe);
ck_automatic_resource->setChecked(acc.opt_automatic_resource);
le_resource->setText(acc.resource);
le_priority->setText(QString::number(acc.priority));
connect(ck_custom_auth,SIGNAL(toggled(bool)), lb_authid, SLOT(setEnabled(bool)));
connect(ck_custom_auth,SIGNAL(toggled(bool)), le_authid, SLOT(setEnabled(bool)));
connect(ck_custom_auth,SIGNAL(toggled(bool)), lb_realm, SLOT(setEnabled(bool)));
connect(ck_custom_auth,SIGNAL(toggled(bool)), le_realm, SLOT(setEnabled(bool)));
ck_custom_auth->setChecked(acc.customAuth);
le_authid->setText(acc.authid);
le_realm->setText(acc.realm);
ck_compress->setChecked(acc.opt_compress);
ck_auto->setChecked(acc.opt_auto);
ck_reconn->setChecked(acc.opt_reconn);
ck_log->setChecked(acc.opt_log);
ck_keepAlive->setChecked(acc.opt_keepAlive);
ck_ignoreSSLWarnings->setChecked(acc.opt_ignoreSSLWarnings);
le_dtProxy->setText(acc.dtProxy.full());
key = acc.pgpSecretKey;
updateUserID();
#ifdef HAVE_PGPUTIL
if(PGPUtil::instance().pgpAvailable()) {
gb_pgp->setEnabled(true);
}
#else
gb_pgp->setEnabled(false);
#endif
pc = psi->proxy()->createProxyChooser(tab_connection);
replaceWidget(lb_proxychooser, pc);
pc->setCurrentItem(acc.proxy_index);
// Security level
cb_security_level->addItem(tr("None"),QCA::SL_None);
cb_security_level->addItem(tr("Integrity"),QCA::SL_Integrity);
cb_security_level->addItem(tr("Baseline"),QCA::SL_Baseline);
cb_security_level->addItem(tr("High"),QCA::SL_High);
cb_security_level->addItem(tr("Highest"),QCA::SL_Highest);
cb_security_level->setCurrentIndex(cb_security_level->findData(acc.security_level));
// Name
if(le_name->text().isEmpty())
le_name->setFocus();
else if(le_jid->text().isEmpty())
le_jid->setFocus();
else
pb_save->setFocus();
// Privacy
privacyInitialized = false;
lb_customPrivacy->hide();
privacyBlockedModel.setSourceModel(&privacyModel);
lv_blocked->setModel(&privacyBlockedModel);
if (pa) {
connect(pa->privacyManager(),SIGNAL(defaultListAvailable(const PrivacyList&)),SLOT(updateBlockedContacts(const PrivacyList&)));
connect(pa->privacyManager(),SIGNAL(defaultListError()),SLOT(getDefaultList_error()));
connect(pa->privacyManager(),SIGNAL(changeList_error()),SLOT(changeList_error()));
connect(pa,SIGNAL(updatedActivity()),SLOT(updatePrivacyTab()));
}
connect(tab_main,SIGNAL(currentChanged(int)),SLOT(tabChanged(int)));
connect(pb_privacy, SIGNAL(clicked()), SLOT(privacyClicked()));
connect(pb_removeBlock, SIGNAL(clicked()), SLOT(removeBlockClicked()));
connect(pb_addBlock, SIGNAL(clicked()), SLOT(addBlockClicked()));
// FIXME: Temporarily disabling blocking
pb_removeBlock->hide();
pb_addBlock->hide();
// QWhatsThis helpers
cb_plain->setWhatsThis(
tr("Normally, Psi logs in using the <i>digest</i> authentication "
"method. Check this box to force a plain text login "
"to the Jabber server. Use this option only if you have "
"problems connecting with the normal login procedure, as it "
"makes your connection potentially vulnerable to "
"attacks."));
ck_auto->setWhatsThis(
tr("Automatically login to this account on Psi startup. Useful if "
"you have Psi automatically launched when an Internet "
"connection is detected."));
ck_reconn->setWhatsThis(
tr("Makes Psi try to reconnect if the connection was broken. "
"Useful, if you have an unstable connection and have to "
"reconnect often."));
ck_log->setWhatsThis(
tr("Keep a log of message history. Disable this "
"option if you want to conserve disk space or if you need "
"maximum security."));
ck_keepAlive->setWhatsThis(
tr("Sends so called \"Keep-alive\" packets periodically. "
"It is useful if your connection is set to be "
"automatically disconnected after a certain period of "
"inactivity (for example, by your ISP) and you want to keep it "
"up all the time."));
ck_ignoreSSLWarnings->setWhatsThis(
tr("Ignores all the SSL warnings, for example, about "
"incorrect certificates. Useful if your server doesn't "
"use a validated SSL certificate and you are "
"annoyed with warnings."));
cb_ssl->setWhatsThis(
tr("Check this option to use an encrypted SSL connection to "
"the Jabber server. You may use this option if your "
"server supports it and if you have the necessary QCA-OpenSSL "
"plugin installed. For more information, check the "
"Psi homepage."));
ck_compress->setWhatsThis(
tr("Check this option to use a compressed connection to "
"the Jabber server, if the server supports it."));
ck_host->setWhatsThis(
tr("Use this option for manual configuration of your Jabber host "
"if it is not the same as the host you are connecting to. This option is mostly useful "
"if you have some sort of proxy route on your "
"local machine (i.e. you connect to localhost), but your "
"account is registered on an external server."));
le_resource->setWhatsThis(
tr("You can have multiple clients connected to the Jabber server "
"with your single account. Each login is distinguished by a \"resource\" "
"name, which you can specify in this field."));
ck_custom_auth->setWhatsThis(
tr("This option sets the user (and realm) you want to "
"authenticate as. This overrides the Jabber ID you are logging in "
"as."));
le_priority->setWhatsThis(
tr("<p>You can have multiple clients connected to the Jabber "
"server with your single account. In such a situation, "
"the client with the highest priority (that is specified in "
"this field) will be the one that will receive "
"all incoming events.</p>"
"<p>For example, if you have a permanent connection "
"to the Internet at your work location, and have a dial-up at home, "
"you can have your Jabber client permanently running at work "
"with a low priority, and you can still use the same account "
"from home, using a client with higher priority to "
"temporary \"disable\" the lower priority client at work.</p>"));
// Hiding of UI components
if ((!pa && acc.name.isEmpty()) || PsiOptions::instance()->getOption("options.ui.account.single").toBool()) {
le_name->hide();
lb_name->hide();
}
if (!PsiOptions::instance()->getOption("options.account.domain").toString().isEmpty()) {
lb_example->hide();
lb_jid->setText(tr("Username:"));
}
if (!PsiOptions::instance()->getOption("options.pgp.enable").toBool()) {
gb_pgp->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.privacy.show").toBool())
tab_main->removeTab(tab_main->indexOf(tab_privacy));
if (!PsiOptions::instance()->getOption("options.ui.account.proxy.show").toBool()) {
lb_proxy->hide();
lb_proxychooser->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.manual-host").toBool()) {
ck_host->hide();
lb_host->hide();
le_host->hide();
lb_port->hide();
le_port->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.ignore-ssl-warnings").toBool())
ck_ignoreSSLWarnings->hide();
if (!PsiOptions::instance()->getOption("options.ui.account.keepalive").toBool())
ck_keepAlive->hide();
if (!PsiOptions::instance()->getOption("options.ui.account.legacy-ssl-probe").toBool())
ck_legacy_ssl_probe->hide();
if (!PsiOptions::instance()->getOption("options.ui.account.security.show").toBool()) {
lb_plain->hide();
cb_plain->hide();
ck_req_mutual->hide();
lb_security_level->hide();
cb_security_level->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.security.show").toBool() && !PsiOptions::instance()->getOption("options.ui.account.legacy-ssl-probe").toBool() && !PsiOptions::instance()->getOption("options.ui.account.keepalive").toBool() && !PsiOptions::instance()->getOption("options.ui.account.ignore-ssl-warnings").toBool() && !PsiOptions::instance()->getOption("options.ui.account.manual-host").toBool() && !PsiOptions::instance()->getOption("options.ui.account.proxy.show").toBool()) {
tab_main->removeTab(tab_main->indexOf(tab_connection));
}
if (!PsiOptions::instance()->getOption("options.ui.account.resource").toBool()) {
ck_automatic_resource->hide();
lb_resource->hide();
le_resource->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.custom-authid").toBool()) {
ck_custom_auth->hide();
lb_authid->hide();
le_authid->hide();
lb_realm->hide();
le_realm->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.priority").toBool()) {
lb_priority->hide();
le_priority->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.data-proxy").toBool()) {
lb_dtProxy->hide();
le_dtProxy->hide();
}
if (!PsiOptions::instance()->getOption("options.ui.account.resource").toBool() && !PsiOptions::instance()->getOption("options.ui.account.priority").toBool() && !PsiOptions::instance()->getOption("options.ui.account.data-proxy").toBool()) {
tab_main->removeTab(tab_main->indexOf(tab_misc));
}
resize(minimumSizeHint());
}
AccountModifyDlg::~AccountModifyDlg()
{
if (pa)
pa->dialogUnregister(this);
}
void AccountModifyDlg::updateUserID()
{
if(key.isNull()) {
setKeyID(false);
}
else {
setKeyID(true, key.primaryUserId());
}
}
void AccountModifyDlg::setKeyID(bool b, const QString &s)
{
if(b) {
lb_keyname->setText(s);
lb_keyname->setMinimumWidth(100);
lb_keyicon->setEnabled(true);
lb_keyname->setEnabled(true);
pb_keyclear->setEnabled(true);
}
else {
lb_keyname->setText(tr("No Key Selected"));
lb_keyicon->setEnabled(false);
lb_keyname->setEnabled(false);
pb_keyclear->setEnabled(false);
}
}
//void AccountModifyDlg::pgpToggled(bool b)
//{
// if(b) {
// gb_pgp->setEnabled(true);
// }
// else {
// gb_pgp->setEnabled(false);
// }
// updateUserID();
//}
void AccountModifyDlg::setPassword(const QString &pw)
{
if (!le_pass->text().isEmpty())
le_pass->setText(pw);
}
void AccountModifyDlg::sslActivated(int i)
{
if ((cb_ssl->itemData(i) == UserAccount::SSL_Yes || cb_ssl->itemData(i) == UserAccount::SSL_Legacy) && !checkSSL()) {
cb_ssl->setCurrentIndex(cb_ssl->findData(UserAccount::SSL_Auto));
}
else if (cb_ssl->itemData(i) == UserAccount::SSL_Legacy && !ck_host->isChecked()) {
QMessageBox::critical(this, tr("Error"), tr("Legacy SSL is only available in combination with manual host/port."));
cb_ssl->setCurrentIndex(cb_ssl->findData(UserAccount::SSL_Auto));
}
}
bool AccountModifyDlg::checkSSL()
{
if(!QCA::isSupported("tls")) {
QMessageBox::critical(this, tr("SSL error"), tr("Cannot enable SSL/TLS. Plugin not found."));
return false;
}
return true;
}
void AccountModifyDlg::hostToggled(bool on)
{
le_host->setEnabled(on);
lb_host->setEnabled(on);
le_port->setEnabled(on);
lb_port->setEnabled(on);
ck_legacy_ssl_probe->setEnabled(!on);
ck_legacy_ssl_probe->setChecked(on ? false : acc.legacy_ssl_probe);
if (!on && cb_ssl->currentIndex() == cb_ssl->findData(UserAccount::SSL_Legacy)) {
cb_ssl->setCurrentIndex(cb_ssl->findData(UserAccount::SSL_Auto));
}
}
void AccountModifyDlg::chooseKey()
{
#ifdef HAVE_PGPUTIL
// Show the key dialog
QString id = (key.isNull() ? "" : key.keyId());
PGPKeyDlg *w = new PGPKeyDlg(PGPKeyDlg::Secret, id, this);
w->setWindowTitle(tr("Secret Key"));
int r = w->exec();
QCA::KeyStoreEntry entry;
if(r == QDialog::Accepted)
entry = w->keyStoreEntry();
delete w;
if(!entry.isNull()) {
key = entry.pgpSecretKey();
updateUserID();
}
#endif
}
void AccountModifyDlg::clearKey()
{
setKeyID(false);
key = QCA::PGPKey();
}
void AccountModifyDlg::detailsVCard()
{
if (pa)
pa->changeVCard();
}
void AccountModifyDlg::detailsChangePW()
{
if (pa) {
pa->changePW();
}
}
void AccountModifyDlg::save()
{
/*if(pa && le_name->text().isEmpty()) {
QMessageBox::information(this, tr("Error"), tr("You must specify a name for the account before you may save it."));
return;
}*/
Jid newJid( JIDUtil::accountFromString(le_jid->text().trimmed()) );
if ( newJid.user().isEmpty() || newJid.host().isEmpty() ) {
if (!PsiOptions::instance()->getOption("options.account.domain").toString().isEmpty()) {
QMessageBox::information(this, tr("Error"), tr("<i>Username</i> is invalid."));
}
else {
QMessageBox::information(this, tr("Error"), tr("<i>Jabber ID</i> must be specified in the format <i>user@host</i>."));
}
return;
}
// do not allow duplicate account names
if (!pa && le_name->text().isEmpty())
le_name->setText(newJid.domain());
QString def = le_name->text();
QString aname = def;
int n = 0;
{
foreach(PsiAccount* pa, psi->contactList()->accounts())
if(aname == pa->name())
n++;
}
if ( aname == acc.name )
n--;
if ( n )
aname = def + '_' + QString::number(++n);
le_name->setText( aname );
acc.name = le_name->text();
acc.jid = JIDUtil::accountFromString(le_jid->text().trimmed()).bare();
acc.pass = le_pass->text();
acc.opt_pass = !acc.pass.isEmpty();
acc.opt_host = ck_host->isChecked();
acc.host = le_host->text();
acc.port = le_port->text().toInt();
acc.req_mutual_auth = ck_req_mutual->isChecked();
if (!ck_host->isChecked())
acc.legacy_ssl_probe = ck_legacy_ssl_probe->isChecked();
acc.security_level = cb_security_level->itemData(cb_security_level->currentIndex()).toInt();
acc.opt_automatic_resource = ck_automatic_resource->isChecked();
acc.resource = le_resource->text();
acc.priority = le_priority->text().toInt();
acc.customAuth = ck_custom_auth->isChecked();
acc.authid = le_authid->text();
acc.realm = le_realm->text();
acc.ssl = (UserAccount::SSLFlag) cb_ssl->itemData(cb_ssl->currentIndex()).toInt();
acc.allow_plain = (ClientStream::AllowPlainType) cb_plain->itemData(cb_plain->currentIndex()).toInt();
acc.opt_compress = ck_compress->isChecked();
acc.opt_auto = ck_auto->isChecked();
acc.opt_reconn = ck_reconn->isChecked();
acc.opt_log = ck_log->isChecked();
acc.opt_keepAlive = ck_keepAlive->isChecked();
acc.opt_ignoreSSLWarnings = ck_ignoreSSLWarnings->isChecked();
acc.dtProxy = le_dtProxy->text();
acc.pgpSecretKey = key;
acc.proxy_index = pc->currentItem();
if(pa && pa->isActive()) {
QMessageBox::information(this, tr("Warning"), tr("This account is currently active, so certain changes may not take effect until the next login."));
}
if (pa)
pa->setUserAccount(acc);
else
psi->contactList()->createAccount(acc);
accept();
}
void AccountModifyDlg::tabChanged(int)
{
updatePrivacyTab();
}
void AccountModifyDlg::addBlockClicked()
{
if (!pa)
return;
bool ok;
QString input = QInputDialog::getText(NULL, tr("Block contact"), tr("Enter the Jabber ID of the contact to block:"), QLineEdit::Normal, "", &ok);
Jid jid(input);
if (ok && !jid.isEmpty()) {
privacyModel.list().insertItem(0,PrivacyListItem::blockItem(jid.full()));
privacyModel.reset();
pa->privacyManager()->changeList(privacyModel.list());
}
}
void AccountModifyDlg::removeBlockClicked()
{
if (!pa)
return;
if (lv_blocked->currentIndex().isValid()) {
QModelIndex idx = privacyBlockedModel.mapToSource(lv_blocked->currentIndex());
privacyModel.removeRow(idx.row(),idx.parent());
pa->privacyManager()->changeList(privacyModel.list());
}
}
void AccountModifyDlg::privacyClicked()
{
PrivacyDlg *d = new PrivacyDlg(pa->name(), pa->privacyManager(), this);
d->show();
}
void AccountModifyDlg::updatePrivacyTab()
{
if (tab_main->currentWidget() == tab_privacy) {
if (pa && pa->loggedIn()) {
if (!privacyInitialized) {
lb_privacyStatus->setText(tr("Retrieving blocked contact list ..."));
setPrivacyTabEnabled(false);
pa->privacyManager()->getDefaultList();
}
//else {
// setPrivacyTabEnabled(true);
//}
}
else {
lb_privacyStatus->setText(tr("You are not connected."));
privacyInitialized = false;
setPrivacyTabEnabled(false);
}
}
}
void AccountModifyDlg::setPrivacyTabEnabled(bool b)
{
ws_privacy->setCurrentWidget(b ? pg_privacy : pg_privacyStatus);
}
void AccountModifyDlg::updateBlockedContacts(const PrivacyList& l)
{
privacyInitialized = true;
privacyModel.setList(l);
lb_customPrivacy->setVisible(!l.onlyBlockItems());
setPrivacyTabEnabled(true);
}
void AccountModifyDlg::changeList_error()
{
privacyInitialized = false;
updatePrivacyTab();
}
void AccountModifyDlg::getDefaultList_error()
{
privacyInitialized = true;
lb_privacyStatus->setText(tr("Your server does not support blocking."));
setPrivacyTabEnabled(false);
}

92
src/accountmodifydlg.h Normal file
View file

@ -0,0 +1,92 @@
/*
* accountmodifydlg.h
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACCOUNTMODIFYDLG_H
#define ACCOUNTMODIFYDLG_H
#include <QDialog>
#include <QtCrypto>
#include "privacylistmodel.h"
#include "privacylistblockedmodel.h"
#include "ui_accountmodify.h"
#include "profiles.h"
class PsiAccount;
class QWidget;
class PsiCon;
class ProxyChooser;
class AccountModifyDlg : public QDialog, public Ui::AccountModify
{
Q_OBJECT
public:
AccountModifyDlg(PsiCon *,QWidget *parent=0);
AccountModifyDlg(PsiAccount *, QWidget *parent=0);
~AccountModifyDlg();
void setPassword(const QString &);
const UserAccount& account() const { return acc; }
protected:
void init();
private slots:
void hostToggled(bool);
void sslActivated(int);
void detailsVCard();
void detailsChangePW();
void save();
//void pgpToggled(bool);
void chooseKey();
void clearKey();
void tabChanged(int);
// Privacy
void privacyClicked();
void updatePrivacyTab();
void setPrivacyTabEnabled(bool b);
void addBlockClicked();
void removeBlockClicked();
void updateBlockedContacts(const PrivacyList&);
void getDefaultList_error();
void changeList_error();
private:
PsiCon *psi;
PsiAccount *pa;
ProxyChooser *pc;
QCA::PGPKey key;
UserAccount acc;
// Privacy
PrivacyListModel privacyModel;
PrivacyListBlockedModel privacyBlockedModel;
bool privacyInitialized;
void updateUserID();
void setKeyID(bool b, const QString &s="");
bool checkSSL();
};
#endif

304
src/accountreg.ui Normal file
View file

@ -0,0 +1,304 @@
<ui version="4.0" >
<class>AccountReg</class>
<widget class="QDialog" name="AccountReg" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>384</width>
<height>339</height>
</rect>
</property>
<property name="windowTitle" >
<string>Register Account</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QStackedWidget" name="sw_register" >
<property name="currentIndex" >
<number>0</number>
</property>
<widget class="QWidget" name="page_server" >
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="gb_server" >
<property name="title" >
<string>Server</string>
</property>
<layout class="QGridLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="lb_jid" >
<property name="text" >
<string>Please enter the name of the server you wish to register with:</string>
</property>
<property name="wordWrap" >
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="UpdatingComboBox" name="le_server" >
<property name="minimumSize" >
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="editable">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="lb_example" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="text" >
<string>Example: capulet.com</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="gb_connection" >
<property name="title" >
<string>Connection settings</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>9</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QCheckBox" name="ck_host" >
<property name="text" >
<string>Manually Specify Server Host/Port:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="lb_host" >
<property name="text" >
<string>Host:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_host" />
</item>
<item>
<widget class="QLabel" name="lb_port" >
<property name="text" >
<string>Port:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_port" >
<property name="minimumSize" >
<size>
<width>56</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>56</width>
<height>32767</height>
</size>
</property>
</widget>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="lb_ssl" >
<property name="text" >
<string>Encrypt connection:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cb_ssl" />
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>151</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="ck_legacy_ssl_probe" >
<property name="text" >
<string>Probe legacy SSL port</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="lb_proxy" >
<property name="text" >
<string>Proxy:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lb_proxychooser" >
<property name="text" >
<string>proxychooser</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType" >
<enum>QSizePolicy::Expanding</enum>
</property>
<property name="sizeHint" >
<size>
<width>20</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
<widget class="QWidget" name="page_fields" />
</widget>
</item>
<item>
<widget class="Line" name="line2" >
<property name="frameShape" >
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="BusyWidget" name="busy" />
</item>
<item>
<spacer>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" >
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="pb_cancel" >
<property name="text" >
<string>&amp;Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pb_next" >
<property name="text" >
<string>&amp;Next</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<tabstops>
<tabstop>le_server</tabstop>
<tabstop>ck_host</tabstop>
<tabstop>le_host</tabstop>
<tabstop>le_port</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

371
src/accountregdlg.cpp Normal file
View file

@ -0,0 +1,371 @@
/*
* accountregdlg.cpp - dialogs for manipulating PsiAccounts
* Copyright (C) 2001, 2002, 2006 Justin Karneges, Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QtCrypto>
#include <QMessageBox>
#include <QScrollArea>
#include "accountregdlg.h"
#include "proxy.h"
#include "serverlistquerier.h"
#include "miniclient.h"
#include "xmpp_tasks.h"
#include "psioptions.h"
#include "jidutil.h"
#include "textutil.h"
#include "xdata_widget.h"
using namespace XMPP;
AccountRegDlg::AccountRegDlg(ProxyManager *pm, QWidget *parent) : QDialog(parent)
{
ui_.setupUi(this);
setModal(false);
// TODO: If the domain is fixed, and the connection settings are fixed, skip first
// step
// Initialize settings
ssl_ = UserAccount::SSL_Auto;
legacy_ssl_probe_ = true;
port_ = 5222;
// Server select button
connect(ui_.le_server,SIGNAL(popup()),SLOT(selectServer()));
serverlist_querier_ = new ServerListQuerier(this);
connect(serverlist_querier_,SIGNAL(listReceived(const QStringList&)),SLOT(serverListReceived(const QStringList&)));
connect(serverlist_querier_,SIGNAL(error(const QString&)),SLOT(serverListError(const QString&)));
// Manual Host/Port
ui_.le_host->setEnabled(false);
ui_.lb_host->setEnabled(false);
ui_.le_port->setEnabled(false);
ui_.lb_port->setEnabled(false);
connect(ui_.ck_host, SIGNAL(toggled(bool)), SLOT(hostToggled(bool)));
// SSL
ui_.cb_ssl->addItem(tr("Always"),UserAccount::SSL_Yes);
ui_.cb_ssl->addItem(tr("When available"),UserAccount::SSL_Auto);
ui_.cb_ssl->addItem(tr("Legacy SSL"), UserAccount::SSL_Legacy);
ui_.cb_ssl->setCurrentIndex(ui_.cb_ssl->findData(ssl_));
connect(ui_.cb_ssl, SIGNAL(activated(int)), SLOT(sslActivated(int)));
ui_.ck_legacy_ssl_probe->setChecked(legacy_ssl_probe_);
// Cancel and next buttons
connect(ui_.pb_cancel, SIGNAL(clicked()), SLOT(close()));
connect(ui_.pb_next, SIGNAL(clicked()), SLOT(next()));
// Proxy
proxy_manager_ = pm;
proxy_chooser_ = proxy_manager_->createProxyChooser(ui_.gb_connection);
replaceWidget(ui_.lb_proxychooser, proxy_chooser_);
proxy_chooser_->fixTabbing(ui_.ck_legacy_ssl_probe, ui_.pb_cancel);
proxy_chooser_->setCurrentItem(0);
// Fields pane
QVBoxLayout *fields_layout = new QVBoxLayout(ui_.page_fields);
fields_layout->setMargin(0);
fields_container_ = new QScrollArea(ui_.page_fields);
fields_layout->addWidget(fields_container_);
fields_container_->setWidgetResizable(true);
fields_layout->addStretch(20);
fields_ = NULL;
ui_.le_port->setText(QString::number(port_));
ui_.le_host->setFocus();
client_ = new MiniClient;
connect(client_, SIGNAL(handshaken()), SLOT(client_handshaken()));
connect(client_, SIGNAL(error()), SLOT(client_error()));
if (!PsiOptions::instance()->getOption("options.account.domain").toString().isEmpty()) {
ui_.gb_server->hide();
}
}
AccountRegDlg::~AccountRegDlg()
{
delete client_;
}
void AccountRegDlg::done(int r)
{
if(ui_.busy->isActive()) {
int n = QMessageBox::information(this, tr("Warning"), tr("Are you sure you want to cancel the registration?"), tr("&Yes"), tr("&No"));
if(n != 0)
return;
}
client_->close();
QDialog::done(r);
}
void AccountRegDlg::sslActivated(int i)
{
if ((ui_.cb_ssl->itemData(i) == UserAccount::SSL_Yes || ui_.cb_ssl->itemData(i) == UserAccount::SSL_Legacy) && !checkSSL()) {
ui_.cb_ssl->setCurrentIndex(ui_.cb_ssl->findData(UserAccount::SSL_Auto));
}
else if (ui_.cb_ssl->itemData(i) == UserAccount::SSL_Legacy && !ui_.ck_host->isChecked()) {
QMessageBox::critical(this, tr("Error"), tr("Legacy SSL is only available in combination with manual host/port."));
ui_.cb_ssl->setCurrentIndex(ui_.cb_ssl->findData(UserAccount::SSL_Auto));
}
}
bool AccountRegDlg::checkSSL()
{
if(!QCA::isSupported("tls")) {
QMessageBox::information(this, tr("SSL error"), tr("Cannot enable SSL/TLS. Plugin not found."));
return false;
}
return true;
}
void AccountRegDlg::hostToggled(bool on)
{
ui_.le_host->setEnabled(on);
ui_.le_port->setEnabled(on);
ui_.lb_host->setEnabled(on);
ui_.lb_port->setEnabled(on);
if (!on && ui_.cb_ssl->currentIndex() == ui_.cb_ssl->findData(UserAccount::SSL_Legacy)) {
ui_.cb_ssl->setCurrentIndex(ui_.cb_ssl->findData(UserAccount::SSL_Auto));
}
}
void AccountRegDlg::selectServer()
{
if (ui_.le_server->count() == 0) {
ui_.busy->start();
block();
serverlist_querier_->getList();
}
}
void AccountRegDlg::serverListReceived(const QStringList& list)
{
ui_.busy->stop();
unblock();
ui_.le_server->clear();
ui_.le_server->addItems(list);
ui_.le_server->showPopup();
}
void AccountRegDlg::serverListError(const QString& e)
{
ui_.busy->stop();
unblock();
QString error = tr("There was an error retrieving the server list");
if (!e.isEmpty()) {
error += ".\n" + tr("Reason: ") + e;
}
qWarning(error);
//QMessageBox::critical(this, tr("Error"), error);
ui_.le_server->setFocus();
}
void AccountRegDlg::next()
{
if (ui_.sw_register->currentWidget() == ui_.page_server) {
// Update settings
server_ = JIDUtil::accountFromString(ui_.le_server->currentText().trimmed());
ssl_ = (UserAccount::SSLFlag) ui_.cb_ssl->itemData(ui_.cb_ssl->currentIndex()).toInt();
legacy_ssl_probe_ = ui_.ck_legacy_ssl_probe->isChecked();
opt_host_ = ui_.ck_host->isChecked();
host_ = ui_.le_host->text();
port_ = ui_.le_port->text().toInt();
proxy_ = proxy_chooser_->currentItem();
// Sanity check
if (server_.isNull() || !server_.node().isEmpty() || !server_.resource().isEmpty()) {
QMessageBox::critical(this, tr("Error"), tr("You have entered an invalid server name"));
return;
}
// Connect to the server
ui_.busy->start();
block();
client_->connectToServer(server_, legacy_ssl_probe_, ssl_ == UserAccount::SSL_Legacy, ssl_ == UserAccount::SSL_Yes, opt_host_ ? host_ : QString(), port_, proxy_manager_, proxy_);
}
else if (ui_.sw_register->currentWidget() == ui_.page_fields) {
// Initialize the form
XMPP::XData fields;
fields.setFields(fields_->fields());
// Determine the username and password
foreach(XMPP::XData::Field field, fields.fields()) {
if (field.var() == "username" && !field.value().isEmpty()) {
jid_.set(server_.bare(), field.value().at(0), "");
}
else if (field.var() == "password" && !field.value().isEmpty()) {
pass_ = field.value().at(0);
}
}
// Register
ui_.busy->start();
block();
JT_Register *reg = new JT_Register(client_->client()->rootTask());
connect(reg, SIGNAL(finished()), SLOT(setFields_finished()));
if (isOld_) {
Form form = convertFromXData(fields);
form.setJid(server_);
reg->setForm(form);
}
else {
reg->setForm(server_,fields);
}
reg->go(true);
}
}
void AccountRegDlg::client_handshaken()
{
// try to register an account
JT_Register *reg = new JT_Register(client_->client()->rootTask());
connect(reg, SIGNAL(finished()), SLOT(getFields_finished()));
reg->getForm(server_);
reg->go(true);
}
void AccountRegDlg::client_error()
{
ui_.busy->stop();
unblock();
if (ui_.sw_register->currentWidget() == ui_.page_fields) {
// Start over
delete fields_;
fields_ = NULL;
ui_.sw_register->setCurrentWidget(ui_.page_server);
}
}
void AccountRegDlg::getFields_finished()
{
JT_Register *reg = (JT_Register *)sender();
ui_.busy->stop();
if (reg->success()) {
unblock();
fields_ = new XDataWidget(ui_.page_fields);
XData xdata;
if (reg->hasXData()) {
isOld_ = false;
xdata = reg->xdata();
}
else {
isOld_ = true;
xdata = convertToXData(reg->form());
}
if (xdata.instructions().isEmpty())
xdata.setInstructions(tr("Please provide the following information:"));
xdata.setInstructions(TextUtil::linkify(xdata.instructions()));
fields_->setForm(xdata);
fields_container_->setWidget(fields_);
fields_container_->updateGeometry();
ui_.sw_register->setCurrentWidget(ui_.page_fields);
}
else {
QMessageBox::critical(this, tr("Error"), tr("This server does not support registration"));
unblock();
}
}
void AccountRegDlg::setFields_finished()
{
JT_Register *reg = (JT_Register *)sender();
ui_.busy->stop();
if (reg->success()) {
QMessageBox::information(this, tr("Success"), QString(tr("You have succesfully registered your account with Jabber ID '%1'")).arg(jid_.bare()));
client_->close();
accept();
}
else {
unblock();
QMessageBox::critical(this, tr("Error"), QString(tr("There was an error registering the account.\nReason: %1")).arg(reg->statusString()));
}
}
XMPP::XData AccountRegDlg::convertToXData(const XMPP::Form& form)
{
// Convert the fields
XData::FieldList fields;
foreach(FormField f, form) {
XData::Field field;
field.setLabel(f.fieldName());
field.setVar(f.realName());
field.setRequired(true);
if (f.isSecret()) {
field.setType(XData::Field::Field_TextPrivate);
}
else {
field.setType(XData::Field::Field_TextSingle);
}
fields.push_back(field);
}
// Create the form
XData xdata;
xdata.setInstructions(form.instructions());
xdata.setFields(fields);
return xdata;
}
XMPP::Form AccountRegDlg::convertFromXData(const XMPP::XData& xdata)
{
Form form;
foreach(XMPP::XData::Field field, xdata.fields()) {
if (!field.value().isEmpty()) {
FormField f;
f.setType(field.var());
f.setValue(field.value().at(0));
form.push_back(f);
}
}
return form;
}
void AccountRegDlg::block()
{
if (ui_.sw_register->currentWidget() == ui_.page_server) {
ui_.gb_server->setEnabled(false);
ui_.gb_connection->setEnabled(false);
ui_.pb_next->setEnabled(false);
}
else if (ui_.sw_register->currentWidget() == ui_.page_fields) {
if (fields_)
fields_->setEnabled(false);
}
}
void AccountRegDlg::unblock()
{
if (ui_.sw_register->currentWidget() == ui_.page_server) {
ui_.gb_server->setEnabled(true);
ui_.gb_connection->setEnabled(true);
ui_.pb_next->setEnabled(true);
}
else if (ui_.sw_register->currentWidget() == ui_.page_fields) {
ui_.pb_next->setEnabled(true);
if (fields_)
fields_->setEnabled(true);
}
}

106
src/accountregdlg.h Normal file
View file

@ -0,0 +1,106 @@
/*
* accountregdlg.h
* Copyright (C) 2001, 2002, 2006 Justin Karneges, Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACCOUNTREGDLG_H
#define ACCOUNTREGDLG_H
#include <QDialog>
#include <QString>
#include "profiles.h"
#include "xmpp_jid.h"
#include "ui_accountreg.h"
class ProxyManager;
class ProxyChooser;
class QWidget;
class QScrollArea;
class QStringList;
class MiniClient;
class XDataWidget;
class ServerListQuerier;
namespace XMPP {
class Form;
class XData;
}
class AccountRegDlg : public QDialog
{
Q_OBJECT
public:
AccountRegDlg(ProxyManager*, QWidget *parent=0);
~AccountRegDlg();
const XMPP::Jid& jid() const { return jid_; }
const QString& pass() const { return pass_; }
bool useHost() const { return opt_host_; }
const QString& host() const { return host_; }
int port() const { return port_; }
bool legacySSLProbe() { return legacy_ssl_probe_; }
UserAccount::SSLFlag ssl() const { return ssl_; }
int proxy() const { return proxy_; }
public slots:
void done(int);
protected:
static XMPP::XData convertToXData(const XMPP::Form&);
static XMPP::Form convertFromXData(const XMPP::XData&);
bool checkSSL();
void block();
void unblock();
protected slots:
void hostToggled(bool);
void sslActivated(int);
void next();
void selectServer();
void serverListReceived(const QStringList&);
void serverListError(const QString&);
void client_handshaken();
void client_error();
void getFields_finished();
void setFields_finished();
private:
Ui::AccountReg ui_;
QScrollArea* fields_container_;
XDataWidget* fields_;
ProxyManager *proxy_manager_;
ProxyChooser *proxy_chooser_;
ServerListQuerier *serverlist_querier_;
MiniClient *client_;
bool isOld_;
// Account settings
XMPP::Jid jid_, server_;
UserAccount::SSLFlag ssl_;
bool opt_host_, legacy_ssl_probe_;
QString host_;
int port_;
QString pass_;
int proxy_;
};
#endif

170
src/accountremove.ui Normal file
View file

@ -0,0 +1,170 @@
<ui version="4.0" stdsetdef="1" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>AccountRemove</class>
<widget class="QDialog" name="AccountRemove" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>429</width>
<height>173</height>
</rect>
</property>
<property name="windowTitle" >
<string>Remove Account</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QGroupBox" name="gb_account" >
<property name="title" >
<string>Remove Account</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QRadioButton" name="rb_remove" >
<property name="text" >
<string>Remove account from Psi only.</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="rb_removeAndUnreg" >
<property name="text" >
<string>Remove account and try to unregister it from the server.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="lb_pass" >
<property name="text" >
<string>Password:</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_pass" >
<property name="maximumSize" >
<size>
<width>100</width>
<height>32767</height>
</size>
</property>
<property name="echoMode" >
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item>
<spacer name="Spacer2" >
<property name="sizeHint" >
<size>
<width>30</width>
<height>0</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="Spacer3" >
<property name="sizeHint" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Vertical</enum>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="BusyWidget" name="busy" />
</item>
<item>
<spacer name="Spacer16" >
<property name="sizeHint" >
<size>
<width>179</width>
<height>16</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="IconButton" name="pb_close" >
<property name="text" >
<string>&amp;Close</string>
</property>
</widget>
</item>
<item>
<widget class="IconButton" name="pb_remove" >
<property name="text" >
<string>&amp;Remove</string>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/remove</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
</ui>

141
src/accountscombobox.cpp Normal file
View file

@ -0,0 +1,141 @@
/*
* accountscombobox.cpp
* Copyright (C) 2001-2008 Justin Karneges, Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "psicon.h"
#include "accountscombobox.h"
#include "psiaccount.h"
#include "psicontactlist.h"
AccountsComboBox::AccountsComboBox(QWidget* parent)
: QComboBox(parent)
, controller_(0)
, account_(0)
, onlineOnly_(false)
, autoHide_(false)
{
setSizePolicy(QSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed));
connect(this, SIGNAL(activated(int)), this, SLOT(changeAccount()));
}
AccountsComboBox::~AccountsComboBox()
{
}
PsiAccount* AccountsComboBox::account() const
{
return account_;
}
void AccountsComboBox::setAccount(PsiAccount* account)
{
account_ = account;
updateAccounts();
}
PsiCon* AccountsComboBox::controller() const
{
return controller_;
}
void AccountsComboBox::setController(PsiCon* controller)
{
if (controller_) {
disconnect(controller_, SIGNAL(accountCountChanged()), this, SLOT(updateAccounts()));
disconnect(controller_, SIGNAL(accountActivityChanged()), this, SLOT(updateAccounts()));
}
controller_ = controller;
if (controller_) {
connect(controller_, SIGNAL(accountCountChanged()), this, SLOT(updateAccounts()));
connect(controller_, SIGNAL(accountActivityChanged()), this, SLOT(updateAccounts()));
}
if (controller_->contactList()->haveEnabledAccounts()) {
setAccount(controller_->contactList()->enabledAccounts().first());
}
updateAccounts();
}
bool AccountsComboBox::onlineOnly() const
{
return onlineOnly_;
}
void AccountsComboBox::setOnlineOnly(bool onlineOnly)
{
onlineOnly_ = onlineOnly;
updateAccounts();
}
void AccountsComboBox::changeAccount()
{
account_ = 0;
if (currentIndex() >= 0 && currentIndex() < accounts().count())
account_ = accounts().at(currentIndex());
emit activated(account_);
}
void AccountsComboBox::updateAccounts()
{
clear();
foreach(PsiAccount* account, accounts()) {
#ifdef YAPSI
addItem(account->jid().bare());
#else
addItem(account->nameWithJid());
#endif
}
if (accounts().indexOf(account_) == -1) {
account_ = accounts().isEmpty() ? 0 : accounts().first();
emit activated(account_);
}
setCurrentIndex(accounts().indexOf(account_));
if (autoHide_) {
setVisible(count() > 1);
}
}
QList<PsiAccount*> AccountsComboBox::accounts() const
{
QList<PsiAccount*> result;
if (controller_) {
foreach(PsiAccount* account, controller_->contactList()->enabledAccounts())
if (!onlineOnly_ || account->isAvailable())
result << account;
}
return result;
}
bool AccountsComboBox::autoHide() const
{
return autoHide_;
}
void AccountsComboBox::setAutoHide(bool autoHide)
{
autoHide_ = autoHide;
updateAccounts();
}

64
src/accountscombobox.h Normal file
View file

@ -0,0 +1,64 @@
/*
* accountscombobox.h
* Copyright (C) 2001-2008 Justin Karneges, Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACCOUNTSCOMBOBOX_H
#define ACCOUNTSCOMBOBOX_H
#include <QComboBox>
class PsiCon;
class PsiAccount;
class AccountsComboBox : public QComboBox
{
Q_OBJECT
public:
AccountsComboBox(QWidget* parent);
~AccountsComboBox();
PsiAccount* account() const;
void setAccount(PsiAccount* account);
PsiCon* controller() const;
void setController(PsiCon* controller);
bool onlineOnly() const;
void setOnlineOnly(bool onlineOnly);
bool autoHide() const;
void setAutoHide(bool autoHide);
signals:
void activated(PsiAccount* account);
private slots:
void changeAccount();
void updateAccounts();
private:
PsiCon* controller_;
PsiAccount* account_;
bool onlineOnly_;
bool autoHide_;
QList<PsiAccount*> accounts() const;
};
#endif

217
src/actionlist.cpp Normal file
View file

@ -0,0 +1,217 @@
/*
* actionlist.cpp - the customizeable action list
* Copyright (C) 2004 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QList>
#include "actionlist.h"
#include <qobject.h>
#include <q3dict.h>
#include "iconaction.h"
//----------------------------------------------------------------------------
// ActionList
//----------------------------------------------------------------------------
class ActionList::Private : public QObject
{
Q_OBJECT
public:
Private() { }
Private( const Private & );
QString name;
int id;
QStringList sortedActions;
Q3Dict<IconAction> actions;
public slots:
void actionDestroyed(QObject *);
};
ActionList::ActionList( QString name, int id, bool autoDelete )
{
d = new Private();
d->actions.setAutoDelete( autoDelete );
d->name = name;
d->id = id;
}
ActionList::ActionList( const ActionList &from )
{
d = new Private( *from.d );
}
ActionList::~ActionList()
{
delete d;
}
QString ActionList::name() const
{
return d->name;
}
int ActionList::id() const
{
return d->id;
}
IconAction *ActionList::action( QString name ) const
{
return d->actions[name];
}
QStringList ActionList::actions() const
{
return d->sortedActions;
}
void ActionList::addAction( QString name, IconAction *action )
{
d->sortedActions << name;
if ( action ) {
action->setObjectName( name );
d->actions.insert( name, action );
d->connect( action, SIGNAL( destroyed(QObject *) ), d, SLOT( actionDestroyed(QObject *) ) );
}
}
void ActionList::clear()
{
d->actions.clear();
}
ActionList::Private::Private( const Private &from )
: QObject()
{
name = from.name;
id = from.id;
actions = from.actions;
actions.setAutoDelete( from.actions.autoDelete() );
sortedActions = from.sortedActions;
}
void ActionList::Private::actionDestroyed(QObject *obj)
{
bool autoDelete = actions.autoDelete();
actions.setAutoDelete( false );
actions.remove( obj->objectName() );
actions.setAutoDelete( autoDelete );
}
//----------------------------------------------------------------------------
// MetaActionList
//----------------------------------------------------------------------------
class MetaActionList::Private
{
public:
Private() { }
QList<ActionList*> lists;
};
MetaActionList::MetaActionList()
{
d = new Private();
}
MetaActionList::~MetaActionList()
{
while (!d->lists.isEmpty())
delete d->lists.takeFirst();
delete d;
}
ActionList *MetaActionList::actionList( QString name ) const
{
foreach(ActionList* a, d->lists) {
if (a->name() == name)
return a;
}
return 0;
}
QList<ActionList*> MetaActionList::actionLists( int id ) const
{
QList<ActionList*> list;
for ( int i = 0; i < 32; i++ ) {
if ( !(id & ( 1 << i )) )
continue;
foreach(ActionList* a, d->lists) {
if ( a->id() & ( 1 << i ) )
list.append(a);
}
}
return list;
}
ActionList MetaActionList::suitableActions( int id ) const
{
QList<ActionList*> lists = actionLists( id );
ActionList actions("", 0, false);
foreach(ActionList* list, lists) {
QStringList actionList = list->actions();
QStringList::Iterator it2 = actionList.begin();
for ( ; it2 != actionList.end(); ++it2 )
actions.addAction( *it2, list->action( *it2 ) );
}
return actions;
}
QStringList MetaActionList::actionLists() const
{
QStringList names;
foreach(ActionList* l, d->lists)
names << l->name();
return names;
}
void MetaActionList::addList( ActionList *list )
{
if ( list )
d->lists.append( list );
}
void MetaActionList::clear()
{
foreach(ActionList* l, d->lists) {
l->clear();
}
}
#include "actionlist.moc"

74
src/actionlist.h Normal file
View file

@ -0,0 +1,74 @@
/*
* actionlist.h - the customizeable action list
* Copyright (C) 2004 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACTIONLIST_H
#define ACTIONLIST_H
#include <QList>
class QString;
class QStringList;
class IconAction;
class ActionList
{
public:
ActionList(QString name, int id, bool autoDelete = true);
ActionList(const ActionList &);
~ActionList();
QString name() const;
int id() const;
IconAction *action( QString name ) const;
QStringList actions() const;
void addAction( QString name, IconAction *action );
void clear();
public:
class Private;
private:
Private *d;
};
class MetaActionList
{
public:
MetaActionList();
~MetaActionList();
ActionList *actionList( QString name ) const;
QList<ActionList*> actionLists( int id ) const;
QStringList actionLists() const;
ActionList suitableActions( int id ) const;
void addList( ActionList * );
void clear();
private:
class Private;
Private *d;
};
#endif

116
src/activeprofiles.cpp Normal file
View file

@ -0,0 +1,116 @@
/*
* activeprofiles.cpp - Class for interacting with other psi instances
* Copyright (C) 2006 Maciej Niedzielski
* Copyright (C) 2006-2007 Martin Hostettler
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "activeprofiles.h"
#include <QMessageBox>
#include "profiles.h"
ActiveProfiles* ActiveProfiles::instance_ = 0;
/**
* \fn virtual ActiveProfiles::~ActiveProfiles();
* \brief Destroys the object.
*/
/**
* \fn bool ActiveProfiles::isActive(const QString &profile) const;
* \brief Returns true if \a profile is running.
*/
/**
* \fn bool ActiveProfiles::isAnyActive() const;
* \brief Returns true if there is at least one running Psi instance.
*/
/**
* \fn bool ActiveProfiles::setThisProfile(const QString &profile);
* \brief Registeres this application instance as \a profile.
* Note: you can call this function multiple times with the same value of \a profile.
*/
/**
* \fn void ActiveProfiles::unsetThisProfile();
* \brief Unregistered this application profile.
*/
/**
* \fn QString ActiveProfiles::thisProfile() const;
* \brief Returns the profile name registered for this application instance.
* Returns empty string if no profile is registered.
*/
/**
* \fn ActiveProfiles::ActiveProfiles(const QString &name);
* \brief Creates new object and registers this application with its \a name.
*/
/**
* \brief Returns the instance of ActiveProfiles.
*/
ActiveProfiles* ActiveProfiles::instance()
{
if (!instance_) {
instance_ = new ActiveProfiles();
}
return instance_;
}
/**
* \fn ActiveProfiles::ActiveProfiles()
* \brief Creates new object.
*/
/**
* \fn bool ActiveProfiles::setStatus(const QString &profile, const QString &status, const QString &message) const
* \brief Requests Psi instance running \a profile to change status.
* If \a profile is empty, other running instance is selected.
* If the request cannot be sent, function returns false.
*/
/**
* \fn bool ActiveProfiles::openUri(const QString &profile, const QString &uri) const
* \brief Requests Psi instance running \a profile to open \a uri.
* If \a profile is empty, other running instance is selected.
* If the request cannot be sent, function returns false.
*/
/**
* \fn bool ActiveProfiles::raise(QString profile, bool withUI) const
* \brief Raises the main windows of Psi instance running \a profile.
* If \a profile is empty, other running instance is selected.
*/
/**
* \fn void setStatusRequested(const QString &status, const QString &message)
* \brief Signal emitted when other Psi instance requested to change status.
*/
/**
* \fn void ActiveProfiles::openUriRequested(const QUrl &uri)
* \brief Signal emitted when other Psi instance requested to open \a uri.
*/
/**
* \fn void ActiveProfiles::raiseRequested()
* \brief Signal emitted when other Psi instance requested to raise main window.
*/

64
src/activeprofiles.h Normal file
View file

@ -0,0 +1,64 @@
/*
* activeprofiles.h - Class for interacting with other psi instances
* Copyright (C) 2006 Maciej Niedzielski
* Copyright (C) 2006-2007 Martin Hostettler
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ACTIVEPSIPROFILES_H
#define ACTIVEPSIPROFILES_H
#include <QStringList>
class ActiveProfiles: public QObject
{
Q_OBJECT
public:
static ActiveProfiles* instance();
bool setThisProfile(const QString &profile);
void unsetThisProfile();
QString thisProfile() const;
bool isActive(const QString &profile) const;
bool isAnyActive() const;
bool setStatus(const QString &profile, const QString &status, const QString &message) const;
bool openUri(const QString &profile, const QString &uri) const;
bool raise(const QString &profile, bool withUI) const;
~ActiveProfiles();
signals:
void setStatusRequested(const QString &status, const QString &message);
void openUriRequested(const QString &uri);
void raiseRequested();
protected:
static ActiveProfiles *instance_;
private:
class Private;
Private *d;
ActiveProfiles();
friend class PsiConAdapter;
friend class PsiMain;
};
#endif

241
src/activeprofiles_dbus.cpp Normal file
View file

@ -0,0 +1,241 @@
/*
* activeprofiles_dbus.cpp - Class for interacting with other psi instances
* Copyright (C) 2006-2007 Martin Hostettler
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "activeprofiles.h"
#include <QCoreApplication>
#include <QString>
#include <QStringList>
#include <QByteArray>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusInterface>
#include <QLabel>
#include <QTimer>
#include "applicationinfo.h"
#include "dbus.h"
/** \brief encodes a string to "[A-Z][a-z][0-9]_-" ascii subset
* [A-Z][a-z][0-9] -> [A-Z][a-z][0-9]
* / -> _
* everything else to "-XX" with XX hex code of char
* (slow)
*/
static QString encodeAlNumD(QString in)
{
QString out;
QByteArray chars = in.toUtf8();
bool first = true;
foreach(char c, chars) {
if (('A' <= c) && (c <= 'z')) {
out += (char)c;
} else if (('0' <= c) && (c <= '9') && !first) {
out += (char)c;
} else if ('/' == c) {
out += "_";
} else {
out += QString("-%1").arg(c, 2, 16, QChar('0'));
}
first = false;
}
return out;
}
/** \brief DBus busname registration helper
* \param dbusIface interface of bus
* \param name busname to register
* \param queue try queueing?
* \return got dbus name?
*/
static bool registerBusname(QDBusConnectionInterface *dbusIface, QString name, bool queue)
{
bool ok = false;
QDBusReply<QDBusConnectionInterface::RegisterServiceReply> reply;
reply = dbusIface->registerService(name,
queue ? QDBusConnectionInterface::QueueService : QDBusConnectionInterface::DontQueueService,
QDBusConnectionInterface::AllowReplacement);
if (reply.isValid()) {
switch (reply.value()) {
case QDBusConnectionInterface::ServiceNotRegistered:
qWarning("failed to register dbus name %s:", qPrintable(name));
break;
case QDBusConnectionInterface::ServiceQueued:
qDebug("dbus name %s already taken, queueing", qPrintable(name));
break;
case QDBusConnectionInterface::ServiceRegistered:
ok = true;
}
} else {
qWarning("failed to register dbus name %s: %s", qPrintable(name), qPrintable(reply.error().message()));
}
return ok;
}
class ActiveProfiles::Private {
public:
QString profile;
QStringList busNames;
bool registerBusnames(QString prof);
QString dbusName(QString prof);
};
QString ActiveProfiles::Private::dbusName(QString prof)
{
QString name = PSIDBUSNAME;
name += ".";
name += encodeAlNumD(ApplicationInfo::homeDir()).right(qMax(0,200-name.size()));
if (!prof.isEmpty()) {
name += ".";
name += encodeAlNumD(prof).right(qMax(0,250-name.size()));
}
return name;
}
bool ActiveProfiles::Private::registerBusnames(QString prof)
{
// FIXME move where?
if (!QDBusConnection::sessionBus().isConnected()) {
qWarning("can't connect to dbus");
return true;
}
QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
QString name = PSIDBUSNAME;
registerBusname(dbusIface, name, true);
busNames << name;
name = dbusName(QString());
registerBusname(dbusIface, name, true);
busNames << name;
name = dbusName(prof);
busNames << name;
return registerBusname(dbusIface, name, false);
}
bool ActiveProfiles::isActive(const QString &profile) const
{
if (!QDBusConnection::sessionBus().isConnected()) {
qWarning("can't connect to dbus");
return false;
}
QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
return dbusIface->isServiceRegistered(d->dbusName(profile));
}
bool ActiveProfiles::isAnyActive() const
{
return isActive("");
}
bool ActiveProfiles::setThisProfile(const QString &profile)
{
if (profile == d->profile)
return true;
if (profile.isEmpty()) {
unsetThisProfile();
return true;
}
bool ok = d->registerBusnames(profile);
if (ok) {
d->profile = profile;
} else {
unsetThisProfile();
}
return ok;
}
void ActiveProfiles::unsetThisProfile()
{
QDBusConnectionInterface *dbusIface = QDBusConnection::sessionBus().interface ();
foreach(QString name, d->busNames) {
dbusIface->unregisterService(name);
}
d->busNames.clear();
d->profile = QString::null;
}
QString ActiveProfiles::thisProfile() const
{
return d->profile;
}
ActiveProfiles::ActiveProfiles()
: QObject(QCoreApplication::instance())
{
d = new ActiveProfiles::Private;
}
ActiveProfiles::~ActiveProfiles()
{
delete d;
d = 0;
}
bool ActiveProfiles::setStatus(const QString &profile, const QString &status, const QString &message) const
{
QDBusInterface(d->dbusName(profile), "/Main", PSIDBUSMAINIF).call(QDBus::NoBlock,
"setStatus", status, message);
return true;
}
bool ActiveProfiles::openUri(const QString &profile, const QString &uri) const
{
QDBusInterface(d->dbusName(profile), "/Main", PSIDBUSMAINIF).call(QDBus::NoBlock,
"openURI", uri);
return true;
}
bool ActiveProfiles::raise(const QString &profile, bool withUI) const
{
QLabel *lab=0;
QDBusMessage msg = QDBusMessage::createMethodCall ( d->dbusName(profile), "/Main", PSIDBUSMAINIF, "raise" );
if (withUI) {
lab = new QLabel(tr("This psi profile is already running...<br>please wait..."));
QTimer::singleShot(250, lab, SLOT(show()));
}
QDBusMessage rmsg = QDBusConnection::sessionBus().call(msg, QDBus::BlockWithGui, 10000);
if (withUI) {
lab->hide();
delete lab;
}
if (rmsg.type() == QDBusMessage::ReplyMessage) {
return true;
} else return false;
}

View file

@ -0,0 +1,82 @@
/*
* activeprofiles_stub.cpp - Class for interacting with other psi instances
* Copyright (C) 2006 Maciej Niedzielski
* Copyright (C) 2006-2007 Martin Hostettler
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "activeprofiles.h"
#include <QCoreApplication>
#include <QString>
bool ActiveProfiles::isActive(const QString &profile) const
{
Q_UNUSED(profile);
return false;
}
bool ActiveProfiles::isAnyActive() const
{
return false;
}
bool ActiveProfiles::setThisProfile(const QString &profile)
{
Q_UNUSED(profile);
return true;
}
void ActiveProfiles::unsetThisProfile()
{
}
QString ActiveProfiles::thisProfile() const
{
return QString();
}
ActiveProfiles::ActiveProfiles()
: QObject(QCoreApplication::instance())
{
}
ActiveProfiles::~ActiveProfiles()
{
}
bool ActiveProfiles::setStatus(const QString &profile, const QString &status, const QString &message) const
{
Q_UNUSED(profile);
Q_UNUSED(status);
Q_UNUSED(message);
return true;
}
bool ActiveProfiles::openUri(const QString &profile, const QString &uri) const
{
Q_UNUSED(uri);
Q_UNUSED(profile);
return true;
}
bool ActiveProfiles::raise(const QString &profile, bool withUI) const
{
Q_UNUSED(profile);
Q_UNUSED(withUI);
return true;
}

368
src/activeprofiles_win.cpp Normal file
View file

@ -0,0 +1,368 @@
/*
* activeprofiles_win.cpp - Class for interacting with other app instances
* Copyright (C) 2006 Maciej Niedzielski
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "activeprofiles.h"
#include "applicationinfo.h"
#include "psicon.h"
#include <QCoreApplication>
#include <QWidget>
#include <QTimer>
#include <QLabel>
#include <windows.h>
/*
Implementor notes:
This file uses WinAPI a lot. It is important to remember that we still want to support Win9x family.
For that reason, we have to use QT_WA macro for functions that exist in two versions.
Also note that writing QString("x").toLocal8Bit().constData() is a bad idea and must not be done.
*/
class ActiveProfiles::Private : public QWidget
{
public:
Private(ActiveProfiles *aprof) : app(ApplicationInfo::IPCName()), home(ApplicationInfo::homeDir()), profile(""), ap(aprof), mutex(0), changesMutex(0) {
app.replace('\\', '/'); // '\\' has a special meaning in mutex name
home.replace('\\', '/');
const QString m = QString("%1 ChangesMutex {4F5AEDA9-7D3D-4ebe-8614-FB338146CE80}").arg(app);
const QString c = QString("%1 IPC Command {4F5AEDA9-7D3D-4ebe-8614-FB338146CE80}").arg(app);
QT_WA(
changesMutex = CreateMutex(0, FALSE, (LPCWSTR)m.utf16());
psiIpcCommand = RegisterWindowMessage((LPCWSTR)c.utf16());
,
QByteArray a = m.toLocal8Bit(); // must not call constData() of a temp object
changesMutex = CreateMutexA(0, FALSE, (LPCSTR)a.constData());
a = c.toLocal8Bit();
psiIpcCommand = RegisterWindowMessageA((LPCSTR)a.constData());
)
if (!changesMutex) {
qWarning("Couldn't create IPC mutex");
}
if (!psiIpcCommand) {
qWarning("Couldn't register IPC WM_message");
}
}
QString app, home, profile;
ActiveProfiles * const ap;
HANDLE mutex, changesMutex;
QString mutexName(const QString &profile) const {
return "ProfileMutex\0x01" + app + "\0x01" + home + "\0x01" + profile + "\0x01 {4F5AEDA9-7D3D-4ebe-8614-FB338146CE80}";
}
QString windowName(const QString &profile) const {
return "ProfileWindow\0x01" + app + "\0x01" + home + "\0x01" + profile + "\0x01 {4F5AEDA9-7D3D-4ebe-8614-FB338146CE80}";
}
void startChanges() {
WaitForSingleObject(changesMutex, INFINITE);
}
void endChanges() {
ReleaseMutex(changesMutex);
}
void setWindowText(const QString &text) {
QT_WA(
SetWindowTextW(winId(), (LPCWSTR)text.utf16());
,
QByteArray a = text.toLocal8Bit();
SetWindowTextA(winId(), (LPCSTR)a.constData());
)
}
// WM_PSICOMMAND
static UINT psiIpcCommand; // = RegisterWindowMessage()
static WPARAM raiseCommand; // = 1
// WM_COPYDATA
static const DWORD stringListMessage = 1;
bool sendMessage(const QString &to, UINT message, WPARAM wParam, LPARAM lParam) const;
bool winEvent(MSG *msg, long *result);
bool sendStringList(const QString &to, const QStringList &list) const;
QString pickProfile() const;
};
UINT ActiveProfiles::Private::psiIpcCommand = 0;
WPARAM ActiveProfiles::Private::raiseCommand = 1;
QString ActiveProfiles::Private::pickProfile() const
{
QStringList profiles = getProfilesList();
foreach (QString p, profiles) {
if (ap->isActive(p)) {
return p;
}
}
return QString();
}
bool ActiveProfiles::Private::sendMessage(const QString &to, UINT message, WPARAM wParam, LPARAM lParam) const
{
QString profile = to;
if (profile.isEmpty()) {
profile = pickProfile();
}
if (profile.isEmpty()) {
return false;
}
HWND hwnd;
QT_WA(
hwnd = FindWindowW(0, (LPCWSTR)windowName(to).utf16());
,
QByteArray a = windowName(to).toLocal8Bit();
hwnd = FindWindowA(0, (LPCSTR)a.constData());
)
if (!hwnd)
return false;
SendMessageA(hwnd, message, wParam, lParam);
return true;
}
bool ActiveProfiles::Private::sendStringList(const QString &to, const QStringList &list) const
{
if (to.isEmpty())
return false;
QByteArray ba;
ba.append(list[0].toUtf8());
for (int i = 1; i < list.size(); ++i) {
const int z = ba.size();
ba.append(" " + list[i].toUtf8());
ba[z] = '\0';
}
COPYDATASTRUCT cd;
cd.dwData = stringListMessage;
cd.cbData = ba.size()+1;
cd.lpData = (void*)ba.data();
return sendMessage(to, WM_COPYDATA, (WPARAM)winId(), (LPARAM)(LPVOID)&cd);
}
bool ActiveProfiles::Private::winEvent(MSG *msg, long *result)
{
if (msg->message == WM_COPYDATA) {
*result = FALSE;
COPYDATASTRUCT *cd = (COPYDATASTRUCT *)msg->lParam;
if (cd->dwData == stringListMessage) {
char *data = (char*)cd->lpData;
const char *end = data + cd->cbData - 1;
// handle this error here, not to worry later
if (*end != '\0') {
return true;
}
QStringList list;
while (data < end) {
QString s = QString::fromUtf8(data);
list << s;
data += strlen(data) + 1;
}
if (list.count() > 1) {
if (list[0] == "openUri") {
emit ap->openUriRequested(list.value(1));
*result = TRUE;
} else if (list[0] == "setStatus") {
emit ap->setStatusRequested(list.value(1), list.value(2));
*result = TRUE;
}
}
}
return true;
}
else if (msg->message == psiIpcCommand) {
*result = FALSE;
if (msg->wParam == raiseCommand) {
emit ap->raiseRequested();
*result = TRUE;
}
return true;
}
return false;
}
ActiveProfiles::ActiveProfiles()
: QObject(QCoreApplication::instance())
, d(0)
{
#ifndef YAPSI
d = new ActiveProfiles::Private(this);
#endif
}
ActiveProfiles::~ActiveProfiles()
{
delete d;
d = 0;
}
bool ActiveProfiles::setThisProfile(const QString &profile)
{
#ifdef YAPSI
return true;
#endif
if (profile == d->profile)
return true;
if (profile.isEmpty()) {
unsetThisProfile();
return true;
}
d->startChanges();
HANDLE m;
QT_WA(
m = CreateMutexW(0, TRUE, (LPCWSTR)d->mutexName(profile).utf16());
,
QByteArray a = d->mutexName(profile).toLocal8Bit();
m = CreateMutexA(0, TRUE, (LPCSTR)a.constData());
)
if (GetLastError() == ERROR_ALREADY_EXISTS) {
CloseHandle(m);
d->endChanges();
return false;
}
else {
if (d->mutex) {
CloseHandle(d->mutex);
}
d->mutex = m;
d->profile = profile;
d->setWindowText(d->windowName(profile));
d->endChanges();
return true;
}
}
void ActiveProfiles::unsetThisProfile()
{
#ifdef YAPSI
return;
#endif
d->startChanges();
CloseHandle(d->mutex);
d->mutex = 0;
d->profile = QString::null;
d->setWindowText("");
d->endChanges();
}
QString ActiveProfiles::thisProfile() const
{
return d->profile;
}
bool ActiveProfiles::isActive(const QString &profile) const
{
#ifdef YAPSI
return true;
#endif
HANDLE m;
QT_WA(
m = OpenMutexW(0, FALSE, (LPCWSTR)d->mutexName(profile).utf16());
,
QByteArray a = d->mutexName(profile).toLocal8Bit();
m = OpenMutexA(0, FALSE, (LPCSTR)a.constData());
)
if (GetLastError() == ERROR_FILE_NOT_FOUND) {
return false;
}
else {
CloseHandle(m);
return true;
}
}
bool ActiveProfiles::isAnyActive() const
{
#ifdef YAPSI
return true;
#endif
return !d->pickProfile().isEmpty();
}
bool ActiveProfiles::raise(const QString &profile, bool withUI) const
{
#ifdef YAPSI
return true;
#endif
QLabel *lab = 0;
if (withUI) {
lab = new QLabel(tr("This psi profile is already running...<br>please wait..."));
QTimer::singleShot(250, lab, SLOT(show()));
}
bool res = d->sendMessage(profile, d->psiIpcCommand, d->raiseCommand, 0);
if (withUI) {
lab->hide();
delete lab;
}
return res;
}
bool ActiveProfiles::openUri(const QString &profile, const QString &uri) const
{
#ifdef YAPSI
return true;
#endif
QStringList list;
list << "openUri" << uri;
return d->sendStringList(profile.isEmpty()? d->pickProfile() : profile, list);
}
bool ActiveProfiles::setStatus(const QString &profile, const QString &status, const QString &message) const
{
#ifdef YAPSI
return true;
#endif
QStringList list;
list << "setStatus" << status << message;
return d->sendStringList(profile.isEmpty()? d->pickProfile() : profile, list);
}

122
src/addurl.ui Normal file
View file

@ -0,0 +1,122 @@
<ui version="4.0" stdsetdef="1" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>AddUrl</class>
<widget class="QDialog" name="AddUrl" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>337</width>
<height>123</height>
</rect>
</property>
<property name="windowTitle" >
<string>Add URL</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<layout class="QGridLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item row="1" column="1" >
<widget class="QLineEdit" name="le_desc" />
</item>
<item row="0" column="0" >
<widget class="QLabel" name="TextLabel1" >
<property name="text" >
<string>URL:</string>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="TextLabel2" >
<property name="text" >
<string>Description:</string>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLineEdit" name="le_url" />
</item>
</layout>
</item>
<item>
<spacer name="Spacer2" >
<property name="sizeHint" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Vertical</enum>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<spacer name="Spacer1" >
<property name="sizeHint" >
<size>
<width>203</width>
<height>16</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="IconButton" name="pb_close" >
<property name="text" >
<string>&amp;Close</string>
</property>
</widget>
</item>
<item>
<widget class="IconButton" name="pb_ok" >
<property name="text" >
<string>&amp;OK</string>
</property>
<property name="shortcut" >
<string>Alt+O</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<tabstops>
<tabstop>le_url</tabstop>
<tabstop>le_desc</tabstop>
</tabstops>
</ui>

439
src/adduser.ui Normal file
View file

@ -0,0 +1,439 @@
<ui version="4.0" stdsetdef="1" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>AddUser</class>
<widget class="QDialog" name="AddUser" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>601</width>
<height>367</height>
</rect>
</property>
<property name="windowTitle" >
<string>Add User</string>
</property>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QTextEdit" name="TextEdit1" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>200</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>200</width>
<height>32767</height>
</size>
</property>
<property name="text" >
<string>&lt;qt>To add a &lt;b>Jabber&lt;/b> user, simply fill out the Jabber ID (and optional nickname and group) at the bottom and press &lt;i>Add&lt;/i>.&lt;br>
&lt;br>
To add a contact from a &lt;b>non-Jabber&lt;/b> service, make sure you are registered with the service first (see Service Discovery from the main menu), and then select the service from the box at the top. Follow the instructions in the &lt;i>Service ID Translation&lt;/i> box and press the &lt;i>Get Jabber ID&lt;/i> button to generate a Jabber ID for the contact.&lt;br>
&lt;/qt></string>
</property>
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="TextLabel5" >
<property name="text" >
<string>Service:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cb_service" >
<item>
<property name="text" >
<string>Jabber</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="Spacer1" >
<property name="sizeHint" >
<size>
<width>20</width>
<height>0</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="gb_trans" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="title" >
<string>Service ID Translation</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLabel" name="lb_transDesc" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>7</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>No description</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QLineEdit" name="le_transPrompt" />
</item>
<item>
<widget class="QPushButton" name="pb_transGet" >
<property name="text" >
<string>Get Jabber ID</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" >
<property name="margin" >
<number>0</number>
</property>
<item row="0" column="1" >
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="le_jid" />
</item>
<item>
<widget class="IconToolButton" name="tb_vCard" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="text" >
<string/>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/vCard</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="TextLabel8" >
<property name="text" >
<string>Group</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="QLineEdit" name="le_nick" />
</item>
<item>
<widget class="QToolButton" name="tb_resolveNick" >
<property name="enabled" >
<bool>false</bool>
</property>
<property name="text" >
<string>Resolve</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="TextLabel7" >
<property name="text" >
<string>Nickname (optional)</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="TextLabel6" >
<property name="text" >
<string>Jabber ID</string>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QComboBox" name="cb_group" >
<property name="editable" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QCheckBox" name="ck_authreq" >
<property name="text" >
<string>Request authorization when adding</string>
</property>
</widget>
</item>
<item>
<spacer name="Spacer3" >
<property name="sizeHint" >
<size>
<width>16</width>
<height>33</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Vertical</enum>
</property>
</spacer>
</item>
<item>
<widget class="QCheckBox" name="ck_close" >
<property name="text" >
<string>Close window after adding</string>
</property>
</widget>
</item>
<item>
<widget class="Line" name="Line2" >
<property name="frameShape" >
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="BusyWidget" name="busy" />
</item>
<item>
<spacer name="Spacer2" >
<property name="sizeHint" >
<size>
<width>140</width>
<height>20</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="IconButton" name="pb_close" >
<property name="text" >
<string>&amp;Close</string>
</property>
</widget>
</item>
<item>
<widget class="IconButton" name="pb_add" >
<property name="text" >
<string>&amp;Add</string>
</property>
<property name="shortcut" >
<string>Alt+A</string>
</property>
<property name="default" >
<bool>true</bool>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/addContact</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction></pixmapfunction>
<customwidgets>
<customwidget>
<class>BusyWidget</class>
<extends></extends>
<header location="local" >busywidget.h</header>
<sizehint>
<width>-1</width>
<height>-1</height>
</sizehint>
<container>0</container>
<sizepolicy>
<hordata>5</hordata>
<verdata>5</verdata>
</sizepolicy>
<pixmap>image0</pixmap>
<properties>
<property type="CString" >name</property>
<property type="Bool" >enabled</property>
<property type="Rect" >geometry</property>
<property type="SizePolicy" >sizePolicy</property>
<property type="Size" >minimumSize</property>
<property type="Size" >maximumSize</property>
<property type="Size" >sizeIncrement</property>
<property type="Size" >baseSize</property>
<property type="Color" >paletteForegroundColor</property>
<property type="Color" >paletteBackgroundColor</property>
<property type="Pixmap" >paletteBackgroundPixmap</property>
<property type="Palette" >palette</property>
<property type="BackgroundOrigin" >backgroundOrigin</property>
<property type="Font" >font</property>
<property type="Cursor" >cursor</property>
<property type="String" >caption</property>
<property type="Pixmap" >icon</property>
<property type="String" >iconText</property>
<property type="Bool" >mouseTracking</property>
<property type="FocusPolicy" >focusPolicy</property>
<property type="Bool" >acceptDrops</property>
<property type="Bool" >active</property>
</properties>
</customwidget>
<customwidget>
<class>IconButton</class>
<extends></extends>
<header location="local" >iconbutton.h</header>
<sizehint>
<width>-1</width>
<height>-1</height>
</sizehint>
<container>0</container>
<sizepolicy>
<hordata>5</hordata>
<verdata>5</verdata>
</sizepolicy>
<pixmap>image0</pixmap>
<properties>
<property type="CString" >name</property>
<property type="Bool" >enabled</property>
<property type="Rect" >geometry</property>
<property type="SizePolicy" >sizePolicy</property>
<property type="Size" >minimumSize</property>
<property type="Size" >maximumSize</property>
<property type="Size" >sizeIncrement</property>
<property type="Size" >baseSize</property>
<property type="Color" >paletteForegroundColor</property>
<property type="Color" >paletteBackgroundColor</property>
<property type="Pixmap" >paletteBackgroundPixmap</property>
<property type="Palette" >palette</property>
<property type="BackgroundOrigin" >backgroundOrigin</property>
<property type="Font" >font</property>
<property type="Cursor" >cursor</property>
<property type="String" >caption</property>
<property type="Pixmap" >icon</property>
<property type="String" >iconText</property>
<property type="Bool" >mouseTracking</property>
<property type="FocusPolicy" >focusPolicy</property>
<property type="Bool" >acceptDrops</property>
<property type="String" >text</property>
<property type="Pixmap" >pixmap</property>
<property type="KeySequence" >accel</property>
<property type="Bool" >autoRepeat</property>
<property type="Bool" >autoDefault</property>
<property type="Bool" >default</property>
<property type="IconSet" >iconSet</property>
<property type="Bool" >toggleButton</property>
<property type="Bool" >on</property>
<property type="Bool" >flat</property>
<property type="Bool" >autoMask</property>
<property type="String" >psiIconName</property>
<property type="Bool" >textVisible</property>
</properties>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cb_service</tabstop>
<tabstop>le_transPrompt</tabstop>
<tabstop>pb_transGet</tabstop>
<tabstop>le_jid</tabstop>
<tabstop>le_nick</tabstop>
<tabstop>cb_group</tabstop>
<tabstop>ck_authreq</tabstop>
<tabstop>ck_close</tabstop>
<tabstop>TextEdit1</tabstop>
</tabstops>
<images>
<image name="image0" >
<data format="PNG" length="1125" >89504e470d0a1a0a0000000d4948445200000016000000160806000000c4b46c3b0000042c49444154789cb5954f6c14551cc73fefcd7476b65bdaae4bb78bb5502a14d404e4801c88182d1c4c2c693da847400f9c24c68b878684238660e2b1e01f12c19493012ef2478c814412d354a46017a8a564bb6da5bbedccee767776e63d0ffb073751d483bfe49799974c3eeffb7ebf37df9fd05a530b2184040cc0042420aaf9a4d0d554800f045a6b256ae0e1e1e1d6bebebe838ee31c48a7d39b5cd7fd075e251cc7617272f2ded8d8d819cff33e0316819259537aead4a9839d5dd6d1784f91f55b0a94830242088404d304292bef68a89f520802a598fecddaa04f1a876f5c250c7c0a64cdeac686e33807e23d45e6b297c8b877f1831542614550b6599835c83c2a81b6786a75134faf2f1169f12997350881d9021d0903e06de0745d3160a6d3e94dbd5b0a64dcbb94b5831d0e3375ab892b1772dcf9790528543f8dd0d367b36768153b5e31503a0f1aecb004580b44ffac58baae8b1714f0833c7638cc8dab303a320f4822ab4c7a37c69196203de3319d5ce1c4d13c733331dedc67a129a154fd128401ab0616d55a130ac3d42d93d1913940d13fd0c9ee0183685c60da01c5421bd72f7a8c8efccef9afd374267ad93d642365be0636a0d28ec7600941d9e6f23917f0e97f23ce5bef35d19ec863da0ed9059b2be70bec196c66dfa10ec0e49b338f7017258651bf95021035c595429bb0903248fe52a2b5b595dd7b4d945cc2340cdca536be389ee3f67886c5798f773fe8e0dac508c989659277a2180da4ca4ff07821058b8b251445d63d6b13ed1098a6417e39cac85197dbe31962ab9bd9f1f22a226d45366f6d0620fdb08c900d281af6110284b20085b414861d905d88f2e52739ee8cbb8022143259d3dd84691730aa2d52da441a8de0c6958068870022a41e9629ad3473fd3b8fdbe319dadb9b4924da994d2d716c7896fbe35152f78b48245d6b2da4507faf582be8eaf159b721cc837b05ae7debb1f79d08cb8b515edad942a22bc4b1c33eb3d34b1c797f06af90a72d16e2f96d9a74aa11dca8586b222d01af0fb60070f6c402d72f15d97f28c6f6d7027a5f5ce6c3233dc4e2ede496b278be4fff608cee8d3e1add806aeca51094cbb06397c1ecc328e746537c7e3ccdb5cb1136bf60635882d4d41c6ec6836ab37efa214f72208ed9f4d7cdd38ee310280542e38b1c43fb6de26b3672e1ec3cc99bcb246f66a938a3241ab3e91f7c861fbf77710b1e5e49915bae974203ba0e9e9c9cbc373d6d6d305a040a89c2a77f50b27d5782bbbf7acccf28349235dd16cf6dd374f7295e1de8a45c02d37499182b01cc0201a085d61a2144d8b2ac8fb6ed340e77240c4261890e04c250185262546d534a032154b59e0ad394e41c98182bf268ce6721ed9f064e0253356f6da2e24c1f030f783c15fe6da680af8021602bd051532ca9b8521488559f61aa86c29343578fbf0264a94c906c7d3409214c20043457a116ff6de6795578012889ff6b98fe016ea0ce1c203e47720000000049454e44ae426082</data>
</image>
</images>
</ui>

304
src/adduserdlg.cpp Normal file
View file

@ -0,0 +1,304 @@
/*
* adduserdlg.cpp - dialog for adding contacts
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "adduserdlg.h"
#include <qlabel.h>
#include <qlayout.h>
#include <qpushbutton.h>
#include <qmessagebox.h>
#include <qcombobox.h>
#include <qstringlist.h>
#include <qlineedit.h>
#include <qcheckbox.h>
#include "xmpp_tasks.h"
#include "psiaccount.h"
#include "psiiconset.h"
#include "busywidget.h"
#include "common.h"
#include "iconwidget.h"
#include "tasklist.h"
#include "xmpp_vcard.h"
#include "vcardfactory.h"
#include "infodlg.h"
class AddUserDlg::Private
{
public:
Private() {}
PsiAccount *pa;
BusyWidget *busy;
QStringList services;
JT_Gateway *jt;
TaskList *tasks;
};
AddUserDlg::AddUserDlg(const QStringList &services, const QStringList &names, const QStringList &groups, PsiAccount *pa)
:QDialog(0)
{
setupUi(this);
setModal(false);
d = new Private;
d->pa = pa;
d->pa->dialogRegister(this);
connect(d->pa, SIGNAL(updatedActivity()), SLOT(pa_updatedActivity()));
d->services = services;
d->jt = 0;
d->tasks = new TaskList;
connect(d->tasks, SIGNAL(started()), busy, SLOT(start()));
connect(d->tasks, SIGNAL(finished()), busy, SLOT(stop()));
setWindowTitle(CAP(caption()));
setWindowIcon(IconsetFactory::icon("psi/addContact").icon());
d->busy = busy;
QStringList::ConstIterator it1 = services.begin();
QStringList::ConstIterator it2 = names.begin();
for(; it1 != services.end(); ++it1, ++it2)
cb_service->insertItem(PsiIconset::instance()->status(*it1, STATUS_ONLINE).impix(), *it2);
connect(cb_service, SIGNAL(activated(int)), SLOT(serviceActivated(int)));
connect(le_transPrompt, SIGNAL(textChanged(const QString &)), SLOT(le_transPromptChanged(const QString &)));
pb_transGet->setEnabled(false);
QString str = tr("<None>");
cb_group->insertItem(str);
cb_group->insertStringList(groups);
// FIXME: Re-do the hidden group trick the proper way
// str = ContactView::tr("Hidden");
// if(!groups.contains(str))
// cb_group->insertItem(str);
cb_group->setAutoCompletion(true);
pb_add->setDefault(true);
connect(pb_add, SIGNAL(clicked()), SLOT(ok()));
connect(pb_close, SIGNAL(clicked()), SLOT(cancel()));
connect(pb_transGet, SIGNAL(clicked()), SLOT(getTransID()));
connect(tb_vCard, SIGNAL(clicked()), SLOT(getVCardActivated()));
connect(tb_resolveNick, SIGNAL(clicked()), SLOT(resolveNickActivated()));
connect(le_jid, SIGNAL(textChanged(QString)), SLOT(jid_Changed()));
ck_authreq->setChecked(true);
ck_close->setChecked(true);
le_jid->setFocus();
}
AddUserDlg::~AddUserDlg()
{
delete d->tasks;
d->pa->dialogUnregister(this);
delete d;
}
void AddUserDlg::pa_updatedActivity()
{
if(!d->pa->loggedIn())
close();
}
Jid AddUserDlg::jid() const
{
return Jid(le_jid->text().stripWhiteSpace());
}
void AddUserDlg::cancel()
{
le_jid->setText("");
le_nick->setText("");
cb_group->setCurrentItem(0);
reject();
}
void AddUserDlg::ok()
{
if(le_jid->text().isEmpty()) {
QMessageBox::information(this, tr("Add User: Error"), tr("Please fill in the Jabber ID of the person you wish to add."));
return;
}
if(!jid().isValid()) {
QMessageBox::information(this, tr("Add User: Error"), tr("The Jabber ID you entered is not valid!\nMake sure you enter a fully qualified Jabber ID."));
return;
}
QString gname = cb_group->currentText();
QStringList list;
if(gname != tr("<None>")) {
list += gname;
}
add(jid(), le_nick->text(), list, ck_authreq->isChecked());
QMessageBox::information(this, tr("Add User: Success"), tr("Added %1 to your roster.").arg(jid().full()));
le_jid->setText("");
le_nick->setText("");
if(ck_close->isChecked()) {
cb_group->setCurrentItem(0);
accept();
} else {
le_jid->setFocus();
}
}
void AddUserDlg::serviceActivated(int x)
{
if(d->jt) {
delete d->jt;
d->jt = 0;
d->busy->stop();
}
gb_trans->setEnabled(false);
le_transPrompt->setText("");
// Jabber entry
if(x == 0)
return;
--x;
if(x >= 0 && x < (int)d->services.count()) {
d->jt = new JT_Gateway(d->pa->client()->rootTask());
connect(d->jt, SIGNAL(finished()), SLOT(jt_getFinished()));
d->jt->get(Jid(d->services[x]));
d->jt->go(true);
d->tasks->append( d->jt );
}
}
void AddUserDlg::le_transPromptChanged(const QString &str)
{
pb_transGet->setEnabled(!str.isEmpty());
}
void AddUserDlg::getTransID()
{
cb_service->setEnabled(false);
le_transPrompt->setEnabled(false);
pb_transGet->setEnabled(false);
d->jt = new JT_Gateway(d->pa->client()->rootTask());
connect(d->jt, SIGNAL(finished()), SLOT(jt_setFinished()));
d->jt->set(Jid(d->services[cb_service->currentItem()-1]), le_transPrompt->text());
d->jt->go(true);
d->tasks->append( d->jt );
}
void AddUserDlg::jt_getFinished()
{
JT_Gateway *jt = d->jt;
d->jt = 0;
if(jt->success()) {
gb_trans->setEnabled(true);
lb_transDesc->setText(jt->desc());
}
else {
errorGateway(cb_service->currentText(), jt->statusString());
}
}
void AddUserDlg::jt_setFinished()
{
cb_service->setEnabled(true);
le_transPrompt->setEnabled(true);
pb_transGet->setEnabled(true);
JT_Gateway *jt = d->jt;
d->jt = 0;
if(jt->success()) {
le_jid->setText(jt->prompt());
le_nick->setText(le_transPrompt->text());
le_transPrompt->setText("");
le_jid->setCursorPosition(0);
le_nick->setCursorPosition(0);
le_nick->setFocus();
le_nick->selectAll();
}
else {
errorGateway(cb_service->currentText(), jt->statusString());
le_transPrompt->setFocus();
}
}
void AddUserDlg::errorGateway(const QString &str, const QString &err)
{
QMessageBox::information(this, CAP(tr("Error")), tr("<qt>\n"
"There was an error getting the Service ID translation information from \"%1\".<br>"
"Reason: %2<br>"
"<br>"
"The service may not support this feature. In this case you "
"will need to enter the Jabber ID manually for the contact you wish "
"to add. Examples:<br>"
"<br>"
"&nbsp;&nbsp;jabberuser@somehost.com<br>"
"&nbsp;&nbsp;aoluser@[Jabber ID of AIM Transport]<br>"
"&nbsp;&nbsp;1234567@[Jabber ID of ICQ Transport]<br>"
"&nbsp;&nbsp;joe%hotmail.com@[Jabber ID of MSN Transport]<br>"
"&nbsp;&nbsp;yahooUser@[Jabber ID of Yahoo Transport]<br>"
"</qt>"
).arg(str).arg(QString(err).replace('\n', "<br>")));
}
void AddUserDlg::getVCardActivated()
{
const VCard *vcard = VCardFactory::instance()->vcard(jid());
VCard tmp;
if ( vcard )
tmp = *vcard;
InfoDlg *w = new InfoDlg(InfoDlg::Contact, jid(), tmp, d->pa, 0, false);
w->show();
// automatically retrieve info if it doesn't exist
if(!vcard)
w->doRefresh();
}
void AddUserDlg::resolveNickActivated()
{
JT_VCard *jt = VCardFactory::instance()->getVCard(jid(), d->pa->client()->rootTask(), this, SLOT(resolveNickFinished()), false);
d->tasks->append( jt );
}
void AddUserDlg::resolveNickFinished()
{
JT_VCard *jt = (JT_VCard *)sender();
if(jt->success()) {
if ( !jt->vcard().nickName().isEmpty() )
le_nick->setText( jt->vcard().nickName() );
}
}
/**
* Called when the Jid changes to enable the vcard and nick resolution buttons.
*/
void AddUserDlg::jid_Changed()
{
bool enableVCardButtons = jid().isValid();
tb_vCard->setEnabled(enableVCardButtons);
tb_resolveNick->setEnabled(enableVCardButtons);
}

67
src/adduserdlg.h Normal file
View file

@ -0,0 +1,67 @@
/*
* adduserdlg.h - dialog for adding contacts
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ADDUSERDLG_H
#define ADDUSERDLG_H
#include "ui_adduser.h"
class QString;
class QStringList;
class PsiAccount;
namespace XMPP {
class Jid;
}
class AddUserDlg : public QDialog, public Ui::AddUser
{
Q_OBJECT
public:
AddUserDlg(const QStringList &services, const QStringList &names, const QStringList &groups, PsiAccount *);
~AddUserDlg();
signals:
void add(const XMPP::Jid &, const QString &, const QStringList &, bool authReq);
private slots:
void ok();
void cancel();
void serviceActivated(int);
void getTransID();
void pa_updatedActivity();
void jt_getFinished();
void jt_setFinished();
void le_transPromptChanged(const QString &);
void getVCardActivated();
void resolveNickActivated();
void resolveNickFinished();
void jid_Changed();
private:
class Private;
Private *d;
XMPP::Jid jid() const;
void errorGateway(const QString &str, const QString &err);
};
#endif

90
src/adhoc_fileserver.cpp Normal file
View file

@ -0,0 +1,90 @@
/*
* adhoc_fileserver.cpp - Implementation of a personal ad-hoc fileserver
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <qdir.h>
#include <qfileinfo.h>
#include "common.h"
#include "psiaccount.h"
#include "adhoc_fileserver.h"
#include "xmpp_xdata.h"
using namespace XMPP;
bool AHFileServer::isAllowed(const Jid& j) const
{
return manager()->account()->jid().compare(j,false);
}
AHCommand AHFileServer::execute(const AHCommand& c, const Jid& requester)
{
// Extract the file
QString file;
if (c.hasData()) {
QString fileName, curDir;
XData::FieldList fl = c.data().fields();
for (unsigned int i=0; i < fl.count(); i++) {
if (fl[i].var() == "file" && !(fl[i].value().isEmpty())) {
file = fl[i].value().first();
}
}
}
else {
file = QDir::currentDirPath();
}
if (QFileInfo(file).isDir()) {
// Return a form with a filelist
XData form;
form.setTitle(QObject::tr("Choose file"));
form.setInstructions(QObject::tr("Choose a file"));
form.setType(XData::Data_Form);
XData::FieldList fields;
XData::Field files_field;
files_field.setType(XData::Field::Field_ListSingle);
files_field.setVar("file");
files_field.setLabel(QObject::tr("File"));
files_field.setRequired(true);
XData::Field::OptionList file_options;
QDir d(file);
//d.setFilter(QDir::Files|QDir::Hidden|QDir::NoSymLinks);
QStringList l = d.entryList();
for (QStringList::Iterator it = l.begin(); it != l.end(); ++it ) {
XData::Field::Option file_option;
QFileInfo fi(QDir(file).filePath(*it));
file_option.label = *it + (fi.isDir() ? QString(" [DIR]") : QString(" (%1 bytes)").arg(QString::number(fi.size())));
file_option.value = QDir(file).absFilePath(*it);
file_options += file_option;
}
files_field.setOptions(file_options);
fields += files_field;
form.setFields(fields);
return AHCommand::formReply(c, form);
}
else {
QStringList l(file);
manager()->account()->sendFiles(requester,l,true);
return AHCommand::completedReply(c);
}
}

38
src/adhoc_fileserver.h Normal file
View file

@ -0,0 +1,38 @@
/*
* adhoc_fileserver.h - Implementation of a personal file server using ad-hoc
* commands
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AHFILESERVER_H
#define AHFILESERVER_H
#include "adhoc.h"
class AHFileServer : public AHCommandServer
{
public:
AHFileServer(AHCServerManager* m) : AHCommandServer(m) { }
virtual QString node() const
{ return QString("http://psi.affinix.com/commands/files"); }
virtual bool isAllowed(const Jid&) const;
virtual QString name() const { return QString("Send file"); }
virtual AHCommand execute(const AHCommand& c, const Jid&);
};
#endif

73
src/ahcexecutetask.cpp Normal file
View file

@ -0,0 +1,73 @@
/*
* ahcexecutetask.cpp - Ad-Hoc Command Execute Task
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "ahcexecutetask.h"
#include "ahcformdlg.h"
#include "xmpp_xmlcommon.h"
#include "psiaccount.h"
using namespace XMPP;
AHCExecuteTask::AHCExecuteTask(const Jid& j, const AHCommand& command, Task* t) : Task(t), receiver_(j), command_(command)
{
}
void AHCExecuteTask::onGo()
{
QDomElement e = createIQ(doc(), "set", receiver_.full(), id());
e.appendChild(command_.toXml(doc(),true));
send(e);
}
bool AHCExecuteTask::take(const QDomElement& e)
{
if(!iqVerify(e, receiver_, id())) {
return false;
}
// Result of a command
if (e.attribute("type") == "result") {
bool found;
QDomElement i = findSubTag(e, "command", &found);
if (found) {
AHCommand c(i);
if (c.status() == AHCommand::Executing) {
AHCFormDlg *w = new AHCFormDlg(c,receiver_,client());
w->show();
}
else if (c.status() == AHCommand::Completed && i.childNodes().count() > 0) {
AHCFormDlg *w = new AHCFormDlg(c,receiver_,client(), true);
w->show();
}
setSuccess();
return true;
}
}
// Error
/*else if (e.attribute("type") == "set") {
AHCError err(e);
if (err.type() != None) {
QMessageBox::critical(0, tr("Error"), AHCommand::error2description(err.type()), QMessageBox::Ok, QMessageBox::NoButton);
}
return true;
}*/
setError(e);
return false;
}

43
src/ahcexecutetask.h Normal file
View file

@ -0,0 +1,43 @@
/*
* ahcexecutetask.h - Ad-Hoc Command Execute Task
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AHCEXECUTETASK_H
#define AHCEXECUTETASK_H
#include "xmpp_task.h"
#include "xmpp_jid.h"
#include "ahcommand.h"
class QDomElement;
class AHCExecuteTask : public XMPP::Task
{
public:
AHCExecuteTask(const XMPP::Jid& j, const AHCommand&, XMPP::Task* t);
void onGo();
bool take(const QDomElement &x);
private:
XMPP::Jid receiver_;
AHCommand command_;
};
#endif

196
src/ahcformdlg.cpp Normal file
View file

@ -0,0 +1,196 @@
/*
* ahcformdlg.cpp - Ad-Hoc Command Form Dialog
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QLayout>
#include <QPushButton>
#include <QLabel>
#include "ahcformdlg.h"
#include "ahcommand.h"
#include "ahcexecutetask.h"
#include "xdata_widget.h"
#include "xmpp_client.h"
#include "psiaccount.h"
#include "busywidget.h"
AHCFormDlg::AHCFormDlg(const AHCommand& r, const Jid& receiver, XMPP::Client* client, bool final)
: QDialog(NULL), receiver_(receiver), client_(client)
{
setAttribute(Qt::WA_DeleteOnClose);
// Save node
node_ = r.node();
sessionId_ = r.sessionId();
QVBoxLayout *vb = new QVBoxLayout(this, 11, 6);
// Instructions
if (!r.data().instructions().isEmpty()) {
QLabel* lb_instructions = new QLabel(r.data().instructions(),this);
vb->addWidget(lb_instructions);
}
// XData form
xdata_ = new XDataWidget(this);
xdata_->setFields(r.data().fields());
vb->addWidget(xdata_);
vb->addStretch(1);
// Buttons
QHBoxLayout *hb = new QHBoxLayout(vb);
pb_complete = pb_cancel = pb_prev = pb_next = 0;
if (!final) {
busy_ = new BusyWidget(this);
hb->addWidget(busy_);
hb->addItem(new QSpacerItem(20,0,QSizePolicy::Expanding));
if (r.actions().empty()) {
// Single stage dialog
pb_complete = new QPushButton(tr("Finish"),this);
connect(pb_complete,SIGNAL(clicked()),SLOT(doExecute()));
hb->addWidget(pb_complete);
}
else {
// Multi-stage dialog
// Previous
pb_prev = new QPushButton(tr("Previous"),this);
if (r.actions().contains(AHCommand::Prev)) {
if (r.defaultAction() == AHCommand::Prev) {
pb_prev->setDefault(true);
pb_prev->setFocus();
}
connect(pb_prev,SIGNAL(clicked()),SLOT(doPrev()));
pb_prev->setEnabled(true);
}
else
pb_prev->setEnabled(false);
hb->addWidget(pb_prev);
// Next
pb_next = new QPushButton(tr("Next"),this);
if (r.actions().contains(AHCommand::Next)) {
if (r.defaultAction() == AHCommand::Next) {
connect(pb_next,SIGNAL(clicked()),SLOT(doExecute()));
pb_next->setDefault(true);
pb_next->setFocus();
}
else
connect(pb_next,SIGNAL(clicked()),SLOT(doNext()));
pb_next->setEnabled(true);
}
else {
pb_next->setEnabled(false);
}
hb->addWidget(pb_next);
// Complete
pb_complete = new QPushButton(tr("Finish"),this);
if (r.actions().contains(AHCommand::Complete)) {
if (r.defaultAction() == AHCommand::Complete) {
connect(pb_complete,SIGNAL(clicked()),SLOT(doExecute()));
pb_complete->setDefault(true);
pb_complete->setFocus();
}
else
connect(pb_complete,SIGNAL(clicked()),SLOT(doComplete()));
pb_complete->setEnabled(true);
}
else {
pb_complete->setEnabled(false);
}
hb->addWidget(pb_complete);
}
pb_cancel = new QPushButton(tr("Cancel"), this);
connect(pb_cancel, SIGNAL(clicked()),SLOT(doCancel()));
hb->addWidget(pb_cancel);
}
else {
hb->addItem(new QSpacerItem(20,0,QSizePolicy::Expanding));
pb_complete = new QPushButton(tr("Ok"),this);
connect(pb_complete,SIGNAL(clicked()),SLOT(close()));
hb->addWidget(pb_complete);
}
if (!r.data().title().isEmpty()) {
setCaption(QString("%1 (%2)").arg(r.data().title()).arg(receiver.full()));
}
else {
setCaption(QString("%1").arg(receiver.full()));
}
}
void AHCFormDlg::doPrev()
{
busy_->start();
AHCExecuteTask* t = new AHCExecuteTask(receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Prev), client_->rootTask());
connect(t,SIGNAL(finished()),SLOT(commandExecuted()));
t->go(true);
}
void AHCFormDlg::doNext()
{
busy_->start();
AHCExecuteTask* t = new AHCExecuteTask(receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Next),client_->rootTask());
connect(t,SIGNAL(finished()),SLOT(commandExecuted()));
t->go(true);
}
void AHCFormDlg::doExecute()
{
busy_->start();
AHCExecuteTask* t = new AHCExecuteTask(receiver_,AHCommand(node_,data(),sessionId_),client_->rootTask());
connect(t,SIGNAL(finished()),SLOT(commandExecuted()));
t->go(true);
}
void AHCFormDlg::doComplete()
{
busy_->start();
AHCExecuteTask* t = new AHCExecuteTask(receiver_,AHCommand(node_,data(),sessionId_,AHCommand::Complete), client_->rootTask());
connect(t,SIGNAL(finished()),SLOT(commandExecuted()));
t->go(true);
}
void AHCFormDlg::doCancel()
{
busy_->start();
AHCExecuteTask* t = new AHCExecuteTask(receiver_,AHCommand(node_,sessionId_,AHCommand::Cancel), client_->rootTask());
connect(t,SIGNAL(finished()),SLOT(commandExecuted()));
t->go(true);
}
void AHCFormDlg::commandExecuted()
{
busy_->stop();
close();
}
XData AHCFormDlg::data() const
{
XData x;
x.setFields(xdata_->fields());
x.setType(XData::Data_Submit);
return x;
}

67
src/ahcformdlg.h Normal file
View file

@ -0,0 +1,67 @@
/*
* ahcformdlg.h - Ad-Hoc Command Form Dialog
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AHCFORMDLG_H
#define AHCFORMDLG_H
#include <QObject>
#include <QDialog>
#include <QString>
#include "xmpp_xdata.h"
#include "xmpp_jid.h"
#include "busywidget.h"
class QPushButton;
class AHCommand;
class XDataWidget;
namespace XMPP {
class Client;
}
class AHCFormDlg : public QDialog
{
Q_OBJECT
public:
AHCFormDlg(const AHCommand& r, const XMPP::Jid& receiver, XMPP::Client* client, bool final = false);
protected:
XMPP::XData data() const;
protected slots:
void doPrev();
void doNext();
void doComplete();
void doExecute();
void doCancel();
void commandExecuted();
private:
QPushButton *pb_prev, *pb_next, *pb_complete, *pb_cancel;
XDataWidget *xdata_;
XMPP::Jid receiver_;
QString node_;
XMPP::Client* client_;
QString sessionId_;
BusyWidget* busy_;
};
#endif

365
src/ahcommand.cpp Normal file
View file

@ -0,0 +1,365 @@
/*
* ahcommand.cpp - Ad-Hoc Command
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QDomDocument>
#include <QDomElement>
#include "ahcommand.h"
#include "xmpp_xdata.h"
#define AHC_NS "http://jabber.org/protocol/commands"
#define XMPPSTANZA_NS "urn:ietf:params:xml:ns:xmpp-stanzas"
using namespace XMPP;
// --------------------------------------------------------------------------
// AHCommand: The class representing an Ad-Hoc command request or reply.
// --------------------------------------------------------------------------
AHCommand::AHCommand(const QString& node, const QString& sessionId, Action action) : node_(node), hasData_(false), status_(NoStatus), defaultAction_(NoAction), action_(action), sessionId_(sessionId)
{
}
AHCommand::AHCommand(const QString& node, XData data, const QString& sessionId, Action action) : node_(node), hasData_(true), data_(data), status_(NoStatus), defaultAction_(NoAction), action_(action), sessionId_(sessionId)
{
}
AHCommand::AHCommand(const QDomElement& q) : hasData_(false), defaultAction_(NoAction)
{
// Parse attributes
QString status = q.attribute("status");
setStatus(string2status(status));
node_ = q.attribute("node");
action_ = string2action(q.attribute("action"));
sessionId_ = q.attribute("sessionid");
// Parse the body
for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement e = n.toElement();
if (e.isNull())
continue;
QString tag = e.tagName();
// A form
if (tag == "x" && e.attribute("xmlns") =="jabber:x:data") {
data_.fromXml(e);
hasData_ = true;
}
// Actions
else if (tag == "actions") {
QString execute = e.attribute("execute");
if (!execute.isEmpty())
setDefaultAction(string2action(execute));
for (QDomNode m = e.firstChild(); !m.isNull(); m = m.nextSibling()) {
Action a = string2action(m.toElement().tagName());
if (a == Prev || a == Next || a == Complete)
actions_ += a;
}
}
else if (tag == "note") {
XMPP::XData::Field field;
field.setType(XMPP::XData::Field::Field_Fixed);
field.setValue(QStringList() << e.text());
data_.setFields(XMPP::XData::FieldList() << field);
hasData_ = true;
}
}
}
QDomElement AHCommand::toXml(QDomDocument* doc, bool submit) const
{
QDomElement command = doc->createElement("command");
command.setAttribute("xmlns", AHC_NS);
if (status_ != NoStatus)
command.setAttribute("status",status2string(status()));
if (hasData())
command.appendChild(data().toXml(doc, submit));
if (action_ != Execute)
command.setAttribute("action",action2string(action_));
command.setAttribute("node", node_);
if (!sessionId_.isEmpty())
command.setAttribute("sessionid", sessionId_);
return command;
}
AHCommand AHCommand::formReply(const AHCommand& c, const XData& data)
{
AHCommand r(c.node(), data, c.sessionId());
r.setStatus(AHCommand::Executing);
return r;
}
AHCommand AHCommand::formReply(const AHCommand& c, const XData& data, const QString& sessionId)
{
AHCommand r(c.node(), data, sessionId);
r.setStatus(AHCommand::Executing);
return r;
}
AHCommand AHCommand::canceledReply(const AHCommand& c)
{
AHCommand r(c.node(), c.sessionId());
r.setStatus(Canceled);
return r;
}
AHCommand AHCommand::completedReply(const AHCommand& c)
{
AHCommand r(c.node(), c.sessionId());
r.setStatus(Completed);
return r;
}
AHCommand AHCommand::completedReply(const AHCommand& c, const XData& d)
{
AHCommand r(c.node(), d, c.sessionId());
r.setStatus(Completed);
return r;
}
//AHCommand AHCommand::errorReply(const AHCommand& c, const AHCError& error)
//{
// AHCommand r(c.node(), c.sessionId());
// r.setError(error);
// return r;
//}
void AHCommand::setStatus(Status s)
{
status_ = s;
}
void AHCommand::setError(const AHCError& e)
{
error_ = e;
}
void AHCommand::setDefaultAction(Action a)
{
defaultAction_ = a;
}
QString AHCommand::status2string(Status status)
{
QString s;
switch (status) {
case Executing : s = "executing"; break;
case Completed : s = "completed"; break;
case Canceled : s = "canceled"; break;
case NoStatus : s = ""; break;
}
return s;
}
QString AHCommand::action2string(Action action)
{
QString s;
switch (action) {
case Prev : s = "prev"; break;
case Next : s = "next"; break;
case Cancel : s = "cancel"; break;
case Complete : s = "complete"; break;
default: break;
}
return s;
}
AHCommand::Action AHCommand::string2action(const QString& s)
{
if (s == "prev")
return Prev;
else if (s == "next")
return Next;
else if (s == "complete")
return Complete;
else if (s == "cancel")
return Cancel;
else
return Execute;
}
AHCommand::Status AHCommand::string2status(const QString& s)
{
if (s == "canceled")
return Canceled;
else if (s == "completed")
return Completed;
else if (s == "executing")
return Executing;
else
return NoStatus;
}
// --------------------------------------------------------------------------
// AHCError: The class representing an Ad-Hoc command error
// --------------------------------------------------------------------------
AHCError::AHCError(ErrorType t) : type_(t)
{
}
AHCError::AHCError(const QDomElement& e) : type_(None)
{
QString errorGeneral = "", errorSpecific = "";
for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement i = n.toElement();
if(i.isNull())
continue;
QString tag = i.tagName();
if ((tag == "bad-request" || tag == "not-allowed" || tag == "forbidden" || tag == "forbidden" || tag == "item-not-found" || tag == "feature-not-implemented") && e.attribute("xmlns") == XMPPSTANZA_NS) {
errorGeneral = tag;
}
else if ((tag == "malformed-action" || tag == "bad-action" || tag == "bad-locale" || tag == "bad-payload" || tag == "bad-sessionid" || tag == "session-expired") && e.attribute("xmlns") == AHC_NS) {
errorSpecific = tag;
}
}
type_ = strings2error(errorGeneral, errorSpecific);
}
QDomElement AHCError::toXml(QDomDocument* doc) const
{
QDomElement err = doc->createElement("error");
// Error handling
if (type_ != None) {
QString desc, specificCondition = "";
switch (type_) {
case MalformedAction:
desc = "bad-request";
specificCondition = "malformed-action";
break;
case BadAction:
desc = "bad-request";
specificCondition = "bad-action";
break;
case BadLocale:
desc = "bad-request";
specificCondition = "bad-locale";
break;
case BadPayload:
desc = "bad-request";
specificCondition = "bad-payload";
break;
case BadSessionID:
desc = "bad-request";
specificCondition = "bad-sessionid";
break;
case SessionExpired:
desc = "not-allowed";
specificCondition = "session-expired";
break;
case Forbidden:
desc = "forbidden";
break;
case ItemNotFound:
desc = "item-not-found";
break;
case FeatureNotImplemented:
desc = "feature-not-implemented";
break;
case None:
break;
}
// General error condition
QDomElement generalElement = doc->createElement(desc);
generalElement.setAttribute("xmlns", XMPPSTANZA_NS);
err.appendChild(generalElement);
// Specific error condition
if (!specificCondition.isEmpty()) {
QDomElement generalElement = doc->createElement(specificCondition);
generalElement.setAttribute("xmlns", AHC_NS);
err.appendChild(generalElement);
}
}
return err;
}
AHCError::ErrorType AHCError::strings2error(const QString& g, const QString& s)
{
if (s == "malformed-action")
return MalformedAction;
if (s == "bad-action")
return BadAction;
if (s == "bad-locale")
return BadLocale;
if (s == "bad-payload")
return BadPayload;
if (s == "bad-sessionid")
return BadSessionID;
if (s == "session-expired")
return SessionExpired;
if (g == "forbidden")
return Forbidden;
if (g == "item-not-found")
return ItemNotFound;
if (g == "feature-not-implemented")
return FeatureNotImplemented;
return None;
}
QString AHCError::error2description(const AHCError& e)
{
QString desc;
switch (e.type()) {
case MalformedAction:
desc = QString("The responding JID does not understand the specified action");
break;
case BadAction:
desc = QString("The responding JID cannot accept the specified action");
break;
case BadLocale:
desc = QString("The responding JID cannot accept the specified language/locale");
break;
case BadPayload:
desc = QString("The responding JID cannot accept the specified payload (eg the data form did not provide one or more required fields)");
break;
case BadSessionID:
desc = QString("The responding JID cannot accept the specified sessionid");
break;
case SessionExpired:
desc = QString("The requesting JID specified a sessionid that is no longer active (either because it was completed, canceled, or timed out)");
break;
case Forbidden:
desc = QString("The requesting JID is not allowed to execute the command");
break;
case ItemNotFound:
desc = QString("The responding JID cannot find the requested command node");
break;
case FeatureNotImplemented:
desc = QString("The responding JID does not support Ad-hoc commands");
break;
case None:
break;
}
return desc;
}

112
src/ahcommand.h Normal file
View file

@ -0,0 +1,112 @@
/*
* ahcommand.h - Ad-Hoc Command
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AHCOMMAND_H
#define AHCOMMAND_H
#include <QString>
#include "xmpp_xdata.h"
class QDomElement;
class QDomDocument;
class AHCError
{
public:
enum ErrorType {
None, MalformedAction, BadAction, BadLocale,
BadPayload, BadSessionID, SessionExpired, Forbidden, ItemNotFound,
FeatureNotImplemented
};
AHCError(ErrorType = None);
AHCError(const QDomElement& e);
ErrorType type() const { return type_; }
static QString error2description(const AHCError&);
QDomElement toXml(QDomDocument* doc) const;
protected:
static ErrorType strings2error(const QString&, const QString&);
private:
ErrorType type_;
};
class AHCommand
{
public:
// Types
enum Action { NoAction, Execute, Prev, Next, Complete, Cancel };
enum Status { NoStatus, Completed, Executing, Canceled };
typedef QList<Action> ActionList;
// Constructors
AHCommand(const QString& node, const QString& sessionId = "", Action action = Execute);
AHCommand(const QString& node, XMPP::XData data, const QString& sessionId = "", Action action = Execute);
AHCommand(const QDomElement &e);
// Inspectors
const QString& node() const { return node_; }
bool hasData() const { return hasData_; }
const XMPP::XData& data() const { return data_; }
const ActionList& actions() const { return actions_; }
Action defaultAction() const { return defaultAction_; }
Status status() const { return status_; }
Action action() const { return action_; }
const QString& sessionId() const { return sessionId_; }
const AHCError& error() const { return error_; }
// XML conversion
QDomElement toXml(QDomDocument* doc, bool submit) const;
// Helper constructors
static AHCommand formReply(const AHCommand&, const XMPP::XData&);
static AHCommand formReply(const AHCommand&, const XMPP::XData&, const QString& sessionId);
static AHCommand canceledReply(const AHCommand&);
static AHCommand completedReply(const AHCommand&);
static AHCommand completedReply(const AHCommand&, const XMPP::XData&);
//static AHCommand errorReply(const AHCommand&, const AHCError&);
protected:
void setStatus(Status s);
void setError(const AHCError& e);
void setDefaultAction(Action a);
static QString action2string(Action);
static QString status2string(Status);
static Action string2action(const QString&);
static Status string2status(const QString&);
private:
QString node_;
bool hasData_;
XMPP::XData data_;
Status status_;
Action defaultAction_;
ActionList actions_;
Action action_;
QString sessionId_;
AHCError error_;
};
#endif

207
src/ahcommanddlg.cpp Normal file
View file

@ -0,0 +1,207 @@
/*
* ahcommanddlg.cpp - Ad-Hoc Command Dialog
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QComboBox>
#include <QMessageBox>
#include <QPushButton>
#include <QLayout>
#include <QLabel>
#include "ahcexecutetask.h"
#include "ahcommanddlg.h"
#include "busywidget.h"
#include "psiaccount.h"
#include "xmpp_xmlcommon.h"
#include "xmpp_client.h"
using namespace XMPP;
#define AHC_NS "http://jabber.org/protocol/commands"
// --------------------------------------------------------------------------
static bool operator<(const AHCommandItem &ci1, const AHCommandItem &ci2)
{
return ci1.name < ci2.name;
}
// --------------------------------------------------------------------------
// JT_AHCGetList: A Task to retreive the available commands of a client
// --------------------------------------------------------------------------
class JT_AHCGetList : public Task
{
public:
JT_AHCGetList(Task* t, const Jid& j);
void onGo();
bool take(const QDomElement &x);
const QList<AHCommandItem>& commands() const { return commands_; }
private:
Jid receiver_;
QList<AHCommandItem> commands_;
};
JT_AHCGetList::JT_AHCGetList(Task* t, const Jid& j) : Task(t), receiver_(j)
{
}
void JT_AHCGetList::onGo()
{
QDomElement e = createIQ(doc(), "get", receiver_.full(), id());
QDomElement q = doc()->createElement("query");
q.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
q.setAttribute("node", AHC_NS);
e.appendChild(q);
send(e);
}
bool JT_AHCGetList::take(const QDomElement& e)
{
if(!iqVerify(e, receiver_, id())) {
return false;
}
if (e.attribute("type") == "result") {
// Extract commands
commands_.clear();
bool found;
QDomElement commands = findSubTag(e, "query", &found);
if(found) {
for(QDomNode n = commands.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement i = n.toElement();
if(i.isNull())
continue;
if(i.tagName() == "item") {
AHCommandItem ci;
ci.node = i.attribute("node");
ci.name = i.attribute("name");
ci.jid = i.attribute("jid");
commands_ += ci;
}
}
qSort(commands_);
}
setSuccess();
return true;
}
else {
setError(e);
return false;
}
}
// --------------------------------------------------------------------------
// JT_AHCommandDlg: Initial command dialog
// --------------------------------------------------------------------------
AHCommandDlg::AHCommandDlg(PsiAccount* pa, const Jid& receiver)
: QDialog(0), pa_(pa), receiver_(receiver)
{
setAttribute(Qt::WA_DeleteOnClose);
QVBoxLayout *vb = new QVBoxLayout(this, 11, 6);
// Command list + Buttons
QLabel* lb_commands = new QLabel(tr("Command:"),this);
vb->addWidget(lb_commands);
cb_commands = new QComboBox(this);
vb->addWidget(cb_commands);
/*pb_info = new QPushButton(tr("Info"), this);
hb1->addWidget(pb_info);*/
// Refresh button
//pb_refresh = new QPushButton(tr("Refresh"), this);
//hb2->addWidget(pb_refresh);
//connect(pb_refresh, SIGNAL(clicked()), SLOT(refreshCommands()));
vb->addStretch(1);
// Bottom row
QHBoxLayout *hb2 = new QHBoxLayout(vb);
busy_ = new BusyWidget(this);
hb2->addWidget(busy_);
hb2->addItem(new QSpacerItem(20,0,QSizePolicy::Expanding));
pb_execute = new QPushButton(tr("Execute"), this);
hb2->addWidget(pb_execute);
connect(pb_execute, SIGNAL(clicked()), SLOT(executeCommand()));
pb_close = new QPushButton(tr("Close"), this);
hb2->addWidget(pb_close);
connect(pb_close, SIGNAL(clicked()), SLOT(close()));
pb_close->setDefault(true);
pb_close->setFocus();
setCaption(QString("Execute Command (%1)").arg(receiver.full()));
// Load commands
refreshCommands();
}
void AHCommandDlg::refreshCommands()
{
cb_commands->clear();
pb_execute->setEnabled(false);
//pb_info->setEnabled(false);
busy_->start();
JT_AHCGetList* t= new JT_AHCGetList(pa_->client()->rootTask(),receiver_);
connect(t,SIGNAL(finished()),SLOT(listReceived()));
t->go(true);
}
void AHCommandDlg::listReceived()
{
JT_AHCGetList* task_list = (JT_AHCGetList*) sender();
foreach(AHCommandItem i, task_list->commands()) {
cb_commands->insertItem(i.name);
commands_.append(i);
}
pb_execute->setEnabled(cb_commands->count()>0);
busy_->stop();
}
void AHCommandDlg::executeCommand()
{
if (cb_commands->count() > 0) {
busy_->start();
Jid to(commands_[cb_commands->currentItem()].jid);
QString node = commands_[cb_commands->currentItem()].node;
AHCExecuteTask* t = new AHCExecuteTask(to,AHCommand(node),pa_->client()->rootTask());
connect(t,SIGNAL(finished()),SLOT(commandExecuted()));
t->go(true);
}
}
void AHCommandDlg::commandExecuted()
{
busy_->stop();
close();
}
void AHCommandDlg::executeCommand(XMPP::Client* c, const XMPP::Jid& to, const QString &node)
{
AHCExecuteTask* t = new AHCExecuteTask(to,AHCommand(node),c->rootTask());
t->go(true);
}

64
src/ahcommanddlg.h Normal file
View file

@ -0,0 +1,64 @@
/*
* ahcommanddlg.h - Ad-Hoc Command Dialog
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AHCOMMANDDLG_H
#define AHCOMMANDDLG_H
#include <QDialog>
#include <QList>
#include "busywidget.h"
#include "xmpp_jid.h"
namespace XMPP {
class Client;
}
class PsiAccount;
class QObject;
class QComboBox;
class QPushButton;
typedef struct { QString jid, node, name; } AHCommandItem;
class AHCommandDlg : public QDialog
{
Q_OBJECT
public:
AHCommandDlg(PsiAccount*, const XMPP::Jid& receiver);
static void executeCommand(XMPP::Client*, const XMPP::Jid& j, const QString &node);
protected slots:
void refreshCommands();
void listReceived();
void executeCommand();
void commandExecuted();
private:
PsiAccount* pa_;
QPushButton *pb_execute, *pb_close, *pb_refresh /*,*pb_info*/;
XMPP::Jid receiver_;
QComboBox* cb_commands;
QList<AHCommandItem> commands_;
BusyWidget* busy_;
};
#endif

40
src/ahcommandserver.cpp Normal file
View file

@ -0,0 +1,40 @@
/*
* ahcommandserver.cpp - Server implementation of JEP-50 (Ad-Hoc Commands)
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "ahcommandserver.h"
#include "ahcservermanager.h"
// --------------------------------------------------------------------------
// AHCommandServer: The server-side implementation of an Ad-hoc command.
// --------------------------------------------------------------------------
AHCommandServer::AHCommandServer(AHCServerManager* manager)
: QObject(manager)
, manager_(manager)
{
manager_->addServer(this);
}
AHCommandServer::~AHCommandServer()
{
manager_->removeServer(this);
}

53
src/ahcommandserver.h Normal file
View file

@ -0,0 +1,53 @@
/*
* ahcommandserver.h - Server implementation of JEP-50 (Ad-Hoc Commands)
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AHCOMMANDSERVER_H
#define AHCOMMANDSERVER_H
#include <QObject>
class AHCServerManager;
class QString;
class AHCommand;
namespace XMPP {
class Jid;
}
class AHCommandServer : public QObject
{
Q_OBJECT
public:
AHCommandServer(AHCServerManager*);
virtual ~AHCommandServer();
virtual QString name() const = 0;
virtual QString node() const = 0;
virtual bool isAllowed(const XMPP::Jid&) const { return true; }
virtual AHCommand execute(const AHCommand&, const XMPP::Jid& requester) = 0;
virtual void cancel(const AHCommand&) { }
protected:
AHCServerManager* manager() const { return manager_; }
private:
AHCServerManager* manager_;
};
#endif

265
src/ahcservermanager.cpp Normal file
View file

@ -0,0 +1,265 @@
/*
* ahcservermanager.cpp - Server implementation of JEP-50 (Ad-Hoc Commands)
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <qcombobox.h>
#include <qmessagebox.h>
#include <qpushbutton.h>
#include <qlayout.h>
#include "ahcservermanager.h"
#include "ahcommandserver.h"
#include "psiaccount.h"
#include "xmpp_xmlcommon.h"
#include "xmpp_tasks.h"
#include "xmpp_xdata.h"
#include "xdata_widget.h"
#include "ahcommand.h"
using namespace XMPP;
#define AHC_NS "http://jabber.org/protocol/commands"
// --------------------------------------------------------------------------
// JT_AHCServer: Task to handle ad-hoc command requests
// --------------------------------------------------------------------------
class JT_AHCServer : public Task
{
Q_OBJECT
public:
JT_AHCServer(Task*, AHCServerManager*);
bool take(const QDomElement& e);
void sendReply(const AHCommand&, const Jid& to, const QString& id);
protected:
bool commandListQuery(const QDomElement& e);
bool commandExecuteQuery(const QDomElement& e);
void sendCommandList(const QString& to, const QString& from, const QString& id);
private:
AHCServerManager* manager_;
};
JT_AHCServer::JT_AHCServer(Task* t, AHCServerManager* manager) : Task(t), manager_(manager)
{
}
bool JT_AHCServer::take(const QDomElement& e)
{
// Check if it's a query
if (e.tagName() != "iq")
return false;
return (commandListQuery(e) || commandExecuteQuery(e));
}
bool JT_AHCServer::commandListQuery(const QDomElement& e)
{
if (e.attribute("type") == "get") {
bool found;
QDomElement q = findSubTag(e, "query", &found);
if (!found)
return false;
// Disco replies to the AdHoc node
if (q.attribute("xmlns") == "http://jabber.org/protocol/disco#items" && q.attribute("node") == AHC_NS) {
sendCommandList(e.attribute("from"),e.attribute("to"),e.attribute("id"));
return true;
}
else if (q.attribute("xmlns") == "http://jabber.org/protocol/disco#info" && q.attribute("node") == AHC_NS) {
QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
QDomElement query = doc()->createElement("query");
query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
iq.appendChild(query);
send(iq);
return true;
}
// Disco replies to specific adhoc nodes
else if (q.attribute("xmlns") == "http://jabber.org/protocol/disco#items" && manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) {
QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
QDomElement query = doc()->createElement("query"); query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items"); query.setAttribute("node",q.attribute("node"));
iq.appendChild(query);
send(iq);
return true;
}
else if (q.attribute("xmlns") == "http://jabber.org/protocol/disco#info" && manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) {
QDomElement iq = createIQ(doc(), "result", e.attribute("from"), e.attribute("id"));
QDomElement query = doc()->createElement("query");
query.setAttribute("xmlns", "http://jabber.org/protocol/disco#info");
query.setAttribute("node",q.attribute("node"));
iq.appendChild(query);
QDomElement identity;
identity = doc()->createElement("identity");
identity.setAttribute("category", "automation");
identity.setAttribute("type", "command-node");
query.appendChild(identity);
QDomElement feature;
feature = doc()->createElement("feature");
feature.setAttribute("var", AHC_NS);
query.appendChild(feature);
feature = doc()->createElement("feature");
feature.setAttribute("var", "jabber:x:data");
query.appendChild(feature);
send(iq);
return true;
}
}
return false;
}
bool JT_AHCServer::commandExecuteQuery(const QDomElement& e)
{
if (e.attribute("type") == "set") {
bool found;
QDomElement q = findSubTag(e, "command", &found);
if (found && q.attribute("xmlns") == AHC_NS && manager_->hasServer(q.attribute("node"), Jid(e.attribute("from")))) {
AHCommand command(q);
manager_->execute(command, Jid(e.attribute("from")), e.attribute("id"));
return true;
}
else
return false;
}
return false;
}
void JT_AHCServer::sendCommandList(const QString& to, const QString& from, const QString& id)
{
// Create query element
QDomElement iq = createIQ(doc(), "result", to, id);
QDomElement query = doc()->createElement("query");
query.setAttribute("xmlns", "http://jabber.org/protocol/disco#items");
query.setAttribute("node", AHC_NS);
iq.appendChild(query);
// Add all commands
foreach(AHCommandServer* c, manager_->commands(Jid(to))) {
QDomElement command = doc()->createElement("item");
command.setAttribute("jid",from);
command.setAttribute("name",c->name());
command.setAttribute("node",c->node());
query.appendChild(command);
}
// Send the message
send(iq);
}
void JT_AHCServer::sendReply(const AHCommand& c, const Jid& to, const QString& id)
{
// if (c.error().type() != AHCError::None) {
QDomElement iq = createIQ(doc(), "result", to.full(), id);
QDomElement command = c.toXml(doc(), false);
iq.appendChild(command);
send(iq);
// }
// else {
// }
}
// --------------------------------------------------------------------------
AHCServerManager::AHCServerManager(PsiAccount *pa)
: QObject(pa)
, pa_(pa)
{
server_task_ = new JT_AHCServer(pa_->client()->rootTask(), this);
}
AHCServerManager::~AHCServerManager()
{
qDeleteAll(servers_);
}
void AHCServerManager::addServer(AHCommandServer* server)
{
servers_.append(server);
}
void AHCServerManager::removeServer(AHCommandServer* server)
{
servers_.removeAll(server);
}
AHCServerManager::ServerList AHCServerManager::commands(const Jid& j) const
{
ServerList list;
for (ServerList::ConstIterator it = servers_.begin(); it != servers_.end(); ++it) {
if ((*it)->isAllowed(j))
list.append(*it);
}
return list;
}
void AHCServerManager::execute(const AHCommand& command, const Jid& requester, QString id)
{
AHCommandServer* c = findServer(command.node());
// Check if the command is provided
if (!c) {
//server_task_->sendReply(AHCommand::errorReply(command,AHCError(AHCError::ItemNotFound)), requester, id);
return;
}
// Check if the requester is allowed to execute the command
if (c->isAllowed(requester)) {
if (command.action() == AHCommand::Cancel) {
c->cancel(command);
server_task_->sendReply(AHCommand::canceledReply(command), requester, id);
}
else
// Execute the command & send back the response
server_task_->sendReply(c->execute(command, requester), requester, id);
}
else {
//server_task_->sendReply(AHCommand::errorReply(command,AHCError(AHCError::Forbidden)), requester, id);
return;
}
}
bool AHCServerManager::hasServer(const QString& node, const Jid& requester) const
{
AHCommandServer* c = findServer(node);
return c && c->isAllowed(requester);
}
AHCommandServer* AHCServerManager::findServer(const QString& node) const
{
for (ServerList::ConstIterator it = servers_.begin(); it != servers_.end(); ++it) {
if ((*it)->node() == node)
return (*it);
}
return 0;
}
#include "ahcservermanager.moc"

60
src/ahcservermanager.h Normal file
View file

@ -0,0 +1,60 @@
/*
* ahcservermanager.h - Server implementation of JEP-50 (Ad-Hoc Commands)
* Copyright (C) 2005 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AHCSERVERMANAGER_H
#define AHCSERVERMANAGER_H
#include <QObject>
#include <QList>
class AHCommandServer;
class AHCommand;
class JT_AHCServer;
class PsiAccount;
class QString;
namespace XMPP {
class Jid;
}
class AHCServerManager : public QObject
{
Q_OBJECT
public:
AHCServerManager(PsiAccount* pa);
~AHCServerManager();
void addServer(AHCommandServer*);
void removeServer(AHCommandServer*);
typedef QList<AHCommandServer*> ServerList;
ServerList commands(const XMPP::Jid&) const;
void execute(const AHCommand& command, const XMPP::Jid& requester, QString id);
PsiAccount* account() const { return pa_; }
bool hasServer(const QString& node, const XMPP::Jid&) const;
protected:
AHCommandServer* findServer(const QString& node) const;
private:
PsiAccount* pa_;
JT_AHCServer* server_task_;
ServerList servers_;
};
#endif

84
src/alertable.cpp Normal file
View file

@ -0,0 +1,84 @@
/*
* alertable.cpp - simplify alert icon plumbing
* Copyright (C) 2006 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "alertable.h"
#include <QIcon>
#include "alerticon.h"
/**
* Class to simplify alert icon plumbing. You'll have to re-implement
* alertFrameUpdated() in your subclass.
*/
Alertable::Alertable(QObject* parent)
: QObject(parent)
{
alert_ = 0;
}
/**
* Destroys alert icon along with itself;
*/
Alertable::~Alertable()
{
setAlert(0);
}
/**
* Returns true if alert is set, and false otherwise.
*/
bool Alertable::alerting() const
{
return alert_ != 0;
}
/**
* Returns current animation frame of alert, or null QIcon, if
* there is no alert.
*/
QIcon Alertable::currentAlertFrame() const
{
if (!alert_)
return QIcon();
return alert_->impix().pixmap();
}
/**
* Creates new AlertIcon based on provided \param icon. If 0 is passed,
* alert is cleared.
*/
void Alertable::setAlert(const PsiIcon* icon)
{
if (alert_) {
alert_->stop();
delete alert_;
alert_ = 0;
}
if (icon) {
alert_ = new AlertIcon(icon);
alert_->activated(false);
connect(alert_, SIGNAL(pixmapChanged()), SLOT(alertFrameUpdated()));
alertFrameUpdated();
}
}

49
src/alertable.h Normal file
View file

@ -0,0 +1,49 @@
/*
* alertable.h - simplify alert icon plumbing
* Copyright (C) 2006 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ALERTABLE_H
#define ALERTABLE_H
#include <QObject>
class AlertIcon;
class PsiIcon;
class QIcon;
class Alertable : public QObject
{
Q_OBJECT
public:
Alertable(QObject* parent = 0);
~Alertable();
bool alerting() const;
QIcon currentAlertFrame() const;
void setAlert(const PsiIcon* icon);
private slots:
virtual void alertFrameUpdated() = 0;
private:
AlertIcon* alert_;
};
#endif

299
src/alerticon.cpp Normal file
View file

@ -0,0 +1,299 @@
/*
* alerticon.cpp - class for handling animating alert icons
* Copyright (C) 2003 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "alerticon.h"
#include "common.h"
#include <qtimer.h>
#include <qapplication.h>
//Added by qt3to4:
#include <QPixmap>
//----------------------------------------------------------------------------
// MetaAlertIcon
//----------------------------------------------------------------------------
class MetaAlertIcon : public QObject
{
Q_OBJECT
public:
MetaAlertIcon();
~MetaAlertIcon();
Impix blank16() const;
int framenumber() const;
signals:
void updateFrame(int frame);
void update();
public slots:
void updateAlertStyle();
private slots:
void animTimeout();
private:
QTimer *animTimer;
int frame;
Impix _blank16;
};
static MetaAlertIcon *metaAlertIcon = 0;
MetaAlertIcon::MetaAlertIcon()
: QObject(qApp)
{
animTimer = new QTimer(this);
connect(animTimer, SIGNAL(timeout()), SLOT(animTimeout()));
animTimer->start(120 * 5);
frame = 0;
// blank icon
QImage blankImg(16, 16, QImage::Format_ARGB32);
blankImg.fill(0x00000000);
_blank16.setImage(blankImg);
}
MetaAlertIcon::~MetaAlertIcon()
{
}
Impix MetaAlertIcon::blank16() const
{
return _blank16;
}
void MetaAlertIcon::animTimeout()
{
frame = !frame;
emit updateFrame(frame);
}
void MetaAlertIcon::updateAlertStyle()
{
emit update();
}
int MetaAlertIcon::framenumber() const
{
return frame;
}
//----------------------------------------------------------------------------
// AlertIcon::Private
//----------------------------------------------------------------------------
class AlertIcon::Private : public QObject
{
Q_OBJECT
public:
Private(AlertIcon *_ai);
~Private();
void init();
public slots:
void update();
void activated(bool playSound);
void stop();
void updateFrame(int frame);
void pixmapChanged();
public:
AlertIcon *ai;
PsiIcon *real;
bool isActivated;
Impix impix;
};
AlertIcon::Private::Private(AlertIcon *_ai)
{
if ( !metaAlertIcon )
metaAlertIcon = new MetaAlertIcon();
ai = _ai;
real = 0;
isActivated = false;
}
AlertIcon::Private::~Private()
{
if ( isActivated )
stop();
if ( real )
delete real;
}
void AlertIcon::Private::init()
{
connect(metaAlertIcon, SIGNAL(update()), SLOT(update()));
connect(real, SIGNAL(iconModified()), SLOT(pixmapChanged()));
if ( option.alertStyle == 2 && real->isAnimated() )
impix = real->frameImpix();
else
impix = real->impix();
}
void AlertIcon::Private::update()
{
stop();
activated(false);
}
void AlertIcon::Private::activated(bool playSound)
{
if ( option.alertStyle == 2 && real->isAnimated() ) {
if ( !isActivated ) {
connect(real, SIGNAL(pixmapChanged()), SLOT(pixmapChanged()));
real->activated(playSound);
isActivated = true;
}
}
else if ( option.alertStyle == 1 || (option.alertStyle == 2 && !real->isAnimated()) ) {
connect(metaAlertIcon, SIGNAL(updateFrame(int)), SLOT(updateFrame(int)));
}
else {
impix = real->impix();
emit ai->pixmapChanged();
}
}
void AlertIcon::Private::stop()
{
disconnect(metaAlertIcon, SIGNAL(updateFrame(int)), this, SLOT(updateFrame(int)));
if ( isActivated ) {
disconnect(real, SIGNAL(pixmapChanged()), this, SLOT(pixmapChanged()));
real->stop();
isActivated = false;
}
}
void AlertIcon::Private::updateFrame(int frame)
{
if ( !metaAlertIcon ) // just in case
metaAlertIcon = new MetaAlertIcon();
if ( frame )
impix = real->impix();
else
impix = metaAlertIcon->blank16();
emit ai->pixmapChanged();
}
void AlertIcon::Private::pixmapChanged()
{
impix = real->frameImpix();
emit ai->pixmapChanged();
}
//----------------------------------------------------------------------------
// AlertIcon
//----------------------------------------------------------------------------
AlertIcon::AlertIcon(const PsiIcon *icon)
{
d = new Private(this);
if ( icon )
d->real = new PsiIcon(*icon);
else
d->real = new PsiIcon();
d->init();
}
AlertIcon::~AlertIcon()
{
delete d;
}
bool AlertIcon::isAnimated() const
{
return d->real->isAnimated();
}
const QPixmap &AlertIcon::pixmap() const
{
return d->impix.pixmap();
}
const QImage &AlertIcon::image() const
{
return d->impix.image();
}
void AlertIcon::activated(bool playSound)
{
d->activated(playSound);
}
void AlertIcon::stop()
{
d->stop();
}
const QIcon &AlertIcon::icon() const
{
return d->real->icon();
}
const Impix &AlertIcon::impix() const
{
return d->impix;
}
int AlertIcon::frameNumber() const
{
if ( option.alertStyle == 2 && d->real->isAnimated() ) {
return d->real->frameNumber();
}
else if ( option.alertStyle == 1 || (option.alertStyle == 2 && !d->real->isAnimated()) ) {
return metaAlertIcon->framenumber();
}
return 0;
}
const QString &AlertIcon::name() const
{
return d->real->name();
}
PsiIcon *AlertIcon::copy() const
{
return new AlertIcon(d->real);
}
//----------------------------------------------------------------------------
void alertIconUpdateAlertStyle()
{
if ( !metaAlertIcon )
metaAlertIcon = new MetaAlertIcon();
metaAlertIcon->updateAlertStyle();
}
#include "alerticon.moc"

63
src/alerticon.h Normal file
View file

@ -0,0 +1,63 @@
/*
* alerticon.h - class for handling animating alert icons
* Copyright (C) 2003 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef ALERTICON_H
#define ALERTICON_H
#include "iconset.h"
class QPixmap;
class Impix;
class QString;
class QIcon;
class QImage;
class AlertIcon : public PsiIcon
{
Q_OBJECT
public:
AlertIcon(const PsiIcon *icon);
~AlertIcon();
// reimplemented
virtual bool isAnimated() const;
virtual const QPixmap &pixmap() const;
virtual const QImage &image() const;
virtual const QIcon & icon() const;
virtual const Impix &impix() const;
virtual int frameNumber() const;
virtual const QString &name() const;
virtual PsiIcon *copy() const;
public slots:
void activated(bool playSound = true);
void stop();
public:
class Private;
private:
Private *d;
friend class Private;
};
void alertIconUpdateAlertStyle();
#endif

216
src/applicationinfo.cpp Normal file
View file

@ -0,0 +1,216 @@
#include <QString>
#include <QDir>
#include <QFile>
#include <QSettings>
#include <QCoreApplication>
#if defined(Q_WS_X11) || defined(Q_WS_MAC)
#include <sys/stat.h> // chmod
#endif
#ifdef Q_WS_WIN
#if __GNUC__ >= 3
# define WINVER 0x0500
# define _WIN32_IE 0x0500
#endif
#include <windows.h>
#include <shlobj.h>
#endif
#ifdef Q_WS_MAC
#include <CoreServices/CoreServices.h>
#endif
#include "applicationinfo.h"
#include "profiles.h"
#ifdef HAVE_CONFIG
#include "config.h"
#endif
#include "yapsi_revision.h"
// Constants. These should be moved to a more 'dynamically changeable'
// place (like an external file loaded through the resources system)
// Should also be overridable through an optional file.
#define PROG_NAME QString::fromUtf8("Я.Онлайн")
#define PROG_CAPS_NODE "http://online.yandex.ru/caps";
#define PROG_IPC_NAME "ru.yandex.online"
#define PROG_OPTIONS_NS "http://online.yandex.ru/options";
#define PROG_STORAGE_NS "http://online.yandex.ru/storage";
#if defined(Q_WS_X11) && !defined(PSI_DATADIR)
#define PSI_DATADIR "/usr/local/share/psi"
#endif
QString ApplicationInfo::name()
{
return PROG_NAME;
}
QString ApplicationInfo::version()
{
return YAPSI_VERSION + "." + QString::number(YAPSI_REVISION);
}
QString ApplicationInfo::capsNode()
{
return PROG_CAPS_NODE;
}
QString ApplicationInfo::capsVersion()
{
return version();
}
QString ApplicationInfo::IPCName()
{
return PROG_IPC_NAME;
}
QString ApplicationInfo::optionsNS()
{
return PROG_OPTIONS_NS;
}
QString ApplicationInfo::storageNS()
{
return PROG_STORAGE_NS;
}
QString ApplicationInfo::resourcesDir()
{
#if defined(Q_WS_X11)
return PSI_DATADIR;
#elif defined(Q_WS_WIN)
return qApp->applicationDirPath();
#elif defined(Q_WS_MAC)
return QCoreApplication::instance()->applicationDirPath() + "/../Resources";
#endif
}
/** \brief return psi's private read write data directory
* unix+mac: $HOME/.psi
* environment variable "PSIDATADIR" overrides
*/
QString ApplicationInfo::homeDir()
{
// Try the environment override first
char *p = getenv("YACHATDATADIR");
if(p) {
QDir proghome(p);
if (!proghome.exists()) {
QDir d;
d.mkpath(proghome.path());
}
return proghome.path();
}
#if defined(Q_WS_X11) || defined(Q_WS_MAC)
QDir proghome(QDir::homePath() + "/.yachat");
if(!proghome.exists()) {
QDir home = QDir::home();
home.mkdir(".yachat");
chmod(QFile::encodeName(proghome.path()), 0700);
}
return proghome.path();
#elif defined(Q_WS_WIN)
QString base = QDir::homePath();
WCHAR str[MAX_PATH+1] = { 0 };
if (SHGetSpecialFolderPathW(0, str, CSIDL_APPDATA, true))
base = QString::fromWCharArray(str);
QDir proghome(base + "/YaChatData");
if(!proghome.exists()) {
QDir home(base);
home.mkdir("YaChatData");
}
return proghome.path();
#endif
}
QString ApplicationInfo::historyDir()
{
QDir history(pathToProfile(activeProfile) + "/history");
if (!history.exists()) {
QDir profile(pathToProfile(activeProfile));
profile.mkdir("history");
}
return history.path();
}
QString ApplicationInfo::vCardDir()
{
QDir vcard(pathToProfile(activeProfile) + "/vcard");
if (!vcard.exists()) {
QDir profile(pathToProfile(activeProfile));
profile.mkdir("vcard");
}
return vcard.path();
}
QString ApplicationInfo::profilesDir()
{
QString profiles_dir(homeDir() + "/profiles");
QDir d(profiles_dir);
if(!d.exists()) {
QDir d(homeDir());
d.mkdir("profiles");
}
return profiles_dir;
}
QString ApplicationInfo::documentsDir()
{
#ifdef Q_WS_WIN
// http://lists.trolltech.com/qt-interest/2006-10/thread00018-0.html
// TODO: haven't a better way been added to Qt since?
QSettings settings(QSettings::UserScope, "Microsoft", "Windows");
settings.beginGroup("CurrentVersion/Explorer/Shell Folders");
return settings.value("Personal").toString();
#else
return QDir::homePath();
#endif
}
#ifdef YAPSI
QString ApplicationInfo::yavatarsDir()
{
QDir yavatars(pathToProfile(activeProfile) + "/yavatars");
if (!yavatars.exists()) {
QDir profile(pathToProfile(activeProfile));
profile.mkdir("yavatars");
}
return yavatars.path();
}
QString ApplicationInfo::yahistoryDir()
{
QDir yahistory(pathToProfile(activeProfile) + "/yahistory");
if (!yahistory.exists()) {
QDir profile(pathToProfile(activeProfile));
profile.mkdir("yahistory");
}
return yahistory.path();
}
#endif
#ifdef HAVE_BREAKPAD
QString ApplicationInfo::crashReporterDir()
{
QString crashReporterDir(homeDir() + "/crash_reporter");
QDir d(crashReporterDir);
if (!d.exists()) {
QDir d(homeDir());
d.mkdir("crash_reporter");
}
return crashReporterDir;
}
#endif

36
src/applicationinfo.h Normal file
View file

@ -0,0 +1,36 @@
#ifndef APPLICATIONINFO_H
#define APPLICATIONINFO_H
class QString;
class ApplicationInfo
{
public:
// Version info
static QString name();
static QString version();
static QString capsNode();
static QString capsVersion();
static QString IPCName();
// Directories
static QString homeDir();
static QString resourcesDir();
static QString profilesDir();
static QString historyDir();
static QString vCardDir();
static QString documentsDir();
#ifdef YAPSI
static QString yavatarsDir();
static QString yahistoryDir();
#endif
#ifdef HAVE_BREAKPAD
static QString crashReporterDir();
#endif
// Namespaces
static QString optionsNS();
static QString storageNS();
};
#endif

747
src/avatars.cpp Normal file
View file

@ -0,0 +1,747 @@
/*
* avatars.cpp
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
/*
* TODO:
* - Be more efficient about storing avatars in memory
* - Move ovotorChanged() to Avatar, and only listen to the active avatars
* being changed.
*/
#include <QDomElement>
#include <QtCrypto>
#include <QPixmap>
#include <QDateTime>
#include <QDir>
#include <QFileInfo>
#include <QFile>
#include <QBuffer>
#include <QPainter>
#include <qca_basic.h>
#include "xmpp_xmlcommon.h"
#include "xmpp_vcard.h"
#include "xmpp_client.h"
#include "xmpp_resource.h"
#include "xmpp_pubsubitem.h"
#include "avatars.h"
#include "applicationinfo.h"
#include "psiaccount.h"
#include "profiles.h"
#include "vcardfactory.h"
#include "pepmanager.h"
#include "pixmaputil.h"
#ifdef YAPSI
#define MAX_AVATAR_SIZE 100
#define MAX_AVATAR_DISPLAY_SIZE 100
#else
#define MAX_AVATAR_SIZE 96
#define MAX_AVATAR_DISPLAY_SIZE 64
#endif
using namespace QCA;
//------------------------------------------------------------------------------
static QByteArray scaleAvatar(const QByteArray& b)
{
//int maxSize = (option.avatarsSize > MAX_AVATAR_SIZE ? MAX_AVATAR_SIZE : option.avatarsSize);
int maxSize = AvatarFactory::maxAvatarSize();
QImage i(b);
if (i.isNull()) {
qWarning("AvatarFactory::scaleAvatar(): Null image (unrecognized format?)");
return QByteArray();
}
else if (i.width() > maxSize || i.height() > maxSize) {
QImage image = i.scaled(QSize(maxSize, maxSize),
Qt::KeepAspectRatio, Qt::SmoothTransformation);
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
image.save(&buffer, "PNG");
return ba;
}
else {
return b;
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Avatar: Base class for avatars.
//------------------------------------------------------------------------------
Avatar::Avatar(AvatarFactory* factory)
: QObject(factory), factory_(factory)
{
}
Avatar::~Avatar()
{
}
void Avatar::setImage(const QImage& i)
{
if (i.width() > MAX_AVATAR_DISPLAY_SIZE || i.height() > MAX_AVATAR_DISPLAY_SIZE) {
pixmap_.convertFromImage(i.scaled(QSize(MAX_AVATAR_DISPLAY_SIZE, MAX_AVATAR_DISPLAY_SIZE),
Qt::KeepAspectRatio, Qt::SmoothTransformation));
}
else {
pixmap_.convertFromImage(i);
}
}
void Avatar::setImage(const QByteArray& ba)
{
setImage(QImage(ba));
}
void Avatar::setImage(const QPixmap& p)
{
if (p.width() > MAX_AVATAR_DISPLAY_SIZE || p.height() > MAX_AVATAR_DISPLAY_SIZE) {
pixmap_ = p.scaled(QSize(MAX_AVATAR_DISPLAY_SIZE, MAX_AVATAR_DISPLAY_SIZE),
Qt::KeepAspectRatio, Qt::SmoothTransformation);
}
else {
pixmap_ = p;
}
}
void Avatar::resetImage()
{
pixmap_ = QPixmap();
}
AvatarFactory* Avatar::factory() const
{
return factory_;
}
//------------------------------------------------------------------------------
// CachedAvatar: Base class for avatars which are requested and are to be cached
//------------------------------------------------------------------------------
class CachedAvatar : public Avatar
{
public:
CachedAvatar(AvatarFactory* factory)
: Avatar(factory)
{ };
virtual void updateHash(const QString& h);
QString cachedFileName() const;
protected:
virtual const QString& hash() const { return hash_; }
virtual void requestAvatar() { }
virtual void avatarUpdated() { }
virtual bool isCached(const QString& hash);
virtual void loadFromCache(const QString& hash);
virtual void saveToCache(const QByteArray& data);
private:
QString hash_;
};
void CachedAvatar::updateHash(const QString& h)
{
if (hash_ != h) {
hash_ = h;
if (h.isEmpty()) {
hash_ = "";
resetImage();
avatarUpdated();
}
else if (isCached(h)) {
loadFromCache(h);
avatarUpdated();
}
else {
resetImage();
avatarUpdated();
requestAvatar();
}
}
}
bool CachedAvatar::isCached(const QString& h)
{
return QDir(AvatarFactory::getCacheDir()).exists(h);
}
void CachedAvatar::loadFromCache(const QString& h)
{
// printf("Loading avatar from cache\n");
hash_ = h;
setImage(QImage(QDir(AvatarFactory::getCacheDir()).filePath(h)));
if (pixmap().isNull()) {
qWarning("CachedAvatar::loadFromCache(): Null pixmap. Unsupported format ?");
}
}
QString CachedAvatar::cachedFileName() const
{
return QDir(AvatarFactory::getCacheDir()).filePath(hash_);
}
void CachedAvatar::saveToCache(const QByteArray& data)
{
QString hash = QCA::Hash("sha1").hashToString(data);
hash_ = hash;
// printf("Saving %s to cache.\n",hash.latin1());
QFile f(cachedFileName());
if (f.open(IO_WriteOnly)) {
f.writeBlock(data);
f.close();
}
else {
printf("Error opening %s for writing.\n",f.name().latin1());
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// PEPAvatar: PEP Avatars
//------------------------------------------------------------------------------
class PEPAvatar : public CachedAvatar
{
Q_OBJECT
public:
PEPAvatar(AvatarFactory* factory, const Jid& jid)
: CachedAvatar(factory), jid_(jid)
{ };
void setData(const QString& h, const QString& data) {
if (h == hash()) {
QByteArray ba = Base64().stringToArray(data).toByteArray();
if (!ba.isEmpty()) {
saveToCache(ba);
setImage(ba);
if (pixmap().isNull()) {
qWarning("PEPAvatar::setData(): Null pixmap. Unsupported format ?");
}
emit avatarChanged(jid_);
}
else
qWarning("PEPAvatar::setData(): Received data is empty. Bad encoding ?");
}
}
signals:
void avatarChanged(const Jid&);
protected:
void requestAvatar() {
factory()->account()->pepManager()->get(jid_,"http://www.xmpp.org/extensions/xep-0084.html#ns-data",hash());
}
void avatarUpdated()
{ emit avatarChanged(jid_); }
private:
Jid jid_;
};
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// VCardAvatar: Avatars coming from VCards of contacts.
//------------------------------------------------------------------------------
class VCardAvatar : public CachedAvatar
{
Q_OBJECT
public:
VCardAvatar(AvatarFactory* factory, const Jid& jid);
signals:
void avatarChanged(const Jid&);
public slots:
void receivedVCard();
protected:
void requestAvatar();
void avatarUpdated()
{ emit avatarChanged(jid_); }
private:
Jid jid_;
};
VCardAvatar::VCardAvatar(AvatarFactory* factory, const Jid& jid)
: CachedAvatar(factory), jid_(jid)
{
}
void VCardAvatar::requestAvatar()
{
VCardFactory::instance()->getVCard(jid_.bare(), factory()->account()->client()->rootTask(), this, SLOT(receivedVCard()));
}
void VCardAvatar::receivedVCard()
{
const VCard* vcard = VCardFactory::instance()->vcard(jid_);
if (vcard) {
saveToCache(vcard->photo());
setImage(vcard->photo());
emit avatarChanged(jid_);
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// VCardStaticAvatar: VCard static photo avatar (not published through presence)
//------------------------------------------------------------------------------
class VCardStaticAvatar : public CachedAvatar
{
Q_OBJECT
public:
VCardStaticAvatar(AvatarFactory* factory, const Jid& j);
public slots:
void vcardChanged(const Jid&);
signals:
void avatarChanged(const Jid&);
private:
Jid jid_;
void setVCard(const VCard* vcard);
};
VCardStaticAvatar::VCardStaticAvatar(AvatarFactory* factory, const Jid& j)
: CachedAvatar(factory), jid_(j.bare())
{
const VCard* vcard = VCardFactory::instance()->vcard(jid_);
setVCard(vcard);
connect(VCardFactory::instance(),SIGNAL(vcardChanged(const Jid&)),SLOT(vcardChanged(const Jid&)));
}
void VCardStaticAvatar::vcardChanged(const Jid& j)
{
if (j.compare(jid_,false)) {
const VCard* vcard = VCardFactory::instance()->vcard(jid_);
setVCard(vcard);
emit avatarChanged(jid_);
}
}
void VCardStaticAvatar::setVCard(const VCard* vcard)
{
if (vcard && !vcard->photo().isEmpty()) {
// TODO: make CachedAvatar automatically call saveToCache in setImage
saveToCache(vcard->photo());
setImage(vcard->photo());
}
else {
resetImage();
}
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// FileAvatar: Avatars coming from local files.
//------------------------------------------------------------------------------
class FileAvatar : public Avatar
{
public:
FileAvatar(AvatarFactory* factory, const Jid& jid);
void import(const QString& file);
void removeFromDisk();
bool exists();
QPixmap getPixmap();
const Jid& getJid() const
{ return jid_; }
protected:
bool isDirty() const;
QString getFileName() const;
void refresh();
QDateTime lastModified() const
{ return lastModified_; }
private:
QDateTime lastModified_;
Jid jid_;
};
FileAvatar::FileAvatar(AvatarFactory* factory, const Jid& jid)
: Avatar(factory), jid_(jid)
{
}
void FileAvatar::import(const QString& file)
{
if (QFileInfo(file).exists()) {
QFile source_file(file);
QFile target_file(getFileName());
if (source_file.open(QIODevice::ReadOnly) && target_file.open(QIODevice::WriteOnly)) {
QByteArray ba = source_file.readAll();
QByteArray data = scaleAvatar(ba);
target_file.write(data);
}
}
}
void FileAvatar::removeFromDisk()
{
QFile f(getFileName());
f.remove();
}
bool FileAvatar::exists()
{
return QFileInfo(getFileName()).exists();
}
QPixmap FileAvatar::getPixmap()
{
refresh();
return pixmap();
}
void FileAvatar::refresh()
{
if (isDirty()) {
if (QFileInfo(getFileName()).exists()) {
QImage img(getFileName());
setImage(QImage(getFileName()));
}
else
resetImage();
}
}
QString FileAvatar::getFileName() const
{
QString f = getJid().bare();
f.replace('@',"_at_");
return QDir(AvatarFactory::getManualDir()).filePath(f);
}
bool FileAvatar::isDirty() const
{
return (pixmap().isNull()
|| !QFileInfo(getFileName()).exists()
|| QFileInfo(getFileName()).lastModified() > lastModified());
}
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// Avatar factory
//------------------------------------------------------------------------------
AvatarFactory::AvatarFactory(PsiAccount* pa) : pa_(pa)
{
// Register iconset
iconset_.addToFactory();
// Connect signals
connect(VCardFactory::instance(),SIGNAL(vcardChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&)));
connect(pa_->client(), SIGNAL(resourceAvailable(const Jid &, const Resource &)), SLOT(resourceAvailable(const Jid &, const Resource &)));
// PEP
connect(pa_->pepManager(),SIGNAL(itemPublished(const Jid&, const QString&, const PubSubItem&)),SLOT(itemPublished(const Jid&, const QString&, const PubSubItem&)));
connect(pa_->pepManager(),SIGNAL(publish_success(const QString&, const PubSubItem&)),SLOT(publish_success(const QString&,const PubSubItem&)));
}
PsiAccount* AvatarFactory::account() const
{
return pa_;
}
inline static QPixmap ensureSquareAvatar(const QPixmap& original)
{
if (original.isNull() || original.width() == original.height())
return original;
int size = qMax(original.width(), original.height());
QPixmap square = PixmapUtil::createTransparentPixmap(size, size);
QPainter p(&square);
p.drawPixmap((size - original.width()) / 2, (size - original.height()) / 2, original);
return square;
}
Avatar* AvatarFactory::getAvatarHelper(const Jid& jid)
{
// NOTE: ensure that 'jid' is safe from being deleted
// in the process of calling 'avatarChanged()' signal
// Compute the avatar of the user
Avatar* av = retrieveAvatar(jid);
// If the avatar changed since the previous request, notify everybody of this
if (av != active_avatars_[jid.full()]) {
active_avatars_[jid.full()] = av;
active_avatars_[jid.bare()] = av;
emit avatarChanged(jid);
}
return av;
}
QPixmap AvatarFactory::getAvatar(XMPP::Jid jid)
{
Avatar* av = getAvatarHelper(jid);
QPixmap pm = (av ? av->getPixmap() : QPixmap());
pm = ensureSquareAvatar(pm);
#ifndef YAPSI
// Update iconset
PsiIcon icon;
icon.setImpix(pm);
iconset_.setIcon(QString("avatars/%1").arg(jid.bare()), icon);
#endif
return pm;
}
QString AvatarFactory::getCachedAvatarFileName(XMPP::Jid jid)
{
Avatar* av = retrieveAvatar(jid);
CachedAvatar* cachedAvatar = dynamic_cast<CachedAvatar*>(av);
if (cachedAvatar) {
return cachedAvatar->cachedFileName();
}
return QString();
}
Avatar* AvatarFactory::retrieveAvatar(const Jid& jid)
{
//printf("Retrieving avatar of %s\n", jid.full().latin1());
// Try finding a file avatar.
//printf("File avatar\n");
if (!file_avatars_.contains(jid.bare())) {
//printf("File avatar not yet loaded\n");
file_avatars_[jid.bare()] = new FileAvatar(this, jid);
}
//printf("Trying file avatar\n");
if (!file_avatars_[jid.bare()]->isEmpty())
return file_avatars_[jid.bare()];
//printf("PEP avatar\n");
if (pep_avatars_.contains(jid.bare()) && !pep_avatars_[jid.bare()]->isEmpty()) {
return pep_avatars_[jid.bare()];
}
// Try finding a vcard avatar
//printf("VCard avatar\n");
if (vcard_avatars_.contains(jid.bare()) && !vcard_avatars_[jid.bare()]->isEmpty()) {
return vcard_avatars_[jid.bare()];
}
// Try finding a static vcard avatar
//printf("Static VCard avatar\n");
if (!vcard_static_avatars_.contains(jid.bare())) {
//printf("Static vcard avatar not yet loaded\n");
vcard_static_avatars_[jid.bare()] = new VCardStaticAvatar(this, jid);
connect(vcard_static_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&)));
}
if (!vcard_static_avatars_[jid.bare()]->isEmpty()) {
return vcard_static_avatars_[jid.bare()];
}
return 0;
}
void AvatarFactory::setSelfAvatar(const QString& fileName)
{
if (!fileName.isEmpty()) {
QFile avatar_file(fileName);
if (!avatar_file.open(QIODevice::ReadOnly))
return;
QByteArray avatar_data = scaleAvatar(avatar_file.readAll());
QImage avatar_image(avatar_data);
if(!avatar_image.isNull()) {
// Publish data
QDomDocument* doc = account()->client()->doc();
QString hash = Hash("sha1").hashToString(avatar_data);
QDomElement el = doc->createElement("data");
el.setAttribute("xmlns","http://www.xmpp.org/extensions/xep-0084.html#ns-data ");
el.appendChild(doc->createTextNode(Base64().arrayToString(avatar_data)));
selfAvatarData_ = avatar_data;
selfAvatarHash_ = hash;
account()->pepManager()->publish("http://www.xmpp.org/extensions/xep-0084.html#ns-data",PubSubItem(hash,el));
}
}
else {
QDomDocument* doc = account()->client()->doc();
QDomElement meta_el = doc->createElement("metadata");
meta_el.setAttribute("xmlns","http://www.xmpp.org/extensions/xep-0084.html#ns-metadata");
meta_el.appendChild(doc->createElement("stop"));
account()->pepManager()->publish("http://www.xmpp.org/extensions/xep-0084.html#ns-metadata",PubSubItem("current",meta_el));
}
}
void AvatarFactory::updateAvatar(const Jid& j)
{
getAvatar(j);
// FIXME: This signal might be emitted twice (first time from getAvatar()).
emit avatarChanged(j);
}
void AvatarFactory::importManualAvatar(const Jid& j, const QString& fileName)
{
FileAvatar(this, j).import(fileName);
emit avatarChanged(j);
}
void AvatarFactory::removeManualAvatar(const Jid& j)
{
FileAvatar(this, j).removeFromDisk();
// TODO: Remove from caches. Maybe create a clearManualAvatar() which
// removes the file but doesn't remove the avatar from caches (since it'll
// be created again whenever the FileAvatar is requested)
emit avatarChanged(j);
}
bool AvatarFactory::hasManualAvatar(const Jid& j)
{
return FileAvatar(this, j).exists();
}
void AvatarFactory::resourceAvailable(const Jid& jid, const Resource& r)
{
if (r.status().hasPhotoHash()) {
QString hash = r.status().photoHash();
if (!vcard_avatars_.contains(jid.bare())) {
vcard_avatars_[jid.bare()] = new VCardAvatar(this, jid);
connect(vcard_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this,SLOT(updateAvatar(const Jid&)));
}
vcard_avatars_[jid.bare()]->updateHash(hash);
}
}
QString AvatarFactory::getManualDir()
{
QDir avatars(pathToProfile(activeProfile) + "/pictures");
if (!avatars.exists()) {
QDir profile(pathToProfile(activeProfile));
profile.mkdir("pictures");
}
return avatars.path();
}
QString AvatarFactory::getCacheDir()
{
QDir avatars(ApplicationInfo::homeDir() + "/avatars");
if (!avatars.exists()) {
QDir home(ApplicationInfo::homeDir());
home.mkdir("avatars");
}
return avatars.path();
}
int AvatarFactory::maxAvatarSize()
{
return MAX_AVATAR_SIZE;
}
void AvatarFactory::itemPublished(const Jid& jid, const QString& n, const PubSubItem& item)
{
if (n == "http://www.xmpp.org/extensions/xep-0084.html#ns-data") {
if (item.payload().tagName() == "data") {
if (pep_avatars_.contains(jid.bare())) {
pep_avatars_[jid.bare()]->setData(item.id(),item.payload().text());
}
}
else {
qWarning("avatars.cpp: Unexpected item payload");
}
}
else if (n == "http://www.xmpp.org/extensions/xep-0084.html#ns-metadata") {
if (!pep_avatars_.contains(jid.bare())) {
pep_avatars_[jid.bare()] = new PEPAvatar(this, jid.bare());
connect(pep_avatars_[jid.bare()],SIGNAL(avatarChanged(const Jid&)),this, SLOT(updateAvatar(const Jid&)));
}
QDomElement e;
bool found;
e = findSubTag(item.payload(), "stop", &found);
if (found) {
pep_avatars_[jid.bare()]->updateHash("");
}
else {
pep_avatars_[jid.bare()]->updateHash(item.id());
}
}
}
void AvatarFactory::publish_success(const QString& n, const PubSubItem& item)
{
if (n == "http://www.xmpp.org/extensions/xep-0084.html#ns-data" && item.id() == selfAvatarHash_) {
// Publish metadata
QDomDocument* doc = account()->client()->doc();
QImage avatar_image(selfAvatarData_);
QDomElement meta_el = doc->createElement("metadata");
meta_el.setAttribute("xmlns","http://www.xmpp.org/extensions/xep-0084.html#ns-metadata");
QDomElement info_el = doc->createElement("info");
info_el.setAttribute("id",selfAvatarHash_);
info_el.setAttribute("bytes",avatar_image.numBytes());
info_el.setAttribute("height",avatar_image.height());
info_el.setAttribute("width",avatar_image.width());
info_el.setAttribute("type",image2type(selfAvatarData_));
meta_el.appendChild(info_el);
account()->pepManager()->publish("http://www.xmpp.org/extensions/xep-0084.html#ns-metadata",PubSubItem(selfAvatarHash_,meta_el));
}
}
void AvatarFactory::clearCache()
{
foreach(Avatar* a, findChildren<Avatar*>()) {
delete a;
}
active_avatars_.clear();
pep_avatars_.clear();
file_avatars_.clear();
vcard_avatars_.clear();
vcard_static_avatars_.clear();
}
//------------------------------------------------------------------------------
#include "avatars.moc"

128
src/avatars.h Normal file
View file

@ -0,0 +1,128 @@
/*
* avatars.h
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef AVATARS_H
#define AVATARS_H
#include <QPixmap>
#include <QMap>
#include <QByteArray>
#include <QString>
#include "iconset.h"
class PsiAccount;
class Avatar;
class VCardAvatar;
class VCardStaticAvatar;
class FileAvatar;
class PEPAvatar;
namespace XMPP {
class Jid;
class Resource;
class PubSubItem;
}
using namespace XMPP;
//------------------------------------------------------------------------------
class AvatarFactory : public QObject
{
Q_OBJECT
public:
AvatarFactory(PsiAccount* pa);
QPixmap getAvatar(XMPP::Jid jid);
QString getCachedAvatarFileName(XMPP::Jid jid);
PsiAccount* account() const;
void setSelfAvatar(const QString& fileName);
void importManualAvatar(const Jid& j, const QString& fileName);
void removeManualAvatar(const Jid& j);
bool hasManualAvatar(const Jid& j);
static QString getManualDir();
static QString getCacheDir();
static int maxAvatarSize();
void clearCache();
signals:
void avatarChanged(const Jid&);
public slots:
void updateAvatar(const Jid&);
protected slots:
void itemPublished(const Jid&, const QString&, const PubSubItem&);
void publish_success(const QString&, const PubSubItem&);
void resourceAvailable(const Jid&, const Resource&);
protected:
Avatar* retrieveAvatar(const Jid& jid);
Avatar* getAvatarHelper(const Jid& jid);
private:
QByteArray selfAvatarData_;
QString selfAvatarHash_;
QMap<QString,Avatar*> active_avatars_;
QMap<QString,PEPAvatar*> pep_avatars_;
QMap<QString,FileAvatar*> file_avatars_;
QMap<QString,VCardAvatar*> vcard_avatars_;
QMap<QString,VCardStaticAvatar*> vcard_static_avatars_;
PsiAccount* pa_;
Iconset iconset_;
};
//------------------------------------------------------------------------------
class Avatar : public QObject
{
Q_OBJECT
public:
Avatar(AvatarFactory* factory);
virtual ~Avatar();
virtual QPixmap getPixmap()
{ return pixmap(); }
virtual bool isEmpty()
{ return getPixmap().isNull(); }
protected:
AvatarFactory* factory() const;
virtual const QPixmap& pixmap() const
{ return pixmap_; }
virtual void setImage(const QImage&);
virtual void setImage(const QByteArray&);
virtual void setImage(const QPixmap&);
virtual void resetImage();
private:
QPixmap pixmap_;
AvatarFactory* factory_;
};
#endif

165
src/bookmarkmanage.ui Normal file
View file

@ -0,0 +1,165 @@
<ui version="4.0" >
<class>BookmarkManage</class>
<widget class="QDialog" name="BookmarkManage" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>539</width>
<height>257</height>
</rect>
</property>
<property name="windowTitle" >
<string>Manage Bookmarks</string>
</property>
<layout class="QGridLayout" >
<item row="0" column="0" >
<layout class="QVBoxLayout" >
<item>
<widget class="QListView" name="listView" >
<property name="dragDropMode" >
<enum>QAbstractItemView::InternalMove</enum>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="bookmarkButtonBox" >
<property name="standardButtons" >
<set>QDialogButtonBox::NoButton</set>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="1" >
<layout class="QGridLayout" >
<item row="0" column="0" >
<widget class="QLabel" name="label" >
<property name="text" >
<string>Host:</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLineEdit" name="host" />
</item>
<item row="1" column="0" >
<widget class="QLabel" name="label_2" >
<property name="text" >
<string>Room:</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLineEdit" name="room" />
</item>
<item row="2" column="0" >
<widget class="QLabel" name="label_3" >
<property name="text" >
<string>Nickname:</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="2" column="1" >
<widget class="QLineEdit" name="nickname" />
</item>
<item row="3" column="0" >
<widget class="QLabel" name="label_4" >
<property name="text" >
<string>Password:</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1" >
<widget class="QLineEdit" name="password" >
<property name="echoMode" >
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="4" column="1" >
<widget class="QCheckBox" name="autoJoin" >
<property name="text" >
<string>Auto-join</string>
</property>
</widget>
</item>
<item row="5" column="1" >
<spacer>
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" >
<size>
<width>66</width>
<height>31</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="1" column="0" colspan="2" >
<widget class="Line" name="line" >
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2" >
<widget class="QDialogButtonBox" name="buttonBox" >
<property name="standardButtons" >
<set>QDialogButtonBox::Cancel|QDialogButtonBox::NoButton|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>BookmarkManage</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel" >
<x>475</x>
<y>252</y>
</hint>
<hint type="destinationlabel" >
<x>454</x>
<y>259</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>BookmarkManage</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel" >
<x>384</x>
<y>252</y>
</hint>
<hint type="destinationlabel" >
<x>352</x>
<y>259</y>
</hint>
</hints>
</connection>
</connections>
</ui>

212
src/bookmarkmanagedlg.cpp Normal file
View file

@ -0,0 +1,212 @@
/*
* bookmarkmanagedlg.cpp - manage groupchat room bookmarks
* Copyright (C) 2008 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "bookmarkmanagedlg.h"
#include <QStandardItemModel>
#include <QPushButton>
#include <QAction>
#include "bookmarkmanager.h"
#include "psiaccount.h"
BookmarkManageDlg::BookmarkManageDlg(PsiAccount* account)
: QDialog()
, account_(account)
, model_(0)
{
setAttribute(Qt::WA_DeleteOnClose, true);
ui_.setupUi(this);
account_->dialogRegister(this);
QAction* removeBookmarkAction = new QAction(this);
connect(removeBookmarkAction, SIGNAL(triggered()), SLOT(removeBookmark()));
removeBookmarkAction->setShortcuts(QKeySequence::Delete);
ui_.listView->addAction(removeBookmarkAction);
addButton_ = ui_.bookmarkButtonBox->addButton(tr("&Add"), QDialogButtonBox::ActionRole);
removeButton_ = ui_.bookmarkButtonBox->addButton(tr("&Remove"), QDialogButtonBox::DestructiveRole);
joinButton_ = ui_.bookmarkButtonBox->addButton(tr("&Join"), QDialogButtonBox::ActionRole);
connect(addButton_, SIGNAL(clicked()), SLOT(addBookmark()));
connect(removeButton_, SIGNAL(clicked()), SLOT(removeBookmark()));
connect(joinButton_, SIGNAL(clicked()), SLOT(joinCurrentRoom()));
model_ = new QStandardItemModel(this);
ui_.listView->setModel(model_);
connect(ui_.listView->itemDelegate(), SIGNAL(closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)), SLOT(closeEditor(QWidget*, QAbstractItemDelegate::EndEditHint)));
connect(ui_.listView->selectionModel(), SIGNAL(selectionChanged(const QItemSelection&, const QItemSelection&)), SLOT(selectionChanged(const QItemSelection&, const QItemSelection&)));
connect(ui_.host, SIGNAL(textEdited(const QString&)), SLOT(updateCurrentItem()));
connect(ui_.room, SIGNAL(textEdited(const QString&)), SLOT(updateCurrentItem()));
connect(ui_.nickname, SIGNAL(textEdited(const QString&)), SLOT(updateCurrentItem()));
connect(ui_.password, SIGNAL(textEdited(const QString&)), SLOT(updateCurrentItem()));
connect(ui_.autoJoin, SIGNAL(clicked(bool)), SLOT(updateCurrentItem()));
connect(account_->bookmarkManager(), SIGNAL(conferencesChanged(const QList<ConferenceBookmark>&)), SLOT(loadBookmarks()));
loadBookmarks();
QItemSelection dummy;
selectionChanged(dummy, dummy);
}
BookmarkManageDlg::~BookmarkManageDlg()
{
account_->dialogUnregister(this);
}
void BookmarkManageDlg::reject()
{
QDialog::reject();
}
void BookmarkManageDlg::accept()
{
if (account_->checkConnected(this)) {
saveBookmarks();
QDialog::accept();
}
}
void BookmarkManageDlg::loadBookmarks()
{
model_->clear();
foreach(ConferenceBookmark c, account_->bookmarkManager()->conferences()) {
QStandardItem* item = new QStandardItem(c.name());
item->setData(QVariant(c.jid().full()), JidRole);
item->setData(QVariant(c.autoJoin()), AutoJoinRole);
item->setData(QVariant(c.nick()), NickRole);
item->setData(QVariant(c.password()), PasswordRole);
appendItem(item);
}
}
ConferenceBookmark BookmarkManageDlg::bookmarkFor(const QModelIndex& index) const
{
return ConferenceBookmark(index.data(Qt::DisplayRole).toString(),
index.data(JidRole).toString(),
index.data(AutoJoinRole).toBool(),
index.data(NickRole).toString(),
index.data(PasswordRole).toString());
}
void BookmarkManageDlg::saveBookmarks()
{
QList<ConferenceBookmark> conferences;
for (int row = 0; row < model_->rowCount(); ++row) {
QModelIndex index = model_->index(row, 0, QModelIndex());
conferences += bookmarkFor(index);
}
account_->bookmarkManager()->setBookmarks(conferences);
}
void BookmarkManageDlg::addBookmark()
{
QStandardItem* item = new QStandardItem(tr("Unnamed"));
appendItem(item);
ui_.listView->reset(); // ensure that open editors won't get in our way
ui_.listView->setCurrentIndex(item->index());
ui_.listView->edit(item->index());
}
void BookmarkManageDlg::removeBookmark()
{
model_->removeRow(currentIndex().row());
}
void BookmarkManageDlg::closeEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint)
{
Q_UNUSED(editor);
if (hint == QAbstractItemDelegate::SubmitModelCache) {
QList<QLineEdit*> lineEdits;
lineEdits << ui_.host << ui_.room << ui_.nickname;
foreach(QLineEdit* lineEdit, lineEdits) {
if (lineEdit->text().isEmpty()) {
lineEdit->setFocus();
break;
}
}
}
}
void BookmarkManageDlg::selectionChanged(const QItemSelection& selected, const QItemSelection& deselected)
{
Q_UNUSED(deselected);
QModelIndex current;
if (!selected.indexes().isEmpty())
current = selected.indexes().first();
XMPP::Jid jid = XMPP::Jid(current.data(JidRole).toString());
ui_.host->setText(jid.domain());
ui_.room->setText(jid.node());
ui_.nickname->setText(current.data(NickRole).toString());
ui_.password->setText(current.data(PasswordRole).toString());
ui_.autoJoin->setChecked(current.data(AutoJoinRole).toBool());
QList<QWidget*> editors;
editors << ui_.host << ui_.room << ui_.nickname << ui_.password << ui_.autoJoin;
foreach(QWidget* w, editors) {
w->setEnabled(current.isValid());
}
removeButton_->setEnabled(current.isValid());
updateCurrentItem();
}
XMPP::Jid BookmarkManageDlg::jid() const
{
XMPP::Jid j;
j.set(ui_.host->text(), ui_.room->text());
return j;
}
void BookmarkManageDlg::updateCurrentItem()
{
joinButton_->setEnabled(!jid().domain().isEmpty() && !jid().node().isEmpty() && !ui_.nickname->text().isEmpty());
QStandardItem* item = model_->item(currentIndex().row());
if (item) {
item->setData(QVariant(jid().full()), JidRole);
item->setData(QVariant(ui_.autoJoin->isChecked()), AutoJoinRole);
item->setData(QVariant(ui_.nickname->text()), NickRole);
item->setData(QVariant(ui_.password->text()), PasswordRole);
}
}
QModelIndex BookmarkManageDlg::currentIndex() const
{
if (!ui_.listView->selectionModel()->selectedIndexes().isEmpty())
return ui_.listView->selectionModel()->selectedIndexes().first();
return QModelIndex();
}
void BookmarkManageDlg::joinCurrentRoom()
{
account_->actionJoin(bookmarkFor(currentIndex()), true);
}
void BookmarkManageDlg::appendItem(QStandardItem* item)
{
item->setDragEnabled(true);
item->setDropEnabled(false);
model_->invisibleRootItem()->appendRow(item);
}

84
src/bookmarkmanagedlg.h Normal file
View file

@ -0,0 +1,84 @@
/*
* bookmarkmanagedlg.h - manage groupchat room bookmarks
* Copyright (C) 2008 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef BOOKMARKMANAGEDLG_H
#define BOOKMARKMANAGEDLG_H
#include <QDialog>
#include "ui_bookmarkmanage.h"
class PsiAccount;
class QPushButton;
class QStandardItemModel;
class QStandardItem;
class ConferenceBookmark;
#include "xmpp_jid.h"
class BookmarkManageDlg : public QDialog
{
Q_OBJECT
public:
BookmarkManageDlg(PsiAccount* account);
~BookmarkManageDlg();
public slots:
// reimplemented
void reject();
void accept();
private:
enum Role {
// DisplayRole / EditRole
JidRole = Qt::UserRole + 0,
AutoJoinRole = Qt::UserRole + 1,
NickRole = Qt::UserRole + 2,
PasswordRole = Qt::UserRole + 3
};
void appendItem(QStandardItem* item);
XMPP::Jid jid() const;
QModelIndex currentIndex() const;
private slots:
void loadBookmarks();
void saveBookmarks();
private slots:
void addBookmark();
void removeBookmark();
void updateCurrentItem();
void joinCurrentRoom();
void closeEditor(QWidget* editor, QAbstractItemDelegate::EndEditHint hint);
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected);
private:
Ui::BookmarkManage ui_;
PsiAccount* account_;
QStandardItemModel* model_;
QPushButton* addButton_;
QPushButton* removeButton_;
QPushButton* joinButton_;
ConferenceBookmark bookmarkFor(const QModelIndex& index) const;
};
#endif

225
src/bookmarkmanager.cpp Normal file
View file

@ -0,0 +1,225 @@
/*
* bookmarkmanager.cpp
* Copyright (C) 2006-2008 Remko Troncon, Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "bookmarkmanager.h"
#include "xmpp_task.h"
#include "xmpp_client.h"
#include "xmpp_xmlcommon.h"
#include "psiaccount.h"
// -----------------------------------------------------------------------------
class BookmarkTask : public Task
{
public:
BookmarkTask(Task* parent) : Task(parent) {
}
void set(const QList<URLBookmark>& urls, const QList<ConferenceBookmark>& conferences) {
iq_ = createIQ(doc(), "set", "", id());
QDomElement prvt = doc()->createElement("query");
prvt.setAttribute("xmlns", "jabber:iq:private");
iq_.appendChild(prvt);
QDomElement storage = doc()->createElement("storage");
storage.setAttribute("xmlns", "storage:bookmarks");
prvt.appendChild(storage);
foreach(URLBookmark u, urls)
storage.appendChild(u.toXml(*doc()));
foreach(ConferenceBookmark c, conferences)
storage.appendChild(c.toXml(*doc()));
}
void get() {
iq_ = createIQ(doc(), "get", "", id());
QDomElement prvt = doc()->createElement("query");
prvt.setAttribute("xmlns", "jabber:iq:private");
iq_.appendChild(prvt);
QDomElement bookmarks = doc()->createElement("storage");
bookmarks.setAttribute("xmlns", "storage:bookmarks");
prvt.appendChild(bookmarks);
}
void onGo() {
send(iq_);
}
bool take(const QDomElement& x) {
if(!iqVerify(x, "", id()))
return false;
if(x.attribute("type") == "result") {
QDomElement q = queryTag(x);
for (QDomNode n = q.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement e = n.toElement();
if (!e.isNull() && e.tagName() == "storage" && e.attribute("xmlns") == "storage:bookmarks") {
for (QDomNode m = e.firstChild(); !m.isNull(); m = m.nextSibling()) {
QDomElement f = m.toElement();
if (f.isNull())
continue;
if (f.tagName() == "url") {
URLBookmark u(f);
if (!u.isNull())
urls_ += u;
}
else if (f.tagName() == "conference") {
ConferenceBookmark c(f);
if (!c.isNull())
conferences_ += c;
}
}
}
}
setSuccess();
}
else {
setError(x);
}
return true;
}
const QList<URLBookmark>& urls() const {
return urls_;
}
const QList<ConferenceBookmark>& conferences() const {
return conferences_;
}
private:
QDomElement iq_;
QList<URLBookmark> urls_;
QList<ConferenceBookmark> conferences_;
};
// -----------------------------------------------------------------------------
BookmarkManager::BookmarkManager(PsiAccount* account)
: account_(account)
, accountAvailable_(false)
, isAvailable_(false)
{
connect(account_, SIGNAL(updatedActivity()), SLOT(accountStateChanged()));
}
bool BookmarkManager::isAvailable() const
{
return isAvailable_;
}
void BookmarkManager::setIsAvailable(bool available)
{
if (available != isAvailable_) {
isAvailable_ = available;
emit availabilityChanged();
}
}
QList<URLBookmark> BookmarkManager::urls() const
{
return urls_;
}
QList<ConferenceBookmark> BookmarkManager::conferences() const
{
return conferences_;
}
void BookmarkManager::accountStateChanged()
{
if (!account_->isAvailable()) {
setIsAvailable(false);
}
if (account_->isAvailable() && !accountAvailable_) {
getBookmarks();
}
accountAvailable_ = account_->isAvailable();
}
void BookmarkManager::getBookmarks()
{
BookmarkTask* t = new BookmarkTask(account_->client()->rootTask());
connect(t,SIGNAL(finished()),SLOT(getBookmarks_finished()));
t->get();
t->go(true);
}
void BookmarkManager::setBookmarks(const QList<URLBookmark>& urls, const QList<ConferenceBookmark>& conferences)
{
bool urlsWereChanged = urls_ != urls;
bool conferencesWereChanged = conferences_ != conferences;
urls_ = urls;
conferences_ = conferences;
BookmarkTask* t = new BookmarkTask(account_->client()->rootTask());
connect(t,SIGNAL(finished()),SLOT(setBookmarks_finished()));
t->set(urls,conferences);
t->go(true);
if (urlsWereChanged)
emit urlsChanged(urls_);
if (conferencesWereChanged)
emit conferencesChanged(conferences_);
}
void BookmarkManager::setBookmarks(const QList<URLBookmark>& urls)
{
setBookmarks(urls, conferences());
}
void BookmarkManager::setBookmarks(const QList<ConferenceBookmark>& conferences)
{
setBookmarks(urls(), conferences);
}
void BookmarkManager::getBookmarks_finished()
{
BookmarkTask* t = static_cast<BookmarkTask*>(sender());
if (t->success()) {
bool urlsWereChanged = urls_ != t->urls();
bool conferencesWereChanged = conferences_ != t->conferences();
urls_ = t->urls();
conferences_ = t->conferences();
if (urlsWereChanged)
emit urlsChanged(urls_);
if (conferencesWereChanged)
emit conferencesChanged(conferences_);
setIsAvailable(true);
}
else {
setIsAvailable(false);
}
}
void BookmarkManager::setBookmarks_finished()
{
BookmarkTask* t = static_cast<BookmarkTask*>(sender());
Q_UNUSED(t);
}

70
src/bookmarkmanager.h Normal file
View file

@ -0,0 +1,70 @@
/*
* bookmarkmanager.h
* Copyright (C) 2006-2008 Remko Troncon, Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef BOOKMARKMANAGER_H
#define BOOKMARKMANAGER_H
#include <QObject>
#include <QList>
#include "conferencebookmark.h"
#include "urlbookmark.h"
class PsiAccount;
class BookmarkManager : public QObject
{
Q_OBJECT
public:
BookmarkManager(PsiAccount* account);
bool isAvailable() const;
QList<URLBookmark> urls() const;
QList<ConferenceBookmark> conferences() const;
void setBookmarks(const QList<URLBookmark>&, const QList<ConferenceBookmark>&);
void setBookmarks(const QList<URLBookmark>&);
void setBookmarks(const QList<ConferenceBookmark>&);
signals:
void availabilityChanged();
void urlsChanged(const QList<URLBookmark>&);
void conferencesChanged(const QList<ConferenceBookmark>&);
private slots:
void getBookmarks_finished();
void setBookmarks_finished();
void accountStateChanged();
private:
void getBookmarks();
void setIsAvailable(bool available);
private:
PsiAccount* account_;
bool accountAvailable_;
bool isAvailable_;
QList<URLBookmark> urls_;
QList<ConferenceBookmark> conferences_;
};
#endif

View file

@ -0,0 +1,12 @@
INCLUDEPATH *= $$PWD
DEPENDPATH *= $$PWD
HEADERS += \
$$PWD/capsspec.h \
$$PWD/capsregistry.h \
$$PWD/capsmanager.h
SOURCES += \
$$PWD/capsspec.cpp \
$$PWD/capsregistry.cpp \
$$PWD/capsmanager.cpp

View file

@ -0,0 +1,294 @@
/*
* capsmanager.cpp
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
// TODO:
// - Fallback on another jid if a disco request should fail. This can be
// done by keeping a second list of candidate jids to query
// - Implement Server Optimization support (Section 5).
// - Implement consistency checking (Section 8).
#include <QString>
#include <QStringList>
#include <QTimer>
#include <QPair>
#include <QApplication>
#include <QDebug>
#include "capsregistry.h"
#include "capsmanager.h"
//#define REQUEST_TIMEOUT 3000
using namespace XMPP;
/**
* \class CapsManager
* \brief A class managing all the capabilities of JIDs and their
* clients.
*/
/**
* \brief Default constructor.
*/
CapsManager::CapsManager(const Jid& jid, CapsRegistry* registry, Protocol::DiscoInfoQuerier* discoInfoQuerier) : jid_(jid), registry_(registry), discoInfoQuerier_(discoInfoQuerier), isEnabled_(true)
{
connect(registry_,SIGNAL(registered(const CapsSpec&)),SLOT(capsRegistered(const CapsSpec&)));
connect(discoInfoQuerier_, SIGNAL(getDiscoInfo_success(const XMPP::Jid&, const QString&, const XMPP::DiscoItem&)), SLOT(getDiscoInfo_success(const XMPP::Jid&, const QString&, const XMPP::DiscoItem&)));
connect(discoInfoQuerier_, SIGNAL(getDiscoInfo_error(const XMPP::Jid&, const QString&, int, const QString&)), SLOT(getDiscoInfo_error(const XMPP::Jid&, const QString&, int, const QString&)));
}
CapsManager::~CapsManager()
{
delete discoInfoQuerier_;
}
/**
* \brief Checks whether the caps manager is enabled (and does lookups).
*/
bool CapsManager::isEnabled()
{
return isEnabled_;
}
/**
* \brief Enables or disables the caps manager.
*/
void CapsManager::setEnabled(bool b)
{
isEnabled_ = b;
}
/**
* \brief Registers new incoming capabilities information of a JID.
* If the features of the entity are unknown, discovery requests are sent to
* retrieve the information.
*
* @param jid The entity's JID
* @param node The entity's caps node
* @param ver The entity's caps version
* @param ext The entity's caps extensions
*/
void CapsManager::updateCaps(const Jid& jid, const QString& node, const QString& ver, const QString& ext)
{
if (jid.compare(jid_,false))
return;
CapsSpec c(node,ver,ext);
CapsSpecs caps = c.flatten();
if (capsSpecs_[jid.full()] != c) {
//qDebug() << QString("caps.cpp: Updating caps for %1 (node=%2,ver=%3,ext=%4)").arg(QString(jid.full()).replace('%',"%%")).arg(node).arg(ver).arg(ext);
// Unregister from all old caps nodes
CapsSpecs old_caps = capsSpecs_[jid.full()].flatten();
foreach(CapsSpec s, old_caps) {
if (s != CapsSpec()) {
capsJids_[s].removeAll(jid.full());
}
}
if (!node.isEmpty() && !ver.isEmpty()) {
// Register with all new caps nodes
capsSpecs_[jid.full()] = c;
foreach(CapsSpec s, caps) {
if (!capsJids_[s].contains(jid.full())) {
capsJids_[s].push_back(jid.full());
}
}
emit capsChanged(jid);
// Register new caps and check if we need to discover features
if (isEnabled()) {
foreach(CapsSpec s, caps) {
if (!registry_->isRegistered(s) && capsJids_[s].count() == 1) {
//qDebug() << QString("caps.cpp: Sending disco request to %1, node=%2").arg(QString(jid.full()).replace('%',"%%")).arg(node + "#" + s.extensions());
discoInfoQuerier_->getDiscoInfo(jid,node + "#" + s.extensions());
}
}
}
}
else {
// Remove all caps specifications
qWarning("caps.cpp: Illegal caps info from %s: node=%s, ver=%s",
qPrintable(QString(jid.full()).replace('%', "%%")),
qPrintable(node),
qPrintable(ver));
capsSpecs_.remove(jid.full());
}
}
else {
// Add to the list of jids
foreach(CapsSpec s, caps) {
capsJids_[s].push_back(jid.full());
}
}
}
/**
* \brief Removes all feature information for a given JID.
*
* @param jid The entity's JID
*/
void CapsManager::disableCaps(const Jid& jid)
{
//qDebug() << QString("caps.cpp: Disabling caps for %1.").arg(QString(jid.full()).replace('%',"%%"));
if (capsEnabled(jid)) {
CapsSpecs cs = capsSpecs_[jid.full()].flatten();
foreach(CapsSpec s, cs) {
if (s != CapsSpec()) {
capsJids_[s].removeAll(jid.full());
}
}
capsSpecs_.remove(jid.full());
emit capsChanged(jid);
}
}
/**
* \brief Called when a reply to disco#info request was received.
* If the result was succesful, the resulting features are recorded in the
* features database for the requested node, and all the affected jids are
* put in the queue for update notification.
*/
void CapsManager::getDiscoInfo_success(const XMPP::Jid& jid, const QString& node, const XMPP::DiscoItem& item)
{
//qDebug() << QString("caps.cpp: Disco response from %1, node=%2").arg(QString(jid.full()).replace('%',"%%")).arg(node);
// Update features
bool ok = false;
CapsSpec cs(getCapsSpecForNode(jid, node, ok));
if (!ok) {
return;
}
registry_->registerCaps(cs,item.identities(),item.features().list());
}
void CapsManager::getDiscoInfo_error(const XMPP::Jid& jid, const QString& node, int, const QString&)
{
qWarning("capsmanager.cpp: Disco to '%s' at node '%s' failed.",
qPrintable(jid.full()),
qPrintable(node));
}
CapsSpec CapsManager::getCapsSpecForNode(const XMPP::Jid& jid, const QString& disco_node, bool& ok) const
{
int hash_index = disco_node.indexOf('#');
if (hash_index == -1) {
qWarning() << "CapsManager: Node" << disco_node << "invalid";
ok = false;
return CapsSpec();
}
QString node = disco_node.left(hash_index);
QString ext = disco_node.right(disco_node.length() - hash_index - 1);
CapsSpec jid_cs = capsSpecs_[jid.full()];
if (jid_cs.node() == node) {
ok = true;
return CapsSpec(node,jid_cs.version(),ext);
}
else {
ok = false;
return CapsSpec();
}
}
/**
* \brief This slot is called whenever capabilities of a client were discovered.
* All jids with the corresponding client are updated.
*/
void CapsManager::capsRegistered(const CapsSpec& cs)
{
// Notify affected jids.
foreach(QString s, capsJids_[cs]) {
//qDebug() << QString("caps.cpp: Notifying %1.").arg(s.replace('%',"%%"));
emit capsChanged(s);
}
}
/**
* \brief Checks whether a given JID is broadcastingn its entity capabilities.
*/
bool CapsManager::capsEnabled(const Jid& jid) const
{
return capsSpecs_.contains(jid.full());
}
/**
* \brief Requests the list of features of a given JID.
*/
XMPP::Features CapsManager::features(const Jid& jid) const
{
//qDebug() << "caps.cpp: Retrieving features of " << jid.full();
QStringList f;
if (capsEnabled(jid)) {
CapsSpecs cs = capsSpecs_[jid.full()].flatten();
foreach(CapsSpec s, cs) {
//qDebug() << QString(" %1").arg(registry_->features(s).list().join("\n"));
f += registry_->features(s).list();
}
}
return Features(f);
}
/**
* \brief Returns the client name of a given jid.
* \param jid the jid to retrieve the client name of
*/
QString CapsManager::clientName(const Jid& jid) const
{
if (capsEnabled(jid)) {
QString name;
CapsSpec cs = capsSpecs_[jid.full()];
const DiscoItem::Identities& i = registry_->identities(CapsSpec(cs.node(),cs.version(),cs.version()));
if (i.count() > 0) {
name = i.first().name;
}
// Try to be intelligent about the name
if (name.isEmpty()) {
name = cs.node();
if (name.startsWith("http://"))
name = name.right(name.length() - 7);
if (name.startsWith("www."))
name = name.right(name.length() - 4);
int cut_pos = name.indexOf("/");
if (cut_pos != -1)
name = name.left(cut_pos);
}
return name;
}
else {
return QString();
}
}
/**
* \brief Returns the client version of a given jid.
*/
QString CapsManager::clientVersion(const Jid& jid) const
{
return (capsEnabled(jid) ? capsSpecs_[jid.full()].version() : QString());
}

View file

@ -0,0 +1,83 @@
/*
* capsmanager.h
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CAPSMANAGER_H
#define CAPSMANAGER_H
#include <QMap>
#include <QList>
#include <QString>
#include <QObject>
#include <QPointer>
#include "protocol/discoinfoquerier.h"
#include "capsspec.h"
#include "capsregistry.h"
#include "xmpp_features.h"
namespace XMPP {
class Jid;
}
using namespace XMPP;
class CapsManager : public QObject
{
Q_OBJECT
public:
CapsManager(const XMPP::Jid&, CapsRegistry* registry, Protocol::DiscoInfoQuerier* discoInfoQuerier);
~CapsManager();
bool isEnabled();
void setEnabled(bool);
void updateCaps(const Jid& jid, const QString& node, const QString& ver, const QString& ext);
void disableCaps(const Jid& jid);
bool capsEnabled(const Jid& jid) const;
XMPP::Features features(const Jid& jid) const;
QString clientName(const Jid& jid) const;
QString clientVersion(const Jid& jid) const;
signals:
/**
* This signal is emitted when the feature list of a given JID have changed.
*/
void capsChanged(const Jid& jid);
protected:
CapsSpec getCapsSpecForNode(const XMPP::Jid& jid, const QString& disco_node, bool& ok) const;
protected slots:
void getDiscoInfo_success(const XMPP::Jid& jid, const QString& node, const XMPP::DiscoItem& item);
void getDiscoInfo_error(const XMPP::Jid& jid, const QString& node, int error_code, const QString& error_string);
void capsRegistered(const CapsSpec&);
private:
XMPP::Jid jid_;
QPointer<CapsRegistry> registry_;
QPointer<Protocol::DiscoInfoQuerier> discoInfoQuerier_;
bool isEnabled_;
QMap<QString,CapsSpec> capsSpecs_;
QMap<CapsSpec,QList<QString> > capsJids_;
};
#endif

View file

@ -0,0 +1,249 @@
/*
* capsregistry.cpp
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QApplication>
#include <QDebug>
#include <QTextCodec>
#include <QFile>
#include <QDomElement>
#include "xmpp_features.h"
#include "capsregistry.h"
#include "iodeviceopener.h"
using namespace XMPP;
// -----------------------------------------------------------------------------
CapsRegistry::CapsInfo::CapsInfo()
{
updateLastSeen();
}
const XMPP::Features& CapsRegistry::CapsInfo::features() const
{
return features_;
}
const DiscoItem::Identities& CapsRegistry::CapsInfo::identities() const
{
return identities_;
}
void CapsRegistry::CapsInfo::setIdentities(const DiscoItem::Identities& i)
{
identities_ = i;
}
void CapsRegistry::CapsInfo::setFeatures(const XMPP::Features& f)
{
features_ = f;
}
void CapsRegistry::CapsInfo::updateLastSeen()
{
lastSeen_ = QDateTime::currentDateTime();
}
QDomElement CapsRegistry::CapsInfo::toXml(QDomDocument *doc) const
{
QDomElement info = doc->createElement("info");
info.setAttribute("last-seen",lastSeen_.toString(Qt::ISODate));
// Identities
for (DiscoItem::Identities::ConstIterator i = identities_.begin(); i != identities_.end(); ++i) {
QDomElement identity = doc->createElement("identity");
identity.setAttribute("category",(*i).category);
identity.setAttribute("name",(*i).name);
identity.setAttribute("type",(*i).type);
info.appendChild(identity);
}
// Features
foreach(QString f, features_.list()) {
QDomElement feature = doc->createElement("feature");
feature.setAttribute("node",f);
info.appendChild(feature);
}
return info;
}
void CapsRegistry::CapsInfo::fromXml(const QDomElement& e)
{
if (e.tagName() != "info") {
qWarning("caps.cpp: Invalid info element");
return;
}
if (!e.attribute("last-seen").isEmpty()) {
QDateTime last = QDateTime::fromString(e.attribute("last-seen"),Qt::ISODate);
if (last.isValid())
lastSeen_ = last;
else
qWarning("Invalid date in caps registry");
}
for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement i = n.toElement();
if(i.isNull()) {
qWarning("caps.cpp: Null element");
continue;
}
if(i.tagName() == "identity") {
DiscoItem::Identity id;
id.category = i.attribute("category");
id.name = i.attribute("name");
id.type = i.attribute("type");
identities_ += id;
}
else if (i.tagName() == "feature") {
features_.addFeature(i.attribute("node"));
}
else {
qWarning("caps.cpp: Unknown element");
}
}
}
// -----------------------------------------------------------------------------
/**
* \class CapsRegistry
* \brief A singleton class managing the capabilities of clients.
*/
/**
* \brief Default constructor.
*/
CapsRegistry::CapsRegistry()
{
}
/**
* \brief Convert all capabilities info to XML.
*/
void CapsRegistry::save(QIODevice& out)
{
// Generate XML
QDomDocument doc;
QDomElement capabilities = doc.createElement("capabilities");
doc.appendChild(capabilities);
QMap<CapsSpec,CapsInfo>::ConstIterator i = capsInfo_.begin();
for( ; i != capsInfo_.end(); i++) {
QDomElement info = i.value().toXml(&doc);
info.setAttribute("node",i.key().node());
info.setAttribute("ver",i.key().version());
info.setAttribute("ext",i.key().extensions());
capabilities.appendChild(info);
}
IODeviceOpener opener(&out, QIODevice::WriteOnly);
if (!opener.isOpen()) {
qWarning() << "Caps: Unable to open IO device";
return;
}
QTextStream t;
t.setDevice(&out);
t.setCodec(QTextCodec::codecForName("UTF-8"));
t << doc.toString();
}
/**
* \brief Sets the file to save the capabilities info to
*/
void CapsRegistry::load(QIODevice& in)
{
// Load settings
QDomDocument doc;
IODeviceOpener opener(&in, QIODevice::ReadOnly);
if (!opener.isOpen()) {
qWarning() << "CapsRegistry: Cannot open input device";
return;
}
if (!doc.setContent(&in)) {
qWarning() << "CapsRegistry: Cannnot parse input";
return;
}
QDomElement caps = doc.documentElement();
if (caps.tagName() != "capabilities") {
qWarning("caps.cpp: Invalid capabilities element");
return;
}
for(QDomNode n = caps.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement i = n.toElement();
if(i.isNull()) {
qWarning("capsregistry.cpp: Null element");
continue;
}
if(i.tagName() == "info") {
CapsInfo info;
info.fromXml(i);
CapsSpec spec(i.attribute("node"),i.attribute("ver"),i.attribute("ext"));
capsInfo_[spec] = info;
//qDebug() << QString("Read %1 %2 %3").arg(spec.node()).arg(spec.version()).arg(spec.extensions());
}
else {
qWarning("capsregistry.cpp: Unknown element");
}
}
}
/**
* \brief Registers capabilities of a client.
*/
void CapsRegistry::registerCaps(const CapsSpec& spec,const XMPP::DiscoItem::Identities& identities,const XMPP::Features& features)
{
if (!isRegistered(spec)) {
CapsInfo info;
info.setIdentities(identities);
info.setFeatures(features);
capsInfo_[spec] = info;
emit registered(spec);
}
}
/**
* \brief Checks if capabilities have been registered.
*/
bool CapsRegistry::isRegistered(const CapsSpec& spec) const
{
return capsInfo_.contains(spec);
}
/**
* \brief Retrieves the features of a given caps spec.
*/
XMPP::Features CapsRegistry::features(const CapsSpec& spec) const
{
return capsInfo_[spec].features();
}
/**
* \brief Retrieves the identities of a given caps spec.
*/
XMPP::DiscoItem::Identities CapsRegistry::identities(const CapsSpec& spec) const
{
return capsInfo_[spec].identities();
}

View file

@ -0,0 +1,84 @@
/*
* capsregistry.h
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CAPSREGISTRY_H
#define CAPSREGISTRY_H
#include <QObject>
#include <QString>
#include <QList>
#include <QMap>
#include <QDateTime>
#include <QPair>
#include "xmpp_features.h"
#include "xmpp_discoitem.h"
#include "capsspec.h"
class QDomDocument;
class QDomElement;
class CapsRegistry : public QObject
{
Q_OBJECT
public:
CapsRegistry();
void registerCaps(const CapsSpec&, const XMPP::DiscoItem::Identities&, const XMPP::Features& features);
bool isRegistered(const CapsSpec&) const;
XMPP::Features features(const CapsSpec&) const;
XMPP::DiscoItem::Identities identities(const CapsSpec&) const;
signals:
void registered(const CapsSpec&);
public slots:
void load(QIODevice& target);
void save(QIODevice& target);
private:
class CapsInfo
{
public:
CapsInfo();
const XMPP::Features& features() const;
const XMPP::DiscoItem::Identities& identities() const;
void setIdentities(const XMPP::DiscoItem::Identities&);
void setFeatures(const XMPP::Features&);
QDomElement toXml(QDomDocument *) const;
void fromXml(const QDomElement&);
protected:
void updateLastSeen();
private:
XMPP::Features features_;
XMPP::DiscoItem::Identities identities_;
QDateTime lastSeen_;
};
QMap<CapsSpec,CapsInfo> capsInfo_;
};
#endif

View file

@ -0,0 +1,118 @@
/*
* capsspec.cpp
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QString>
#include <QStringList>
#include "capsspec.h"
/**
* \class CapsSpec
* \brief A class representing an entity capability specification.
* An entity capability is a combination of a node, a version, and a set of
* extensions.
*/
/**
* Default constructor.
*/
CapsSpec::CapsSpec()
{
}
/**
* \brief Basic constructor.
* @param node the node
* @param ven the version
* @param ext the list of extensions (separated by spaces)
*/
CapsSpec::CapsSpec(const QString& node, const QString& ver, const QString& ext) : node_(node), ver_(ver), ext_(ext)
{
}
/**
* \brief Returns the node of the capabilities specification.
*/
const QString& CapsSpec::node() const
{
return node_;
}
/**
* \brief Returns the version of the capabilities specification.
*/
const QString& CapsSpec::version() const
{
return ver_;
}
/**
* \brief Returns the extensions of the capabilities specification.
*/
const QString& CapsSpec::extensions() const
{
return ext_;
}
/**
* \brief Flattens the caps specification into the set of 'simple'
* specifications.
* A 'simple' specification is a specification with exactly one extension,
* or with the version number as the extension.
*
* Example: A caps specification with node=http://psi-im.org, version=0.10,
* and ext='achat vchat' would be expanded into the following list of specs:
* node=http://psi-im.org, ver=0.10, ext=0.10
* node=http://psi-im.org, ver=0.10, ext=achat
* node=http://psi-im.org, ver=0.10, ext=vchat
*/
CapsSpecs CapsSpec::flatten() const
{
CapsSpecs l;
l.append(CapsSpec(node(),version(),version()));
QStringList exts(extensions().split(" ",QString::SkipEmptyParts));
for (QStringList::ConstIterator i = exts.begin(); i != exts.end(); ++i) {
l.append(CapsSpec(node(),version(),*i));
}
return l;
}
bool CapsSpec::operator==(const CapsSpec& s) const
{
return (node() == s.node() && version() == s.version() && extensions() == s.extensions());
}
bool CapsSpec::operator!=(const CapsSpec& s) const
{
return !((*this) == s);
}
bool CapsSpec::operator<(const CapsSpec& s) const
{
return (node() != s.node() ? node() < s.node() :
(version() != s.version() ? version() < s.version() :
extensions() < s.extensions()));
}

View file

@ -0,0 +1,49 @@
/*
* capsspec.h
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CAPSSPEC_H
#define CAPSSPEC_H
#include <QList>
#include <QString>
class CapsSpec;
typedef QList<CapsSpec> CapsSpecs;
class CapsSpec
{
public:
CapsSpec();
CapsSpec(const QString&, const QString&, const QString&);
const QString& node() const;
const QString& version() const;
const QString& extensions() const;
CapsSpecs flatten() const;
bool operator==(const CapsSpec&) const;
bool operator!=(const CapsSpec&) const;
bool operator<(const CapsSpec&) const;
private:
QString node_, ver_, ext_;
};
#endif

View file

@ -0,0 +1,183 @@
/**
* Copyright (C) 2007, Remko Troncon
* See COPYING file for the detailed license.
*/
// FIXME: This test suite is far from complete
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include "protocol/discoinfoquerier.h"
#include "xmpp_jid.h"
#include "capsmanager.h"
using namespace XMPP;
// -----------------------------------------------------------------------------
class TestDiscoInfoQuerier : public Protocol::DiscoInfoQuerier
{
public:
struct DiscoInfo {
XMPP::Jid jid;
QString node;
XMPP::DiscoItem item;
};
TestDiscoInfoQuerier() {
nb_getdiscoinfo_called_ = 0;
}
void getDiscoInfo(const Jid& j, const QString& n) {
emitDiscoInfo(j,n);
}
void emitDiscoInfo(const XMPP::Jid& j, const QString& n) {
nb_getdiscoinfo_called_++;
foreach(DiscoInfo info, infos_) {
if (info.jid.compare(j,true) && info.node == n) {
emit getDiscoInfo_success(j,n,info.item);
return;
}
}
}
void addInfo(const XMPP::Jid& j, const QString& n, const XMPP::DiscoItem& it) {
DiscoInfo i;
i.jid = j;
i.node = n;
i.item = it;
infos_ += i;
}
unsigned int nb_getdiscoinfo_called_;
QList<DiscoInfo> infos_;
};
// -----------------------------------------------------------------------------
class CapsManagerTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(CapsManagerTest);
CPPUNIT_TEST(testUpdateCaps);
CPPUNIT_TEST(testCapsEnabled);
CPPUNIT_TEST(testCapsEnabled_NoCaps);
CPPUNIT_TEST(testDisableCaps);
CPPUNIT_TEST_SUITE_END();
public:
CapsManagerTest();
void setUp();
void tearDown();
void addContact(const QString& jid, const QString& client, const QString& version, const QStringList& capabilities);
CapsManager* createManager(const QString& jid);
void testUpdateCaps();
void testCapsEnabled();
void testCapsEnabled_NoCaps();
void testDisableCaps();
private:
CapsRegistry registry_;
TestDiscoInfoQuerier* querier_;
CapsManager* manager_;
};
CPPUNIT_TEST_SUITE_REGISTRATION(CapsManagerTest);
// -----------------------------------------------------------------------------
CapsManagerTest::CapsManagerTest()
{
}
void CapsManagerTest::setUp()
{
manager_ = NULL;
querier_ = new TestDiscoInfoQuerier();
}
void CapsManagerTest::tearDown()
{
delete manager_;
}
CapsManager* CapsManagerTest::createManager(const QString& jid)
{
manager_ = new CapsManager(jid, &registry_, querier_);
return manager_;
}
void CapsManagerTest::addContact(const QString& jid, const QString& client,
const QString& version, const QStringList& capabilities)
{
XMPP::DiscoItem i;
i.setFeatures(XMPP::Features(version + "_f1"));
querier_->addInfo(jid, client + "#" + version, i);
foreach(QString s, capabilities) {
XMPP::DiscoItem i2;
QStringList f;
f << s + "_f1" << s + "_f2";
i2.setFeatures(XMPP::Features(f));
querier_->addInfo(jid, client + "#" + s, i2);
}
}
void CapsManagerTest::testUpdateCaps()
{
QStringList capabilities;
capabilities << "c1" << "c2" << "c3";
addContact("you@example.com/a", "myclient", "myversion", capabilities);
CapsManager* manager = createManager("me@example.com");
manager->updateCaps("you@example.com/a", "myclient", "myversion", "c1 c2");
XMPP::Features features(manager->features("you@example.com/a"));
CPPUNIT_ASSERT_EQUAL(3U, querier_->nb_getdiscoinfo_called_);
CPPUNIT_ASSERT(features.test(QStringList("myversion_f1")));
CPPUNIT_ASSERT(features.test(QStringList("c1_f1")));
CPPUNIT_ASSERT(features.test(QStringList("c1_f2")));
CPPUNIT_ASSERT(features.test(QStringList("c2_f1")));
CPPUNIT_ASSERT(features.test(QStringList("c2_f2")));
}
void CapsManagerTest::testCapsEnabled()
{
QStringList capabilities;
capabilities << "c1" << "c2";
addContact("you@example.com/a", "myclient", "myversion", capabilities);
CapsManager* manager = createManager("me@example.com");
manager->updateCaps("you@example.com/a", "myclient", "myversion", "c1 c2");
CPPUNIT_ASSERT(manager->capsEnabled("you@example.com/a"));
}
void CapsManagerTest::testCapsEnabled_NoCaps()
{
CapsManager* manager = createManager("me@example.com/a");
CPPUNIT_ASSERT(!manager->capsEnabled("you@example.com/b"));
}
void CapsManagerTest::testDisableCaps()
{
QStringList capabilities;
capabilities << "c1" << "c2";
addContact("you@example.com/a", "myclient", "myversion", capabilities);
CapsManager* manager = createManager("me@example.com");
manager->updateCaps("you@example.com/a", "myclient", "myversion", "c1 c2");
manager->disableCaps("you@example.com/a");
CPPUNIT_ASSERT(!manager->capsEnabled("you@example.com/a"));
}

View file

@ -0,0 +1,124 @@
/**
* Copyright (C) 2007, Remko Troncon
*/
#include <cppunit/extensions/HelperMacros.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include "capsspec.h"
// -----------------------------------------------------------------------------
class CapsSpecTest : public CppUnit::TestFixture
{
CPPUNIT_TEST_SUITE(CapsSpecTest);
CPPUNIT_TEST(testConstructor);
CPPUNIT_TEST(testEqualsNotEquals_Equal);
CPPUNIT_TEST(testEqualsNotEquals_NotEqual);
CPPUNIT_TEST(testSmallerThan);
CPPUNIT_TEST(testSmallerThan_SameNode);
CPPUNIT_TEST(testSmallerThan_SameNodeVersion);
CPPUNIT_TEST(testSmallerThan_Equals);
CPPUNIT_TEST(testFlatten);
CPPUNIT_TEST_SUITE_END();
public:
CapsSpecTest();
void testConstructor();
void testFlatten();
void testEqualsNotEquals_Equal();
void testEqualsNotEquals_NotEqual();
void testSmallerThan();
void testSmallerThan_SameNode();
void testSmallerThan_SameNodeVersion();
void testSmallerThan_Equals();
};
CPPUNIT_TEST_SUITE_REGISTRATION(CapsSpecTest);
// -----------------------------------------------------------------------------
CapsSpecTest::CapsSpecTest()
{
}
void CapsSpecTest::testConstructor()
{
CapsSpec c("a","b","c d");
CPPUNIT_ASSERT(c.node() == "a");
CPPUNIT_ASSERT(c.version() == "b");
CPPUNIT_ASSERT(c.extensions() == "c d");
}
void CapsSpecTest::testEqualsNotEquals_Equal()
{
CapsSpec c1("a","b","c d");
CapsSpec c2("a","b","c d");
CPPUNIT_ASSERT(c1 == c2);
CPPUNIT_ASSERT(!(c1 != c2));
}
void CapsSpecTest::testEqualsNotEquals_NotEqual()
{
CapsSpec c1("a","b","c d");
CapsSpec c2("a","e","c d");
CPPUNIT_ASSERT(!(c1 == c2));
CPPUNIT_ASSERT(c1 != c2);
}
void CapsSpecTest::testSmallerThan()
{
CapsSpec c1("a","b","c");
CapsSpec c2("d","e","f");
CPPUNIT_ASSERT(c1 < c2);
CPPUNIT_ASSERT(!(c2 < c1));
}
void CapsSpecTest::testSmallerThan_SameNode()
{
CapsSpec c1("a","b","c");
CapsSpec c2("a","e","f");
CPPUNIT_ASSERT(c1 < c2);
CPPUNIT_ASSERT(!(c2 < c1));
}
void CapsSpecTest::testSmallerThan_SameNodeVersion()
{
CapsSpec c1("a","b","c");
CapsSpec c2("a","b","f");
CPPUNIT_ASSERT(c1 < c2);
CPPUNIT_ASSERT(!(c2 < c1));
}
void CapsSpecTest::testSmallerThan_Equals()
{
CapsSpec c1("a","b","c");
CapsSpec c2("a","b","c");
CPPUNIT_ASSERT(!(c1 < c2));
CPPUNIT_ASSERT(!(c2 < c1));
}
void CapsSpecTest::testFlatten()
{
CapsSpec c("a","b","c d");
CapsSpecs cs = c.flatten();
CPPUNIT_ASSERT_EQUAL(3, cs.count());
CPPUNIT_ASSERT(cs[0] == CapsSpec("a", "b", "b"));
CPPUNIT_ASSERT(cs[1] == CapsSpec("a", "b", "c"));
CPPUNIT_ASSERT(cs[2] == CapsSpec("a", "b", "d"));
}

View file

@ -0,0 +1,3 @@
SOURCES += \
$$PWD/capsspectest.cpp \
$$PWD/capsmanagertest.cpp

165
src/certutil.cpp Normal file
View file

@ -0,0 +1,165 @@
#include <QtCrypto>
#include <QStringList>
#include <QDomDocument>
#include <QDebug>
#include <QDir>
#include <QFile>
#include "applicationinfo.h"
#include "certutil.h"
using namespace QCA;
/**
* \class CertUtil
* \brief A class providing utility functions for Certificates.
*/
/**
* \brief Returns the list of directories with certificates.
*/
static QStringList certificateStores()
{
QStringList l;
l += ApplicationInfo::resourcesDir() + "/certs";
l += ApplicationInfo::homeDir() + "/certs";
return l;
}
/**
* \brief Returns the collection of all available certificates.
* This collection includes the system-wide certificates, as well as any
* custom certificate in the Psi-specific cert dirs.
*/
CertificateCollection CertUtil::allCertificates()
{
// ONLINE-1864
CertificateCollection certs;
return certs;
// CertificateCollection certs(systemStore());
QStringList stores = certificateStores();
for (QStringList::ConstIterator s = stores.begin(); s != stores.end(); ++s) {
QDir store(*s);
// Read in PEM certificates
store.setNameFilters(QStringList("*.crt") + QStringList("*.pem"));
QStringList cert_files = store.entryList();
for (QStringList::ConstIterator c = cert_files.begin(); c != cert_files.end(); ++c) {
//qDebug() << "certutil.cpp: Reading " << store.filePath(*c);
ConvertResult result;
Certificate cert = Certificate::fromPEMFile(store.filePath(*c),&result);
if (result == ConvertGood) {
certs.addCertificate(cert);
}
else {
qWarning("certutil.cpp: Invalid PEM certificate: %s", qPrintable(store.filePath(*c)));
}
}
// Read in old XML format certificates (DEPRECATED)
store.setNameFilter("*.xml");
cert_files = store.entryList();
for(QStringList::ConstIterator it = cert_files.begin(); it != cert_files.end(); ++it) {
qWarning("Loading certificate in obsolete XML format: %s", qPrintable(store.filePath(*it)));
QFile f(store.filePath(*it));
if(!f.open(QIODevice::ReadOnly))
continue;
QDomDocument doc;
bool ok = doc.setContent(&f);
f.close();
if(!ok)
continue;
QDomElement base = doc.documentElement();
if(base.tagName() != "store")
continue;
QDomNodeList cl = base.elementsByTagName("certificate");
for(int n = 0; n < (int)cl.count(); ++n) {
QDomElement data = cl.item(n).toElement().elementsByTagName("data").item(0).toElement();
if(!data.isNull()) {
ConvertResult result;
Certificate cert = Certificate::fromDER(Base64().stringToArray(data.text()).toByteArray(),&result);
if (result == ConvertGood) {
certs.addCertificate(cert);
}
else {
qWarning("certutil.cpp: Invalid XML certificate: %s", qPrintable(store.filePath(*it)));
}
}
}
}
}
return certs;
}
QString CertUtil::validityToString(QCA::Validity v)
{
QString s;
switch(v)
{
case QCA::ValidityGood:
s = "Validated";
break;
case QCA::ErrorRejected:
s = "Root CA is marked to reject the specified purpose";
break;
case QCA::ErrorUntrusted:
s = "Certificate not trusted for the required purpose";
break;
case QCA::ErrorSignatureFailed:
s = "Invalid signature";
break;
case QCA::ErrorInvalidCA:
s = "Invalid CA certificate";
break;
case QCA::ErrorInvalidPurpose:
s = "Invalid certificate purpose";
break;
case QCA::ErrorSelfSigned:
s = "Certificate is self-signed";
break;
case QCA::ErrorRevoked:
s = "Certificate has been revoked";
break;
case QCA::ErrorPathLengthExceeded:
s = "Maximum certificate chain length exceeded";
break;
case QCA::ErrorExpired:
s = "Certificate has expired";
break;
case QCA::ErrorExpiredCA:
s = "CA has expired";
break;
case QCA::ErrorValidityUnknown:
default:
s = "General certificate validation error";
break;
}
return s;
}
QString CertUtil::resultToString(int result, QCA::Validity validity)
{
QString s;
switch(result) {
case QCA::TLS::NoCertificate:
s = QObject::tr("The server did not present a certificate.");
break;
case QCA::TLS::Valid:
s = QObject::tr("Certificate is valid.");
break;
case QCA::TLS::HostMismatch:
s = QObject::tr("The hostname does not match the one the certificate was issued to.");
break;
case QCA::TLS::InvalidCertificate:
s = validityToString(validity);
break;
default:
s = QObject::tr("General certificate validation error.");
break;
}
return s;
}

19
src/certutil.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef CERTUTIL_H
#define CERTUTIL_H
class QString;
namespace QCA {
class CertificateCollection;
}
class CertUtil
{
public:
static QCA::CertificateCollection allCertificates();
static QString resultToString(int result, QCA::Validity);
protected:
static QString validityToString(QCA::Validity);
};
#endif

155
src/changepw.ui Normal file
View file

@ -0,0 +1,155 @@
<ui version="4.0" stdsetdef="1" >
<author></author>
<comment></comment>
<exportmacro></exportmacro>
<class>ChangePassword</class>
<widget class="QDialog" name="ChangePassword" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>331</width>
<height>149</height>
</rect>
</property>
<property name="windowTitle" >
<string>Change Password</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>11</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<layout class="QGridLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item row="2" column="1" >
<widget class="QLineEdit" name="le_pwver" >
<property name="echoMode" >
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="0" column="1" >
<widget class="QLineEdit" name="le_pwcur" >
<property name="echoMode" >
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="2" column="0" >
<widget class="QLabel" name="lb_pwver" >
<property name="text" >
<string>Confirm new password:</string>
</property>
</widget>
</item>
<item row="0" column="0" >
<widget class="QLabel" name="lb_pwcur" >
<property name="text" >
<string>Current password:</string>
</property>
</widget>
</item>
<item row="1" column="1" >
<widget class="QLineEdit" name="le_pwnew" >
<property name="echoMode" >
<enum>QLineEdit::Password</enum>
</property>
</widget>
</item>
<item row="1" column="0" >
<widget class="QLabel" name="lb_pwnew" >
<property name="text" >
<string>New password:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<spacer name="Spacer2" >
<property name="sizeHint" >
<size>
<width>20</width>
<height>16</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Vertical</enum>
</property>
</spacer>
</item>
<item>
<widget class="Line" name="Line1" >
<property name="frameShape" >
<enum>QFrame::HLine</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<item>
<widget class="BusyWidget" name="busy" />
</item>
<item>
<spacer name="Spacer1" >
<property name="sizeHint" >
<size>
<width>110</width>
<height>20</height>
</size>
</property>
<property name="sizeType" >
<enum>Expanding</enum>
</property>
<property name="orientation" >
<enum>Horizontal</enum>
</property>
</spacer>
</item>
<item>
<widget class="IconButton" name="pb_close" >
<property name="text" >
<string>&amp;Close</string>
</property>
</widget>
</item>
<item>
<widget class="IconButton" name="pb_apply" >
<property name="text" >
<string>&amp;Apply</string>
</property>
<property name="psiIconName" stdset="0" >
<string>psi/register</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11" />
<pixmapfunction>qPixmapFromMimeSource</pixmapfunction>
<tabstops>
<tabstop>le_pwcur</tabstop>
<tabstop>le_pwnew</tabstop>
<tabstop>le_pwver</tabstop>
</tabstops>
</ui>

160
src/changepwdlg.cpp Normal file
View file

@ -0,0 +1,160 @@
/*
* changepwdlg.cpp - dialog for changing password
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "changepwdlg.h"
#include <qlayout.h>
#include <qlabel.h>
#include <qpushbutton.h>
#include <qlineedit.h>
#include <qmessagebox.h>
#include "profiles.h"
#include "psiaccount.h"
#include "busywidget.h"
#include "xmpp_tasks.h"
#include "accountmodifydlg.h"
#include "iconwidget.h"
using namespace XMPP;
ChangePasswordDlg::ChangePasswordDlg(PsiAccount *_pa, QWidget *parent)
: QDialog(parent)
{
setAttribute(Qt::WA_DeleteOnClose, true);
setupUi(this);
setModal(false);
pa = _pa;
pa->dialogRegister(this);
connect(pa, SIGNAL(disconnected()), SLOT(disc()));
setWindowTitle(CAP(caption()));
connect(pb_close, SIGNAL(clicked()), SLOT(close()));
connect(pb_apply, SIGNAL(clicked()), SLOT(apply()));
}
ChangePasswordDlg::~ChangePasswordDlg()
{
pa->dialogUnregister(this);
}
/*void ChangePasswordDlg::closeEvent(QCloseEvent *e)
{
e->ignore();
reject();
}*/
void ChangePasswordDlg::done(int r)
{
if(busy->isActive())
return;
QDialog::done(r);
}
void ChangePasswordDlg::apply()
{
// sanity check
if(le_pwcur->text().isEmpty() || le_pwnew->text().isEmpty() || le_pwver->text().isEmpty()) {
QMessageBox::information(this, tr("Error"), tr("You must fill out the fields properly before you can proceed."));
return;
}
if(le_pwcur->text() != pa->userAccount().pass) {
QMessageBox::information(this, tr("Error"), tr("You entered your old password incorrectly. Try again."));
le_pwcur->setText("");
le_pwcur->setFocus();
return;
}
if(le_pwnew->text() != le_pwver->text()) {
QMessageBox::information(this, tr("Error"), tr("New password and confirmation do not match. Please enter them again."));
le_pwnew->setText("");
le_pwver->setText("");
le_pwnew->setFocus();
return;
}
busy->start();
blockWidgets();
JT_Register *reg = new JT_Register(pa->client()->rootTask());
connect(reg, SIGNAL(finished()), SLOT(finished()));
Jid j = pa->userAccount().jid;
reg->reg(j.user(), le_pwnew->text());
reg->go(true);
}
void ChangePasswordDlg::finished()
{
busy->stop();
JT_Register *reg = (JT_Register *)sender();
QString err = reg->statusString();
int code = reg->statusCode();
bool ok = reg->success();
if(ok) {
UserAccount acc = pa->userAccount();
acc.pass = le_pwnew->text();
pa->setUserAccount(acc);
AccountModifyDlg *amd = pa->findDialog<AccountModifyDlg*>();
if(amd)
amd->setPassword(acc.pass);
QMessageBox::information(this, tr("Success"), tr("Successfully changed password."));
close();
}
else {
// ignore task disconnect error
if(code == Task::ErrDisc)
return;
QMessageBox::critical(this, tr("Error"), QString(tr("There was an error when trying to set the password.\nReason: %1")).arg(QString(err).replace('\n', "<br>")));
restoreWidgets();
}
}
void ChangePasswordDlg::disc()
{
busy->stop();
close();
}
void ChangePasswordDlg::blockWidgets()
{
setWidgetsEnabled(false);
}
void ChangePasswordDlg::restoreWidgets()
{
setWidgetsEnabled(true);
}
void ChangePasswordDlg::setWidgetsEnabled(bool enabled)
{
lb_pwcur->setEnabled(enabled);
lb_pwnew->setEnabled(enabled);
lb_pwver->setEnabled(enabled);
le_pwcur->setEnabled(enabled);
le_pwnew->setEnabled(enabled);
le_pwver->setEnabled(enabled);
pb_close->setEnabled(enabled);
pb_apply->setEnabled(enabled);
}

54
src/changepwdlg.h Normal file
View file

@ -0,0 +1,54 @@
/*
* changepwdlg.h - dialog for changing password
* Copyright (C) 2001, 2002 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CHANGEPWDLG_H
#define CHANGEPWDLG_H
#include "ui_changepw.h"
class PsiAccount;
class ChangePasswordDlg : public QDialog, public Ui::ChangePassword
{
Q_OBJECT
public:
ChangePasswordDlg(PsiAccount *, QWidget *parent=0);
~ChangePasswordDlg();
protected:
//void closeEvent(QCloseEvent *e);
public slots:
void done(int);
private slots:
void apply();
void finished();
void disc();
private:
void blockWidgets();
void restoreWidgets();
void setWidgetsEnabled(bool enabled);
PsiAccount *pa;
};
#endif

1073
src/chatdlg.cpp Normal file

File diff suppressed because it is too large Load diff

187
src/chatdlg.h Normal file
View file

@ -0,0 +1,187 @@
/*
* chatdlg.h - dialog for handling chats
* Copyright (C) 2001-2007 Justin Karneges, Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CHATDLG_H
#define CHATDLG_H
#include "chatdlgbase.h"
#include "xmpp_receipts.h"
#include "xmpp_yadatetime.h"
namespace XMPP
{
class Jid;
class Message;
}
using namespace XMPP;
class PsiAccount;
class UserListItem;
class QDropEvent;
class QDragEnterEvent;
class ChatDlg : public ChatDlgBase
{
Q_OBJECT
protected:
ChatDlg(const Jid& jid, PsiAccount* account, TabManager* tabManager);
virtual void init();
public:
static ChatDlg* create(const Jid& jid, PsiAccount* account, TabManager* tabManager);
~ChatDlg();
// reimplemented
void setJid(const Jid &);
QString getDisplayName() const;
static QSize defaultSize();
bool readyToHide();
// reimplemented
virtual TabbableWidget::State state() const;
virtual QString desiredCaption() const;
virtual void invalidateTab();
virtual void receivedPendingMessage();
signals:
void aInfo(const Jid &);
void aHistory(const Jid &);
void aVoice(const Jid &);
void messagesRead(const Jid &);
void aSend(const Message &);
void aFile(const Jid &);
/**
* Signals if user (re)started/stopped composing
*/
void composing(bool);
protected:
// reimplemented
void closeEvent(QCloseEvent *);
void resizeEvent(QResizeEvent *);
void hideEvent(QHideEvent *);
void showEvent(QShowEvent *);
void windowActivationChange(bool);
virtual void dropEvent(QDropEvent* event);
void dragEnterEvent(QDragEnterEvent* event);
public slots:
// reimplemented
virtual void deactivated();
virtual void activated();
virtual void optionsUpdate();
virtual void updateContact(const Jid &, bool);
void incomingMessage(const Message &);
virtual void updateAvatar() = 0;
void updateAvatar(const Jid&);
protected slots:
void doInfo();
virtual void doHistory();
virtual bool doSend();
void sendMessage(XMPP::Message m, bool userAction);
void doVoice();
void doFile();
private slots:
void setKeepOpenFalse();
void setWarnSendFalse();
virtual void updatePGP();
virtual void setPGPEnabled(bool enabled);
void encryptedMessageSent(int, bool, int);
void slotScroll();
void setChatState(XMPP::ChatState s);
void updateIsComposing(bool);
void setContactChatState(ChatState s);
void capsChanged(const Jid&);
void setComposing();
protected slots:
void checkComposing();
virtual void setLooks();
// reimplemented
virtual void chatEditCreated();
protected:
// reimplemented
virtual void doSendMessage(const XMPP::Message& m);
void resetComposing();
virtual void doneSend(const XMPP::Message& m);
void setSelfDestruct(int);
void deferredScroll();
bool isEmoteText(const QString& text);
bool isEmoteMessage(const XMPP::Message& m);
QString messageText(const QString& text, bool isEmote, bool isHtml = false);
QString messageText(const XMPP::Message& m);
virtual void capsChanged();
virtual void contactUpdated(UserListItem* u, int status, const QString& statusString);
virtual QString colorString(bool local, SpooledType spooled) const = 0;
void appendMessage(const Message &, bool local = false);
virtual bool isEncryptionEnabled() const;
virtual void appendSysMsg(const QString& txt) = 0;
#ifndef YAPSI
virtual void appendEmoteMessage(SpooledType spooled, const QDateTime& time, bool local, QString txt) = 0;
virtual void appendNormalMessage(SpooledType spooled, const QDateTime& time, bool local, QString txt) = 0;
#else
virtual void appendEmoteMessage(SpooledType spooled, const QDateTime& time, bool local, int spamFlag, QString id, XMPP::MessageReceipt messageReceipt, QString txt, const XMPP::YaDateTime& yaTime, int yaFlags) = 0;
virtual void appendNormalMessage(SpooledType spooled, const QDateTime& time, bool local, int spamFlag, QString id, XMPP::MessageReceipt messageReceipt, QString txt, const XMPP::YaDateTime& yaTime, int yaFlags) = 0;
#endif
virtual void appendMessageFields(const Message& m) = 0;
virtual void nicksChanged();
QString whoNick(bool local) const;
bool canChatState() const;
private:
QString dispNick_;
int status_;
QString statusString_;
bool keepOpen_;
bool warnSend_;
QTimer* selfDestruct_;
QString key_;
int transid_;
Message m_;
bool lastMessageUserAction_;
bool lastWasEncrypted_;
// Message Events & Chat States
QTimer* composingTimer_;
bool isComposing_;
bool sendComposingEvents_;
QString eventId_;
ChatState contactChatState_;
ChatState lastChatState_;
};
#endif

325
src/chatdlg.ui Normal file
View file

@ -0,0 +1,325 @@
<ui version="4.0" >
<class>ChatDlg</class>
<widget class="QWidget" name="ChatDlg" >
<property name="geometry" >
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>300</height>
</rect>
</property>
<property name="windowTitle" >
<string>Form</string>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>4</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="ChatSplitter" name="splitter" >
<property name="orientation" >
<enum>Qt::Vertical</enum>
</property>
<widget class="QFrame" name="topFrame" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>3</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape" >
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>4</number>
</property>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>4</number>
</property>
<item>
<widget class="IconLabel" name="lb_status" >
<property name="minimumSize" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="le_jid" >
<property name="focusPolicy" >
<enum>Qt::NoFocus</enum>
</property>
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="lb_count" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>0</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize" >
<size>
<width>40</width>
<height>0</height>
</size>
</property>
<property name="maximumSize" >
<size>
<width>40</width>
<height>16777215</height>
</size>
</property>
<property name="toolTip" >
<string>Message length</string>
</property>
<property name="frameShape" >
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Sunken</enum>
</property>
<property name="text" >
<string>0</string>
</property>
<property name="alignment" >
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item>
<widget class="AccountLabel" name="lb_ident" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>1</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>AccountLabel</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="tb_pgp" >
<property name="toolTip" >
<string>Toggle encryption</string>
</property>
<property name="text" >
<string/>
</property>
<property name="iconSize" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="tb_emoticons" >
<property name="toolTip" >
<string>Select icon</string>
</property>
<property name="text" >
<string/>
</property>
<property name="iconSize" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="popupMode" >
<enum>QToolButton::InstantPopup</enum>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="tb_actions" >
<property name="toolTip" >
<string>Actions</string>
</property>
<property name="text" >
<string/>
</property>
<property name="iconSize" >
<size>
<width>16</width>
<height>16</height>
</size>
</property>
<property name="popupMode" >
<enum>QToolButton::InstantPopup</enum>
</property>
<property name="arrowType" >
<enum>Qt::DownArrow</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="ChatView" name="log" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>3</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy" >
<enum>Qt::NoFocus</enum>
</property>
<property name="readOnly" >
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QFrame" name="bottomFrame" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>4</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape" >
<enum>QFrame::NoFrame</enum>
</property>
<property name="frameShadow" >
<enum>QFrame::Plain</enum>
</property>
<layout class="QVBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>6</number>
</property>
<item>
<widget class="QToolBar" name="toolbar" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>4</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="orientation" >
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" >
<property name="margin" >
<number>0</number>
</property>
<property name="spacing" >
<number>4</number>
</property>
<item>
<widget class="ChatEditProxy" name="mle" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>7</hsizetype>
<vsizetype>5</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="avatar" >
<property name="sizePolicy" >
<sizepolicy>
<hsizetype>5</hsizetype>
<vsizetype>4</vsizetype>
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text" >
<string>Avatar</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>ChatView</class>
<extends>QTextEdit</extends>
<header>msgmle.h</header>
</customwidget>
<customwidget>
<class>ChatSplitter</class>
<extends>QSplitter</extends>
<header>chatsplitter.h</header>
</customwidget>
<customwidget>
<class>ChatEditProxy</class>
<extends>QTextEdit</extends>
<header>chateditproxy.h</header>
</customwidget>
<customwidget>
<class>AccountLabel</class>
<extends>QLabel</extends>
<header>accountlabel.h</header>
</customwidget>
<customwidget>
<class>IconLabel</class>
<extends>QLabel</extends>
<header>iconlabel.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>

405
src/chatdlgbase.cpp Normal file
View file

@ -0,0 +1,405 @@
/*
* chatdlgbase.cpp
* Copyright (C) 2010 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "chatdlgbase.h"
#include <QTimer>
#include <QScrollBar>
#include "psiaccount.h"
#include "psicon.h"
#include "yachatviewmodel.h"
#include "yachatdlgshared.h"
#include "psioptions.h"
#include "yachatview.h"
#include "msgmle.h"
#include "shortcutmanager.h"
#include "xmpp_xmlcommon.h"
#include "fileutil.h"
#ifdef YAPSI
#include "yadayuse.h"
#endif
static const QString textColorOptionPath = "options.ya.chat-window.text-color";
ChatDlgBase::ChatDlgBase(const Jid& jid, PsiAccount* acc, TabManager* tabManager)
: TabbableWidget(jid, acc, tabManager)
, highlightersInstalled_(false)
#ifdef YAPSI
, model_(0)
#endif
, pending_(0)
{
#ifdef YAPSI
model_ = new YaChatViewModel(account()->deliveryConfirmationManager(), account()->psi()->yaNarodDiskManager());
connect(model_, SIGNAL(uploadFinished(const QString&, const QString&, qint64)), SLOT(uploadFinished(const QString&, const QString&, qint64)));
connect(YaChatDlgShared::instance(account()->psi()), SIGNAL(uploadFile()), SLOT(uploadFile()));
connect(YaChatDlgShared::instance(account()->psi()), SIGNAL(uploadRecentFile(const QString&, const QString&, qint64)), SLOT(uploadRecentFile(const QString&, const QString&, qint64)));
connect(PsiOptions::instance(), SIGNAL(optionChanged(const QString&)), SLOT(optionChanged(const QString&)));
connect(account(), SIGNAL(updatedActivity()), SLOT(updateModelNotices()));
connect(account(), SIGNAL(enabledChanged()), SLOT(updateModelNotices()));
QTimer::singleShot(0, this, SLOT(updateModelNotices()));
#endif
}
ChatDlgBase::~ChatDlgBase()
{
account()->dialogUnregister(this);
delete model_;
}
void ChatDlgBase::init()
{
initActions();
setShortcuts();
initUi();
setLooks();
connect(account(), SIGNAL(updatedActivity()), SLOT(updateSendAction()));
connect(account(), SIGNAL(enabledChanged()), SLOT(updateSendAction()));
updateSendAction();
// SyntaxHighlighters modify the QTextEdit in a QTimer::singleShot(0, ...) call
// so we need to install our hooks after it fired for the first time
QTimer::singleShot(10, this, SLOT(initComposing()));
setAcceptDrops(true);
chatView()->setFocusPolicy(Qt::NoFocus);
chatEdit()->setFocus();
account()->dialogRegister(this, jid());
}
QAction* ChatDlgBase::actionSend() const
{
return act_send_;
}
void ChatDlgBase::initActions()
{
act_send_ = new QAction(tr("Send"), this);
addAction(act_send_);
connect(act_send_, SIGNAL(activated()), SLOT(doSend()));
act_close_ = new QAction(this);
addAction(act_close_);
connect(act_close_, SIGNAL(activated()), SLOT(close()));
act_scrollup_ = new QAction(this);
addAction(act_scrollup_);
connect(act_scrollup_, SIGNAL(activated()), SLOT(scrollUp()));
act_scrolldown_ = new QAction(this);
addAction(act_scrolldown_);
connect(act_scrolldown_, SIGNAL(activated()), SLOT(scrollDown()));
}
void ChatDlgBase::setShortcuts()
{
//act_send_->setShortcuts(ShortcutManager::instance()->shortcuts("chat.send"));
act_scrollup_->setShortcuts(ShortcutManager::instance()->shortcuts("common.scroll-up"));
act_scrolldown_->setShortcuts(ShortcutManager::instance()->shortcuts("common.scroll-down"));
//if(!option.useTabs) {
// act_close_->setShortcuts(ShortcutManager::instance()->shortcuts("common.close"));
//}
}
bool ChatDlgBase::doSend()
{
if (!act_send_->isEnabled()) {
return false;
}
if (chatEdit()->text().trimmed().isEmpty()) {
chatEdit()->clear();
return false;
}
if (chatEdit()->text() == "/clear") {
chatEdit()->clear();
doClear();
return false;
}
return true;
}
void ChatDlgBase::scrollUp()
{
chatView()->verticalScrollBar()->setValue(chatView()->verticalScrollBar()->value() - chatView()->verticalScrollBar()->pageStep() / 2);
}
void ChatDlgBase::scrollDown()
{
chatView()->verticalScrollBar()->setValue(chatView()->verticalScrollBar()->value() + chatView()->verticalScrollBar()->pageStep() / 2);
}
bool ChatDlgBase::couldSendMessages() const
{
return chatEdit()->isEnabled() &&
!chatEdit()->text().trimmed().isEmpty() &&
account()->isAvailable();
}
void ChatDlgBase::updateSendAction()
{
#ifndef YAPSI
act_send_->setEnabled(couldSendMessages());
#endif
}
void ChatDlgBase::chatEditCreated()
{
chatView()->setDialog(this);
chatEdit()->setDialog(this);
chatEdit()->setSendAction(act_send_);
chatEdit()->installEventFilter(this);
disconnect(chatEdit(), SIGNAL(textChanged()), this, SLOT(updateSendAction()));
connect(chatEdit(), SIGNAL(textChanged()), this, SLOT(updateSendAction()));
#ifdef YAPSI
chatEdit()->setController(account()->psi());
chatEdit()->setUploadFileAction(YaChatDlgShared::instance(account()->psi())->uploadFileAction());
chatEdit()->setRecentFilesMenu(YaChatDlgShared::instance(account()->psi())->recentFilesMenu());
chatEdit()->setTypographyAction(YaChatDlgShared::instance(account()->psi())->typographyAction());
chatEdit()->setEmoticonsAction(YaChatDlgShared::instance(account()->psi())->emoticonsAction());
chatEdit()->setCheckSpellingAction(YaChatDlgShared::instance(account()->psi())->checkSpellingAction());
chatEdit()->setSendButtonEnabledAction(YaChatDlgShared::instance(account()->psi())->sendButtonEnabledAction());
optionChanged(textColorOptionPath);
#endif
}
void ChatDlgBase::initComposing()
{
highlightersInstalled_ = true;
chatEditCreated();
}
bool ChatDlgBase::highlightersInstalled() const
{
return highlightersInstalled_;
}
void ChatDlgBase::doClear()
{
#ifndef YAPSI
chatView()->clear();
#else
model_->doClear();
#endif
}
void ChatDlgBase::optionChanged(const QString& option)
{
if (option == textColorOptionPath) {
chatView()->setTextColor(PsiOptions::instance()->getOption(textColorOptionPath).value<QColor>());
}
}
void ChatDlgBase::updateModelNotices()
{
model_->setAccountIsOfflineNoticeVisible(!account()->isAvailable());
model_->setAccountIsDisabledNoticeVisible(!account()->enabled());
}
YaChatViewModel* ChatDlgBase::model() const
{
return model_;
}
void ChatDlgBase::setLooks()
{
// update the font
QFont f;
f.fromString(option.font[fChat]);
chatView()->setFont(f);
chatEdit()->setFont(f);
}
void ChatDlgBase::uploadFile()
{
if (!isActiveTab())
return;
QStringList fileNames = FileUtil::getOpenAnyFileNames(this);
if (!fileNames.isEmpty()) {
uploadFiles(fileNames);
}
}
void ChatDlgBase::uploadRecentFile(const QString& fileName, const QString& url, qint64 size)
{
if (!isActiveTab())
return;
uploadFinished(fileName, url, size);
}
void ChatDlgBase::uploadFile(const QString& fileName)
{
account()->psi()->yaNarodDiskManager()->uploadFile(fileName, this, "uploadFileStarted");
}
void ChatDlgBase::uploadFileStarted(const QString& id)
{
if (id.isEmpty())
return;
// QMap<QString, YaNarodDiskManager::RecentFile> files = account()->psi()->yaNarodDiskManager()->recentFiles();
// if (files.contains(id)) {
// uploadFinished(files[id].fileName, files[id].url, files[id].size);
// }
// else {
model()->addFileUpload(id);
// }
}
void ChatDlgBase::uploadFiles(const QStringList& fileNames)
{
foreach(const QString& fileName, fileNames) {
uploadFile(fileName);
}
}
void ChatDlgBase::uploadFinished(const QString& fileName, const QString& url, qint64 size)
{
#ifdef YAPSI
YaDayUse::instance(account()->psi())->stat(135, 70521);
#endif
QDomDocument doc;
QDomElement body = doc.createElementNS("http://www.w3.org/1999/xhtml", "body");
doc.appendChild(body);
QDomElement html = textTag(&doc, "a", YaNarodDiskManager::humanReadableName(fileName, size));
html.setAttribute("href", url);
body.appendChild(html);
Message m(jid());
m.setType("chat");
m.setHTML(body);
m.setBody(QString("%1 (%2)")
.arg(url)
.arg(YaNarodDiskManager::humanReadableSize(size)));
doSendMessage(m);
}
void ChatDlgBase::addEmoticon(QString text)
{
if (!isActiveTab())
return;
chatEdit()->insert(text + " ");
}
// FIXME: This should be unnecessary, since these keys are all registered as
// actions in the constructor. Somehow, Qt ignores this sometimes (i think
// just for actions that have no modifier).
void ChatDlgBase::keyPressEvent(QKeyEvent *e)
{
QKeySequence key = e->key() + (e->modifiers() & ~Qt::KeypadModifier);
if (!option.useTabs && ShortcutManager::instance()->shortcuts("common.close").contains(key)) {
close();
}
else if (ShortcutManager::instance()->shortcuts("chat.send").contains(key)) {
doSend();
}
else if (ShortcutManager::instance()->shortcuts("common.scroll-up").contains(key)) {
scrollUp();
}
else if (ShortcutManager::instance()->shortcuts("common.scroll-down").contains(key)) {
scrollDown();
}
else if (key.toString(QKeySequence::PortableText).contains("Enter") ||
key.toString(QKeySequence::PortableText).contains("Return"))
{
chatEdit()->insert("\n");
}
else {
e->ignore();
}
}
void ChatDlgBase::activated()
{
doFlash(false);
chatEdit()->setFocus();
if (unreadMessageCount() > 0) {
setUnreadMessageCount(0);
}
}
QString ChatDlgBase::desiredCaption() const
{
QString cap = "";
if (unreadMessageCount() > 0) {
cap += "* ";
if (unreadMessageCount() > 1) {
cap += QString("[%1] ").arg(unreadMessageCount());
}
}
cap += getDisplayName();
return cap;
}
int ChatDlgBase::unreadMessageCount() const
{
return pending_;
}
void ChatDlgBase::setUnreadMessageCount(int pending)
{
if (pending_ != pending) {
pending_ = pending;
invalidateTab();
}
}
bool ChatDlgBase::eventFilter(QObject *obj, QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
keyPressEvent(static_cast<QKeyEvent*>(event));
if (event->isAccepted())
return true;
}
if (chatView()->handleCopyEvent(obj, event, chatEdit()))
return true;
return QWidget::eventFilter(obj, event);
}
void ChatDlgBase::optionsUpdate()
{
setLooks();
setShortcuts();
}
void ChatDlgBase::showEvent(QShowEvent* e)
{
optionsUpdate();
TabbableWidget::showEvent(e);
}

122
src/chatdlgbase.h Normal file
View file

@ -0,0 +1,122 @@
/*
* chatdlgbase.h
* Copyright (C) 2010 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CHATDLGBASE_H
#define CHATDLGBASE_H
#include "tabbablewidget.h"
#ifdef YAPSI
class YaChatView;
typedef YaChatView ChatViewClass;
#else
class ChatView;
typedef ChatView ChatViewClass;
#endif
class ChatEdit;
class YaChatViewModel;
class ChatDlgBase : public TabbableWidget
{
Q_OBJECT
protected:
ChatDlgBase(const Jid& jid, PsiAccount* account, TabManager* tabManager);
virtual void init();
public:
~ChatDlgBase();
void uploadFile(const QString& fileName);
void uploadFiles(const QStringList& fileNames);
// reimplemented
virtual QString desiredCaption() const;
int unreadMessageCount() const;
void setUnreadMessageCount(int pending);
enum SpooledType {
Spooled_None,
Spooled_OfflineStorage,
Spooled_History,
Spooled_Sync
};
public slots:
virtual void doClear();
virtual void optionsUpdate();
// reimplemented
virtual void activated();
protected slots:
virtual bool doSend();
void scrollUp();
void scrollDown();
void updateSendAction();
virtual void chatEditCreated();
virtual void optionChanged(const QString& option);
virtual void updateModelNotices();
private slots:
void initComposing();
void addEmoticon(QString text);
void uploadFile();
void uploadRecentFile(const QString& fileName, const QString& url, qint64 size);
void uploadFinished(const QString& fileName, const QString& url, qint64 size);
void uploadFileStarted(const QString& id);
protected:
// reimplemented
void keyPressEvent(QKeyEvent *);
bool eventFilter(QObject *obj, QEvent *event);
void showEvent(QShowEvent*);
virtual void setShortcuts();
virtual void initUi() = 0;
virtual void doSendMessage(const XMPP::Message& m) = 0;
virtual ChatViewClass* chatView() const = 0;
virtual ChatEdit* chatEdit() const = 0;
#ifdef YAPSI
virtual YaChatViewModel* model() const;
#endif
virtual void setLooks();
QAction* actionSend() const;
virtual bool couldSendMessages() const;
bool highlightersInstalled() const;
private:
bool highlightersInstalled_;
#ifdef YAPSI
YaChatViewModel* model_;
#endif
void initActions();
QAction* act_send_;
QAction* act_scrollup_;
QAction* act_scrolldown_;
QAction* act_close_;
int pending_;
};
#endif

122
src/chateditproxy.cpp Normal file
View file

@ -0,0 +1,122 @@
/*
* chateditproxy.cpp - abstraction to change ChatEdit type in runtime
* Copyright (C) 2007 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "chateditproxy.h"
#include <QVBoxLayout>
#include "msgmle.h"
#include "psioptions.h"
#include "common.h"
ChatEditProxy::ChatEditProxy(QWidget* parent)
: QFrame(parent)
, lineEditEnabled_(false)
, textEdit_(0)
, layout_(0)
{
layout_ = new QVBoxLayout(this);
layout_->setMargin(0);
layout_->setSpacing(0);
connect(PsiOptions::instance(), SIGNAL(optionChanged(const QString&)), SLOT(optionsChanged()));
optionsChanged();
if (!textEdit_)
updateLayout();
}
/**
* If \a enable is true, then the LineEdit is used as internal
* QTextEdit. Updates internal layout if necessary.
*/
void ChatEditProxy::setLineEditEnabled(bool enable)
{
if (lineEditEnabled_ == enable)
return;
lineEditEnabled_ = enable;
updateLayout();
}
/**
* Creates new QTextEdit basing on ChatEditProxy's properties.
*/
ChatEdit* ChatEditProxy::createTextEdit()
{
if (lineEditEnabled())
return new LineEdit(this);
return new ChatEdit(this);
}
/**
* Moves the QTextDocument and QTextCursor data from \a oldTextEdit
* to \a newTextEdit.
*
* NB: Make sure that all QSyntaxHighlighters are detached prior to calling
* this function.
*/
void ChatEditProxy::moveData(QTextEdit* newTextEdit, QTextEdit* oldTextEdit) const
{
QTextDocument* doc = oldTextEdit->document();
QTextCursor cursor = oldTextEdit->textCursor();
doc->setParent(newTextEdit);
oldTextEdit->setDocument(0);
newTextEdit->setDocument(doc);
newTextEdit->setTextCursor(cursor);
}
/**
* Creates new QTextEdit and moves data to it from the old one.
* Text, selection and cursor position are left intact.
*/
void ChatEditProxy::updateLayout()
{
ChatEdit* newEdit = createTextEdit();
newEdit->setFrameShape(frameShape());
if (textEdit_) {
// all syntaxhighlighters should be removed while we move
// the documents around, and should be reattached afterwards
textEdit_->setCheckSpelling(false);
newEdit->setCheckSpelling(false);
moveData(newEdit, textEdit_);
newEdit->setCheckSpelling(PsiOptions::instance()->getOption("options.ui.spell-check.enabled").toBool());
}
delete textEdit_;
textEdit_ = newEdit;
layout_->addWidget(textEdit_);
emit textEditCreated(textEdit_);
}
/**
* Update ChatEdit widget according to current options.
* FIXME: When option.chatLineEdit finally makes it to PsiOptions, make this slot
* private.
*/
void ChatEditProxy::optionsChanged()
{
setLineEditEnabled(option.chatLineEdit);
}

68
src/chateditproxy.h Normal file
View file

@ -0,0 +1,68 @@
/*
* chateditproxy.h - abstraction to change ChatEdit type in runtime
* Copyright (C) 2007 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CHATEDITPROXY_H
#define CHATEDITPROXY_H
#include <QFrame>
class QTextEdit;
class QLayout;
class ChatEdit;
class ChatEditProxy : public QFrame
{
Q_OBJECT
public:
ChatEditProxy(QWidget* parent);
/**
* Returns encapsulated QTextEdit widget.
*/
ChatEdit* chatEdit() const { return textEdit_; }
signals:
/**
* Emitted when internal QTextEdit gets replaced with
* another one.
*/
void textEditCreated(QTextEdit* textEdit);
protected:
/**
* Returns true if line edit mode is enabled.
*/
bool lineEditEnabled() const { return lineEditEnabled_; }
void setLineEditEnabled(bool enable);
public slots:
void optionsChanged();
private:
virtual ChatEdit* createTextEdit();
void moveData(QTextEdit* newTextEdit, QTextEdit* oldTextEdit) const;
void updateLayout();
bool lineEditEnabled_;
ChatEdit* textEdit_;
QLayout* layout_;
};
#endif

167
src/chatsplitter.cpp Normal file
View file

@ -0,0 +1,167 @@
/*
* chatsplitter.cpp - QSplitter replacement that masquerades it
* Copyright (C) 2007 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "chatsplitter.h"
#include <QSplitter>
#include <QVBoxLayout>
#include <QChildEvent>
#include "psioptions.h"
#include "common.h"
/**
* Handy widget that masquerades itself as QSplitter, and could work
* in both QSplitter mode, and QSplitter-less mode.
*/
ChatSplitter::ChatSplitter(QWidget* parent)
: QWidget(parent)
, splitterEnabled_(true)
, childrenCollapsible_(true)
, splitter_(0)
, layout_(0)
{
connect(PsiOptions::instance(), SIGNAL(optionChanged(const QString&)), SLOT(optionsChanged()));
optionsChanged();
if (!layout_)
updateLayout();
}
/**
* Dummy function to fool Qt Designer-generated forms.
*/
void ChatSplitter::setOrientation(Qt::Orientation orientation)
{
Q_ASSERT(orientation == Qt::Vertical);
Q_UNUSED(orientation);
}
/**
* Returns true if child widgets are collapsible in splitter mode.
*/
bool ChatSplitter::childrenCollapsible() const
{
return childrenCollapsible_;
}
/**
* Proxy function that passes its value to the real splitter.
*/
void ChatSplitter::setChildrenCollapsible(bool collapsible)
{
childrenCollapsible_ = collapsible;
if (splitter_)
splitter_->setChildrenCollapsible(childrenCollapsible_);
}
/**
* Adds the given \a widget to the splitter's layout after all the
* other items. Don't call this function if widget is already added
* to the splitter's layout.
*/
void ChatSplitter::addWidget(QWidget* widget)
{
Q_ASSERT(!children_.contains(widget));
children_ << widget;
connect(widget, SIGNAL(destroyed(QObject*)), SLOT(childDestroyed(QObject*)));
updateChildLayout(widget);
}
/**
* If splitter mode is enabled, the \a list is passed to the
* actual splitter. Has no effect otherwise.
*/
void ChatSplitter::setSizes(const QList<int>& list)
{
if (splitter_)
splitter_->setSizes(list);
}
/**
* Moves \a child either to the real QSplitter, or adds it to the
* private layout.
*/
void ChatSplitter::updateChildLayout(QWidget* child)
{
if (splitterEnabled() && splitter_) {
splitter_->addWidget(child);
}
else {
layout_->addWidget(child);
}
}
/**
* Removes destroyed widget from the splitter's child list.
*/
void ChatSplitter::childDestroyed(QObject* obj)
{
Q_ASSERT(obj->isWidgetType());
children_.removeAll(static_cast<QWidget*>(obj));
}
/**
* If \a enable is true, the true QSplitter gets enabled, otherwise
* all widgets are managed by QLayout.
*/
void ChatSplitter::setSplitterEnabled(bool enable)
{
if (splitterEnabled_ == enable)
return;
splitterEnabled_ = enable;
updateLayout();
}
/**
* Invalidates layout and moves all child widgets to the proper position.
*/
void ChatSplitter::updateLayout()
{
foreach(QWidget* child, children_)
child->setParent(this);
delete splitter_;
delete layout_;
splitter_ = new QSplitter(this);
splitter_->setChildrenCollapsible(childrenCollapsible_);
layout_ = new QVBoxLayout(this);
layout_->setMargin(0);
#ifdef YAPSI
layout_->setSpacing(0);
#endif
layout_->addWidget(splitter_);
splitter_->setOrientation(Qt::Vertical);
splitter_->setVisible(splitterEnabled());
foreach(QWidget* child, children_)
updateChildLayout(child);
}
/**
* Updates layout according to current options.
* FIXME: When option.chatLineEdit finally makes it to PsiOptions, make this slot
* private.
*/
void ChatSplitter::optionsChanged()
{
setSplitterEnabled(!option.chatLineEdit);
}

66
src/chatsplitter.h Normal file
View file

@ -0,0 +1,66 @@
/*
* chatsplitter.h - QSplitter replacement that masquerades it
* Copyright (C) 2007 Michail Pishchagin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CHATSPLITTER_H
#define CHATSPLITTER_H
#include <QWidget>
#include <QList>
class QSplitter;
class ChatSplitter : public QWidget
{
Q_OBJECT
public:
ChatSplitter(QWidget* parent);
void setOrientation(Qt::Orientation orientation);
void addWidget(QWidget* widget);
void setSizes(const QList<int>& list);
bool childrenCollapsible() const;
void setChildrenCollapsible(bool collapsible);
protected:
/**
* Returns true if all child widgets are managed by QLayout.
*/
bool splitterEnabled() const { return splitterEnabled_; }
void setSplitterEnabled(bool enable);
public slots:
void optionsChanged();
private slots:
void childDestroyed(QObject* obj);
private:
void updateChildLayout(QWidget* child);
void updateLayout();
bool splitterEnabled_;
bool childrenCollapsible_;
QList<QWidget*> children_;
QSplitter* splitter_;
QLayout* layout_;
};
#endif

486
src/common.cpp Normal file
View file

@ -0,0 +1,486 @@
/*
* common.cpp - contains all the common variables and functions for Psi
* Copyright (C) 2001-2003 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "common.h"
#include <QUrl>
#include <QProcess>
#include <QBoxLayout>
#include <QRegExp>
#include <QFile>
#include <QApplication>
#include <QSound>
#include <QObject>
#include <QMessageBox>
#include <stdio.h>
#ifdef Q_WS_X11
#include <QX11Info>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#endif
#ifdef Q_WS_WIN
#include <windows.h>
#endif
#ifdef Q_WS_MAC
#include <sys/types.h>
#include <sys/stat.h>
#include <Carbon/Carbon.h> // for HIToolbox/InternetConfig
#include <CoreServices/CoreServices.h>
#endif
#include "profiles.h"
#include "rtparse.h"
#include "psievent.h"
#include "psiiconset.h"
#include "applicationinfo.h"
#ifdef YAPSI
#include "yavisualutil.h"
#endif
Qt::WFlags psi_dialog_flags = (Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
Options option;
bool useSound;
QString CAP(const QString &str)
{
return QString("%1: %2").arg(ApplicationInfo::name()).arg(str);
}
// clips plain text
QString clipStatus(const QString &str, int width, int height)
{
QString out = "";
int at = 0;
int len = str.length();
if(len == 0)
return out;
// only take the first "height" lines
for(int n2 = 0; n2 < height; ++n2) {
// only take the first "width" chars
QString line;
bool hasNewline = false;
for(int n = 0; at < len; ++n, ++at) {
if(str.at(at) == '\n') {
hasNewline = true;
break;
}
line += str.at(at);
}
++at;
if((int)line.length() > width) {
line.truncate(width-3);
line += "...";
}
out += line;
if(hasNewline)
out += '\n';
if(at >= len)
break;
}
return out;
}
QString encodePassword(const QString &pass, const QString &key)
{
QString result;
int n1, n2;
if(key.length() == 0)
return pass;
for(n1 = 0, n2 = 0; n1 < pass.length(); ++n1) {
ushort x = pass.at(n1).unicode() ^ key.at(n2++).unicode();
QString hex;
hex.sprintf("%04x", x);
result += hex;
if(n2 >= key.length())
n2 = 0;
}
return result;
}
QString decodePassword(const QString &pass, const QString &key)
{
QString result;
int n1, n2;
if(key.length() == 0)
return pass;
for(n1 = 0, n2 = 0; n1 < pass.length(); n1 += 4) {
ushort x = 0;
if(n1 + 4 > pass.length())
break;
x += QString(pass.at(n1)).toInt(NULL,16)*4096;
x += QString(pass.at(n1+1)).toInt(NULL,16)*256;
x += QString(pass.at(n1+2)).toInt(NULL,16)*16;
x += QString(pass.at(n1+3)).toInt(NULL,16);
QChar c(x ^ key.at(n2++).unicode());
result += c;
if(n2 >= key.length())
n2 = 0;
}
return result;
}
QString status2txt(int status)
{
switch(status) {
case STATUS_OFFLINE: return QObject::tr("Offline");
case STATUS_AWAY: return QObject::tr("Away");
case STATUS_XA: return QObject::tr("Not Available");
case STATUS_DND: return QObject::tr("Do not Disturb");
case STATUS_CHAT: return QObject::tr("Free for Chat");
case STATUS_INVISIBLE: return QObject::tr("Invisible");
case STATUS_ONLINE:
default: return QObject::tr("Online");
}
}
QString logencode(QString str)
{
str.replace(QRegExp("\\\\"), "\\\\"); // backslash to double-backslash
str.replace(QRegExp("\\|"), "\\p"); // pipe to \p
str.replace(QRegExp("\n"), "\\n"); // newline to \n
return str;
}
QString logdecode(const QString &str)
{
QString ret;
for(int n = 0; n < str.length(); ++n) {
if(str.at(n) == '\\') {
++n;
if(n >= str.length())
break;
if(str.at(n) == 'n')
ret.append('\n');
if(str.at(n) == 'p')
ret.append('|');
if(str.at(n) == '\\')
ret.append('\\');
}
else {
ret.append(str.at(n));
}
}
return ret;
}
bool fileCopy(const QString &src, const QString &dest)
{
QFile in(src);
QFile out(dest);
if(!in.open(QIODevice::ReadOnly))
return FALSE;
if(!out.open(QIODevice::WriteOnly))
return FALSE;
char *dat = new char[16384];
int n = 0;
while(!in.atEnd()) {
n = in.readBlock(dat, 16384);
if(n == -1) {
delete dat;
return FALSE;
}
out.writeBlock(dat, n);
}
delete dat;
out.close();
in.close();
return TRUE;
}
void soundPlay(const QString &str)
{
if(str == "!beep") {
QApplication::beep();
return;
}
if(!QFile::exists(str))
return;
#if defined(Q_WS_WIN) || defined(Q_WS_MAC)
QSound::play(str);
#else
if(!option.player.isEmpty()) {
QStringList args;
args = QStringList::split(' ', option.player);
args += str;
QString prog = args.takeFirst();
QProcess::startDetached(prog, args);
}
#endif
}
XMPP::Status makeStatus(int x, const QString &str, int priority)
{
XMPP::Status s = makeStatus(x,str);
if (priority > 127)
s.setPriority(127);
else if (priority < -128)
s.setPriority(-128);
else
s.setPriority(priority);
return s;
}
XMPP::Status makeStatus(int x, const QString &str)
{
return XMPP::Status(static_cast<XMPP::Status::Type>(x), str);
}
XMPP::Status::Type makeSTATUS(const XMPP::Status &s)
{
return s.type();
}
#include <qlayout.h>
QLayout *rw_recurseFindLayout(QLayout *lo, QWidget *w)
{
//printf("scanning layout: %p\n", lo);
QLayoutIterator it = lo->iterator();
for(QLayoutItem *i; (i = it.current()); ++it) {
//printf("found: %p,%p\n", i->layout(), i->widget());
QLayout *slo = i->layout();
if(slo) {
QLayout *tlo = rw_recurseFindLayout(slo, w);
if(tlo)
return tlo;
}
else if(i->widget() == w)
return lo;
}
return 0;
}
QLayout *rw_findLayoutOf(QWidget *w)
{
return rw_recurseFindLayout(w->parentWidget()->layout(), w);
}
void replaceWidget(QWidget *a, QWidget *b)
{
if(!a)
return;
QLayout *lo = rw_findLayoutOf(a);
if(!lo)
return;
//printf("decided on this: %p\n", lo);
if(lo->inherits("QBoxLayout")) {
QBoxLayout *bo = (QBoxLayout *)lo;
int n = bo->findWidget(a);
bo->insertWidget(n+1, b);
delete a;
}
}
void closeDialogs(QWidget *w)
{
// close qmessagebox?
QList<QDialog*> dialogs;
QObjectList list = w->children();
for(QObjectList::Iterator it = list.begin() ; it != list.end(); ++it) {
if((*it)->inherits("QDialog"))
dialogs.append((QDialog *)(*it));
}
for(QList<QDialog*>::Iterator w = dialogs.begin(); w != dialogs.end(); ++w) {
(*w)->close();
}
}
#ifdef Q_WS_X11
#include <X11/Xlib.h>
#include <X11/Xutil.h> // needed for WM_CLASS hinting
void x11wmClass(Display *dsp, WId wid, QString resName)
{
char app_name[] = "yachat";
//Display *dsp = x11Display(); // get the display
//WId win = winId(); // get the window
XClassHint classhint; // class hints
classhint.res_name = (char *)resName.latin1(); // res_name
classhint.res_class = app_name; // res_class
XSetClassHint(dsp, wid, &classhint); // set the class hints
}
//>>>-- Nathaniel Gray -- Caltech Computer Science ------>
//>>>-- Mojave Project -- http://mojave.cs.caltech.edu -->
// Copied from http://www.nedit.org/archives/discuss/2002-Aug/0386.html
// Helper function
bool getCardinal32Prop(Display *display, Window win, char *propName, long *value)
{
Atom nameAtom, typeAtom, actual_type_return;
int actual_format_return, result;
unsigned long nitems_return, bytes_after_return;
long *result_array=NULL;
nameAtom = XInternAtom(display, propName, False);
typeAtom = XInternAtom(display, "CARDINAL", False);
if (nameAtom == None || typeAtom == None) {
//qDebug("Atoms not interned!");
return false;
}
// Try to get the property
result = XGetWindowProperty(display, win, nameAtom, 0, 1, False,
typeAtom, &actual_type_return, &actual_format_return,
&nitems_return, &bytes_after_return,
(unsigned char **)&result_array);
if( result != Success ) {
//qDebug("not Success");
return false;
}
if( actual_type_return == None || actual_format_return == 0 ) {
//qDebug("Prop not found");
return false;
}
if( actual_type_return != typeAtom ) {
//qDebug("Wrong type atom");
}
*value = result_array[0];
XFree(result_array);
return true;
}
// Get the desktop number that a window is on
bool desktopOfWindow(Window *window, long *desktop)
{
Display *display = QX11Info::display();
bool result = getCardinal32Prop(display, *window, (char *)"_NET_WM_DESKTOP", desktop);
//if( result )
// qDebug("Desktop: " + QString::number(*desktop));
return result;
}
// Get the current desktop the WM is displaying
bool currentDesktop(long *desktop)
{
Window rootWin;
Display *display = QX11Info::display();
bool result;
rootWin = RootWindow(QX11Info::display(), XDefaultScreen(QX11Info::display()));
result = getCardinal32Prop(display, rootWin, (char *)"_NET_CURRENT_DESKTOP", desktop);
//if( result )
// qDebug("Current Desktop: " + QString::number(*desktop));
return result;
}
#endif
#ifdef Q_WS_WIN
// ripped from advwidget.cpp
extern bool ForceForegroundWindow(HWND hwnd);
#endif
void bringToFront(QWidget *widget, bool grabFocus)
{
Q_ASSERT(widget);
QWidget* w = widget->window();
#ifdef Q_WS_X11
// If we're not on the current desktop, do the hide/show trick
long dsk, curr_dsk;
Window win = w->winId();
if(desktopOfWindow(&win, &dsk) && currentDesktop(&curr_dsk)) {
if((dsk != curr_dsk) && (dsk != -1)) { // second condition for sticky windows
w->hide();
}
}
// FIXME: multi-desktop hacks for Win and Mac required
#endif
if(w->isMaximized())
w->showMaximized();
else
w->showNormal();
//if(grabFocus)
// w->setActiveWindow();
w->raise();
w->activateWindow();
#ifdef Q_WS_WIN
// ONLINE-1441
bool staysOnTop = GetWindowLong(w->winId(), GWL_EXSTYLE) & WS_EX_TOPMOST;
if (!staysOnTop && grabFocus) {
// ONLINE-914: Work-around for chat window now being properly deactivated
// after it got ForceForegroundWindow'd by clicking on Online toaster
QWidget tmp;
tmp.move(0, Ya::VisualUtil::belowScreenGeometry() + 100);
ForceForegroundWindow(tmp.winId());
}
// TODO: unify with AdvancedWidget::bringToFront()
ForceForegroundWindow(w->winId());
#else
Q_UNUSED(grabFocus);
#endif
}
bool operator!=(const QMap<QString, QString> &m1, const QMap<QString, QString> &m2)
{
if ( m1.size() != m2.size() )
return true;
QMap<QString, QString>::ConstIterator it = m1.begin(), it2;
for ( ; it != m1.end(); ++it) {
it2 = m2.find( it.key() );
if ( it2 == m2.end() )
return true;
if ( it.data() != it2.data() )
return true;
}
return false;
}

265
src/common.h Normal file
View file

@ -0,0 +1,265 @@
/*
* common.h - contains all the common variables and functions for Psi
* Copyright (C) 2001-2003 Justin Karneges
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef COMMON_H
#define COMMON_H
#include <QString>
#include <QMap>
#include <QSize>
#include <QStringList>
#include <QList>
#include <QColor>
#include "statuspreset.h"
// -----------------------------------------------------------------------------
// Options
// -----------------------------------------------------------------------------
enum {
cOnline,
cOffline,
cAway,
cDND,
cProfileFore,
cProfileBack,
cGroupFore,
cGroupBack,
cListBack,
cAnimFront,
cAnimBack,
cStatus,
cNumColors // A guard to store the number of colors
};
enum { fRoster, fMessage, fChat, fPopup };
enum { eMessage, eChat1, eChat2, eHeadline, eSystem, eOnline, eOffline, eSend, eIncomingFT, eFTComplete,
#ifdef YAPSI
eSubscribe, eUnsubscribe,
#endif
eSoundLast
};
enum { dcClose, dcHour, dcDay, dcNever };
struct Options
{
QColor color[cNumColors];
QString font[4];
int smallFontSize; // do not modify or save/load this value! it is calculated at run time!
int alertStyle;
QString systemIconset;
QStringList emoticons;
QString defaultRosterIconset;
QMap<QString, QString> serviceRosterIconset;
QMap<QString, QString> customRosterIconset;
bool useleft, singleclick, hideMenubar, askOnline, askOffline, popupMsgs, popupChats, popupHeadlines, popupFiles, raise;
bool alwaysOnTop, noAwaySound, noAwayPopup, noUnlistedPopup, rosterAnim, autoVCardOnLogin, xmlConsoleOnLogin;
bool useDock, dockDCstyle, dockHideMW, dockToolMW;
bool smallChats, chatLineEdit, useTabs, putTabsAtBottom, usePerTabCloseButton, autoRosterSize, autoRosterSizeGrowTop, autoResolveNicksOnAdd, brushedMetal;
bool autoCopy; // although this setting is ignored under linux,
// it is preserved in case user uses the same config file on different platforms
bool useCaps;
bool oldSmallChats; //Filthy hack, see chat code.
int delChats, browser;
bool useRC;
int defaultAction;
int incomingAs;
QStringList recentStatus; //recent status messages
QMap<QString,StatusPreset> sp; // Status message presets.
int asAway, asXa, asOffline;
bool use_asAway, use_asXa, use_asOffline;
QString asMessage;
QString onevent[eSoundLast];
// Added by Kiko 020621: points to the directory where the trusted
// certificates used in validating the server's certificate are kept
QString trustCertStoreDir;
QString player;
bool ignoreHeadline, ignoreNonRoster, excludeGroupChatsFromIgnore, scrollTo, keepSizes, useEmoticons, alertOpenChats;
bool raiseChatWindow, showSubjects, showCounter, chatSays, showGroupCounts;
QSize sizeEventDlg, sizeChatDlg, sizeTabDlg;
bool jidComplete, grabUrls, noGCSound;
struct ToolbarPrefs {
bool dirty;
QString name;
bool on;
bool locked;
bool stretchable;
QStringList keys;
Qt::Dock dock;
int index;
bool nl;
int extraOffset;
};
QMap< QString, QList<ToolbarPrefs> > toolbars;
// groupchat highlighting/nick colouring
bool gcHighlighting, gcNickColoring;
QStringList gcHighlights, gcNickColors;
bool clNewHeadings;
bool outlineHeadings;
// passive popups
bool ppIsOn;
bool ppMessage, ppHeadline, ppChat, ppOnline, ppStatus, ppOffline, ppFile;
int ppJidClip, ppStatusClip, ppTextClip, ppHideTime;
QColor ppBorderColor;
// Bouncing of the dock (Mac OS X)
typedef enum { NoBounce, BounceOnce, BounceForever } BounceDockSetting;
BounceDockSetting bounceDock;
struct {
bool roster, services;
} lockdown;
bool useTransportIconsForContacts;
// roster sorting styles
enum Roster_ContactSortStyle {
ContactSortStyle_Status = 0,
ContactSortStyle_Alpha
};
Roster_ContactSortStyle rosterContactSortStyle;
enum Roster_GroupSortStyle {
GroupSortStyle_Alpha = 0,
GroupSortStyle_Rank
};
Roster_GroupSortStyle rosterGroupSortStyle;
enum Roster_AccountSortStyle {
AccountSortStyle_Alpha = 0,
AccountSortStyle_Rank
};
Roster_AccountSortStyle rosterAccountSortStyle;
bool discoItems, discoInfo;
bool autoAuth, notifyAuth;
// event priority
enum { EventPriorityDontCare = -1 };
int eventPriorityHeadline;
int eventPriorityChat;
int eventPriorityMessage;
int eventPriorityAuth;
int eventPriorityFile;
int eventPriorityRosterExchange;
// Message events
bool messageEvents;
bool inactiveEvents;
int dtPort;
QString dtExternal;
// Last used path remembering
QString lastPath;
QString lastSavePath;
//background images
QString chatBgImage, rosterBgImage;
};
extern
Options option;
// -----------------------------------------------------------------------------
// Status
// -----------------------------------------------------------------------------
#include "xmpp_status.h"
#define STATUS_OFFLINE XMPP::Status::Offline
#define STATUS_ONLINE XMPP::Status::Online
#define STATUS_AWAY XMPP::Status::Away
#define STATUS_XA XMPP::Status::XA
#define STATUS_DND XMPP::Status::DND
#define STATUS_INVISIBLE XMPP::Status::Invisible
#define STATUS_CHAT XMPP::Status::FFC
#define STATUS_ASK 100
#define STATUS_NOAUTH 101
#define STATUS_ERROR 102
QString status2txt(int status);
XMPP::Status makeStatus(int, const QString &);
XMPP::Status makeStatus(int, const QString &, int);
XMPP::Status::Type makeSTATUS(const XMPP::Status &);
QString clipStatus(const QString &str, int width, int height);
// -----------------------------------------------------------------------------
// Widget tools
// -----------------------------------------------------------------------------
void bringToFront(QWidget *w, bool grabFocus = true);
void replaceWidget(QWidget *, QWidget *);
void closeDialogs(QWidget *);
#ifdef Q_WS_X11
#include <QWidget>
void x11wmClass(Display *dsp, WId wid, QString resName);
#define X11WM_CLASS(x) x11wmClass(x11Display(), winId(), (x));
#else
#define X11WM_CLASS(x) /* dummy */
#endif
// -----------------------------------------------------------------------------
// History utilities
// -----------------------------------------------------------------------------
QString logencode(QString);
QString logdecode(const QString &);
// -----------------------------------------------------------------------------
// Misc.
// -----------------------------------------------------------------------------
QString CAP(const QString &str);
QString encodePassword(const QString &, const QString &);
QString decodePassword(const QString &, const QString &);
bool operator!=(const QMap<QString, QString> &, const QMap<QString, QString> &);
bool fileCopy(const QString &src, const QString &dest);
void soundPlay(const QString &);
extern Qt::WFlags psi_dialog_flags;
extern bool useSound;
#endif

29
src/conf_iris.pri Normal file
View file

@ -0,0 +1,29 @@
include(../conf.pri)
windows:include(../conf_windows.pri)
CONFIG += iris_bundle
# don't build iris apps
CONFIG += no_tests
# use qca from psi if necessary
qca-static {
DEFINES += QCA_STATIC
INCLUDEPATH += $$PWD/../third-party/qca/qca/include/QtCrypto
}
else {
CONFIG += crypto
}
# use zlib from psi if necessary
psi-zip {
INCLUDEPATH += $$PWD/tools/zip/minizip/win32
}
mac {
# Universal binaries
qc_universal {
CONFIG += x86 x86_64
QMAKE_MAC_SDK=/Developer/SDKs/MacOSX10.5.sdk
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.5
}
}

125
src/conferencebookmark.cpp Normal file
View file

@ -0,0 +1,125 @@
/*
* conferencebookmark.cpp
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include <QDomDocument>
#include <QDomElement>
#include "conferencebookmark.h"
#include "xmpp_xmlcommon.h"
ConferenceBookmark::ConferenceBookmark(const QString& name, const XMPP::Jid& jid, bool auto_join, const QString& nick, const QString& password)
: name_(name)
, jid_(jid)
, auto_join_(auto_join)
, nick_(nick)
, password_(password)
{
}
ConferenceBookmark::ConferenceBookmark(const QDomElement& el)
: auto_join_(false)
{
fromXml(el);
}
void ConferenceBookmark::setName(const QString& name)
{
name_ = name;
}
const QString& ConferenceBookmark::name() const
{
return name_;
}
const XMPP::Jid& ConferenceBookmark::jid() const
{
return jid_;
}
bool ConferenceBookmark::autoJoin() const
{
return auto_join_;
}
const QString& ConferenceBookmark::nick() const
{
return nick_;
}
const QString& ConferenceBookmark::password() const
{
return password_;
}
bool ConferenceBookmark::isNull() const
{
return name_.isEmpty() && jid_.isEmpty();
}
void ConferenceBookmark::fromXml(const QDomElement& e)
{
jid_ = e.attribute("jid");
name_ = e.attribute("name");
if (e.attribute("autojoin") == "true" || e.attribute("autojoin") == "1")
auto_join_ = true;
for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement i = n.toElement();
if (i.isNull())
continue;
else if (i.tagName() == "nick") {
nick_ = i.text();
}
else if (i.tagName() == "password") {
password_ = i.text();
}
}
}
QDomElement ConferenceBookmark::toXml(QDomDocument& doc) const
{
QDomElement e = doc.createElement("conference");
e.setAttribute("jid",jid_.full());
e.setAttribute("name",name_);
if (auto_join_)
e.setAttribute("autojoin","true");
if (!nick_.isEmpty())
e.appendChild(textTag(&doc,"nick",nick_));
if (!password_.isEmpty())
e.appendChild(textTag(&doc,"password",password_));
return e;
}
bool ConferenceBookmark::operator==(const ConferenceBookmark & other) const
{
return
name_ == other.name_ &&
jid_.full() == other.jid_.full() &&
auto_join_ == other.auto_join_ &&
nick_ == other.nick_ &&
password_ == other.password_;
}
bool ConferenceBookmark::operator!=(const ConferenceBookmark& other) const
{
return !operator==(other);
}

60
src/conferencebookmark.h Normal file
View file

@ -0,0 +1,60 @@
/*
* conferencebookmark.h
* Copyright (C) 2006 Remko Troncon
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CONFERENCEBOOKMARK_H
#define CONFERENCEBOOKMARK_H
#include <QString>
#include "xmpp_jid.h"
class QDomElement;
class QDomDocument;
class ConferenceBookmark
{
public:
ConferenceBookmark(const QString& name, const XMPP::Jid& jid, bool auto_join, const QString& nick = QString(), const QString& password = QString());
ConferenceBookmark(const QDomElement&);
void setName(const QString& name);
const QString& name() const;
const XMPP::Jid& jid() const;
bool autoJoin() const;
const QString& nick() const;
const QString& password() const;
bool isNull() const;
void fromXml(const QDomElement&);
QDomElement toXml(QDomDocument&) const;
bool operator==(const ConferenceBookmark& other) const;
bool operator!=(const ConferenceBookmark& other) const;
private:
QString name_;
XMPP::Jid jid_;
bool auto_join_;
QString nick_;
QString password_;
};
#endif

396
src/contactlistgroup.cpp Normal file
View file

@ -0,0 +1,396 @@
/*
* contactlistgroup.cpp - flat contact list group class
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "contactlistgroup.h"
#include <QTimer>
#include "contactlistitemproxy.h"
#include "contactlistgroupmenu.h"
#include "contactlistmodel.h"
#include "psicontact.h"
#include "contactlistgroupstate.h"
#include "contactlistgroupcache.h"
#include "fakegroupcontact.h"
static QString GROUP_DELIMITER = "::";
/**
* Flat group class.
*/
ContactListGroup::ContactListGroup(ContactListModel* model, ContactListGroup* parent)
: ContactListItem()
, model_(model)
, parent_(parent)
, updateOnlineContactsTimer_(0)
, haveOnlineContacts_(false)
{
updateOnlineContactsTimer_ = new QTimer(this);
connect(updateOnlineContactsTimer_, SIGNAL(timeout()), SLOT(updateOnlineContactsFlag()));
updateOnlineContactsTimer_->setSingleShot(true);
updateOnlineContactsTimer_->setInterval(100);
}
ContactListGroup::~ContactListGroup()
{
foreach(PsiContact* contact, contacts_)
removeContact(contact);
}
QString ContactListGroup::fullName() const
{
QStringList name;
const ContactListGroup* group = this;
while (group) {
if (!group->name().isEmpty())
name.prepend(group->name());
group = group->parent();
}
return name.join(groupDelimiter());
}
const QString& ContactListGroup::groupDelimiter()
{
return GROUP_DELIMITER;
}
void ContactListGroup::setGroupDelimiter(const QString& str)
{
GROUP_DELIMITER = str;
}
QString ContactListGroup::sanitizeGroupName(const QString& name) const
{
return name.split(groupDelimiter(), QString::SkipEmptyParts).join(groupDelimiter());
}
QStringList ContactListGroup::sanitizeGroupNames(const QStringList& names) const
{
QStringList sanitized;
foreach(QString name, names) {
sanitized.append(sanitizeGroupName(name));
}
return sanitized;
}
ContactListModel::Type ContactListGroup::type() const
{
return ContactListModel::GroupType;
}
const QString& ContactListGroup::name() const
{
return name_;
}
void ContactListGroup::setName(const QString& name)
{
model()->renameGroup(this, name);
}
void ContactListGroup::internalSetName(const QString& name)
{
if (!name_.isNull())
model_->groupCache()->removeGroup(this);
name_ = name;
if (!name_.isNull())
model_->groupCache()->addGroup(this);
}
bool ContactListGroup::isExpandable() const
{
return true;
}
bool ContactListGroup::expanded() const
{
return model()->groupState()->groupExpanded(this);
}
void ContactListGroup::setExpanded(bool expanded)
{
model()->groupState()->setGroupExpanded(this, expanded);
}
bool ContactListGroup::isEditable() const
{
bool result = false;
foreach(ContactListItemProxy* proxy, items_) {
if (proxy->item()) {
if (proxy->item()->isEditable()) {
result = true;
break;
}
}
}
return result && model()->contactList()->haveAvailableAccounts();
}
bool ContactListGroup::isRemovable() const
{
bool result = false;
foreach(ContactListItemProxy* proxy, items_) {
if (proxy->item()) {
if (proxy->item()->isRemovable()) {
result = true;
break;
}
}
}
return result && isEditable();
}
ContactListItemMenu* ContactListGroup::contextMenu(ContactListModel* model)
{
return new ContactListGroupMenu(this, model);
}
void ContactListGroup::addItem(ContactListItemProxy* item)
{
Q_ASSERT(!items_.contains(item));
int index = items_.count();
model_->itemAboutToBeInserted(this, index);
items_.append(item);
model_->insertedItem(this, index);
updateOnlineContactsTimer_->start();
}
void ContactListGroup::removeItem(ContactListItemProxy* item)
{
int index = items_.indexOf(item);
Q_ASSERT(index != -1);
model_->itemAboutToBeRemoved(this, index);
items_.remove(index);
delete item;
model_->removedItem(this, index);
updateOnlineContactsTimer_->start();
}
void ContactListGroup::addContact(PsiContact* contact, QStringList contactGroups)
{
if (contactGroups.isEmpty())
return;
if (findContact(contact))
return;
Q_ASSERT(!contacts_.contains(contact));
// qWarning("ContactListGroup(%x)::addContact: %s (items = %d, contacts = %d)", this, qPrintable(contact->jid().full()), items_.count(), contacts_.count());
contacts_.append(contact);
addItem(new ContactListItemProxy(this, contact));
model_->groupCache()->addContact(this, contact);
}
void ContactListGroup::removeContact(PsiContact* contact)
{
int index = contacts_.indexOf(contact);
Q_ASSERT(index != -1);
// qWarning("ContactListGroup(%x)::removeContact: %s (items = %d, contacts = %d)", this, qPrintable(contact->jid().full()), items_.count(), contacts_.count());
removeItem(findContact(contact));
contacts_.remove(index);
model_->groupCache()->removeContact(this, contact);
}
// Some room for future optimizations here
ContactListItemProxy* ContactListGroup::findContact(PsiContact* contact) const
{
foreach(ContactListItemProxy* item, items_)
if (item->item() == contact)
return item;
return 0;
}
ContactListItemProxy* ContactListGroup::findGroup(ContactListGroup* group) const
{
foreach(ContactListItemProxy* item, items_)
if (item->item() == group)
return item;
return 0;
}
void ContactListGroup::contactUpdated(PsiContact* contact)
{
ContactListItemProxy* item = findContact(contact);
if (!item)
return;
updateOnlineContactsTimer_->start();
model_->updatedItem(item);
}
void ContactListGroup::contactGroupsChanged(PsiContact* contact, QStringList contactGroups)
{
if (contactGroups.isEmpty()) {
if (contacts_.contains(contact))
removeContact(contact);
}
else if (!findContact(contact)) {
addContact(contact, contactGroups);
}
updateOnlineContactsTimer_->start();
}
ContactListItemProxy* ContactListGroup::item(int index) const
{
Q_ASSERT(index >= 0);
Q_ASSERT(index < items_.count());
return items_.at(index);
}
int ContactListGroup::itemsCount() const
{
return items_.count();
}
// Some room for optimizations here
int ContactListGroup::indexOf(const ContactListItem* item) const
{
for (int i = 0; i < items_.count(); ++i)
if (items_.at(i)->item() == item)
return i;
Q_ASSERT(false);
return -1;
}
ContactListGroup* ContactListGroup::parent() const
{
return parent_;
}
QModelIndex ContactListGroup::toModelIndex() const
{
if (!parent())
return QModelIndex();
int index = parent()->indexOf(this);
return model()->itemProxyToModelIndex(parent()->item(index), index);
}
const QVector<ContactListItemProxy*>& ContactListGroup::items() const
{
return items_;
}
bool ContactListGroup::haveOnlineContacts() const
{
return haveOnlineContacts_;
}
int ContactListGroup::contactsCount() const
{
return contacts_.count();
}
void ContactListGroup::updateOnlineContactsFlag()
{
updateOnlineContactsTimer_->stop();
if (!parent())
return;
bool haveOnlineContacts = false;
foreach(ContactListItemProxy* item, items_) {
PsiContact* contact = 0;
ContactListGroup* group = 0;
if ((contact = dynamic_cast<PsiContact*>(item->item()))) {
if (contact->isOnline()) {
haveOnlineContacts = true;
break;
}
}
else if ((group = dynamic_cast<ContactListGroup*>(item->item()))) {
if (group->haveOnlineContacts()) {
haveOnlineContacts = true;
break;
}
}
}
if (haveOnlineContacts_ != haveOnlineContacts) {
haveOnlineContacts_ = haveOnlineContacts;
if (parent()) {
parent()->updateOnlineContactsFlag();
model_->updatedGroupVisibility(this);
}
}
}
bool ContactListGroup::isFake() const
{
if (items_.count() != contacts_.count())
return false;
foreach(PsiContact* contact, contacts_) {
if (!contact->isFake())
return false;
}
return name().startsWith(FakeGroupContact::defaultGroupName());
}
bool ContactListGroup::compare(const ContactListItem* other) const
{
// this code creates very weird user experience
// const PsiContact* contact = dynamic_cast<const PsiContact*>(other);
// if (contact) {
// if (isFake())
// return true;
// }
const ContactListGroup* group = dynamic_cast<const ContactListGroup*>(other);
if (group) {
int order = model()->groupState()->groupOrder(group) - model()->groupState()->groupOrder(this);
if (order) {
return order > 0;
}
}
return ContactListItem::compare(other);
}
#ifdef UNIT_TEST
void ContactListGroup::dumpTree(int indent) const
{
dumpInfo(this, 0);
foreach(ContactListItemProxy* item, items_) {
ContactListGroup* group = dynamic_cast<ContactListGroup*>(item->item());
if (group)
dumpTree(indent + 1);
else
dumpInfo(item->item(), indent + 1);
}
}
void ContactListGroup::dumpInfo(const ContactListItem* item, int indent) const
{
qWarning("%sname = '%s', type = %s", qPrintable(QString(indent, ' ')),
qPrintable(item->name()),
dynamic_cast<const ContactListGroup*>(item) ? "group" : "contact");
}
void ContactListGroup::dumpTree() const
{
dumpTree(0);
}
#endif

115
src/contactlistgroup.h Normal file
View file

@ -0,0 +1,115 @@
/*
* contactlistgroup.h - flat contact list group class
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CONTACTLISTGROUP_H
#define CONTACTLISTGROUP_H
#include "contactlistitem.h"
#include <QVector>
#include <QModelIndex>
class QTimer;
class PsiContact;
class ContactListModel;
class ContactListItemMenu;
class ContactListItemProxy;
class ContactListGroup : public ContactListItem
{
Q_OBJECT
public:
ContactListGroup(ContactListModel* model, ContactListGroup* parent);
~ContactListGroup();
ContactListItemProxy* item(int index) const;
int itemsCount() const;
int indexOf(const ContactListItem* item) const;
ContactListModel* model() const { return model_; }
ContactListGroup* parent() const;
QModelIndex toModelIndex() const;
bool isFake() const;
bool haveOnlineContacts() const;
int contactsCount() const;
// QStringList:
// 1. Пустой, значит добавлять ни в коем случае не надо
// 2. Элемент — пустая строка — это значит что надо добавлять в General-группу
// 3. Элементы — непустые строки — названия групп, включая сепараторы (сплитить отдельно)
virtual void addContact(PsiContact* contact, QStringList contactGroups);
virtual void contactUpdated(PsiContact* contact);
// модель фильтрует группы перед запуском contactGroupsChanged
virtual void contactGroupsChanged(PsiContact* contact, QStringList contactGroups);
QString fullName() const;
// reimplemented
virtual ContactListModel::Type type() const;
virtual const QString& name() const;
virtual void setName(const QString& name);
virtual bool isEditable() const;
virtual bool isRemovable() const;
virtual bool isExpandable() const;
virtual bool expanded() const;
virtual void setExpanded(bool expanded);
virtual ContactListItemMenu* contextMenu(ContactListModel* model);
virtual bool compare(const ContactListItem* other) const;
static const QString& groupDelimiter();
static void setGroupDelimiter(const QString&);
QString sanitizeGroupName(const QString&) const;
QStringList sanitizeGroupNames(const QStringList& names) const;
#ifdef UNIT_TEST
void dumpTree() const;
#endif
protected:
void addItem(ContactListItemProxy* item);
void removeItem(ContactListItemProxy* item);
ContactListItemProxy* findContact(PsiContact* contact) const;
ContactListItemProxy* findGroup(ContactListGroup* group) const;
const QVector<ContactListItemProxy*>& items() const;
void internalSetName(const QString& name);
public slots:
void updateOnlineContactsFlag();
private:
ContactListModel* model_;
ContactListGroup* parent_;
QTimer* updateOnlineContactsTimer_;
QString name_;
QVector<PsiContact*> contacts_;
QVector<ContactListItemProxy*> items_;
bool haveOnlineContacts_;
void removeContact(PsiContact* contact);
#ifdef UNIT_TEST
void dumpInfo(const ContactListItem* item, int indent) const;
void dumpTree(int indent) const;
#endif
};
#endif

View file

@ -0,0 +1,100 @@
/*
* contactlistgroupcache.cpp - contact list group cache
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "contactlistgroupcache.h"
#include <QStringList>
#include "contactlistgroup.h"
#include "psicontact.h"
ContactListGroupCache::ContactListGroupCache(QObject *parent)
: QObject(parent)
{
}
ContactListGroupCache::~ContactListGroupCache()
{
}
QStringList ContactListGroupCache::groups() const
{
return groups_.keys();
}
QList<ContactListGroup*> ContactListGroupCache::groupsFor(PsiContact* contact) const
{
return contacts_[contact];
}
ContactListGroup* ContactListGroupCache::findGroup(const QString& fullName) const
{
return groups_[fullName];
}
void ContactListGroupCache::addContact(ContactListGroup* group, PsiContact* contact)
{
Q_ASSERT(group);
Q_ASSERT(contact);
if (!contacts_.contains(contact))
contacts_[contact] = QList<ContactListGroup*>();
if (!contacts_[contact].contains(group))
contacts_[contact] << group;
}
void ContactListGroupCache::removeContact(ContactListGroup* group, PsiContact* contact)
{
Q_ASSERT(group);
Q_ASSERT(contact);
if (contacts_[contact].contains(group))
contacts_[contact].remove(group);
if (contacts_[contact].isEmpty())
contacts_.remove(contact);
}
void ContactListGroupCache::addGroup(ContactListGroup* group)
{
Q_ASSERT(group);
groups_[group->fullName()] = group;
}
void ContactListGroupCache::removeGroup(ContactListGroup* group)
{
groups_.remove(group->fullName());
}
bool ContactListGroupCache::hasContacts(bool onlineOnly) const
{
bool result = false;
QHashIterator<PsiContact*, QList<ContactListGroup*> > it(contacts_);
while (it.hasNext()) {
it.next();
if (!it.key()->isHidden()) {
if (onlineOnly && !it.key()->isOnline())
continue;
result = true;
break;
}
}
return result;
}

View file

@ -0,0 +1,53 @@
/*
* contactlistgroupcache.h - contact list group cache
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CONTACTLISTGROUPCACHE_H
#define CONTACTLISTGROUPCACHE_H
#include <QHash>
class ContactListGroup;
class PsiContact;
class ContactListGroupCache : public QObject
{
Q_OBJECT
public:
ContactListGroupCache(QObject *parent);
~ContactListGroupCache();
QStringList groups() const;
bool hasContacts(bool onlineOnly) const;
QList<ContactListGroup*> groupsFor(PsiContact* contact) const;
ContactListGroup* findGroup(const QString& fullName) const;
void addContact(ContactListGroup* group, PsiContact* contact);
void removeContact(ContactListGroup* group, PsiContact* contact);
void addGroup(ContactListGroup* group);
void removeGroup(ContactListGroup* group);
private:
QHash<PsiContact*, QList<ContactListGroup*> > contacts_;
QHash<QString, ContactListGroup*> groups_;
};
#endif

View file

@ -0,0 +1,117 @@
/*
* contactlistgroupmenu.cpp - context menu for contact list groups
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "contactlistgroupmenu.h"
#include <QPointer>
#include "iconaction.h"
#include "iconset.h"
#include "contactlistgroup.h"
#include "psioptions.h"
#include "shortcutmanager.h"
#include "psicontactlist.h"
class ContactListGroupMenu::Private : public QObject
{
Q_OBJECT
QPointer<ContactListGroup> group;
IconAction* act_rename;
QAction* act_removeGroupAndContacts;
QAction* act_addGroup;
public:
Private(ContactListGroupMenu* menu, ContactListGroup* _group)
: QObject(0)
, group(_group)
, menu_(menu)
{
connect(menu, SIGNAL(aboutToShow()), SLOT(updateActions()));
act_rename = new IconAction("", tr("Re&name"), menu->shortcuts("contactlist.rename"), this, "act_rename");
connect(act_rename, SIGNAL(activated()), this, SLOT(rename()));
act_removeGroupAndContacts = new QAction(tr("&Remove"), this);
act_removeGroupAndContacts->setShortcuts(ShortcutManager::instance()->shortcuts("contactlist.delete"));
connect(act_removeGroupAndContacts, SIGNAL(activated()), SLOT(removeGroupAndContacts()));
act_addGroup = new QAction(tr("&Add group..."), this);
connect(act_addGroup, SIGNAL(activated()), SLOT(addGroup()));
updateActions();
// menu->addAction(act_sendMessage);
menu->addAction(act_rename);
// menu->addSeparator();
// menu->addAction(act_removeGroup); // disassociates all contacts with this group
menu->addAction(act_removeGroupAndContacts); // removes all contacts within this group
menu->addAction(act_addGroup);
}
private slots:
void updateActions()
{
if (!group)
return;
act_rename->setEnabled(group->isEditable());
act_removeGroupAndContacts->setEnabled(group->isRemovable());
act_addGroup->setEnabled(group->model()->contactList()->haveAvailableAccounts());
}
void rename()
{
if (group) {
// group->contactList()->emitRename();
menu_->model()->renameSelectedItem();
}
}
void removeGroupAndContacts()
{
if (group) {
emit menu_->removeSelection();
}
}
void addGroup()
{
if (group) {
emit menu_->addGroup();
}
}
private:
ContactListGroupMenu* menu_;
};
ContactListGroupMenu::ContactListGroupMenu(ContactListGroup* group, ContactListModel* model)
: ContactListItemMenu(group, model)
{
d = new Private(this, group);
}
ContactListGroupMenu::~ContactListGroupMenu()
{
delete d;
}
#include "contactlistgroupmenu.moc"

View file

@ -0,0 +1,44 @@
/*
* contactlistgroupmenu.h - context menu for contact list groups
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef PSICONTACTGROUPMENU_H
#define PSICONTACTGROUPMENU_H
#include "contactlistitemmenu.h"
class ContactListGroup;
class ContactListGroupMenu : public ContactListItemMenu
{
Q_OBJECT
public:
ContactListGroupMenu(ContactListGroup* contact, ContactListModel* model);
~ContactListGroupMenu();
signals:
void removeSelection();
void addGroup();
private:
class Private;
Private* d;
};
#endif

View file

@ -0,0 +1,225 @@
/*
* contactlistgroupstate.cpp - saves state of groups in a contact list model
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "contactlistgroupstate.h"
#include <QTimer>
#include <QStringList>
#include <QDomElement>
#include "contactlistmodel.h"
#include "contactlistgroup.h"
#include "psioptions.h"
#include "xmpp_xmlcommon.h"
static const QString groupStateOptionPath = "options.ya.main-window.contact-list.group-state.%1";
ContactListGroupState::ContactListGroupState(QObject* parent)
: QObject(parent)
{
orderChangedTimer_ = new QTimer(this);
connect(orderChangedTimer_, SIGNAL(timeout()), SIGNAL(orderChanged()));
orderChangedTimer_->setSingleShot(true);
orderChangedTimer_->setInterval(0);
saveGroupStateTimer_ = new QTimer(this);
connect(saveGroupStateTimer_, SIGNAL(timeout()), SLOT(save()));
saveGroupStateTimer_->setSingleShot(true);
saveGroupStateTimer_->setInterval(1000);
}
ContactListGroupState::~ContactListGroupState()
{
}
bool ContactListGroupState::groupExpanded(const ContactListGroup* group) const
{
Q_ASSERT(group);
QString fullName = group->fullName();
if (expanded_.contains(fullName))
return expanded_[fullName];
return true;
}
void ContactListGroupState::setGroupExpanded(const ContactListGroup* group, bool expanded)
{
Q_ASSERT(group);
expanded_[group->fullName()] = expanded;
saveGroupStateTimer_->start();
}
ContactListGroupState::GroupExpandedState ContactListGroupState::groupExpandedState() const
{
return expanded_;
}
void ContactListGroupState::restoreGroupExpandedState(ContactListGroupState::GroupExpandedState groupExpandedState)
{
expanded_ = groupExpandedState;
}
int ContactListGroupState::groupOrder(const ContactListGroup* group) const
{
Q_ASSERT(group);
QString fullName = group->fullName();
if (order_.contains(fullName))
return order_[fullName];
if (group->isFake())
return -1;
return 0;
}
void ContactListGroupState::setGroupOrder(const ContactListGroup* group, int order)
{
orderChangedTimer_->start();
saveGroupStateTimer_->start();
Q_ASSERT(group);
order_[group->fullName()] = order;
}
void ContactListGroupState::updateGroupList(const ContactListModel* model)
{
GroupExpandedState newExpanded;
QMap<QString, int> newOrder;
foreach(QString group, groupNames(model, QModelIndex(), QStringList())) {
if (expanded_.contains(group))
if (!expanded_[group])
newExpanded[group] = expanded_[group];
if (order_.contains(group))
newOrder[group] = order_[group];
}
expanded_ = newExpanded;
order_ = newOrder;
}
QStringList ContactListGroupState::groupNames(const ContactListModel* model, const QModelIndex& parent, QStringList parentName) const
{
QStringList result;
for (int row = 0; row < model->rowCount(parent); ++row) {
QModelIndex index = model->index(row, 0, parent);
if (model->indexType(index) == ContactListModel::GroupType) {
QStringList groupName = parentName;
groupName << index.data().toString();
foreach(QString name, groupNames(model, index, groupName)) {
if (!result.contains(name))
result += name;
}
}
}
if (result.isEmpty()) {
for (int len = 1; len < parentName.count() + 1; ++len)
result += QStringList(parentName.mid(0, len)).join(ContactListGroup::groupDelimiter());
}
return result;
}
void ContactListGroupState::load(const QString& id)
{
expanded_.clear();
order_.clear();
id_ = id;
if (id_.isEmpty())
return;
QDomDocument doc;
if (!doc.setContent(PsiOptions::instance()->getOption(groupStateOptionPath.arg(id_)).toString()))
return;
QDomElement root = doc.documentElement();
if (root.tagName() != "group-state" || root.attribute("version") != "1.0")
return;
{
QDomElement expanded = findSubTag(root, "expanded", 0);
if (!expanded.isNull()) {
for (QDomNode n = expanded.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement e = n.toElement();
if (e.isNull())
continue;
if (e.tagName() == "item") {
expanded_[e.attribute("fullName")] = e.text() == "true";
}
}
}
}
{
QDomElement order = findSubTag(root, "order", 0);
if (!order.isNull()) {
for (QDomNode n = order.firstChild(); !n.isNull(); n = n.nextSibling()) {
QDomElement e = n.toElement();
if (e.isNull())
continue;
if (e.tagName() == "item") {
order_[e.attribute("fullName")] = e.text().toInt();
}
}
}
}
}
void ContactListGroupState::save()
{
if (id_.isEmpty())
return;
QDomDocument doc;
QDomElement root = doc.createElement("group-state");
root.setAttribute("version", "1.0");
doc.appendChild(root);
{
QDomElement expanded = XMLHelper::emptyTag(&doc, "expanded");
root.appendChild(expanded);
QMap<QString, bool>::iterator i = expanded_.begin();
for (; i != expanded_.end(); ++i) {
if (i.value() == false) {
QDomElement item = textTag(&doc, "item", "false");
item.setAttribute("fullName", i.key());
expanded.appendChild(item);
}
}
}
{
QDomElement order = XMLHelper::emptyTag(&doc, "order");
root.appendChild(order);
QMap<QString, int>::iterator i = order_.begin();
for (; i != order_.end(); ++i) {
if (i.value() != 0) {
QDomElement item = textTag(&doc, "item", QString::number(i.value()));
item.setAttribute("fullName", i.key());
order.appendChild(item);
}
}
}
PsiOptions::instance()->setOption(groupStateOptionPath.arg(id_), doc.toString());
}

View file

@ -0,0 +1,73 @@
/*
* contactlistgroupstate.h - saves state of groups in a contact list model
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CONTACTLISTGROUPSTATE_H
#define CONTACTLISTGROUPSTATE_H
#include <QObject>
#include <QMap>
class ContactListModel;
class ContactListGroup;
class QTimer;
class QModelIndex;
class QDomElement;
class QDomDocument;
class ContactListGroupState : public QObject
{
Q_OBJECT
public:
typedef QMap<QString, bool> GroupExpandedState;
ContactListGroupState(QObject* parent = 0);
~ContactListGroupState();
bool groupExpanded(const ContactListGroup* group) const;
void setGroupExpanded(const ContactListGroup* group, bool expanded);
GroupExpandedState groupExpandedState() const;
void restoreGroupExpandedState(GroupExpandedState groupExpandedState);
int groupOrder(const ContactListGroup* group) const;
void setGroupOrder(const ContactListGroup* group, int order);
void updateGroupList(const ContactListModel* model);
void load(const QString& id);
public slots:
void save();
signals:
void orderChanged();
private:
QTimer* orderChangedTimer_;
QTimer* saveGroupStateTimer_;
QString id_;
GroupExpandedState expanded_;
QMap<QString, int> order_;
QStringList groupNames(const ContactListModel* model, const QModelIndex& parent, QStringList parentName) const;
};
#endif

99
src/contactlistitem.cpp Normal file
View file

@ -0,0 +1,99 @@
/*
* contactlistitem.cpp - base class for contact list items
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "contactlistitem.h"
#include "psicontact.h"
ContactListItem::ContactListItem(QObject* parent)
: QObject(parent)
, editing_(false)
{
}
ContactListItem::~ContactListItem()
{
}
bool ContactListItem::isEditable() const
{
return false;
}
bool ContactListItem::isDragEnabled() const
{
return isEditable();
}
bool ContactListItem::isRemovable() const
{
return false;
}
bool ContactListItem::isExpandable() const
{
return false;
}
bool ContactListItem::expanded() const
{
return false;
}
void ContactListItem::setExpanded(bool expanded)
{
Q_UNUSED(expanded);
}
ContactListItemMenu* ContactListItem::contextMenu(ContactListModel* model)
{
Q_UNUSED(model);
return 0;
}
bool ContactListItem::isFixedSize() const
{
return true;
}
bool ContactListItem::compare(const ContactListItem* other) const
{
const PsiContact* left = dynamic_cast<const PsiContact*>(this);
const PsiContact* right = dynamic_cast<const PsiContact*>(other);
if (!left ^ !right) {
return !right;
}
return comparisonName() < other->comparisonName();
}
const QString& ContactListItem::comparisonName() const
{
return name();
}
bool ContactListItem::editing() const
{
return editing_;
}
void ContactListItem::setEditing(bool editing)
{
editing_ = editing;
}

64
src/contactlistitem.h Normal file
View file

@ -0,0 +1,64 @@
/*
* contactlistitem.h - base class for contact list items
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CONTACTLISTITEM_H
#define CONTACTLISTITEM_H
#include <QObject>
#include <QString>
#include "contactlistmodel.h"
class ContactListItemMenu;
class ContactListItem : public QObject
{
public:
ContactListItem(QObject* parent = 0);
virtual ~ContactListItem();
virtual ContactListModel::Type type() const = 0;
virtual const QString& name() const = 0;
virtual void setName(const QString& name) = 0;
virtual const QString& comparisonName() const;
virtual bool isEditable() const;
virtual bool isDragEnabled() const;
virtual bool isRemovable() const;
virtual bool isExpandable() const;
virtual bool expanded() const;
virtual void setExpanded(bool expanded);
virtual ContactListItemMenu* contextMenu(ContactListModel* model);
virtual bool isFixedSize() const;
virtual bool compare(const ContactListItem* other) const;
virtual bool editing() const;
virtual void setEditing(bool editing);
private:
bool editing_;
};
#endif

View file

@ -0,0 +1,73 @@
/*
* contactlistitemmenu.cpp - base class for contact list item context menus
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "contactlistitemmenu.h"
#include "shortcutmanager.h"
ContactListItemMenu::ContactListItemMenu(ContactListItem* item, ContactListModel* model)
: QMenu(0)
, item_(item)
, model_(model)
{
}
ContactListItemMenu::~ContactListItemMenu()
{
}
ContactListItem* ContactListItemMenu::item() const
{
return item_;
}
/**
* Removes all actions which objectNames are present in \param actionNames.
*/
void ContactListItemMenu::removeActions(QStringList actionNames)
{
foreach(QString actionName, actionNames) {
foreach(QAction* action, actions()) {
if (action->objectName() == actionName) {
delete action;
break;
}
}
}
}
QList<QAction*> ContactListItemMenu::availableActions() const
{
QList<QAction*> result;
foreach(QAction* action, actions())
if (!action->isSeparator())
result << action;
return result;
}
QList<QKeySequence> ContactListItemMenu::shortcuts(const QString& name) const
{
return ShortcutManager::instance()->shortcuts(name);
}
ContactListModel* ContactListItemMenu::model() const
{
return model_;
}

52
src/contactlistitemmenu.h Normal file
View file

@ -0,0 +1,52 @@
/*
* contactlistitemmenu.h - base class for contact list item context menus
* Copyright (C) 2008 Yandex LLC (Michail Pishchagin)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef CONTACTLISTITEMMENU_H
#define CONTACTLISTITEMMENU_H
#include <QMenu>
#include <QList>
class ContactListModel;
class ContactListItem;
class QAction;
class ContactListItemMenu : public QMenu
{
Q_OBJECT
public:
ContactListItemMenu(ContactListItem* item, ContactListModel* model);
virtual ~ContactListItemMenu();
virtual ContactListItem* item() const;
virtual void removeActions(QStringList actionNames);
virtual QList<QAction*> availableActions() const;
protected:
QList<QKeySequence> shortcuts(const QString& name) const;
ContactListModel* model() const;
private:
ContactListItem* item_;
ContactListModel* model_;
};
#endif

Some files were not shown because too many files have changed in this diff Show more