From ba05ea0de9c0dbca45cd42f07c0ad017c61f1424 Mon Sep 17 00:00:00 2001 From: Selva Nair Date: Wed, 27 Dec 2017 17:27:01 -0500 Subject: [PATCH] Detect urls in echo message make them clickable - Left clickng on http or https url will open it on the default browser Several other URL schemes are detected and formatted as clickable links, but we only support opening of http/https links. Note on spaces in URLs: We unescape all %xx occurrences in the echo message text so that %20 will be converted to space in plain text. This means embedded spaces in URLs will not work even if written as %20. An option is to use %2520 which will get conveted to %20 after the unescaping. A better option is to enclose the URL in <>. If the text inside <> starts with a valid scheme (http, https etc.), the entire text including spaces is parsed as the URL. Signed-off-by: Selva Nair --- echo.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ misc.c | 20 ++++++++++++++++++ misc.h | 3 +++ 3 files changed, 87 insertions(+) diff --git a/echo.c b/echo.c index bfe226d..da17de0 100644 --- a/echo.c +++ b/echo.c @@ -339,6 +339,60 @@ echo_msg_clear(connection_t *c, BOOL clear_history) } } +/* + * 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. + */ +static wchar_t * +get_text_in_range(HWND h, CHARRANGE chrg) +{ + if (chrg.cpMax <= chrg.cpMin) + return NULL; + + size_t len = chrg.cpMax - chrg.cpMin; + wchar_t *txt = malloc((len + 1)*sizeof(wchar_t)); + + if (txt) + { + TEXTRANGEW txtrg = {chrg, txt}; + if (SendMessage(h, EM_GETTEXTRANGE, 0, (LPARAM)&txtrg) <= 0) + txt[0] = '\0'; + else + txt[len] = '\0'; /* safety */ + } + return txt; +} + +/* Enable url detection and subscribe to link click notification in an edit control */ +static void +enable_url_detection(HWND hmsg) +{ + /* Recognize URLs embedded in message text */ + SendMessage(hmsg, EM_AUTOURLDETECT, AURL_ENABLEURL, 0); + /* Have notified by EN_LINK messages when URLs are clicked etc. */ + LRESULT evmask = SendMessage(hmsg, EM_GETEVENTMASK, 0, 0); + SendMessage(hmsg, EM_SETEVENTMASK, 0, evmask | ENM_LINK); +} + +/* Open URL when ENLINK notification is received */ +static int +OnEnLinkNotify(HWND UNUSED hwnd, ENLINK *el) +{ + if (el->msg == WM_LBUTTONUP) + { + /* get the link text */ + wchar_t *url = get_text_in_range(el->nmhdr.hwndFrom, el->chrg); + if (url) + open_url(url); + free(url); + return 1; + } + return 0; +} + /* Add new message to the message box window and optionally show it */ static void AddMessageBoxText(HWND hwnd, const wchar_t *text, const wchar_t *title, BOOL show) @@ -394,6 +448,7 @@ MessageDialogFunc(HWND hwnd, UINT msg, UNUSED WPARAM wParam, LPARAM lParam) HWND hmsg; const UINT top_margin = DPI_SCALE(16); const UINT side_margin = DPI_SCALE(20); + NMHDR *nmh; switch (msg) { @@ -408,6 +463,8 @@ MessageDialogFunc(HWND hwnd, UINT msg, UNUSED WPARAM wParam, LPARAM lParam) SendMessage(hmsg, EM_SETMARGINS, EC_LEFTMARGIN|EC_RIGHTMARGIN, MAKELPARAM(side_margin, side_margin)); + enable_url_detection(hmsg); + /* Position the window close to top right corner of the screen */ RECT rc; GetWindowRect(hwnd, &rc); @@ -447,6 +504,13 @@ MessageDialogFunc(HWND hwnd, UINT msg, UNUSED WPARAM wParam, LPARAM lParam) } break; + case WM_NOTIFY: + nmh = (NMHDR*) lParam; + /* We handle only EN_LINK messages */ + if (nmh->idFrom == ID_TXT_MESSAGE && nmh->code == EN_LINK) + return OnEnLinkNotify(hwnd, (ENLINK*)lParam); + break; + case WM_CLOSE: ShowWindow(hwnd, SW_HIDE); return TRUE; diff --git a/misc.c b/misc.c index 0d31d4e..1a53b33 100644 --- a/misc.c +++ b/misc.c @@ -29,6 +29,7 @@ #include #include #include +#include #include "options.h" #include "manage.h" @@ -570,3 +571,22 @@ md_final(md_ctx *ctx, BYTE *md) CryptReleaseContext(ctx->prov, 0); return status; } + +/* Open specified http/https URL using ShellExecute. */ +BOOL +open_url(const wchar_t *url) +{ + if (!url || !wcsbegins(url, L"http")) + { + return false; + } + + HINSTANCE ret = ShellExecuteW(NULL, L"open", url, NULL, NULL, SW_SHOWNORMAL); + + if (ret <= (HINSTANCE) 32) + { + MsgToEventLog(EVENTLOG_ERROR_TYPE, L"launch_url: ShellExecute <%s> returned error: %d", url, ret); + return false; + } + return true; +} diff --git a/misc.h b/misc.h index 6e9f7b6..e6464d8 100644 --- a/misc.h +++ b/misc.h @@ -65,4 +65,7 @@ DWORD md_init(md_ctx *ctx, ALG_ID hash_type); DWORD md_update(md_ctx *ctx, const BYTE *data, size_t size); DWORD md_final(md_ctx *ctx, BYTE *md); +/* Open specified http/https URL using ShellExecute. */ +BOOL open_url(const wchar_t *url); + #endif