diff --git a/misc.c b/misc.c index fb1e5d9..e464c2f 100644 --- a/misc.c +++ b/misc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "options.h" #include "manage.h" @@ -462,3 +463,48 @@ validate_input(const WCHAR *input, const WCHAR *exclude) exclude = L"\n"; return (wcspbrk(input, exclude) == NULL); } + +/* + * Read the text from edit control h within the range specified in the + * CHARRANGE structure chrg. Return the result in a newly allocated + * string or NULL on error. + * + * The caller must free the returned pointer. + */ +wchar_t * +get_link_text(HWND h, CHARRANGE chrg) +{ + size_t len = chrg.cpMax - chrg.cpMin; + wchar_t *url = malloc((len + 1)*sizeof(wchar_t)); + + if (url) + { + TEXTRANGEW txt = {chrg, url}; + if (SendMessage(h, EM_GETTEXTRANGE, 0, (LPARAM)&txt) <= 0) + url[0] = '\0'; + else + url[len] = '\0'; /* nul termination paranoia */ + PrintDebug(L"get_link_text: url = <%s>", url); + } + return url; +} + +/* Open specified http/https URL using ShellExecute. */ +BOOL +open_url(const wchar_t *url) +{ + if (!url || !wcsbegins(url, L"http")) + { + PrintDebug(L"launch_url: empty url or unsupported scheme: <%s>", url); + return true; + } + + HINSTANCE ret = ShellExecuteW(NULL, L"open", url, NULL, NULL, SW_SHOWNORMAL); + + if (ret <= (HINSTANCE) 32) + { + PrintDebug(L"launch_url: ShellExecute <%s> returned error: %d", url, ret); + return false; + } + return true; +} diff --git a/misc.h b/misc.h index f7f43cc..98c42ca 100644 --- a/misc.h +++ b/misc.h @@ -22,6 +22,8 @@ #ifndef MISC_H #define MISC_H +#include + BOOL ManagementCommandFromInput(connection_t *, LPCSTR, HWND, int); BOOL ManagementCommandFromInputBase64(connection_t *, LPCSTR, HWND, int, int); @@ -40,4 +42,6 @@ BOOL Base64Encode(const char *input, int input_len, char **output); int Base64Decode(const char *input, char **output); WCHAR *Widen(const char *utf8); BOOL validate_input(const WCHAR *input, const WCHAR *exclude); +WCHAR *get_link_text(HWND hwnd, CHARRANGE chrg); +BOOL open_url(const WCHAR *url); #endif diff --git a/openvpn.c b/openvpn.c index e9d6a0a..ff6a1ab 100644 --- a/openvpn.c +++ b/openvpn.c @@ -1381,6 +1381,7 @@ INT_PTR CALLBACK StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { connection_t *c; + NMHDR* nmh; switch (msg) { @@ -1420,6 +1421,13 @@ StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) if (SendMessage(hLogWnd, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM) &cfm) == 0) ShowLocalizedMsg(IDS_ERR_SET_SIZE); + /* Recognize URLs embedded in log text */ + /* using wParam = AURL_ENABLEURL = 1 */ + SendMessage(hLogWnd, EM_AUTOURLDETECT, 1, 0); + /* Have notified by EN_LINK messages when URLs are clicked etc. */ + LRESULT evmask = SendMessage(hLogWnd, EM_GETEVENTMASK, 0, 0); + SendMessage(hLogWnd, EM_SETEVENTMASK, 0, evmask | ENM_LINK); + /* Set size and position of controls */ RECT rect; GetClientRect(hwndDlg, &rect); @@ -1457,6 +1465,29 @@ StatusDialogFunc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) } break; + case WM_NOTIFY: + c = (connection_t *) GetProp(hwndDlg, cfgProp); + nmh = (NMHDR*) lParam; + + /* We handle only EN_LINK messages */ + if (nmh->idFrom == ID_EDT_LOG && nmh->code == EN_LINK) + { + ENLINK *el = (ENLINK*) lParam; + + if (el->msg == WM_LBUTTONUP) + { + /* get the link text */ + wchar_t *url = get_link_text(nmh->hwndFrom, el->chrg); + if (!url || !open_url(url)) + { + WriteStatusLog(c, L"GUI> ", L"Opening URL failed.", false); + } + free(url); + return TRUE; + } + } + break; + case WM_SHOWWINDOW: if (wParam == TRUE) {