|
|
|
/*
|
|
|
|
* OpenVPN-PLAP-Provider
|
|
|
|
*
|
|
|
|
* Copyright (C) 2017-2022 Selva Nair <selva.nair@gmail.com>
|
|
|
|
*
|
|
|
|
* 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 (see the file COPYING included with this
|
|
|
|
* distribution); if not, write to the Free Software Foundation, Inc.,
|
|
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include <config.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "plap_common.h"
|
|
|
|
#include "plap_connection.h"
|
|
|
|
#include "plap_dll.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "resource.h"
|
|
|
|
|
|
|
|
/* A "class" that implements IConnectableCredentialProviderCredential */
|
|
|
|
|
|
|
|
struct OpenVPNConnection
|
|
|
|
{
|
|
|
|
const IConnectableCredentialProviderCredentialVtbl *lpVtbl; /* base class vtable */
|
|
|
|
|
|
|
|
/* "private" members */
|
|
|
|
ICredentialProviderCredentialEvents *events; /* Passed in by Logon UI for callbacks */
|
|
|
|
connection_t *c; /* GUI connection data */
|
|
|
|
const wchar_t *display_name;
|
|
|
|
IQueryContinueWithStatus *qc; /* Passed in by LogonUI for checking status of connect */
|
|
|
|
BOOL connect_cancelled; /* we set this true if user cancels the connect operation */
|
|
|
|
LONG ref_count;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define ICCPC IConnectableCredentialProviderCredential /* shorthand for a long name */
|
|
|
|
|
|
|
|
#define ISCONNECTED(c) (ConnectionState(c) == state_connected)
|
|
|
|
#define ISDISCONNECTED(c) (ConnectionState(c) == state_disconnected)
|
|
|
|
#define ISONHOLD(c) (ConnectionState(c) == state_onhold)
|
|
|
|
|
|
|
|
extern DWORD status_menu_id;
|
|
|
|
|
|
|
|
/* Methods in IConnectableCredentialProviderCredential that we need to define */
|
|
|
|
|
|
|
|
/* IUnknown */
|
|
|
|
static HRESULT WINAPI QueryInterface(ICCPC *this, REFIID riid, void** ppv);
|
|
|
|
static ULONG WINAPI AddRef(ICCPC *this);
|
|
|
|
static ULONG WINAPI Release(ICCPC *this);
|
|
|
|
|
|
|
|
/* ICredentialProviderCredential */
|
|
|
|
static HRESULT WINAPI Advise(ICCPC *this, ICredentialProviderCredentialEvents *e);
|
|
|
|
static HRESULT WINAPI UnAdvise(ICCPC *this);
|
|
|
|
static HRESULT WINAPI SetSelected(ICCPC *this, BOOL *auto_logon);
|
|
|
|
static HRESULT WINAPI SetDeselected(ICCPC *this);
|
|
|
|
static HRESULT WINAPI GetFieldState(ICCPC *this, DWORD field, CREDENTIAL_PROVIDER_FIELD_STATE *fs,
|
|
|
|
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE *fis);
|
|
|
|
static HRESULT WINAPI GetStringValue(ICCPC *this, DWORD index, WCHAR **ws);
|
|
|
|
static HRESULT WINAPI GetBitmapValue(ICCPC *this, DWORD field, HBITMAP *bmp);
|
|
|
|
static HRESULT WINAPI GetSubmitButtonValue(ICCPC *this, DWORD field, DWORD *adjacent);
|
|
|
|
static HRESULT WINAPI SetStringValue(ICCPC *this, DWORD field, const WCHAR *ws);
|
|
|
|
static HRESULT WINAPI GetCheckboxValue(ICCPC *this, DWORD field, BOOL *checked, wchar_t **label);
|
|
|
|
static HRESULT WINAPI GetComboBoxValueCount(ICCPC *this, DWORD field, DWORD *items, DWORD *selected_item);
|
|
|
|
static HRESULT WINAPI GetComboBoxValueAt(ICCPC *this, DWORD field, DWORD item, wchar_t **item_value);
|
|
|
|
static HRESULT WINAPI SetCheckboxValue(ICCPC *this, DWORD field, BOOL checked);
|
|
|
|
static HRESULT WINAPI SetComboBoxSelectedValue(ICCPC *this, DWORD field, DWORD selected_item);
|
|
|
|
static HRESULT WINAPI CommandLinkClicked(ICCPC *this, DWORD field);
|
|
|
|
static HRESULT WINAPI GetSerialization(ICCPC *this,
|
|
|
|
CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *response,
|
|
|
|
CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *cs, wchar_t **text,
|
|
|
|
CREDENTIAL_PROVIDER_STATUS_ICON *icon);
|
|
|
|
static HRESULT WINAPI ReportResult(ICCPC *this, NTSTATUS status, NTSTATUS substatus,
|
|
|
|
wchar_t **status_text, CREDENTIAL_PROVIDER_STATUS_ICON *icon);
|
|
|
|
|
|
|
|
/* IConnectableCredentialProviderCredential */
|
|
|
|
static HRESULT WINAPI Connect(ICCPC *this, IQueryContinueWithStatus *qc);
|
|
|
|
static HRESULT WINAPI Disconnect(ICCPC *this);
|
|
|
|
|
|
|
|
/* use a static table for filling in the methods */
|
|
|
|
#define M_(x) .x = x
|
|
|
|
static const IConnectableCredentialProviderCredentialVtbl iccpc_vtbl = {
|
|
|
|
M_(QueryInterface),
|
|
|
|
M_(AddRef),
|
|
|
|
M_(Release),
|
|
|
|
M_(Advise),
|
|
|
|
M_(UnAdvise),
|
|
|
|
M_(SetSelected),
|
|
|
|
M_(SetDeselected),
|
|
|
|
M_(GetFieldState),
|
|
|
|
M_(GetStringValue),
|
|
|
|
M_(GetBitmapValue),
|
|
|
|
M_(GetCheckboxValue),
|
|
|
|
M_(GetSubmitButtonValue),
|
|
|
|
M_(GetComboBoxValueCount),
|
|
|
|
M_(GetComboBoxValueAt),
|
|
|
|
M_(SetStringValue),
|
|
|
|
M_(SetCheckboxValue),
|
|
|
|
M_(SetComboBoxSelectedValue),
|
|
|
|
M_(CommandLinkClicked),
|
|
|
|
M_(GetSerialization),
|
|
|
|
M_(ReportResult),
|
|
|
|
M_(Connect),
|
|
|
|
M_(Disconnect),
|
|
|
|
};
|
|
|
|
|
|
|
|
/* constructor */
|
|
|
|
OpenVPNConnection *
|
|
|
|
OpenVPNConnection_new()
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
|
|
|
|
OpenVPNConnection *oc = calloc(sizeof(*oc), 1);
|
|
|
|
if (oc)
|
|
|
|
{
|
|
|
|
oc->lpVtbl = &iccpc_vtbl;
|
|
|
|
oc->ref_count = 1;
|
|
|
|
|
|
|
|
dll_addref();
|
|
|
|
}
|
|
|
|
return oc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* destructor */
|
|
|
|
static void
|
|
|
|
OpenVPNConnection_free(OpenVPNConnection *oc)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry: profile: <%ls>", oc->display_name);
|
|
|
|
|
|
|
|
free(oc);
|
|
|
|
dll_release();
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI
|
|
|
|
AddRef(ICCPC *this)
|
|
|
|
{
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
|
|
|
|
dmsg(L"Connection: ref_count after increment = %lu", oc->ref_count + 1);
|
|
|
|
return InterlockedIncrement(&oc->ref_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ULONG WINAPI
|
|
|
|
Release(ICCPC *this)
|
|
|
|
{
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
|
|
|
|
int count = InterlockedDecrement(&oc->ref_count);
|
|
|
|
dmsg(L"Connection: ref_count after decrement = %lu", count);
|
|
|
|
if (count == 0)
|
|
|
|
{
|
|
|
|
OpenVPNConnection_free(oc); /* delete self */
|
|
|
|
}
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
QueryInterface(ICCPC *this, REFIID riid, void** ppv)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
debug_print_guid(riid, L"In OVPN Connection QueryInterface with riid = ");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ppv != NULL)
|
|
|
|
{
|
|
|
|
if (IsEqualIID(riid, &IID_IUnknown)
|
|
|
|
|| IsEqualIID(riid, &IID_ICredentialProviderCredential)
|
|
|
|
|| IsEqualIID(riid, &IID_IConnectableCredentialProviderCredential))
|
|
|
|
{
|
|
|
|
*ppv = this;
|
|
|
|
ADDREF(this);
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*ppv = NULL;
|
|
|
|
hr = E_NOINTERFACE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hr = E_POINTER;
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LogonUI calls Advise first and the passed in events may be used for
|
|
|
|
* making callbacks to notify changes asynchronously
|
|
|
|
*/
|
|
|
|
static HRESULT
|
|
|
|
WINAPI Advise(ICCPC *this, ICredentialProviderCredentialEvents *e)
|
|
|
|
{
|
|
|
|
HWND hwnd;
|
|
|
|
|
|
|
|
dmsg(L"Entry");
|
|
|
|
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
|
|
|
|
if (oc->events)
|
|
|
|
RELEASE(oc->events);
|
|
|
|
oc->events = e;
|
|
|
|
ADDREF(e);
|
|
|
|
|
|
|
|
if (e
|
|
|
|
&& e->lpVtbl->OnCreatingWindow(e, &hwnd) == S_OK)
|
|
|
|
{
|
|
|
|
dmsg(L"Setting hwnd");
|
|
|
|
SetParentWindow(hwnd);
|
|
|
|
}
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We keep a copy of events pointer in Advise, release it here */
|
|
|
|
static HRESULT WINAPI
|
|
|
|
UnAdvise(ICCPC *this)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
|
|
|
|
if (oc->events)
|
|
|
|
RELEASE(oc->events);
|
|
|
|
oc->events = NULL;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* LogonUI calls this function when our tile is selected
|
|
|
|
* We do nothing here and let the user choose which profile to connect.
|
|
|
|
*/
|
|
|
|
static HRESULT WINAPI
|
|
|
|
SetSelected(ICCPC *this, BOOL *auto_logon)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
dmsg(L"profile: %ls", oc->display_name);
|
|
|
|
#else
|
|
|
|
(void) this;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* setting true here will autoconnect the first entry and Windows will
|
|
|
|
* autoselect it for the rest of the login session, blocking all other
|
|
|
|
* entries and other providers, if any. Some commercial products do
|
|
|
|
* that but its not a good idea, IMO. So we set false here.
|
|
|
|
*/
|
|
|
|
*auto_logon = FALSE;
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
SetDeselected(ICCPC *this)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
dmsg(L"profile: %ls", oc->display_name);
|
|
|
|
#else
|
|
|
|
(void) this;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return display states for a particular field of a tile
|
|
|
|
*/
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetFieldState(UNUSED ICCPC *this, DWORD field,
|
|
|
|
CREDENTIAL_PROVIDER_FIELD_STATE *fs,
|
|
|
|
CREDENTIAL_PROVIDER_FIELD_INTERACTIVE_STATE *fis)
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
dmsg(L"field = %lu", field);
|
|
|
|
|
|
|
|
if (field < _countof(field_states))
|
|
|
|
{
|
|
|
|
if (fs)
|
|
|
|
*fs = field_states[field];
|
|
|
|
if (fis)
|
|
|
|
*fis = (field_desc[field].cpft == CPFT_SUBMIT_BUTTON) ? CPFIS_FOCUSED : CPFIS_NONE;
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hr = E_INVALIDARG;
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the string value of the field at the index
|
|
|
|
*/
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetStringValue(ICCPC *this, DWORD index, WCHAR **ws)
|
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
dmsg(L"index = %lu", index);
|
|
|
|
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
|
|
|
|
const wchar_t *val = L"";
|
|
|
|
|
|
|
|
if (index >= _countof(field_desc))
|
|
|
|
{
|
|
|
|
return E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* we have custom field strings only for connection name */
|
|
|
|
if (field_desc[index].cpft == CPFT_LARGE_TEXT)
|
|
|
|
{
|
|
|
|
val = oc->display_name; /* connection name */
|
|
|
|
}
|
|
|
|
|
|
|
|
/* do not to use malloc here as the client will free using CoTaskMemFree */
|
|
|
|
hr = SHStrDupW(val, ws);
|
|
|
|
if (!SUCCEEDED(hr))
|
|
|
|
{
|
|
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
dmsg(L"OOM while copying field_strings[%lu]", index);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dmsg(L"Returning %ls", *ws);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* return the image for the tile at index field
|
|
|
|
*/
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetBitmapValue(UNUSED ICCPC *this, DWORD field, HBITMAP *bmp)
|
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
dmsg(L"field = %lu ", field);
|
|
|
|
|
|
|
|
if (field_desc[field].cpft == CPFT_TILE_IMAGE && bmp)
|
|
|
|
{
|
|
|
|
HBITMAP tmp = LoadBitmapW(hinst_global, MAKEINTRESOURCE(IDB_TILE_IMAGE));
|
|
|
|
if (tmp)
|
|
|
|
{
|
|
|
|
*bmp = tmp; /* The caller takes ownership of this memory */
|
|
|
|
dmsg(L"Returning a bitmap for PLAP tile %lu", field);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
dmsg(L"LoadBitmap failed with error = 0x%08x", hr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hr = E_INVALIDARG;
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return the index of the field the button should be placed adjacent to.
|
|
|
|
*/
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetSubmitButtonValue(UNUSED ICCPC *this, DWORD field, DWORD *adjacent )
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
dmsg(L"field = %lu", field);
|
|
|
|
|
|
|
|
/* we have defined field ids in order of appearance */
|
|
|
|
if (field_desc[field].cpft == CPFT_SUBMIT_BUTTON && field > 0 && adjacent)
|
|
|
|
{
|
|
|
|
*adjacent = field - 1;
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hr = E_INVALIDARG;
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the value of a field which can accept user text input
|
|
|
|
* This gets called as user types into a field.
|
|
|
|
*/
|
|
|
|
static HRESULT WINAPI
|
|
|
|
SetStringValue(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED const WCHAR *ws )
|
|
|
|
{
|
|
|
|
HRESULT hr;
|
|
|
|
|
|
|
|
dmsg(L"field = %lu", field);
|
|
|
|
|
|
|
|
/* We do not allow setting any fields -- at least not yet */
|
|
|
|
hr = E_INVALIDARG;
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetCheckboxValue(UNUSED ICCPC *this, UNUSED DWORD field,
|
|
|
|
UNUSED BOOL *checked, UNUSED wchar_t **label)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetComboBoxValueCount(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED DWORD *items,
|
|
|
|
UNUSED DWORD *selected_item)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetComboBoxValueAt(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED DWORD item,
|
|
|
|
UNUSED wchar_t **item_value)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
SetCheckboxValue(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED BOOL checked)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
SetComboBoxSelectedValue(UNUSED ICCPC *this, UNUSED DWORD field, UNUSED DWORD selected_item)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
CommandLinkClicked(UNUSED ICCPC *this, UNUSED DWORD field)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Collect the username and password into a serialized credential for the usage scenario
|
|
|
|
* This gets called soon after connect -- result returned here influences the status of
|
|
|
|
* Connect.
|
|
|
|
* We do not support SSO, so no credentials are serialized. We just indicate whether
|
|
|
|
* we got correct credentials for the Connect process or not.
|
|
|
|
*/
|
|
|
|
static HRESULT WINAPI
|
|
|
|
GetSerialization(ICCPC *this, CREDENTIAL_PROVIDER_GET_SERIALIZATION_RESPONSE *response,
|
|
|
|
UNUSED CREDENTIAL_PROVIDER_CREDENTIAL_SERIALIZATION *cs, wchar_t **text,
|
|
|
|
CREDENTIAL_PROVIDER_STATUS_ICON *icon)
|
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
dmsg(L"Entry: profile <%ls>", oc->display_name);
|
|
|
|
|
|
|
|
if (oc->connect_cancelled || !ISCONNECTED(oc->c))
|
|
|
|
{
|
|
|
|
dmsg(L"connect cancelled or failed");
|
|
|
|
/* return _NOT_FINISHED response here for the prompt go
|
|
|
|
* back to the PLAP tiles screen. Unclear the return value
|
|
|
|
* should be success or not : using S_OK.
|
|
|
|
*/
|
|
|
|
*response = CPGSR_NO_CREDENTIAL_NOT_FINISHED;
|
|
|
|
|
|
|
|
if (text && icon)
|
|
|
|
{
|
|
|
|
if (oc->connect_cancelled)
|
|
|
|
hr = SHStrDupW(L"Connection cancelled by user", text);
|
|
|
|
else if (text)
|
|
|
|
hr = SHStrDupW(L"Connection failed to complete", text);
|
|
|
|
|
|
|
|
*icon = CPSI_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Finished but no credentials to pass on to LogonUI */
|
|
|
|
*response = CPGSR_NO_CREDENTIAL_FINISHED;
|
|
|
|
/* return CPGSR_RETURN_NO_CREDENTIAL_FINISHED leads to a loop! */
|
|
|
|
if (text && icon)
|
|
|
|
{
|
|
|
|
hr = SHStrDupW(L"Connected..", text);
|
|
|
|
*icon = CPSI_SUCCESS;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
ReportResult(UNUSED ICCPC *this, UNUSED NTSTATUS status, UNUSED NTSTATUS substatus,
|
|
|
|
UNUSED wchar_t **status_text, UNUSED CREDENTIAL_PROVIDER_STATUS_ICON *icon)
|
|
|
|
{
|
|
|
|
dmsg(L"Entry");
|
|
|
|
return E_NOTIMPL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Helper to send status string feedback to events object and qc if available */
|
|
|
|
static void
|
|
|
|
NotifyEvents(OpenVPNConnection *oc, const wchar_t *status)
|
|
|
|
{
|
|
|
|
if (oc->events)
|
|
|
|
{
|
|
|
|
oc->events->lpVtbl->SetFieldString(oc->events, (ICredentialProviderCredential*) oc,
|
|
|
|
STATUS_FIELD_INDEX, status);
|
|
|
|
}
|
|
|
|
if (oc->qc)
|
|
|
|
{
|
|
|
|
oc->qc->lpVtbl->SetStatusMessage(oc->qc, status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
ProgressCallback(HWND hwnd, UINT msg, WPARAM wParam, UNUSED LPARAM lParam, LONG_PTR ref_data)
|
|
|
|
{
|
|
|
|
assert(ref_data);
|
|
|
|
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) ref_data;
|
|
|
|
connection_t *c = oc->c;
|
|
|
|
IQueryContinueWithStatus *qc = oc->qc;
|
|
|
|
|
|
|
|
assert(qc);
|
|
|
|
|
|
|
|
HRESULT hr = S_FALSE;
|
|
|
|
WCHAR status[256] = L"";
|
|
|
|
|
|
|
|
if (msg == TDN_BUTTON_CLICKED && wParam == IDCANCEL)
|
|
|
|
{
|
|
|
|
dmsg(L"wParam=IDCANCEL -- returning S_OK");
|
|
|
|
hr = S_OK; /* this will cause the progress dialog to close */
|
|
|
|
}
|
|
|
|
else if (ISCONNECTED(c) || ISDISCONNECTED(c) || (qc->lpVtbl->QueryContinue(qc) != S_OK))
|
|
|
|
{
|
|
|
|
/* this will trigger IDCANCEL */
|
|
|
|
PostMessageW(hwnd, WM_CLOSE, 0, 0);
|
|
|
|
dmsg(L"profile = %ls, closing progress dialog", oc->display_name);
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
else if (ISONHOLD(c)) /* Try to connect again */
|
|
|
|
{
|
|
|
|
ConnectHelper(c);
|
|
|
|
}
|
|
|
|
else if (!ISCONNECTED(c) && msg == TDN_BUTTON_CLICKED && wParam == status_menu_id)
|
|
|
|
{
|
|
|
|
dmsg(L"wParam = status_menu_id -- showing status window");
|
|
|
|
ShowStatusWindow(c, TRUE);
|
|
|
|
}
|
|
|
|
else if (!ISCONNECTED(c) && msg == TDN_BUTTON_CLICKED && wParam == IDRETRY)
|
|
|
|
{
|
|
|
|
dmsg(L"wParam = IDRETRY -- closing progress dialog for restart");
|
|
|
|
hr = S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg == TDN_CREATED)
|
|
|
|
{
|
|
|
|
dmsg(L"starting progress bar marquee");
|
|
|
|
PostMessageW(hwnd, TDM_SET_PROGRESS_BAR_MARQUEE, 1, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (msg == TDN_TIMER)
|
|
|
|
{
|
|
|
|
GetConnectionStatusText(c, status, _countof(status));
|
|
|
|
NotifyEvents(oc, status);
|
|
|
|
SendMessageW(hwnd, TDM_UPDATE_ELEMENT_TEXT, TDE_CONTENT, (LPARAM) status);
|
|
|
|
dmsg(L"Connection status <%ls>", status);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
Connect(ICCPC *this, IQueryContinueWithStatus *qc)
|
|
|
|
{
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
wchar_t status[256] = L"";
|
|
|
|
|
|
|
|
dmsg(L"profile: <%ls>", oc->display_name);
|
|
|
|
|
|
|
|
oc->qc = qc;
|
|
|
|
NotifyEvents(oc, L"");
|
|
|
|
|
|
|
|
again:
|
|
|
|
oc->connect_cancelled = FALSE;
|
|
|
|
SetActiveProfile(oc->c); /* This enables UI dialogs to be shown for c */
|
|
|
|
|
|
|
|
ConnectHelper(oc->c);
|
|
|
|
|
|
|
|
Sleep(100);
|
|
|
|
|
|
|
|
/* if not immediately connected, show a progress dialog with
|
|
|
|
* service state changes and retry/cancel options. Progress dialog
|
|
|
|
* returns on error, cancel or when connected.
|
|
|
|
*/
|
|
|
|
if (!ISCONNECTED(oc->c) && !ISDISCONNECTED(oc->c))
|
|
|
|
{
|
|
|
|
dmsg(L"Runninng progress dialog");
|
|
|
|
int res = RunProgressDialog(oc->c, ProgressCallback, (LONG_PTR) oc);
|
|
|
|
dmsg(L"Out of progress dialog with res = %d", res);
|
|
|
|
|
|
|
|
if (res == IDRETRY && !ISCONNECTED(oc->c))
|
|
|
|
{
|
|
|
|
wcsncpy_s(status, _countof(status), L"Current State: Retrying", _TRUNCATE);
|
|
|
|
NotifyEvents(oc, status);
|
|
|
|
|
|
|
|
DisconnectHelper(oc->c);
|
|
|
|
goto again;
|
|
|
|
}
|
|
|
|
else if (res == IDCANCEL && !ISCONNECTED(oc->c) && !ISDISCONNECTED(oc->c))
|
|
|
|
{
|
|
|
|
wcsncpy_s(status, _countof(status), L"Current State: Cancelling", _TRUNCATE);
|
|
|
|
NotifyEvents(oc, status);
|
|
|
|
|
|
|
|
DisconnectHelper(oc->c);
|
|
|
|
oc->connect_cancelled = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
GetConnectionStatusText(oc->c, status, _countof(status));
|
|
|
|
NotifyEvents(oc, status);
|
|
|
|
|
|
|
|
ShowStatusWindow(oc->c, FALSE);
|
|
|
|
oc->qc = NULL;
|
|
|
|
SetActiveProfile(NULL);
|
|
|
|
|
|
|
|
dmsg (L"Exit with: <%ls>", ISCONNECTED(oc->c) ? L"success" : L"error/cancel");
|
|
|
|
|
|
|
|
return ISCONNECTED(oc->c) ? S_OK : E_FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static HRESULT WINAPI
|
|
|
|
Disconnect(ICCPC *this)
|
|
|
|
{
|
|
|
|
OpenVPNConnection *oc = (OpenVPNConnection *) this;
|
|
|
|
dmsg (L"profile <%ls>", oc->display_name);
|
|
|
|
|
|
|
|
NotifyEvents(oc, L"Disconnecting");
|
|
|
|
|
|
|
|
DisconnectHelper(oc->c);
|
|
|
|
|
|
|
|
NotifyEvents(oc, L""); /* clear status text */
|
|
|
|
|
|
|
|
return S_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
HRESULT WINAPI
|
|
|
|
OVPNConnection_Initialize(OpenVPNConnection *this, connection_t *conn, const wchar_t *display_name)
|
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
this->c = conn;
|
|
|
|
this->display_name = display_name;
|
|
|
|
dmsg(L"profile <%ls>", display_name);
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Copy field descriptor -- caller will free using CoTaskMemFree, alloc using compatible allocator */
|
|
|
|
HRESULT CopyFieldDescriptor(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *fd_out,
|
|
|
|
const CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR *fd_in)
|
|
|
|
{
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
|
|
|
memcpy(fd_out, fd_in, sizeof(CREDENTIAL_PROVIDER_FIELD_DESCRIPTOR));
|
|
|
|
/* now copy the label string if present */
|
|
|
|
if (fd_in->pszLabel)
|
|
|
|
{
|
|
|
|
hr = SHStrDupW(fd_in->pszLabel, &fd_out->pszLabel);
|
|
|
|
}
|
|
|
|
|
|
|
|
return hr;
|
|
|
|
}
|