summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPali Rohár <pali.rohar@gmail.com>2016-10-21 16:44:55 (GMT)
committerPali Rohár <pali.rohar@gmail.com>2016-10-21 16:44:55 (GMT)
commit3bff188483fd2ee01bb8310a511e8cc9a4808d22 (patch)
tree8e6e8bbf18f9d31ee9acc7cedd68008954049255
parent9b48acb5551f205bec4ac1b5bcb32f00a59d13b9 (diff)
Add support for X-OAuth2 authentication in Jabber protocolrefs/backups/branch-jabber-xoauth2-1478195267
Based on Psi demo code from: https://github.com/psi-im/iris/issues/35 https://github.com/psi-plus/main/blob/master/patches/dev/xoauth2-support-demo.diff REVIEW: 129239 BUG: 354473 FIXED-IN: 16.12
-rw-r--r--CMakeLists.txt3
-rw-r--r--protocols/CMakeLists.txt8
-rw-r--r--protocols/jabber/CMakeLists.txt6
-rw-r--r--protocols/jabber/jabberaccount.cpp5
-rw-r--r--protocols/jabber/jabberclient.cpp40
-rw-r--r--protocols/jabber/jabberclient.h3
-rw-r--r--protocols/jabber/ui/dlgjabbereditaccountwidget.ui98
-rw-r--r--protocols/jabber/ui/dlgjabberxoauth2.cpp76
-rw-r--r--protocols/jabber/ui/dlgjabberxoauth2.h43
-rw-r--r--protocols/jabber/ui/dlgxoauth2.ui161
-rw-r--r--protocols/jabber/ui/jabbereditaccountwidget.cpp19
-rw-r--r--protocols/jabber/ui/jabbereditaccountwidget.h1
-rw-r--r--protocols/jabber/xoauth2provider.cpp331
-rw-r--r--protocols/jabber/xoauth2provider.h25
14 files changed, 812 insertions, 7 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a2bc384..5216807 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -95,6 +95,9 @@ macro_log_feature(OPENSSL_FOUND "OpenSSL" "OpenSSL implementation of SSL" "https
macro_optional_find_package(QCA2)
macro_log_feature(QCA2_FOUND "QCA2" "Qt Cryptographic Architecture" "http://delta.affinix.com/qca/" FALSE "2.0.0" "Required for the GroupWise and Jabber protocols")
+macro_optional_find_package(QJSON)
+macro_log_feature(QJSON_FOUND "QJSON" "JSON handling library for Qt" "http://qjson.sourceforge.net/" FALSE "" "Required for the Jabber protocol")
+
macro_optional_find_package(QGpgme)
macro_log_feature(QGPGME_FOUND "QGpgme" "QGpgME library (from kdepimlibs)" "http://www.kde.org/" FALSE "" "Required for the Cryptography plugin")
diff --git a/protocols/CMakeLists.txt b/protocols/CMakeLists.txt
index d631c23..c1d488e 100644
--- a/protocols/CMakeLists.txt
+++ b/protocols/CMakeLists.txt
@@ -81,13 +81,13 @@ if(NOT WIN32)
endif(NOT WIN32)
if(QCA2_FOUND AND ZLIB_FOUND)
- if(IDN_FOUND)
+ if(IDN_FOUND AND QJSON_FOUND)
if(WITH_jabber)
add_subdirectory( jabber )
endif(WITH_jabber)
- else(IDN_FOUND)
- message(STATUS "${CMAKE_CURRENT_SOURCE_DIR}: Disabled Jabber because libidn-devel was not found")
- endif(IDN_FOUND)
+ else(IDN_FOUND AND QJSON_FOUND)
+ message(STATUS "${CMAKE_CURRENT_SOURCE_DIR}: Disabled Jabber because libidn-devel or qjson was not found")
+ endif(IDN_FOUND AND QJSON_FOUND)
if(WITH_groupwise)
add_subdirectory( groupwise )
endif(WITH_groupwise)
diff --git a/protocols/jabber/CMakeLists.txt b/protocols/jabber/CMakeLists.txt
index 41f2b5d..2764ccb 100644
--- a/protocols/jabber/CMakeLists.txt
+++ b/protocols/jabber/CMakeLists.txt
@@ -11,6 +11,7 @@ ${CMAKE_CURRENT_SOURCE_DIR}/tasks/
${CMAKE_CURRENT_SOURCE_DIR}/libiris/include/iris
${CMAKE_CURRENT_SOURCE_DIR}/libiris/src
${QCA2_INCLUDE_DIR}
+${QJSON_INCLUDE_DIR}
)
if(BUILD_LIBJINGLE)
@@ -49,6 +50,7 @@ set(kopete_jabber_ui_SRCS
ui/privacylistmodel.cpp
ui/privacydlg.cpp
ui/privacyruledlg.cpp
+ ui/dlgjabberxoauth2.cpp
)
if(BUILD_LIBJINGLE)
@@ -82,6 +84,7 @@ set(kopete_jabber_ui_files
ui/dlgchatroomslist.ui
ui/privacyrule.ui
ui/privacy.ui
+ ui/dlgxoauth2.ui
)
if(BUILD_LIBJINGLE)
@@ -133,6 +136,7 @@ set(kopete_jabber_PART_SRCS
jabberbookmarks.cpp
jabberclient.cpp
jabberbobcache.cpp
+ xoauth2provider.cpp
)
if(BUILD_LIBJINGLE)
@@ -160,7 +164,7 @@ endif(BUILD_JINGLE)
kde4_add_plugin(kopete_jabber ${kopete_jabber_PART_SRCS})
-target_link_libraries(kopete_jabber ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${QCA2_LIBRARIES} ${KDE4_SOLID_LIBS} kopete iris_kopete)
+target_link_libraries(kopete_jabber ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${QCA2_LIBRARIES} ${QJSON_LIBRARIES} ${KDE4_SOLID_LIBS} kopete iris_kopete)
if(BUILD_JINGLE)
target_link_libraries(kopete_jabber ${LIBORTP_LIBRARY} ${ASOUND_LIBRARY} ${SPEEX_LIBRARY})
endif(BUILD_JINGLE)
diff --git a/protocols/jabber/jabberaccount.cpp b/protocols/jabber/jabberaccount.cpp
index a72afea..8446333 100644
--- a/protocols/jabber/jabberaccount.cpp
+++ b/protocols/jabber/jabberaccount.cpp
@@ -376,6 +376,9 @@ void JabberAccount::connectWithPassword ( const QString &password )
// allow plaintext password authentication or not?
m_jabberClient->setAllowPlainTextPassword ( configGroup()->readEntry ( "AllowPlainTextPassword", false ) );
+ // use X-OAUTH2
+ m_jabberClient->setUseXOAuth2 ( configGroup()->readEntry ( "UseXOAuth2", false ) );
+
// enable file transfer (if empty, IP will be set after connection has been established)
KConfigGroup config = KGlobal::config()->group ( "Jabber" );
m_jabberClient->setFileTransfersEnabled ( true, config.readEntry ( "LocalIP" ) );
@@ -1865,7 +1868,7 @@ void JabberAccount::loginLibjingleResolver(const QHostAddress &address, quint16
bool JabberAccount::enabledLibjingle()
{
- return configGroup()->readEntry("Libjingle", true);
+ return configGroup()->readEntry("Libjingle", true) && !configGroup()->readEntry("UseXOAuth2", false);
}
void JabberAccount::enableLibjingle(bool b)
diff --git a/protocols/jabber/jabberclient.cpp b/protocols/jabber/jabberclient.cpp
index 1a3b0e3..0929e63 100644
--- a/protocols/jabber/jabberclient.cpp
+++ b/protocols/jabber/jabberclient.cpp
@@ -43,6 +43,8 @@
#include "privacymanager.h"
+#include "xoauth2provider.h"
+
#define JABBER_PENALTY_TIME 2
class JabberClient::Private
@@ -114,6 +116,9 @@ public:
// allow transmission of plaintext passwords
bool allowPlainTextPassword;
+ // use X-OAUTH2
+ bool useXOAuth2;
+
// enable file transfers
bool fileTransfersEnabled;
@@ -185,6 +190,7 @@ void JabberClient::cleanUp ()
setOverrideHost ( false );
setAllowPlainTextPassword ( true );
+ setUseXOAuth2 ( false );
setFileTransfersEnabled ( false );
setS5BServerPort ( 8010 );
@@ -412,6 +418,20 @@ bool JabberClient::allowPlainTextPassword () const
}
+void JabberClient::setUseXOAuth2 ( bool flag )
+{
+
+ d->useXOAuth2 = flag;
+
+}
+
+bool JabberClient::useXOAuth2 () const
+{
+
+ return d->useXOAuth2;
+
+}
+
void JabberClient::setFileTransfersEnabled ( bool flag, const QString &localAddress )
{
@@ -679,6 +699,26 @@ JabberClient::ErrorCode JabberClient::connect ( const XMPP::Jid &jid, const QStr
this, SLOT (slotCSConnected()) );
}
+ if ( useXOAuth2() )
+ {
+ /*
+ * Load X-OAUTH2 SASL provider
+ */
+ bool loaded = false;
+ foreach ( QCA::Provider *p, QCA::providers() ) {
+ if ( p->name() == "xoauth2sasl" ) {
+ loaded = true;
+ break;
+ }
+ }
+ if ( ! loaded ) {
+ /* install with higher priority as simplesasl to prevent loading xoauth2sasl automatically */
+ QCA::insertProvider(createProviderXOAuth2());
+ QCA::setProviderPriority("xoauth2sasl", 11);
+ }
+ d->jabberClientStream->setSaslMechanismProvider("X-OAUTH2", "xoauth2sasl");
+ }
+
/*
* Initiate anti-idle timer (will be triggered every 55 seconds).
*/
diff --git a/protocols/jabber/jabberclient.h b/protocols/jabber/jabberclient.h
index 2737e05..a0a6e59 100644
--- a/protocols/jabber/jabberclient.h
+++ b/protocols/jabber/jabberclient.h
@@ -214,6 +214,9 @@ public:
*/
bool allowPlainTextPassword () const;
+ void setUseXOAuth2 ( bool flag );
+ bool useXOAuth2 () const;
+
/**
* Enable file transfers. Default is false.
* @param flag Whether to enable file transfers.
diff --git a/protocols/jabber/ui/dlgjabbereditaccountwidget.ui b/protocols/jabber/ui/dlgjabbereditaccountwidget.ui
index 3d73a5a..b309e1a 100644
--- a/protocols/jabber/ui/dlgjabbereditaccountwidget.ui
+++ b/protocols/jabber/ui/dlgjabbereditaccountwidget.ui
@@ -459,6 +459,40 @@
</item>
</layout>
</item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <item>
+ <widget class="QCheckBox" name="cbUseXOAuth2">
+ <property name="text">
+ <string>Use X-OAuth2 authentication</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="btnXOAuth2">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Manage X-OAuth2 tokens</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
</layout>
</widget>
</item>
@@ -1333,5 +1367,69 @@ One media type uses 2 ports and each new Jingle session will use the next 2 avai
</hint>
</hints>
</connection>
+ <connection>
+ <sender>cbUseXOAuth2</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>btnXOAuth2</receiver>
+ <slot>setEnabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>121</x>
+ <y>193</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>441</x>
+ <y>193</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbUseXOAuth2</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>mPass</receiver>
+ <slot>setHidden(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>121</x>
+ <y>193</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>276</x>
+ <y>94</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbUseXOAuth2</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>Libjingle</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>121</x>
+ <y>193</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>276</x>
+ <y>94</y>
+ </hint>
+ </hints>
+ </connection>
+ <connection>
+ <sender>cbUseXOAuth2</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>cbAllowPlainTextPassword</receiver>
+ <slot>setDisabled(bool)</slot>
+ <hints>
+ <hint type="sourcelabel">
+ <x>121</x>
+ <y>193</y>
+ </hint>
+ <hint type="destinationlabel">
+ <x>276</x>
+ <y>94</y>
+ </hint>
+ </hints>
+ </connection>
</connections>
</ui>
diff --git a/protocols/jabber/ui/dlgjabberxoauth2.cpp b/protocols/jabber/ui/dlgjabberxoauth2.cpp
new file mode 100644
index 0000000..16f0346
--- /dev/null
+++ b/protocols/jabber/ui/dlgjabberxoauth2.cpp
@@ -0,0 +1,76 @@
+/*
+ dlgjabberxoauth2.cpp - X-OAuth2 dialog
+
+ Copyright (c) 2016 by Pali Rohár <pali.rohar@gmail.com>
+
+ *************************************************************************
+ * *
+ * This library 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. *
+ * *
+ *************************************************************************
+*/
+
+#include "dlgjabberxoauth2.h"
+
+#include <klocale.h>
+#include <kopetepassword.h>
+
+#include "jabberaccount.h"
+#include "ui_dlgxoauth2.h"
+
+DlgJabberXOAuth2::DlgJabberXOAuth2(JabberAccount *account, QWidget *parent) : KDialog(parent), m_account(account) {
+
+ setCaption(i18n("Manage X-OAuth2 tokens"));
+ setButtons(KDialog::Ok | KDialog::Cancel);
+ setDefaultButton(KDialog::Ok);
+ showButtonSeparator(true);
+
+ m_mainWidget = new Ui::DlgXOAuth2();
+ m_mainWidget->setupUi(mainWidget());
+
+ const QString &token = m_account->password().cachedValue();
+ if (token.contains(QChar(0x7F))) {
+ const QStringList &tokens = token.split(QChar(0x7F));
+ if (tokens.size() == 5) {
+ m_mainWidget->cbUseAccessToken->setChecked(!tokens.at(3).isEmpty());
+ m_mainWidget->clientId->setText(tokens.at(0));
+ m_mainWidget->clientSecretKey->setText(tokens.at(1));
+ m_mainWidget->refreshToken->setText(tokens.at(2));
+ m_mainWidget->accessToken->setText(tokens.at(3));
+ m_mainWidget->requestUrl->setText(tokens.at(4));
+ }
+ }
+
+ connect(this, SIGNAL(okClicked()), this, SLOT(slotOk()));
+ connect(this, SIGNAL(cancelClicked()), this, SLOT(slotCancel()));
+
+}
+
+DlgJabberXOAuth2::~DlgJabberXOAuth2() {
+
+ delete m_mainWidget;
+
+}
+
+void DlgJabberXOAuth2::slotOk() {
+
+ QStringList tokens;
+ tokens << m_mainWidget->clientId->text();
+ tokens << m_mainWidget->clientSecretKey->text();
+ tokens << m_mainWidget->refreshToken->text();
+ tokens << m_mainWidget->accessToken->text();
+ tokens << m_mainWidget->requestUrl->text();
+ m_account->password().set(tokens.join(QChar(0x7F)));
+
+}
+
+void DlgJabberXOAuth2::slotCancel() {
+
+ deleteLater();
+
+}
+
+#include "dlgjabberxoauth2.moc"
diff --git a/protocols/jabber/ui/dlgjabberxoauth2.h b/protocols/jabber/ui/dlgjabberxoauth2.h
new file mode 100644
index 0000000..25b01b3
--- /dev/null
+++ b/protocols/jabber/ui/dlgjabberxoauth2.h
@@ -0,0 +1,43 @@
+/*
+ dlgjabberxoauth2.cpp - X-OAuth2 dialog
+
+ Copyright (c) 2016 by Pali Rohár <pali.rohar@gmail.com>
+
+ *************************************************************************
+ * *
+ * This library 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. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef DLGJABBERXOAUTH2_H
+#define DLGJABBERXOAUTH2_H
+
+#include <kdialog.h>
+
+class JabberAccount;
+namespace Ui {
+ class DlgXOAuth2;
+}
+
+class DlgJabberXOAuth2 : public KDialog {
+
+Q_OBJECT
+
+public:
+ DlgJabberXOAuth2(JabberAccount *account, QWidget *parent = NULL);
+ virtual ~DlgJabberXOAuth2();
+
+private slots:
+ void slotOk();
+ void slotCancel();
+
+private:
+ Ui::DlgXOAuth2 *m_mainWidget;
+ JabberAccount *m_account;
+};
+
+#endif
diff --git a/protocols/jabber/ui/dlgxoauth2.ui b/protocols/jabber/ui/dlgxoauth2.ui
new file mode 100644
index 0000000..f839431
--- /dev/null
+++ b/protocols/jabber/ui/dlgxoauth2.ui
@@ -0,0 +1,161 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>DlgXOAuth2</class>
+ <widget class="QWidget" name="DlgXOAuth2">
+ <property name="minimumSize">
+ <size>
+ <width>500</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="windowTitle">
+ <string>Manage X-OAuth2 tokens</string>
+ </property>
+ <layout class="QGridLayout" name="layout">
+ <item row="0" column="0" colspan="2">
+ <widget class="QCheckBox" name="cbUseAccessToken">
+ <property name="text">
+ <string>Authenticate directly with Access Token</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelAccessToken">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="text">
+ <string>Access Token</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QLineEdit" name="accessToken">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="echoMode">
+ <enum>QLineEdit::PasswordEchoOnEdit</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="0">
+ <widget class="QLabel" name="labelRequetUrl">
+ <property name="text">
+ <string>Request URL</string>
+ </property>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QLineEdit" name="requestUrl"/>
+ </item>
+ <item row="3" column="0">
+ <widget class="QLabel" name="labelClientId">
+ <property name="text">
+ <string>Client ID</string>
+ </property>
+ </widget>
+ </item>
+ <item row="3" column="1">
+ <widget class="QLineEdit" name="clientId"/>
+ </item>
+ <item row="4" column="0">
+ <widget class="QLabel" name="labelClientSecretKey">
+ <property name="text">
+ <string>Client Secret Key</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="1">
+ <widget class="QLineEdit" name="clientSecretKey">
+ <property name="echoMode">
+ <enum>QLineEdit::PasswordEchoOnEdit</enum>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QLabel" name="labelRefreshToken">
+ <property name="text">
+ <string>Refresh Token</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="1">
+ <widget class="QLineEdit" name="refreshToken">
+ <property name="echoMode">
+ <enum>QLineEdit::PasswordEchoOnEdit</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>accessToken</receiver>
+ <slot>clear()</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>accessToken</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>labelAccessToken</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>clientId</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>labelClientId</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>clientSecretKey</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>labelClientSecretKey</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>refreshToken</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>labelRefreshToken</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>requestUrl</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>cbUseAccessToken</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>labelRequetUrl</receiver>
+ <slot>setDisabled(bool)</slot>
+ </connection>
+ </connections>
+</ui>
diff --git a/protocols/jabber/ui/jabbereditaccountwidget.cpp b/protocols/jabber/ui/jabbereditaccountwidget.cpp
index 51b9e44..0585163 100644
--- a/protocols/jabber/ui/jabbereditaccountwidget.cpp
+++ b/protocols/jabber/ui/jabbereditaccountwidget.cpp
@@ -44,6 +44,7 @@
#include "jabberclient.h"
#include "jabberregisteraccount.h"
#include "dlgjabberchangepassword.h"
+#include "dlgjabberxoauth2.h"
#include "privacydlg.h"
#include "xmpp.h"
@@ -66,6 +67,8 @@ JabberEditAccountWidget::JabberEditAccountWidget (JabberProtocol * proto, Jabber
connect (cbUseSSL, SIGNAL (toggled(bool)), this, SLOT (sslToggled(bool)));
connect (btnChangePassword, SIGNAL (clicked()), this, SLOT (slotChangePasswordClicked()));
+
+ connect (btnXOAuth2, SIGNAL (clicked()), this, SLOT (slotManageXOAuth2Clicked()));
connect (privacyListsButton, SIGNAL (clicked()), this, SLOT (slotPrivacyListsClicked()) );
@@ -212,6 +215,7 @@ void JabberEditAccountWidget::reopen ()
}
cbAllowPlainTextPassword->setChecked (account()->configGroup()->readEntry("AllowPlainTextPassword", true));
+ cbUseXOAuth2->setChecked (account()->configGroup()->readEntry("UseXOAuth2", false));
KConfigGroup config = KGlobal::config()->group("Jabber");
leLocalIP->setText (config.readEntry("LocalIP", QString()));
@@ -290,6 +294,7 @@ void JabberEditAccountWidget::writeConfig ()
{
account()->configGroup()->writeEntry("UseSSL", cbUseSSL->isChecked());
+ if (!cbUseXOAuth2->isChecked())
mPass->save(&account()->password ());
account()->configGroup()->writeEntry("CustomServer", cbCustomServer->isChecked());
@@ -299,6 +304,7 @@ void JabberEditAccountWidget::writeConfig ()
//account()->setAccountId(mID->text());
account()->configGroup()->writeEntry("AllowPlainTextPassword", cbAllowPlainTextPassword->isChecked());
+ account()->configGroup()->writeEntry("UseXOAuth2", cbUseXOAuth2->isChecked());
account()->configGroup()->writeEntry("Server", mServer->text().trimmed ());
account()->configGroup()->writeEntry("Resource", mResource->text ());
account()->configGroup()->writeEntry("Priority", QString::number (mPriority->value ()));
@@ -344,7 +350,7 @@ void JabberEditAccountWidget::writeConfig ()
account()->setOldEncrypted(oldEncrypted->isChecked());
#ifdef LIBJINGLE_SUPPORT
- account()->enableLibjingle(Libjingle->isChecked());
+ account()->enableLibjingle(Libjingle->isChecked() && !cbUseXOAuth2->isChecked());
#endif
}
@@ -429,6 +435,17 @@ void JabberEditAccountWidget::slotChangePasswordFinished ()
}
+void JabberEditAccountWidget::slotManageXOAuth2Clicked ()
+{
+
+ DlgJabberXOAuth2 *xoath2Dlg = new DlgJabberXOAuth2 ( account(), this );
+
+ xoath2Dlg->show();
+
+ mPass->setPassword ( QString() );
+
+}
+
void JabberEditAccountWidget::sslToggled (bool value)
{
if (value && (mPort->value() == 5222))
diff --git a/protocols/jabber/ui/jabbereditaccountwidget.h b/protocols/jabber/ui/jabbereditaccountwidget.h
index 07f7e70..5465484 100644
--- a/protocols/jabber/ui/jabbereditaccountwidget.h
+++ b/protocols/jabber/ui/jabbereditaccountwidget.h
@@ -55,6 +55,7 @@ private slots:
void awayPriorityToggled (bool);
void updateServerField ();
void slotPrivacyListsClicked ();
+ void slotManageXOAuth2Clicked ();
private:
JabberProtocol *m_protocol;
diff --git a/protocols/jabber/xoauth2provider.cpp b/protocols/jabber/xoauth2provider.cpp
new file mode 100644
index 0000000..819951c
--- /dev/null
+++ b/protocols/jabber/xoauth2provider.cpp
@@ -0,0 +1,331 @@
+/*
+ xoauth2provider.cpp - X-OAuth2 provider for QCA
+
+ Copyright (c) 2016 by Pali Rohár <pali.rohar@gmail.com>
+
+ *************************************************************************
+ * *
+ * This library 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. *
+ * *
+ *************************************************************************
+*/
+
+#include <QNetworkAccessManager>
+#include <QNetworkRequest>
+#include <QNetworkReply>
+#include <qca.h>
+
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+#include <qjson/parser.h>
+#else
+#include <QJsonDocument>
+#include <QUrlQuery>
+#endif
+
+#include "xoauth2provider.h"
+
+class XOAuth2SASLContext : public QCA::SASLContext {
+ Q_OBJECT
+
+private:
+ QString user;
+ QString clientId;
+ QString requestUrl;
+ QCA::SecureArray clientSecretKey;
+ QCA::SecureArray refreshToken;
+ QCA::SecureArray accessToken;
+
+ QByteArray data;
+ QByteArray result_to_net;
+ QByteArray result_to_app;
+ QCA::SASLContext::Result result_;
+ QCA::SASL::AuthCondition authCondition_;
+
+ QNetworkAccessManager *manager;
+
+public:
+ XOAuth2SASLContext(QCA::Provider *p) : QCA::SASLContext(p) {
+ manager = new QNetworkAccessManager(this);
+ reset();
+ }
+
+ virtual ~XOAuth2SASLContext() {
+ reset();
+ }
+
+ virtual QCA::Provider::Context *clone() const {
+ XOAuth2SASLContext *s = new XOAuth2SASLContext(provider());
+ s->user = user;
+ s->clientId = clientId;
+ s->clientSecretKey = clientSecretKey;
+ s->refreshToken = refreshToken;
+ s->accessToken = accessToken;
+ s->requestUrl = requestUrl;
+ return s;
+ }
+
+ virtual void reset() {
+ user.clear();
+ clientId.clear();
+ clientSecretKey.clear();
+ refreshToken.clear();
+ accessToken.clear();
+ requestUrl.clear();
+ data.clear();
+ authCondition_ = QCA::SASL::AuthFail;
+ }
+
+ virtual void setup(const QString &, const QString &, const QCA::SASLContext::HostPort *, const QCA::SASLContext::HostPort *, const QString &, int) {
+ }
+
+ virtual void setConstraints(QCA::SASL::AuthFlags, int, int) {
+ }
+
+ virtual void startClient(const QStringList &mechlist, bool) {
+ if (!mechlist.contains(QLatin1String("X-OAUTH2"))) {
+ qWarning("No X-OAUTH2 auth method");
+ authCondition_ = QCA::SASL::NoMechanism;
+ QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
+ return;
+ }
+ authCondition_ = QCA::SASL::AuthFail;
+ result_ = QCA::SASLContext::Continue;
+ data.clear();
+ tryAgain();
+ }
+
+ virtual void startServer(const QString &, bool) {
+ result_ = QCA::SASLContext::Error;
+ QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
+ }
+
+ virtual void serverFirstStep(const QString &, const QByteArray *) {
+ result_ = QCA::SASLContext::Error;
+ QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
+ }
+
+ virtual void nextStep(const QByteArray &) {
+ tryAgain();
+ }
+
+ virtual void tryAgain() {
+ if (user.isEmpty() || (accessToken.isEmpty() && (clientId.isEmpty() || clientSecretKey.isEmpty() || requestUrl.isEmpty() || refreshToken.isEmpty()))) {
+ result_ = QCA::SASLContext::Params;
+ QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
+ return;
+ }
+ if (accessToken.isEmpty()) {
+ requestAccessToken();
+ return;
+ }
+ sendAuth();
+ }
+
+ virtual void update(const QByteArray &from_net, const QByteArray &from_app) {
+ result_to_app = from_net;
+ result_to_net = from_app;
+ result_ = QCA::SASLContext::Success;
+ QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
+ }
+
+ virtual bool waitForResultsReady(int) {
+ return true;
+ }
+
+ virtual QCA::SASLContext::Result result() const {
+ return result_;
+ }
+
+ virtual QStringList mechlist() const {
+ return QStringList();
+ }
+
+ virtual QString mech() const {
+ return QLatin1String("X-OAUTH2");
+ }
+
+ virtual bool haveClientInit() const {
+ return false;
+ }
+
+ virtual QByteArray stepData() const {
+ return data;
+ }
+
+ virtual QByteArray to_net() {
+ return result_to_net;
+ }
+
+ virtual int encoded() const {
+ return result_to_net.size();
+ }
+
+ virtual QByteArray to_app() {
+ return result_to_app;
+ }
+
+ virtual int ssf() const {
+ return 0;
+ }
+
+ virtual QCA::SASL::AuthCondition authCondition() const {
+ return authCondition_;
+ }
+
+ virtual QCA::SASL::Params clientParams() const {
+ bool needUser = user.isEmpty();
+ bool needPass = (accessToken.isEmpty() && (clientId.isEmpty() || clientSecretKey.isEmpty() || requestUrl.isEmpty() || refreshToken.isEmpty()));
+ return QCA::SASL::Params(needUser, false, needPass, false);
+ }
+
+ virtual void setClientParams(const QString *userParam, const QString *, const QCA::SecureArray *passParam, const QString *) {
+ if (userParam)
+ user = *userParam;
+ if (passParam) {
+ const QList<QByteArray> &params = passParam->toByteArray().split(0x7F);
+ if (params.size() == 5) {
+ clientId = QString::fromUtf8(params.at(0));
+ clientSecretKey = params.at(1);
+ refreshToken = params.at(2);
+ accessToken = params.at(3);
+ requestUrl = QString::fromUtf8(params.at(4));
+ } else {
+ clientId.clear();
+ clientSecretKey.clear();
+ refreshToken.clear();
+ requestUrl.clear();
+ if (params.size() == 1)
+ accessToken = params.at(0);
+ else
+ accessToken.clear();
+ }
+ }
+ }
+
+ virtual QStringList realmlist() const {
+ return QStringList();
+ }
+
+ virtual QString username() const {
+ return QString();
+ }
+
+ virtual QString authzid() const {
+ return QString();
+ }
+
+private:
+ void requestAccessToken() {
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ QUrl query;
+#else
+ QUrlQuery query;
+#endif
+ query.addQueryItem(QLatin1String("client_id"), clientId);
+ query.addQueryItem(QLatin1String("client_secret"), QString::fromUtf8(clientSecretKey.toByteArray()));
+ query.addQueryItem(QLatin1String("refresh_token"), QString::fromUtf8(refreshToken.toByteArray()));
+ query.addQueryItem(QLatin1String("grant_type"), QLatin1String("refresh_token"));
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ const QByteArray &data = query.encodedQuery();
+#else
+ const QByteArray &data = query.toString(QUrl::FullyEncoded).toUtf8();
+#endif
+ QNetworkRequest request(requestUrl);
+ request.setHeader(QNetworkRequest::ContentTypeHeader, QLatin1String("application/x-www-form-urlencoded"));
+ QNetworkReply *reply = manager->post(request, data);
+ connect(reply, SIGNAL(finished()), this, SLOT(accessTokenReceived()));
+ }
+
+ void sendAuth() {
+ if (accessToken.isEmpty()) {
+ authCondition_ = QCA::SASL::AuthFail;
+ result_ = QCA::SASLContext::Error;
+ } else {
+ data.clear();
+ data += '\0';
+ data += user.toUtf8();
+ data += '\0';
+ data += accessToken.toByteArray();
+ result_ = QCA::SASLContext::Success;
+ }
+ QMetaObject::invokeMethod(this, "resultsReady", Qt::QueuedConnection);
+ }
+
+private slots:
+ void accessTokenReceived() {
+ QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
+ const QByteArray &replyData = reply->readAll();
+ reply->deleteLater();
+#if QT_VERSION < QT_VERSION_CHECK(5,0,0)
+ QJson::Parser parser;
+ bool ok = false;
+ const QVariant &replyVariant = parser.parse(replyData, &ok);
+#else
+ QJsonParseError error;
+ const QJsonDocument &replyJson = QJsonDocument::fromJson(replyData, &error);
+ bool ok = (error.error() == QJsonParseError::NoError);
+ const QVariant &replyVariant = replyJson.toVariant();
+#endif
+ if (ok && replyVariant.type() == QVariant::Map) {
+ const QVariantMap &replyMap = replyVariant.toMap();
+ if (replyMap.contains(QLatin1String("access_token"))) {
+ const QVariant &accessTokenVariant = replyMap.value(QLatin1String("access_token"));
+ if (accessTokenVariant.type() == QVariant::String)
+ accessToken = accessTokenVariant.toString().toUtf8();
+ else
+ ok = false;
+ } else {
+ ok = false;
+ }
+ if (!ok || replyMap.contains(QLatin1String("error"))) {
+ const QString &error = replyMap.value(QLatin1String("error")).toString();
+ const QString &errorDescription = replyMap.value(QLatin1String("error_description")).toString();
+ qWarning("requestAccessToken failed, error: %s, description: %s", error.toUtf8().data(), errorDescription.toUtf8().data());
+ accessToken.clear();
+ }
+ } else {
+ qWarning("requestAccessToken failed, invalid reply: %s", replyData.data());
+ accessToken.clear();
+ }
+ sendAuth();
+ }
+};
+
+class QCAXOAuth2SASL : public QCA::Provider {
+public:
+ QCAXOAuth2SASL() {
+ }
+
+ virtual ~QCAXOAuth2SASL() {
+ }
+
+ virtual void init() {
+ }
+
+ virtual int qcaVersion() const {
+ return QCA_VERSION;
+ }
+
+ virtual QString name() const {
+ return QLatin1String("xoauth2sasl");
+ }
+
+ virtual QStringList features() const {
+ return QStringList(QLatin1String("sasl"));
+ }
+
+ QCA::Provider::Context *createContext(const QString& type) {
+ if (type == QLatin1String("sasl"))
+ return new XOAuth2SASLContext(this);
+ return NULL;
+ }
+};
+
+QCA::Provider *createProviderXOAuth2() {
+ return new QCAXOAuth2SASL();
+}
+
+#include "xoauth2provider.moc"
diff --git a/protocols/jabber/xoauth2provider.h b/protocols/jabber/xoauth2provider.h
new file mode 100644
index 0000000..2ca8369
--- /dev/null
+++ b/protocols/jabber/xoauth2provider.h
@@ -0,0 +1,25 @@
+/*
+ xoauth2provider.h - X-OAuth2 provider for QCA
+
+ Copyright (c) 2016 by Pali Rohár <pali.rohar@gmail.com>
+
+ *************************************************************************
+ * *
+ * This library 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. *
+ * *
+ *************************************************************************
+*/
+
+#ifndef XOAUTH2PROVIDER_H
+#define XOAUTH2PROVIDER_H
+
+namespace QCA {
+ class Provider;
+}
+
+QCA::Provider* createProviderXOAuth2();
+
+#endif // XOAUTH2PROVIDER_H