[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-commits
Subject:    [plasma-nm] vpn: add OpenConnect VPN plugin
From:       Lukáš Tinkl <lukas () kde ! org>
Date:       2013-06-11 14:13:46
Message-ID: 20130611141346.7F2F8A605A () git ! kde ! org
[Download RAW message or body]

Git commit 5cab9552630928d9ed8c3f5ec85cfa0fe894e323 by Lukáš Tinkl.
Committed on 11/06/2013 at 16:13.
Pushed by lukas into branch 'master'.

add OpenConnect VPN plugin

M  +1    -0    vpn/CMakeLists.txt
A  +43   -0    vpn/openconnect/CMakeLists.txt
A  +38   -0    vpn/openconnect/FindOpenConnect.cmake
A  +89   -0    vpn/openconnect/README
A  +47   -0    vpn/openconnect/nm-openconnect-service.h     [License: GPL (v2+)]
A  +647  -0    vpn/openconnect/openconnectauth.cpp     [License: BSD]
A  +67   -0    vpn/openconnect/openconnectauth.h     [License: BSD]
A  +201  -0    vpn/openconnect/openconnectauth.ui
A  +196  -0    vpn/openconnect/openconnectauthworkerthread.cpp     [License: BSD]
A  +80   -0    vpn/openconnect/openconnectauthworkerthread.h     [License: BSD]
A  +159  -0    vpn/openconnect/openconnectprop.ui
A  +90   -0    vpn/openconnect/openconnectui.cpp     [License: BSD]
A  +47   -0    vpn/openconnect/openconnectui.h     [License: BSD]
A  +110  -0    vpn/openconnect/openconnectwidget.cpp     [License: BSD]
A  +46   -0    vpn/openconnect/openconnectwidget.h     [License: BSD]
A  +97   -0    vpn/openconnect/plasmanm_openconnectui.desktop [TRAILING SPACE] ** \
[TRAILING SPACE] **

The files marked with ** at the end have a problem. either the file contains a \
trailing space or the file contains a call to a potentially dangerous code. Please \
read: http://community.kde.org/Sysadmin/CommitHooks#Email_notifications Either fix \
the trailing space or review the dangerous code.


http://commits.kde.org/plasma-nm/5cab9552630928d9ed8c3f5ec85cfa0fe894e323

diff --git a/vpn/CMakeLists.txt b/vpn/CMakeLists.txt
index 9d28dd7..040b01b 100644
--- a/vpn/CMakeLists.txt
+++ b/vpn/CMakeLists.txt
@@ -3,3 +3,4 @@ add_subdirectory(openvpn)
 add_subdirectory(l2tp)
 add_subdirectory(pptp)
 add_subdirectory(openswan)
+add_subdirectory(openconnect)
diff --git a/vpn/openconnect/CMakeLists.txt b/vpn/openconnect/CMakeLists.txt
new file mode 100644
index 0000000..2d2edb6
--- /dev/null
+++ b/vpn/openconnect/CMakeLists.txt
@@ -0,0 +1,43 @@
+set (MINIMUM_OPENCONNECT_VERSION_REQUIRED "3.99")
+
+set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_MODULE_PATH})
+
+macro_optional_find_package(OpenConnect ${MINIMUM_OPENCONNECT_VERSION_REQUIRED})
+if (OPENCONNECT_FOUND)
+    if (${OPENCONNECT_VERSION} VERSION_LESS "3.99")
+        macro_optional_find_package(OpenSSL)
+        macro_log_feature(OPENSSL_FOUND "OpenSSL headers" "Encryption suite" \
"http://www.openssl.org" FALSE "" "Needed for OpenConnect support in Network \
Management") +        if (OPENSSL_FOUND)
+            set (MINIMUM_OPENCONNECT_VERSION_REQUIRED "3.03")
+            set (maybe_OPENSSL_LIBRARIES ${OPENSSL_LIBRARIES})
+        endif()
+    endif()
+
+    if (${OPENCONNECT_VERSION} VERSION_GREATER \
${MINIMUM_OPENCONNECT_VERSION_REQUIRED} OR +        ${OPENCONNECT_VERSION} \
VERSION_EQUAL ${MINIMUM_OPENCONNECT_VERSION_REQUIRED}) +
+        set(openconnect_SRCS
+        openconnectui.cpp
+        openconnectwidget.cpp
+        openconnectauth.cpp
+        openconnectauthworkerthread.cpp
+        )
+
+        kde4_add_ui_files(openconnect_SRCS openconnectprop.ui openconnectauth.ui)
+
+        kde4_add_plugin(plasmanm_openconnectui ${openconnect_SRCS})
+
+        target_link_libraries(plasmanm_openconnectui plasmanm-internal \
NetworkManagerQt ${KDE4_KIO_LIBS} ${OPENCONNECT_LIBRARIES} \
${maybe_OPENSSL_LIBRARIES}) +        install(TARGETS plasmanm_openconnectui  \
DESTINATION ${PLUGIN_INSTALL_DIR}) +
+        install( FILES plasmanm_openconnectui.desktop  DESTINATION \
${SERVICES_INSTALL_DIR}) +    else()
+        message("ERROR: OpenConnection version '${OPENCONNECT_VERSION}' does not \
match minimum required (${MINIMUM_OPENCONNECT_VERSION_REQUIRED})") +        \
message("OpenConnect plugin will not be built") +        set(OPENCONNECT_FOUND False)
+    endif()
+else (OPENCONNECT_FOUND)
+    message("OpenConnect plugin will not be built")
+endif (OPENCONNECT_FOUND)
+
+macro_log_feature(OPENCONNECT_FOUND "OpenConnect headers and library" "Cisco \
AnyConnect compatible VPN client" "http://www.infradead.org/openconnect.html" FALSE \
${MINIMUM_OPENCONNECT_VERSION_REQUIRED} "Needed for OpenConnect support in Plasma \
                NM")
diff --git a/vpn/openconnect/FindOpenConnect.cmake \
b/vpn/openconnect/FindOpenConnect.cmake new file mode 100644
index 0000000..c772883
--- /dev/null
+++ b/vpn/openconnect/FindOpenConnect.cmake
@@ -0,0 +1,38 @@
+# - Try to find OpenConnect
+# Once done this will define
+#
+#  OPENCONNECT_FOUND - system has OpenConnect
+#  OPENCONNECT_INCLUDE_DIRS - the OpenConnect include directories
+#  OPENCONNECT_LIBRARIES - the libraries needed to use OpenConnect
+#  OPENCONNECT_CFLAGS - Compiler switches required for using OpenConnect
+#  OPENCONNECT_VERSION - version number of OpenConnect
+
+# Copyright (c) 2011, Ilia Kats <ilia-kats@gmx.net>
+#
+# Redistribution and use is allowed according to the terms of the BSD license.
+# For details see the accompanying COPYING-CMAKE-SCRIPTS file.
+
+
+IF (OPENCONNECT_INCLUDE_DIRS)
+    # in cache already
+    SET(OpenConnect_FIND_QUIETLY TRUE)
+ENDIF (OPENCONNECT_INCLUDE_DIRS)
+
+IF (NOT WIN32)
+    # use pkg-config to get the directories and then use these values
+    # in the FIND_PATH() and FIND_LIBRARY() calls
+    find_package(PkgConfig)
+    pkg_search_module(OPENCONNECT openconnect)
+ENDIF (NOT WIN32)
+
+IF (OPENCONNECT_FOUND)
+    IF (NOT OpenConnect_FIND_QUIETLY)
+        MESSAGE(STATUS "Found OpenConnect ${OPENCONNECT_VERSION}: \
${OPENCONNECT_LIBRARIES}") +    ENDIF (NOT OpenConnect_FIND_QUIETLY)
+ELSE (OPENCONNECT_FOUND)
+    IF (OpenConnect_FIND_REQUIRED)
+        MESSAGE(FATAL_ERROR "Could NOT find OpenConnect, check FindPkgConfig output \
above!") +    ENDIF (OpenConnect_FIND_REQUIRED)
+ENDIF (OPENCONNECT_FOUND)
+
+MARK_AS_ADVANCED(OPENCONNECT_INCLUDE_DIRS OPENCONNECT_LIBRARIES \
                OPENCONNECT_STATIC_LIBRARIES)
diff --git a/vpn/openconnect/README b/vpn/openconnect/README
new file mode 100644
index 0000000..3008214
--- /dev/null
+++ b/vpn/openconnect/README
@@ -0,0 +1,89 @@
+README taken with slight modifications from the network-manager-openconnect
+auth dialog
+
+AnyConnect works over HTTPS; authentication is through HTTP forms and
+POST responses. Once you've filled in the forms that the server demands,
+you're rewarded with an HTTP cookie which is handed on to OpenConnect to
+actually make the connection.
+
+The auth-dialog handles the arbitrary forms as the server presents them,
+and spits out the cookie after a successful authentication. It's just a
+really simple web-browser, effectively. (It has its own HTTP client
+implementation instead of using libsoup because it needs to be able to
+support certificates from a TPM, and to work around Cisco bugs).
+
+To make it slightly more fun, you have a *choice* of servers; an
+AnyConnect VPN is provisioned with an XML file that gives various pieces
+of configuration for the client. We ignore most of the XML file, except
+the list of available VPN server addresses.
+
+So this is a brief flow of what the auth-dialog does...
+
+ 1. Choose a server to connect to.
+
+    If we already have the XML configuration file for this VPN, you get
+    to choose a server from the list. Otherwise, you only have the host
+    that you configured in the VPN setup.
+
+    The auth-dialog will give you the choice of automatically connecting
+    to the last server you used. It does so by storing the boolean
+    'autoconnect' option, as well as the address of the last successful
+    server, in "secrets" that NetworkManager stores for it, but which
+    aren't actually used by OpenConnect itself at all.
+
+ 2. Offer your SSL certificate and fill in all the forms it presents.
+
+    The server will present a sequence of forms which are filled in just
+    like normal web forms. At this point, the auth-dialog is just acting
+    like a really simple web browser. It uses the same trick with fake
+    secrets to remember the answers for any multiple-choice selection,
+    or input elements of type 'text'. Input elements of type 'password'
+    are not currently saved, but probably should be.
+
+    The choices and input boxes that we fill in at this point may not be
+    limited to *just* authenticating ourselves. You may also get to make
+    choices which affect your resulting connectivity. Some networks
+    offer the choice of full-tunnel or split-tunnel routing, IPv6 or
+    Legacy-only connectivity, etc. (The routing configuration is not
+    handled by the auth-dialog; that just manifests itself in the IP
+    configuration which is given to OpenConnect by the server, much
+    later when the connection is actually made.)
+
+(
+ 2 . Run the "Cisco Secure Desktop"[sic] trojan.
+
+    In some cases you are required to download a strange executable from
+    the server and run it. It is supposed to perform various "checks" on
+    your system and report its results to the server. The authentication
+    sequence is kept in a holding pattern with HTTP refresh responses
+    until the "trojan" has done its job.
+
+    Most people seem to bypass this crap and run a local tool of their
+    own devising to report the "correct" results. It's just another
+    simple HTTP POST, although the exact results that are expected may
+    vary from one server/configuration to another.
+
+    Try not to think about it. It will only make you sad.
+)
+
+ 3. Note the 'webvpn' cookie.
+
+    Once authentication is complete, the server's HTTP response will
+    include a 'webvpn' cookie.
+
+    This cookie is one of the three *real* secrets which are actually
+    passed to OpenConnect to make the connection. The other two are
+    the address of the server we finally ended up talking to (after
+    the user's initial choice and any HTTP redirects), and a hash of
+    the server's SSL certificate (to prevent MiTM attacks).
+
+ 4. Check the XML configuration file.
+
+    With a successful authentication, we are *also* given the SHA1 of
+    the current XML configuration for this VPN connection. If it differs
+    from what we have, we are expected to fetch the new one. We store
+    this, base64-encoded, in yet another fake "secret".
+
+ 5. Dump all the "secrets" to NetworkManager.
+
+    Finally, we write the secrets into the setting, so that NM can use them.
diff --git a/vpn/openconnect/nm-openconnect-service.h \
b/vpn/openconnect/nm-openconnect-service.h new file mode 100644
index 0000000..4b45f20
--- /dev/null
+++ b/vpn/openconnect/nm-openconnect-service.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 5; indent-tabs-mode: t; c-basic-offset: 5 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * 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 program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *   Copyright   2008 - 2009 Intel Corporation. //krazy:exclude=copyright
+ *
+ * Based on nm-openconnect-vpnc.h:
+ *   Copyright   2005 - 2008 Red Hat, Inc. //krazy:exclude=copyright
+ *   Copyright   2007 - 2008 Novell, Inc. //krazy:exclude=copyright
+ */
+
+#ifndef NM_OPENCONNECT_SERVICE_H
+#define NM_OPENCONNECT_SERVICE_H
+
+#define NM_DBUS_SERVICE_OPENCONNECT    "org.freedesktop.NetworkManager.openconnect"
+#define NM_DBUS_INTERFACE_OPENCONNECT  "org.freedesktop.NetworkManager.openconnect"
+#define NM_DBUS_PATH_OPENCONNECT       "/org/freedesktop/NetworkManager/openconnect"
+
+#define NM_OPENCONNECT_KEY_GATEWAY "gateway"
+#define NM_OPENCONNECT_KEY_COOKIE "cookie"
+#define NM_OPENCONNECT_KEY_GWCERT "gwcert"
+#define NM_OPENCONNECT_KEY_AUTHTYPE "authtype"
+#define NM_OPENCONNECT_KEY_USERCERT "usercert"
+#define NM_OPENCONNECT_KEY_CACERT "cacert"
+#define NM_OPENCONNECT_KEY_PRIVKEY "userkey"
+#define NM_OPENCONNECT_KEY_MTU "mtu"
+#define NM_OPENCONNECT_KEY_PEM_PASSPHRASE_FSID "pem_passphrase_fsid"
+#define NM_OPENCONNECT_KEY_PROXY "proxy"
+#define NM_OPENCONNECT_KEY_CSD_ENABLE "enable_csd_trojan"
+#define NM_OPENCONNECT_KEY_CSD_WRAPPER "csd_wrapper"
+
+#define NM_OPENCONNECT_USER "nm-openconnect"
+
+#endif /* NM_OPENCONNECT_PLUGIN_H */
diff --git a/vpn/openconnect/openconnectauth.cpp \
b/vpn/openconnect/openconnectauth.cpp new file mode 100644
index 0000000..bd98460
--- /dev/null
+++ b/vpn/openconnect/openconnectauth.cpp
@@ -0,0 +1,647 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.net>
+Copyright 2013 Lukáš Tinkl <ltinkl@redhat.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "openconnectauth.h"
+#include "openconnectauthworkerthread.h"
+#include "ui_openconnectauth.h"
+
+#include <KDialog>
+#include <QPushButton>
+#include <QString>
+#include <QLabel>
+#include <QEventLoop>
+#include <QFormLayout>
+#include <KLineEdit>
+#include <KDialogButtonBox>
+#include <KPushButton>
+#include <KComboBox>
+#include <KDebug>
+#include <QDomDocument>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QCryptographicHash>
+#include <QFile>
+
+#include "nm-openconnect-service.h"
+
+#include <cstdarg>
+
+extern "C"
+{
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+}
+
+// name/address: IP/domain name of the host (OpenConnect accepts both, so no \
difference here) +// group: user group on the server
+typedef struct {
+    QString name;
+    QString group;
+    QString address;
+} VPNHost;
+
+class OpenconnectAuthWidgetPrivate
+{
+public:
+    Ui_OpenconnectAuth ui;
+    NetworkManager::VpnSetting::Ptr setting;
+    struct openconnect_info *vpninfo;
+    QStringList certificateFingerprints;
+    NMStringMap secrets;
+    QMutex mutex;
+    QWaitCondition workerWaiting;
+    OpenconnectAuthWorkerThread *worker;
+    QList<VPNHost> hosts;
+    bool userQuit;
+    int cancelPipes[2];
+    QList<QPair<QString, int> > serverLog;
+
+    enum LogLevels {Error = 0, Info, Debug, Trace};
+};
+
+
+OpenconnectAuthWidget::OpenconnectAuthWidget(const NetworkManager::VpnSetting::Ptr \
&setting, QWidget * parent) +    : SettingWidget(setting, parent), d_ptr(new \
OpenconnectAuthWidgetPrivate) +{
+    Q_D(OpenconnectAuthWidget);
+    d->setting = setting;
+    d->ui.setupUi(this);
+    d->userQuit = false;
+    if (pipe2(d->cancelPipes, O_NONBLOCK|O_CLOEXEC)) {
+        // Should never happen. Just don't do real cancellation if it does
+        d->cancelPipes[0] = -1;
+        d->cancelPipes[1] = -1;
+    }
+
+    connect(d->ui.cmbLogLevel, SIGNAL(currentIndexChanged(int)), this, \
SLOT(logLevelChanged(int))); +    connect(d->ui.viewServerLog, SIGNAL(toggled(bool)), \
this, SLOT(viewServerLogToggled(bool))); +    connect(d->ui.btnConnect, \
SIGNAL(clicked()), this, SLOT(connectHost())); +    connect(d->ui.cmbHosts, \
SIGNAL(currentIndexChanged(int)), this, SLOT(connectHost())); +
+    d->ui.cmbLogLevel->setCurrentIndex(OpenconnectAuthWidgetPrivate::Debug);
+    d->ui.btnConnect->setIcon(KIcon("network-connect"));
+    d->ui.viewServerLog->setChecked(false);
+
+    d->worker = new OpenconnectAuthWorkerThread(&d->mutex, &d->workerWaiting, \
&d->userQuit, d->cancelPipes[0]); +
+    // gets the pointer to struct openconnect_info (defined in openconnect.h), which \
contains data that OpenConnect needs, +    // and which needs to be populated with \
settings we get from NM, like host, certificate or private key +    d->vpninfo = \
d->worker->getOpenconnectInfo(); +
+    connect(d->worker, SIGNAL(validatePeerCert(QString,QString,QString,bool*)), \
this, SLOT(validatePeerCert(QString,QString,QString,bool*))); +    connect(d->worker, \
SIGNAL(processAuthForm(oc_auth_form*)), this, SLOT(processAuthForm(oc_auth_form*))); \
+    connect(d->worker, SIGNAL(updateLog(QString,int)), this, \
SLOT(updateLog(QString,int))); +    connect(d->worker, \
SIGNAL(writeNewConfig(QString)), this, SLOT(writeNewConfig(QString))); +    \
connect(d->worker, SIGNAL(cookieObtained(int)), this, SLOT(workerFinished(int))); +
+    readConfig();
+    readSecrets();
+}
+
+OpenconnectAuthWidget::~OpenconnectAuthWidget()
+{
+    Q_D(OpenconnectAuthWidget);
+    d->userQuit = true;
+    if (write(d->cancelPipes[1], "x", 1)) {
+        // not a lot we can do
+    }
+    d->workerWaiting.wakeAll();
+    d->worker->wait();
+    ::close(d->cancelPipes[0]);
+    ::close(d->cancelPipes[1]);
+    deleteAllFromLayout(d->ui.loginBoxLayout);
+    delete d->worker;
+    delete d;
+}
+
+void OpenconnectAuthWidget::readConfig()
+{
+    Q_D(OpenconnectAuthWidget);
+
+    const NMStringMap dataMap = d->setting->data();
+
+    if (!dataMap[NM_OPENCONNECT_KEY_GATEWAY].isEmpty()) {
+        const QString gw = dataMap[NM_OPENCONNECT_KEY_GATEWAY];
+        VPNHost host;
+        const int index = gw.indexOf(QLatin1Char('/'));
+        if (index > -1) {
+            host.name = host.address = gw.left(index);
+            host.group = gw.right(gw.length() - index - 1);
+        } else {
+            host.name = host.address = gw;
+        }
+        d->hosts.append(host);
+    }
+    if (!dataMap[NM_OPENCONNECT_KEY_CACERT].isEmpty()) {
+        const QByteArray crt = \
QFile::encodeName(dataMap[NM_OPENCONNECT_KEY_CACERT]); +        \
openconnect_set_cafile(d->vpninfo, strdup(crt.data())); +    }
+    if (dataMap[NM_OPENCONNECT_KEY_CSD_ENABLE] == "yes") {
+        char *wrapper;
+        wrapper = 0;
+        if (!dataMap[NM_OPENCONNECT_KEY_CSD_WRAPPER].isEmpty()) {
+            const QByteArray wrapperScript = \
QFile::encodeName(dataMap[NM_OPENCONNECT_KEY_CSD_WRAPPER]); +            wrapper = \
strdup(wrapperScript.data()); +        }
+        openconnect_setup_csd(d->vpninfo, getuid(), 1, wrapper);
+    }
+    if (!dataMap[NM_OPENCONNECT_KEY_PROXY].isEmpty()) {
+        const QByteArray proxy = \
QFile::encodeName(dataMap[NM_OPENCONNECT_KEY_PROXY]); +        \
openconnect_set_http_proxy(d->vpninfo, strdup(proxy.data())); +    }
+    if (!dataMap[NM_OPENCONNECT_KEY_USERCERT].isEmpty()) {
+        const QByteArray crt = \
QFile::encodeName(dataMap[NM_OPENCONNECT_KEY_USERCERT]); +        const QByteArray \
key = QFile::encodeName(dataMap[NM_OPENCONNECT_KEY_PRIVKEY]); +        \
openconnect_set_client_cert (d->vpninfo, strdup(crt.data()), strdup(key.data())); +
+        if (!crt.isEmpty() && dataMap[NM_OPENCONNECT_KEY_PEM_PASSPHRASE_FSID] == \
"yes") { +            openconnect_passphrase_from_fsid(d->vpninfo);
+        }
+    }
+}
+
+void OpenconnectAuthWidget::readSecrets()
+{
+    Q_D(OpenconnectAuthWidget);
+
+    d->secrets = d->setting->secrets();
+
+    if (!d->secrets[NM_OPENCONNECT_KEY_GWCERT].isEmpty()) {
+        d->certificateFingerprints.append(d->secrets[NM_OPENCONNECT_KEY_GWCERT]);
+    }
+
+    if (!d->secrets["xmlconfig"].isEmpty()) {
+
+        QByteArray config = \
QByteArray::fromBase64(d->secrets["xmlconfig"].toAscii()); +
+        QCryptographicHash hash(QCryptographicHash::Sha1);
+        hash.addData(config.data(), config.size());
+        const char *sha1_text = hash.result().toHex();
+        openconnect_set_xmlsha1 (d->vpninfo, (char *)sha1_text, \
strlen(sha1_text)+1); +
+        QDomDocument xmlconfig;
+        xmlconfig.setContent(config);
+        const QDomNode anyConnectProfile = \
xmlconfig.elementsByTagName(QLatin1String("AnyConnectProfile")).at(0); +        bool \
matchedGw = false; +        const QDomNode serverList = \
anyConnectProfile.firstChildElement(QLatin1String("ServerList")); +        for \
(QDomElement entry = serverList.firstChildElement(QLatin1String("HostEntry")); \
!entry.isNull(); entry = entry.nextSiblingElement(QLatin1String("HostEntry"))) { +    \
VPNHost host; +            host.name = \
entry.firstChildElement(QLatin1String("HostName")).text(); +            host.group = \
entry.firstChildElement(QLatin1String("UserGroup")).text(); +            host.address \
= entry.firstChildElement(QLatin1String("HostAddress")).text(); +            // We \
added the originally configured host in readConfig(). But if +            // it \
matches one of the ones in the XML config (as presumably it +            // should), \
remove the original and use the one with the pretty name. +            if (!matchedGw \
&& host.address == d->hosts.at(0).address) { +                d->hosts.removeFirst();
+                matchedGw = true;
+            }
+            d->hosts.append(host);
+        }
+    }
+
+    for (int i = 0; i < d->hosts.size(); i++) {
+        d->ui.cmbHosts->addItem(d->hosts.at(i).name, i);
+        if (d->secrets["lasthost"] == d->hosts.at(i).name || d->secrets["lasthost"] \
== d->hosts.at(i).address) +            d->ui.cmbHosts->setCurrentIndex(i);
+    }
+
+    if (d->secrets["autoconnect"] == "yes") {
+        d->ui.chkAutoconnect->setChecked(true);
+        connectHost();
+    }
+    if (!d->secrets["certsigs"].isEmpty()) {
+        d->certificateFingerprints.append(d->secrets["certsigs"].split('\t'));
+    }
+    d->certificateFingerprints.removeDuplicates();
+}
+
+void OpenconnectAuthWidget::acceptDialog()
+{
+    KDialog *dialog = qobject_cast<KDialog*>(parentWidget());
+    if (dialog) {
+        dialog->accept();
+    }
+}
+
+// This starts the worker thread, which connects to the selected AnyConnect host
+// and retrieves the login form
+void OpenconnectAuthWidget::connectHost()
+{
+    Q_D(OpenconnectAuthWidget);
+    d->userQuit = true;
+    if (write(d->cancelPipes[1], "x", 1)) {
+        // not a lot we can do
+    }
+    d->workerWaiting.wakeAll();
+    d->worker->wait();
+    d->userQuit = false;
+
+    /* Suck out the cancel byte(s) */
+    char buf;
+    while (read(d->cancelPipes[0], &buf, 1) == 1)
+        ;
+    deleteAllFromLayout(d->ui.loginBoxLayout);
+    int i = d->ui.cmbHosts->currentIndex();
+    if (i == -1)
+        return;
+    i = d->ui.cmbHosts->itemData(i).toInt();
+    const VPNHost &host = d->hosts.at(i);
+    if (openconnect_parse_url(d->vpninfo, host.address.toAscii().data())) {
+        kWarning() << "Failed to parse server URL" << host.address;
+        openconnect_set_hostname(d->vpninfo, strdup(host.address.toAscii().data()));
+    }
+    if (!openconnect_get_urlpath(d->vpninfo) && !host.group.isEmpty())
+        openconnect_set_urlpath(d->vpninfo, strdup(host.group.toAscii().data()));
+    d->secrets["lasthost"] = host.name;
+    addFormInfo(QLatin1String("dialog-information"), i18n("Contacting host, please \
wait...")); +    d->worker->start();
+}
+
+QVariantMap OpenconnectAuthWidget::setting(bool agentOwned) const
+{
+    Q_D(const OpenconnectAuthWidget);
+    Q_UNUSED(agentOwned)
+
+    NMStringMap secrets;
+    QVariantMap secretData;
+
+    secrets.unite(d->secrets);
+    QString host(openconnect_get_hostname(d->vpninfo));
+    QString port = QString::number(openconnect_get_port(d->vpninfo));
+    secrets.insert(QLatin1String(NM_OPENCONNECT_KEY_GATEWAY), host + ':' + port);
+
+    secrets.insert(QLatin1String(NM_OPENCONNECT_KEY_COOKIE), \
QLatin1String(openconnect_get_cookie(d->vpninfo))); +    \
openconnect_clear_cookie(d->vpninfo); +
+    OPENCONNECT_X509 *cert = openconnect_get_peer_cert(d->vpninfo);
+    char fingerprint[41];
+    openconnect_get_cert_sha1(d->vpninfo, cert, fingerprint);
+    secrets.insert(QLatin1String(NM_OPENCONNECT_KEY_GWCERT), \
QLatin1String(fingerprint)); +    secrets.insert(QLatin1String("certsigs"), \
d->certificateFingerprints.join("\t")); +    \
secrets.insert(QLatin1String("autoconnect"), d->ui.chkAutoconnect->isChecked() ? \
"yes" : "no"); +
+    NMStringMap::iterator i = secrets.begin();
+    while (i != secrets.end()) {
+        if (i.value().isEmpty())
+            i = secrets.erase(i);
+        else
+            i++;
+    }
+
+    secretData.insert("secrets", QVariant::fromValue<NMStringMap>(secrets));
+    return secretData;
+}
+
+void OpenconnectAuthWidget::writeNewConfig(const QString & buf)
+{
+    Q_D(OpenconnectAuthWidget);
+    d->secrets["xmlconfig"] = buf;
+}
+
+void OpenconnectAuthWidget::updateLog(const QString &message, const int &level)
+{
+    Q_D(OpenconnectAuthWidget);
+    QPair<QString, int> pair;
+    pair.first = message;
+    if (pair.first.endsWith(QLatin1String("\n")))
+        pair.first.chop(1);
+    switch (level)
+    {
+    case PRG_ERR:
+        pair.second = OpenconnectAuthWidgetPrivate::Error;
+        break;
+    case PRG_INFO:
+        pair.second = OpenconnectAuthWidgetPrivate::Info;
+        break;
+    case PRG_DEBUG:
+        pair.second = OpenconnectAuthWidgetPrivate::Debug;
+        break;
+    case PRG_TRACE:
+        pair.second = OpenconnectAuthWidgetPrivate::Trace;
+        break;
+    }
+    if (pair.second <= d->ui.cmbLogLevel->currentIndex()) {
+        d->ui.serverLog->append(pair.first);
+    }
+
+    d->serverLog.append(pair);
+    if (d->serverLog.size() > 100) {
+        d->serverLog.removeFirst();
+    }
+}
+
+void OpenconnectAuthWidget::logLevelChanged(int newLevel)
+{
+    Q_D(OpenconnectAuthWidget);
+    d->ui.serverLog->clear();
+    QList<QPair<QString, int> >::const_iterator i;
+
+    for (i = d->serverLog.constBegin(); i != d->serverLog.constEnd(); ++i) {
+        QPair<QString, int> pair = *i;
+        if(pair.second <= newLevel) {
+            d->ui.serverLog->append(pair.first);
+        }
+    }
+}
+
+void OpenconnectAuthWidget::addFormInfo(const QString &iconName, const QString \
&message) +{
+    Q_D(OpenconnectAuthWidget);
+    QHBoxLayout *layout = new QHBoxLayout();
+    QLabel *icon = new QLabel(this);
+    QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+    sizePolicy.setHorizontalStretch(0);
+    sizePolicy.setVerticalStretch(0);
+    sizePolicy.setHeightForWidth(icon->sizePolicy().hasHeightForWidth());
+    icon->setSizePolicy(sizePolicy);
+    icon->setMinimumSize(QSize(16, 16));
+    icon->setMaximumSize(QSize(16, 16));
+    layout->addWidget(icon);
+
+    QLabel *text = new QLabel(this);
+    text->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
+    text->setWordWrap(false);
+    layout->addWidget(text);
+
+    icon->setPixmap(KIcon(iconName).pixmap(QSize(16,16)));
+    text->setText(message);
+
+    d->ui.loginBoxLayout->addLayout(layout);
+}
+
+void OpenconnectAuthWidget::processAuthForm(struct oc_auth_form *form)
+{
+    Q_D(OpenconnectAuthWidget);
+    deleteAllFromLayout(d->ui.loginBoxLayout);
+    if (form->banner) {
+        addFormInfo(QLatin1String("dialog-information"), form->banner);
+    }
+    if (form->message) {
+        addFormInfo(QLatin1String("dialog-information"), form->message);
+    }
+    if (form->error) {
+        addFormInfo(QLatin1String("dialog-error"), form->error);
+    }
+
+    struct oc_form_opt *opt;
+    QFormLayout *layout = new QFormLayout();
+    QSizePolicy policy(QSizePolicy::Expanding, QSizePolicy::Fixed);
+    QCheckBox *togglePasswordMode = new QCheckBox(this);
+    togglePasswordMode->setText(i18n("&Show password"));
+    togglePasswordMode->setChecked(false);
+    connect(togglePasswordMode, SIGNAL(toggled(bool)), this, \
SLOT(passwordModeToggled(bool))); +    int passwordnumber = 0;
+    bool focusSet = false;
+    for (opt = form->opts; opt; opt = opt->next) {
+        if (opt->type == OC_FORM_OPT_HIDDEN)
+            continue;
+        QLabel *text = new QLabel(this);
+        text->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
+        text->setText(QString(opt->label));
+        QWidget *widget = 0;
+        const QString key = \
QString("form:%1:%2").arg(QLatin1String(form->auth_id)).arg(QLatin1String(opt->name));
 +        const QString value = d->secrets.value(key);
+        if (opt->type == OC_FORM_OPT_PASSWORD || opt->type == OC_FORM_OPT_TEXT) {
+            KLineEdit *le = new KLineEdit(this);
+            if (opt->type == OC_FORM_OPT_PASSWORD) {
+                le->setPasswordMode(true);
+                passwordnumber++;
+            }
+            else {
+                le->setText(value);
+            }
+            if (!focusSet && le->text().isEmpty()) {
+                le->setFocus(Qt::OtherFocusReason);
+                focusSet = true;
+            }
+            widget = qobject_cast<QWidget*>(le);
+        } else if (opt->type == OC_FORM_OPT_SELECT) {
+            KComboBox *cmb = new KComboBox(this);
+            struct oc_form_opt_select *sopt = reinterpret_cast<oc_form_opt_select \
*>(opt); +            for (int i = 0; i < sopt->nr_choices; i++) {
+                cmb->addItem(QString::fromUtf8(sopt->choices[i].label), \
QString::fromUtf8(sopt->choices[i].name)); +                if (value == \
QString::fromUtf8(sopt->choices[i].name)) +                    \
cmb->setCurrentIndex(i); +            }
+            widget = qobject_cast<QWidget*>(cmb);
+        }
+        if (widget) {
+            widget->setProperty("openconnect_opt", (quintptr)opt);
+            widget->setSizePolicy(policy);
+            layout->addRow(text, widget);
+        }
+    }
+    d->ui.loginBoxLayout->addLayout(layout);
+    d->ui.loginBoxLayout->addWidget(togglePasswordMode);
+    if (passwordnumber == 0)
+        togglePasswordMode->setVisible(false);
+    KDialogButtonBox *box = new KDialogButtonBox(this);
+    QPushButton *btn = box->addButton(QDialogButtonBox::Ok);
+    btn->setText(i18n("Login"));
+    btn->setDefault(true);
+    d->ui.loginBoxLayout->addWidget(box);
+    box->setProperty("openconnect_form", (quintptr)form);
+
+    connect(box, SIGNAL(accepted()), this, SLOT(formLoginClicked()));
+}
+
+void OpenconnectAuthWidget::validatePeerCert(const QString &fingerprint,
+                                             const QString &peerCert, const QString \
&reason, bool *accepted) +{
+    Q_D(OpenconnectAuthWidget);
+
+    if (!d->certificateFingerprints.contains(fingerprint)) {
+        QWidget *widget = new QWidget();
+        QVBoxLayout *verticalLayout;
+        QHBoxLayout *horizontalLayout;
+        QLabel *icon;
+        QLabel *infoText;
+        KTextBrowser *certificate;
+
+        verticalLayout = new QVBoxLayout(widget);
+        horizontalLayout = new QHBoxLayout(widget);
+        icon = new QLabel(widget);
+        QSizePolicy sizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
+        sizePolicy.setHorizontalStretch(0);
+        sizePolicy.setVerticalStretch(0);
+        sizePolicy.setHeightForWidth(icon->sizePolicy().hasHeightForWidth());
+        icon->setSizePolicy(sizePolicy);
+        icon->setMinimumSize(QSize(48, 48));
+        icon->setMaximumSize(QSize(48, 48));
+
+        horizontalLayout->addWidget(icon);
+
+        infoText = new QLabel(widget);
+        infoText->setAlignment(Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter);
+
+        horizontalLayout->addWidget(infoText);
+
+        verticalLayout->addLayout(horizontalLayout);
+
+        certificate = new KTextBrowser(widget);
+        certificate->setTextInteractionFlags(Qt::TextSelectableByMouse);
+        certificate->setOpenLinks(false);
+
+        verticalLayout->addWidget(certificate);
+
+        icon->setPixmap(KIcon("dialog-information").pixmap(QSize(48,48)));
+        infoText->setText(i18n("Check failed for certificate from VPN server \
\"%1\".\n" +                               "Reason: %2\nAccept it anyway?", \
openconnect_get_hostname(d->vpninfo),reason)); +        infoText->setWordWrap(true);
+        certificate->setText(peerCert);
+
+        QWeakPointer<KDialog> dialog = new KDialog(this);
+        dialog.data()->setWindowModality(Qt::WindowModal);
+        dialog.data()->setButtons(KDialog::Yes | KDialog::No);
+        dialog.data()->setMainWidget(widget);
+        if(dialog.data()->exec() == KDialog::Yes) {
+            d->certificateFingerprints.append(fingerprint);
+            *accepted = true;
+        } else {
+            *accepted = false;
+        }
+        if (dialog) {
+            dialog.data()->deleteLater();
+        }
+        widget->deleteLater();
+    } else {
+        *accepted = true;
+    }
+    d->mutex.lock();
+    d->workerWaiting.wakeAll();
+    d->mutex.unlock();
+}
+
+// Writes the user input from the form into the oc_auth_form structs we got from
+// libopenconnect, and wakes the worker thread up to try to log in and obtain a
+// cookie with this data
+void OpenconnectAuthWidget::formLoginClicked()
+{
+    Q_D(OpenconnectAuthWidget);
+    int lastIndex = d->ui.loginBoxLayout->count() - 1;
+    QLayout *layout = d->ui.loginBoxLayout->itemAt(lastIndex - 2)->layout();
+    struct oc_auth_form *form = (struct oc_auth_form *) \
d->ui.loginBoxLayout->itemAt(lastIndex)->widget()->property("openconnect_form").value<quintptr>();
 +
+    for (int i = 0; i < layout->count(); i++) {
+        QLayoutItem *item = layout->itemAt(i);
+        QWidget *widget = item->widget();
+        if (widget && widget->property("openconnect_opt").isValid()) {
+            struct oc_form_opt *opt = (struct oc_form_opt *) \
widget->property("openconnect_opt").value<quintptr>(); +            QString key = \
QString("form:%1:%2").arg(QLatin1String(form->auth_id)).arg(QLatin1String(opt->name));
 +            if (opt->type == OC_FORM_OPT_PASSWORD || opt->type == OC_FORM_OPT_TEXT) \
{ +                KLineEdit *le = qobject_cast<KLineEdit*>(widget);
+                QByteArray text = le->text().toUtf8();
+                opt->value = strdup(text.data());
+                if (opt->type == OC_FORM_OPT_TEXT) {
+                    d->secrets.insert(key,le->text());
+                }
+            } else if (opt->type == OC_FORM_OPT_SELECT) {
+                KComboBox *cbo = qobject_cast<KComboBox*>(widget);
+                QByteArray text = \
cbo->itemData(cbo->currentIndex()).toString().toAscii(); +                opt->value \
= strdup(text.data()); +                \
d->secrets.insert(key,cbo->itemData(cbo->currentIndex()).toString()); +            }
+        }
+    }
+    deleteAllFromLayout(d->ui.loginBoxLayout);
+    d->workerWaiting.wakeAll();
+}
+
+void OpenconnectAuthWidget::workerFinished(const int &ret)
+{
+    Q_D(OpenconnectAuthWidget);
+    if (ret < 0) {
+        QString message;
+        QList<QPair<QString, int> >::const_iterator i;
+        for (i = d->serverLog.constEnd()-1; i >= d->serverLog.constBegin(); --i) {
+            QPair<QString, int> pair = *i;
+            if(pair.second <= OpenconnectAuthWidgetPrivate::Error) {
+                message = pair.first;
+                break;
+            }
+        }
+        if (message.isEmpty())
+            message = i18n("Connection attempt was unsuccessful.");
+        deleteAllFromLayout(d->ui.loginBoxLayout);
+        addFormInfo(QLatin1String("dialog-error"), message);
+    } else {
+        deleteAllFromLayout(d->ui.loginBoxLayout);
+        acceptDialog();
+    }
+}
+
+void OpenconnectAuthWidget::deleteAllFromLayout(QLayout *layout)
+{
+    while (QLayoutItem *item = layout->takeAt(0)) {
+        if (QLayout *itemLayout = item->layout()) {
+            deleteAllFromLayout(itemLayout);
+            itemLayout->deleteLater();
+        }
+        else
+            item->widget()->deleteLater();
+        delete item;
+    }
+    layout->invalidate();
+}
+
+void OpenconnectAuthWidget::viewServerLogToggled(bool toggled)
+{
+    Q_D(OpenconnectAuthWidget);
+    d->ui.lblLogLevel->setVisible(toggled);
+    d->ui.cmbLogLevel->setVisible(toggled);
+    if (toggled) {
+        QLayoutItem *item = d->ui.verticalLayout->takeAt(4);
+        if (item) {
+            delete item;
+        }
+        QSizePolicy policy = d->ui.serverLogBox->sizePolicy();
+        policy.setVerticalPolicy(QSizePolicy::Expanding);
+        d->ui.serverLogBox->setSizePolicy(policy);
+        d->ui.serverLog->setVisible(true);
+    } else {
+        QSpacerItem *verticalSpacer = new QSpacerItem(0, 0, QSizePolicy::Minimum, \
QSizePolicy::Expanding); +        d->ui.verticalLayout->addItem(verticalSpacer);
+        d->ui.serverLog->setVisible(false);
+        QSizePolicy policy = d->ui.serverLogBox->sizePolicy();
+        policy.setVerticalPolicy(QSizePolicy::Fixed);
+        d->ui.serverLogBox->setSizePolicy(policy);
+    }
+}
+
+void OpenconnectAuthWidget::passwordModeToggled(bool toggled)
+{
+    Q_D(OpenconnectAuthWidget);
+    int lastIndex = d->ui.loginBoxLayout->count() - 1;
+    QLayout *layout = d->ui.loginBoxLayout->itemAt(lastIndex - 2)->layout();
+    for (int i = 0; i < layout->count(); i++) {
+        QLayoutItem *item = layout->itemAt(i);
+        QWidget *widget = item->widget();
+        if (widget && widget->property("openconnect_opt").isValid()) {
+            struct oc_form_opt *opt = (struct oc_form_opt *) \
widget->property("openconnect_opt").value<quintptr>(); +            if (opt->type == \
OC_FORM_OPT_PASSWORD) { +                KLineEdit *le = \
qobject_cast<KLineEdit*>(widget); +                le->setPasswordMode(!toggled);
+            }
+        }
+    }
+}
diff --git a/vpn/openconnect/openconnectauth.h b/vpn/openconnect/openconnectauth.h
new file mode 100644
index 0000000..50ff893
--- /dev/null
+++ b/vpn/openconnect/openconnectauth.h
@@ -0,0 +1,67 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.net>
+Copyright 2013 Lukáš Tinkl <ltinkl@redhat.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef OPENCONNECTAUTH_H
+#define OPENCONNECTAUTH_H
+
+#include "settingwidget.h"
+
+#include <NetworkManagerQt/VpnSetting>
+
+#include <QString>
+
+class QLayout;
+struct openconnect_info;
+struct oc_auth_form;
+
+class OpenconnectAuthWidgetPrivate;
+
+class OpenconnectAuthWidget : public SettingWidget
+{
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(OpenconnectAuthWidget)
+public:
+    explicit OpenconnectAuthWidget(const NetworkManager::VpnSetting::Ptr &setting, \
QWidget * parent = 0); +    ~OpenconnectAuthWidget();
+    virtual void readSecrets();
+    void readConfig();
+    virtual QVariantMap setting(bool agentOwned = false) const;
+
+private:
+    OpenconnectAuthWidgetPrivate * d_ptr;
+    void acceptDialog();
+    void addFormInfo(const QString &, const QString &);
+    void deleteAllFromLayout(QLayout *);
+
+private slots:
+    void writeNewConfig(const QString &);
+    void validatePeerCert(const QString &, const QString &, const QString &, bool*);
+    void processAuthForm(struct oc_auth_form *);
+    void updateLog(const QString &, const int &);
+    void logLevelChanged(int);
+    void formLoginClicked();
+    void workerFinished(const int&);
+    void viewServerLogToggled(bool);
+    void passwordModeToggled(bool);
+    void connectHost();
+};
+
+#endif // OPENCONNECTAUTH_H
diff --git a/vpn/openconnect/openconnectauth.ui b/vpn/openconnect/openconnectauth.ui
new file mode 100644
index 0000000..538d109
--- /dev/null
+++ b/vpn/openconnect/openconnectauth.ui
@@ -0,0 +1,201 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OpenconnectAuth</class>
+ <widget class="QWidget" name="OpenconnectAuth">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>341</width>
+    <height>297</height>
+   </rect>
+  </property>
+  <property name="minimumSize">
+   <size>
+    <width>0</width>
+    <height>0</height>
+   </size>
+  </property>
+  <property name="windowTitle">
+   <string>OpenConnect VPN Authentication</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <property name="sizeConstraint">
+    <enum>QLayout::SetMinimumSize</enum>
+   </property>
+   <property name="margin">
+    <number>0</number>
+   </property>
+   <item>
+    <layout class="QHBoxLayout" name="horizontalLayout_3">
+     <item>
+      <widget class="QLabel" name="label_3">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="text">
+        <string>VPN Host</string>
+       </property>
+      </widget>
+     </item>
+     <item>
+      <widget class="KComboBox" name="cmbHosts"/>
+     </item>
+     <item>
+      <widget class="KPushButton" name="btnConnect">
+       <property name="sizePolicy">
+        <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+         <horstretch>0</horstretch>
+         <verstretch>0</verstretch>
+        </sizepolicy>
+       </property>
+       <property name="toolTip">
+        <string>Connect</string>
+       </property>
+       <property name="autoDefault">
+        <bool>false</bool>
+       </property>
+       <property name="default">
+        <bool>false</bool>
+       </property>
+       <property name="flat">
+        <bool>false</bool>
+       </property>
+       <property name="isDragEnabled" stdset="0">
+        <bool>false</bool>
+       </property>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <widget class="QCheckBox" name="chkAutoconnect">
+     <property name="text">
+      <string>Automatically start connecting next time</string>
+     </property>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="loginBox">
+     <property name="minimumSize">
+      <size>
+       <width>0</width>
+       <height>0</height>
+      </size>
+     </property>
+     <property name="title">
+      <string notr="true"/>
+     </property>
+     <layout class="QVBoxLayout" name="loginBoxLayout"/>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="serverLogBox">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <layout class="QVBoxLayout" name="logLayout">
+      <property name="margin">
+       <number>0</number>
+      </property>
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_2">
+        <item>
+         <widget class="QCheckBox" name="viewServerLog">
+          <property name="text">
+           <string>View Log</string>
+          </property>
+          <property name="checked">
+           <bool>true</bool>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLabel" name="lblLogLevel">
+          <property name="text">
+           <string>Log Level:</string>
+          </property>
+          <property name="alignment">
+           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="KComboBox" name="cmbLogLevel">
+          <item>
+           <property name="text">
+            <string>Error</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>Info</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string comment="like in Debug log level">Debug</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>Trace</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+       </layout>
+      </item>
+      <item>
+       <widget class="KTextBrowser" name="serverLog">
+        <property name="enabled">
+         <bool>true</bool>
+        </property>
+        <property name="frameShape">
+         <enum>QFrame::StyledPanel</enum>
+        </property>
+        <property name="frameShadow">
+         <enum>QFrame::Sunken</enum>
+        </property>
+        <property name="acceptRichText">
+         <bool>false</bool>
+        </property>
+        <property name="textInteractionFlags">
+         <set>Qt::TextSelectableByMouse</set>
+        </property>
+        <property name="openLinks">
+         <bool>false</bool>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KTextBrowser</class>
+   <extends>QTextBrowser</extends>
+   <header>ktextbrowser.h</header>
+  </customwidget>
+  <customwidget>
+   <class>KComboBox</class>
+   <extends>QComboBox</extends>
+   <header>kcombobox.h</header>
+  </customwidget>
+  <customwidget>
+   <class>KPushButton</class>
+   <extends>QPushButton</extends>
+   <header>kpushbutton.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/vpn/openconnect/openconnectauthworkerthread.cpp \
b/vpn/openconnect/openconnectauthworkerthread.cpp new file mode 100644
index 0000000..6f604b0
--- /dev/null
+++ b/vpn/openconnect/openconnectauthworkerthread.cpp
@@ -0,0 +1,196 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.net>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "openconnectauthworkerthread.h"
+
+#include <QThread>
+#include <QMutex>
+#include <QWaitCondition>
+#include <QString>
+#include <QByteArray>
+
+extern "C"
+{
+#include <openconnect.h>
+#include <stdlib.h>
+#if !OPENCONNECT_CHECK_VER(1,5)
+#include <openssl/ssl.h>
+#include <openssl/bio.h>
+#include <openssl/ossl_typ.h>
+#endif
+#include <errno.h>
+}
+
+#include <cstdarg>
+
+class OpenconnectAuthStaticWrapper
+{
+public:
+    static int writeNewConfig(void *obj, char *str, int num)
+    {
+        if (obj)
+            return static_cast<OpenconnectAuthWorkerThread*>(obj)->writeNewConfig(str, \
num); +        return -1;
+    }
+    static int validatePeerCert(void *obj, OPENCONNECT_X509 *cert, const char *str)
+    {
+        if (obj)
+            return static_cast<OpenconnectAuthWorkerThread*>(obj)->validatePeerCert(cert, \
str); +        return -1;
+    }
+    static int processAuthForm(void *obj, struct oc_auth_form *form)
+    {
+        if (obj)
+            return static_cast<OpenconnectAuthWorkerThread*>(obj)->processAuthFormP(form);
 +        return -1;
+    }
+    static void writeProgress(void *obj, int level, const char *str, ...)
+    {
+        if (obj) {
+            va_list argPtr;
+            va_start(argPtr, str);
+            static_cast<OpenconnectAuthWorkerThread*>(obj)->writeProgress(level, \
str, argPtr); +            va_end(argPtr);
+        }
+    }
+};
+
+OpenconnectAuthWorkerThread::OpenconnectAuthWorkerThread(QMutex *mutex, \
QWaitCondition *waitForUserInput, bool *userDecidedToQuit, int cancelFd) +    : \
QThread(), m_mutex(mutex), m_waitForUserInput(waitForUserInput), \
m_userDecidedToQuit(userDecidedToQuit) +{
+    m_openconnectInfo = openconnect_vpninfo_new((char*)"OpenConnect VPN Agent \
(PlasmaNM - running on KDE)", +                                                \
OpenconnectAuthStaticWrapper::validatePeerCert, +                                     \
OpenconnectAuthStaticWrapper::writeNewConfig, +                                       \
OpenconnectAuthStaticWrapper::processAuthForm, +                                      \
OpenconnectAuthStaticWrapper::writeProgress, +                                        \
this); +#if OPENCONNECT_CHECK_VER(1,4)
+    openconnect_set_cancel_fd(m_openconnectInfo, cancelFd);
+#else
+    // Silence warning about unused parameter
+    Q_UNUSED(cancelFd);
+#endif
+}
+
+OpenconnectAuthWorkerThread::~OpenconnectAuthWorkerThread()
+{
+    openconnect_vpninfo_free(m_openconnectInfo);
+}
+
+void OpenconnectAuthWorkerThread::run()
+{
+    openconnect_init_ssl();
+    int ret = openconnect_obtain_cookie(m_openconnectInfo);
+    if (*m_userDecidedToQuit)
+        return;
+    emit cookieObtained(ret);
+}
+
+struct openconnect_info* OpenconnectAuthWorkerThread::getOpenconnectInfo()
+{
+    return m_openconnectInfo;
+}
+
+int OpenconnectAuthWorkerThread::writeNewConfig(char *buf, int buflen)
+{
+    Q_UNUSED(buflen)
+    if (*m_userDecidedToQuit)
+        return -EINVAL;
+    emit writeNewConfig(QString(QByteArray(buf).toBase64()));
+    return 0;
+}
+
+#if !OPENCONNECT_CHECK_VER(1,5)
+static char *openconnect_get_cert_details(struct openconnect_info *vpninfo,
+                                          OPENCONNECT_X509 *cert)
+{
+    Q_UNUSED(vpninfo)
+
+    BIO *bp = BIO_new(BIO_s_mem());
+    BUF_MEM *certinfo;
+    char zero = 0;
+    char *ret;
+
+    X509_print_ex(bp, cert, 0, 0);
+    BIO_write(bp, &zero, 1);
+    BIO_get_mem_ptr(bp, &certinfo);
+
+    ret = strdup(certinfo->data);
+    BIO_free(bp);
+
+    return ret;
+}
+#endif
+
+int OpenconnectAuthWorkerThread::validatePeerCert(OPENCONNECT_X509 *cert, const char \
*reason) +{
+    if (*m_userDecidedToQuit)
+        return -EINVAL;
+    char fingerprint[41];
+    int ret = 0;
+
+    ret = openconnect_get_cert_sha1(m_openconnectInfo, cert, fingerprint);
+    if (ret)
+        return ret;
+
+    char *details = openconnect_get_cert_details(m_openconnectInfo, cert);
+
+    bool accepted = false;
+    m_mutex->lock();
+    QString qFingerprint(fingerprint);
+    QString qCertinfo(details);
+    QString qReason(reason);
+    emit validatePeerCert(qFingerprint, qCertinfo, qReason, &accepted);
+    m_waitForUserInput->wait(m_mutex);
+    m_mutex->unlock();
+    ::free(details);
+    if (*m_userDecidedToQuit)
+        return -EINVAL;
+
+    if (accepted)
+        return 0;
+    else
+        return -EINVAL;
+
+}
+
+int OpenconnectAuthWorkerThread::processAuthFormP(struct oc_auth_form *form)
+{
+    if (*m_userDecidedToQuit)
+        return -1;
+
+    m_mutex->lock();
+    emit processAuthForm(form);
+    m_waitForUserInput->wait(m_mutex);
+    m_mutex->unlock();
+    if (*m_userDecidedToQuit)
+        return -1;
+
+    return 0;
+}
+
+void OpenconnectAuthWorkerThread::writeProgress(int level, const char *fmt, va_list \
argPtr) +{
+    if (*m_userDecidedToQuit)
+        return;
+    QString msg;
+    msg.vsprintf(fmt, argPtr);
+    emit updateLog(msg, level);
+}
diff --git a/vpn/openconnect/openconnectauthworkerthread.h \
b/vpn/openconnect/openconnectauthworkerthread.h new file mode 100644
index 0000000..bceea8a
--- /dev/null
+++ b/vpn/openconnect/openconnectauthworkerthread.h
@@ -0,0 +1,80 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.net>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef OPENCONNECTAUTHWORKERTHREAD_H
+#define OPENCONNECTAUTHWORKERTHREAD_H
+
+extern "C" {
+#include <openconnect.h>
+}
+
+#if OPENCONNECT_API_VERSION_MAJOR == 1
+#define openconnect_vpninfo_new openconnect_vpninfo_new_with_cbdata
+#define openconnect_init_ssl openconnect_init_openssl
+#endif
+
+#ifndef OPENCONNECT_CHECK_VER
+#define OPENCONNECT_CHECK_VER(x,y) 0
+#endif
+
+#if !OPENCONNECT_CHECK_VER(1,5)
+struct x509_st;
+#define OPENCONNECT_X509 struct x509_st
+#define OPENCONNECT_OPENSSL
+#endif
+
+#include <QThread>
+
+class QMutex;
+class QWaitCondition;
+struct openconnect_info;
+
+class OpenconnectAuthWorkerThread : public QThread
+{
+    Q_OBJECT
+    friend class OpenconnectAuthStaticWrapper;
+public:
+    OpenconnectAuthWorkerThread(QMutex *, QWaitCondition *, bool *, int);
+    ~OpenconnectAuthWorkerThread();
+    struct openconnect_info* getOpenconnectInfo();
+
+signals:
+    void validatePeerCert(const QString &, const QString &, const QString &, bool*);
+    void processAuthForm(struct oc_auth_form *);
+    void updateLog(const QString &, const int&);
+    void writeNewConfig(const QString &);
+    void cookieObtained(const int&);
+
+protected:
+    void run();
+
+private:
+    int writeNewConfig(char *, int);
+    int validatePeerCert(OPENCONNECT_X509 *, const char *);
+    int processAuthFormP(struct oc_auth_form *);
+    void writeProgress(int level, const char *, va_list);
+
+    QMutex *m_mutex;
+    QWaitCondition *m_waitForUserInput;
+    bool *m_userDecidedToQuit;
+    struct openconnect_info *m_openconnectInfo;
+};
+
+#endif
diff --git a/vpn/openconnect/openconnectprop.ui b/vpn/openconnect/openconnectprop.ui
new file mode 100644
index 0000000..939f04e
--- /dev/null
+++ b/vpn/openconnect/openconnectprop.ui
@@ -0,0 +1,159 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>OpenconnectProp</class>
+ <widget class="QWidget" name="OpenconnectProp">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>306</width>
+    <height>327</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>OpenConnect Settings</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout_2">
+   <item>
+    <widget class="QGroupBox" name="grp_general">
+     <property name="title">
+      <string comment="like in General settings">General</string>
+     </property>
+     <layout class="QFormLayout" name="formLayout">
+      <property name="fieldGrowthPolicy">
+       <enum>QFormLayout::ExpandingFieldsGrow</enum>
+      </property>
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_4">
+        <property name="text">
+         <string>&amp;Gateway:</string>
+        </property>
+        <property name="buddy">
+         <cstring>leGateway</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="KLineEdit" name="leGateway"/>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label">
+        <property name="text">
+         <string>&amp;CA Certificate:</string>
+        </property>
+        <property name="buddy">
+         <cstring>leCaCertificate</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="1">
+       <widget class="KUrlRequester" name="leCaCertificate"/>
+      </item>
+      <item row="2" column="0">
+       <widget class="QLabel" name="label_2">
+        <property name="text">
+         <string>&amp;Proxy:</string>
+        </property>
+        <property name="buddy">
+         <cstring>leProxy</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="2" column="1">
+       <widget class="KLineEdit" name="leProxy"/>
+      </item>
+      <item row="4" column="0">
+       <widget class="QLabel" name="label_3">
+        <property name="text">
+         <string>CSD &amp;Wrapper Script:</string>
+        </property>
+        <property name="buddy">
+         <cstring>leCsdWrapperScript</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="3" column="0" colspan="2">
+       <widget class="QCheckBox" name="chkAllowTrojan">
+        <property name="text">
+         <string>Allow Cisco Secure Desktop &amp;trojan</string>
+        </property>
+       </widget>
+      </item>
+      <item row="4" column="1">
+       <widget class="KUrlRequester" name="leCsdWrapperScript"/>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QGroupBox" name="groupBox">
+     <property name="title">
+      <string>Certificate Authentication</string>
+     </property>
+     <layout class="QFormLayout" name="formLayout_2">
+      <item row="0" column="0">
+       <widget class="QLabel" name="label_5">
+        <property name="text">
+         <string>&amp;User Certificate:</string>
+        </property>
+        <property name="buddy">
+         <cstring>leUserCert</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="1" column="0">
+       <widget class="QLabel" name="label_6">
+        <property name="text">
+         <string>Private &amp;Key:</string>
+        </property>
+        <property name="buddy">
+         <cstring>leUserPrivateKey</cstring>
+        </property>
+       </widget>
+      </item>
+      <item row="0" column="1">
+       <widget class="KUrlRequester" name="leUserCert"/>
+      </item>
+      <item row="1" column="1">
+       <widget class="KUrlRequester" name="leUserPrivateKey"/>
+      </item>
+      <item row="2" column="0" colspan="2">
+       <widget class="QCheckBox" name="chkUseFsid">
+        <property name="text">
+         <string>Use &amp;FSID for key passphrase</string>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>0</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <customwidgets>
+  <customwidget>
+   <class>KUrlRequester</class>
+   <extends>QFrame</extends>
+   <header>kurlrequester.h</header>
+  </customwidget>
+  <customwidget>
+   <class>KLineEdit</class>
+   <extends>KLineEdit</extends>
+   <header>klineedit.h</header>
+  </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections/>
+</ui>
diff --git a/vpn/openconnect/openconnectui.cpp b/vpn/openconnect/openconnectui.cpp
new file mode 100644
index 0000000..fdcc485
--- /dev/null
+++ b/vpn/openconnect/openconnectui.cpp
@@ -0,0 +1,90 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.net>
+Copyright 2013 Lukas Tinkl <ltinkl@redhat.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "openconnectui.h"
+
+#include <KPluginFactory>
+
+#include "openconnectwidget.h"
+#include "openconnectauth.h"
+
+K_PLUGIN_FACTORY(OpenconnectUiPluginFactory, registerPlugin<OpenconnectUiPlugin>();)
+K_EXPORT_PLUGIN(OpenconnectUiPluginFactory("plasmanm_openconnectui"))
+
+OpenconnectUiPlugin::OpenconnectUiPlugin(QObject * parent, const QVariantList &) : \
VpnUiPlugin(parent) +{
+
+}
+
+OpenconnectUiPlugin::~OpenconnectUiPlugin()
+{
+
+}
+
+SettingWidget * OpenconnectUiPlugin::widget(const NetworkManager::VpnSetting::Ptr \
&setting, QWidget * parent) +{
+    return new OpenconnectSettingWidget(setting, parent);
+}
+
+SettingWidget * OpenconnectUiPlugin::askUser(const NetworkManager::VpnSetting::Ptr \
&setting, QWidget * parent) +{
+    return new OpenconnectAuthWidget(setting, parent);
+}
+
+#if 0
+KDialog::ButtonCodes OpenconnectUiPlugin::suggestAuthDialogButtons()
+{
+    return KDialog::Cancel;
+}
+
+QString OpenconnectUiPlugin::suggestedFileName(Knm::Connection *connection) const
+{
+    Q_UNUSED(connection);
+
+    // TODO : implement suggested file name
+    return QString();
+}
+
+QString OpenconnectUiPlugin::supportedFileExtensions() const
+{
+    // TODO: return supported file extensions
+    return QString();
+}
+
+QVariantList OpenconnectUiPlugin::importConnectionSettings(const QString &fileName)
+{
+    Q_UNUSED(fileName);
+
+    // TODO : import the Openconnect connection from file and return settings
+    mError = VpnUiPlugin::NotImplemented;
+    return QVariantList();
+}
+
+bool OpenconnectUiPlugin::exportConnectionSettings(Knm::Connection * connection, \
const QString &fileName) +{
+    Q_UNUSED(connection);
+    Q_UNUSED(fileName);
+
+    // TODO : export Openconnect connection to file
+    mError = VpnUiPlugin::NotImplemented;
+    return false;
+}
+#endif
diff --git a/vpn/openconnect/openconnectui.h b/vpn/openconnect/openconnectui.h
new file mode 100644
index 0000000..39994f3
--- /dev/null
+++ b/vpn/openconnect/openconnectui.h
@@ -0,0 +1,47 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.net>
+Copyright 2013 Lukas Tinkl <ltinkl@redhat.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef OPENCONNECT_UI_H
+#define OPENCONNECT_UI_H
+
+#include "vpnuiplugin.h"
+
+#include <QVariant>
+#include <KDialog>
+
+class OpenconnectUiPlugin : public VpnUiPlugin
+{
+    Q_OBJECT
+public:
+    explicit OpenconnectUiPlugin(QObject * parent = 0, const QVariantList& = \
QVariantList()); +    virtual ~OpenconnectUiPlugin();
+    virtual SettingWidget * widget(const NetworkManager::VpnSetting::Ptr &setting, \
QWidget * parent = 0); +    virtual SettingWidget * askUser(const \
NetworkManager::VpnSetting::Ptr &setting, QWidget * parent = 0); +#if 0
+    QString suggestedFileName(Knm::Connection *connection) const;
+    QString supportedFileExtensions() const;
+    QVariantList importConnectionSettings(const QString &fileName);
+    bool exportConnectionSettings(Knm::Connection * connection, const QString \
&fileName); +    KDialog::ButtonCodes suggestAuthDialogButtons();
+#endif
+};
+
+#endif //  OPENCONNECT_UI_H
diff --git a/vpn/openconnect/openconnectwidget.cpp \
b/vpn/openconnect/openconnectwidget.cpp new file mode 100644
index 0000000..a3a20fe
--- /dev/null
+++ b/vpn/openconnect/openconnectwidget.cpp
@@ -0,0 +1,110 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.de>
+Copyright 2013 Lukas Tinkl <ltinkl@redhat.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "openconnectwidget.h"
+#include <KDialog>
+
+#include "ui_openconnectprop.h"
+
+#include <QString>
+#include "nm-openconnect-service.h"
+
+class OpenconnectSettingWidgetPrivate
+{
+public:
+    Ui_OpenconnectProp ui;
+    NetworkManager::VpnSetting::Ptr setting;
+};
+
+OpenconnectSettingWidget::OpenconnectSettingWidget(const \
NetworkManager::VpnSetting::Ptr &setting, QWidget * parent) +: SettingWidget(setting, \
parent), d_ptr(new OpenconnectSettingWidgetPrivate) +{
+    Q_D(OpenconnectSettingWidget);
+    d->ui.setupUi(this);
+    d->setting = setting;
+
+    connect(d->ui.leGateway, SIGNAL(textChanged(QString)), \
SLOT(slotWidgetChanged())); +
+    if (d->setting)
+        loadConfig(d->setting);
+}
+
+OpenconnectSettingWidget::~OpenconnectSettingWidget()
+{
+    delete d_ptr;
+}
+
+void OpenconnectSettingWidget::loadConfig(const NetworkManager::Setting::Ptr \
&setting) +{
+    Q_D(OpenconnectSettingWidget);
+    Q_UNUSED(setting)
+
+    // General settings
+    const NMStringMap dataMap = d->setting->data();
+
+    d->ui.leGateway->setText(dataMap[NM_OPENCONNECT_KEY_GATEWAY]);
+    d->ui.leCaCertificate->setUrl(KUrl(dataMap[NM_OPENCONNECT_KEY_CACERT]));
+    d->ui.leProxy->setText(dataMap[NM_OPENCONNECT_KEY_PROXY]);
+    d->ui.chkAllowTrojan->setChecked(dataMap[NM_OPENCONNECT_KEY_CSD_ENABLE] == \
"yes"); +    d->ui.leCsdWrapperScript->setUrl(KUrl(dataMap[NM_OPENCONNECT_KEY_CSD_WRAPPER]));
 +    d->ui.leUserCert->setUrl(KUrl(dataMap[NM_OPENCONNECT_KEY_USERCERT]));
+    d->ui.leUserPrivateKey->setUrl(KUrl(dataMap[NM_OPENCONNECT_KEY_PRIVKEY]));
+    d->ui.chkUseFsid->setChecked(dataMap[NM_OPENCONNECT_KEY_PEM_PASSPHRASE_FSID] == \
"yes"); +}
+
+QVariantMap OpenconnectSettingWidget::setting(bool agentOwned) const
+{
+    Q_D(const OpenconnectSettingWidget);
+    Q_UNUSED(agentOwned)
+
+    NetworkManager::VpnSetting setting;
+    setting.setServiceType(QLatin1String(NM_DBUS_SERVICE_OPENCONNECT));
+
+    NMStringMap data;
+
+    data.insert(QLatin1String(NM_OPENCONNECT_KEY_GATEWAY), d->ui.leGateway->text());
+    if (!d->ui.leCaCertificate->url().isEmpty())
+        data.insert(QLatin1String(NM_OPENCONNECT_KEY_CACERT), \
d->ui.leCaCertificate->url().path()); +    if (!d->ui.leProxy->text().isEmpty())
+        data.insert(QLatin1String(NM_OPENCONNECT_KEY_PROXY), d->ui.leProxy->text());
+    data.insert(QLatin1String(NM_OPENCONNECT_KEY_CSD_ENABLE), \
d->ui.chkAllowTrojan->isChecked() ? "yes" : "no"); +    if \
(!d->ui.leCsdWrapperScript->url().isEmpty()) +        \
data.insert(QLatin1String(NM_OPENCONNECT_KEY_CSD_WRAPPER), \
d->ui.leCsdWrapperScript->url().path()); +    if (!d->ui.leUserCert->url().isEmpty())
+        data.insert(QLatin1String(NM_OPENCONNECT_KEY_USERCERT), \
d->ui.leUserCert->url().path()); +    if (!d->ui.leUserPrivateKey->url().isEmpty())
+        data.insert(QLatin1String(NM_OPENCONNECT_KEY_PRIVKEY), \
d->ui.leUserPrivateKey->url().path()); +    \
data.insert(QLatin1String(NM_OPENCONNECT_KEY_PEM_PASSPHRASE_FSID), \
d->ui.chkUseFsid->isChecked() ? "yes" : "no"); +
+    /* These are different for every login session, and should not be stored */
+    data.insert(QLatin1String(NM_OPENCONNECT_KEY_COOKIE"-flags"), \
QString::number(NetworkManager::Setting::NotSaved)); +    \
data.insert(QLatin1String(NM_OPENCONNECT_KEY_GWCERT"-flags"), \
QString::number(NetworkManager::Setting::NotSaved)); +    \
data.insert(QLatin1String(NM_OPENCONNECT_KEY_GATEWAY"-flags"), \
QString::number(NetworkManager::Setting::NotSaved)); +
+    setting.setData(data);
+    return setting.toMap();
+}
+
+bool OpenconnectSettingWidget::isValid() const
+{
+    Q_D(const OpenconnectSettingWidget);
+    return !d->ui.leGateway->text().isEmpty();
+}
diff --git a/vpn/openconnect/openconnectwidget.h \
b/vpn/openconnect/openconnectwidget.h new file mode 100644
index 0000000..516b704
--- /dev/null
+++ b/vpn/openconnect/openconnectwidget.h
@@ -0,0 +1,46 @@
+/*
+Copyright 2011 Ilia Kats <ilia-kats@gmx.net>
+Copyright 2013 Lukas Tinkl <ltinkl@redhat.com>
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) version 3, or any
+later version accepted by the membership of KDE e.V. (or its
+successor approved by the membership of KDE e.V.), which shall
+act as a proxy defined in Section 6 of version 3 of the license.
+
+This library 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
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef OPENCONNECTWIDGET_H
+#define OPENCONNECTWIDGET_H
+
+#include "settingwidget.h"
+
+#include <NetworkManagerQt/VpnSetting>
+
+class OpenconnectSettingWidgetPrivate;
+
+class OpenconnectSettingWidget : public SettingWidget
+{
+    Q_OBJECT
+    Q_DECLARE_PRIVATE(OpenconnectSettingWidget)
+public:
+    explicit OpenconnectSettingWidget(const NetworkManager::VpnSetting::Ptr \
&setting, QWidget * parent = 0); +    ~OpenconnectSettingWidget();
+    virtual void loadConfig(const NetworkManager::Setting::Ptr &setting);
+    virtual QVariantMap setting(bool agentOwned = false) const;
+    virtual bool isValid() const;
+
+private:
+    OpenconnectSettingWidgetPrivate * d_ptr;
+};
+
+#endif // OPENCONNECTWIDGET_H
diff --git a/vpn/openconnect/plasmanm_openconnectui.desktop \
b/vpn/openconnect/plasmanm_openconnectui.desktop new file mode 100644
index 0000000..f5d4e77
--- /dev/null
+++ b/vpn/openconnect/plasmanm_openconnectui.desktop
@@ -0,0 +1,97 @@
+[Desktop Entry]
+Type=Service
+Icon=
+ServiceTypes=PlasmaNM/VpnUiPlugin
+X-KDE-Library=plasmanm_openconnectui
+X-NetworkManager-Services=org.freedesktop.NetworkManager.openconnect
+X-KDE-PluginInfo-Author=Lukáš Tinkl
+X-KDE-PluginInfo-Email=ltinkl@redhat.com
+X-KDE-PluginInfo-Name=plasmanm_openconnectui
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-Website=
+X-KDE-PluginInfo-Category=VPNService
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false
+Name=OpenConnect
+Name[ar]=OpenConnect
+Name[bs]=OpenConnect
+Name[ca]=OpenConnect
+Name[ca@valencia]=OpenConnect
+Name[cs]=OpenConnect
+Name[da]=OpenConnect
+Name[de]=OpenConnect
+Name[el]=OpenConnect
+Name[es]=OpenConnect
+Name[et]=OpenConnect
+Name[fi]=OpenConnect
+Name[fr]=OpenConnect
+Name[gl]=OpenConnect
+Name[hu]=OpenConnect
+Name[ia]=OpenConnect
+Name[it]=OpenConnect
+Name[km]=OpenConnect
+Name[lt]=OpenConnect
+Name[mr]=ओपन कनेक्ट
+Name[nb]=OpenConnect
+Name[nds]=OpenConnect
+Name[nl]=OpenConnect
+Name[pa]=OpenConnect
+Name[pl]=OpenConnect
+Name[pt]=OpenConnect
+Name[pt_BR]=OpenConnect
+Name[ro]=OpenConnect
+Name[ru]=OpenConnect
+Name[sk]=OpenConnect
+Name[sl]=OpenConnect
+Name[sr]=Опенконект
+Name[sr@ijekavian]=Опенконект
+Name[sr@ijekavianlatin]=OpenConnect
+Name[sr@latin]=OpenConnect
+Name[sv]=OpenConnect
+Name[tr]=OpenConnect
+Name[uk]=OpenConnect
+Name[x-test]=xxOpenConnectxx
+Name[zh_CN]=OpenConnect
+Name[zh_TW]=OpenConnect
+Comment=OpenConnect Plugin
+Comment[ar]=ملحق OpenConnect 
+Comment[bs]=OpenConnect Dodatak
+Comment[ca]=Connector d'OpenConnect
+Comment[ca@valencia]=Connector d'OpenConnect
+Comment[cs]=Modul OpenConnect
+Comment[da]=Plugin til OpenConnect
+Comment[de]=OpenConnect-Modul
+Comment[el]= ρόσθετο OpenConnect
+Comment[es]=Complemento de OpenConnect
+Comment[et]=OpenConnecti plugin
+Comment[fi]=OpenConnect-liitännäinen
+Comment[fr]=Module externe OpenConnect 
+Comment[gl]=Complemento de OpenConnect.
+Comment[hu]=OpenConnect modul
+Comment[ia]=Plugin de OpenConnect
+Comment[it]=Estensione OpenConnect
+Comment[km]=កម្មវិធី​ជំនួយ​ OpenConnect
+Comment[lt]=OpenConnect papildinys
+Comment[mr]=ओपन कनेक्ट प्लगइन
+Comment[nb]=OpenConnect programtillegg
+Comment[nds]=OpenConnect-Moduul
+Comment[nl]=OpenConnect-plug-in
+Comment[pa]=OpenConnect ਪਲੱਗਇਨ
+Comment[pl]=Wtyczka OpenConnect
+Comment[pt]='Plugin' do OpenConnect
+Comment[pt_BR]=Plugin do OpenConnect
+Comment[ro]=Extensie OpenConnect
+Comment[ru]=Модуль OpenConnect
+Comment[sk]=Plugin OpenConnect
+Comment[sl]=Vstavek OpenConnect
+Comment[sr]=Прикључак Опенконекта
+Comment[sr@ijekavian]=Прикључак Опенконекта
+Comment[sr@ijekavianlatin]=Priključak OpenConnecta
+Comment[sr@latin]=Priključak OpenConnecta
+Comment[sv]=Insticksprogram för OpenConnect
+Comment[tr]=OpenConnect Eklentisi
+Comment[uk]=Додаток OpenConnect
+Comment[x-test]=xxOpenConnect Pluginxx
+Comment[zh_CN]=OpenConnect 插件
+Comment[zh_TW]=OpenConnect 外掛程式


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic