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 <selva.nair@gmail.com>
pull/391/head
Selva Nair 2017-12-27 17:27:01 -05:00
parent 482bf586df
commit ba05ea0de9
3 changed files with 87 additions and 0 deletions

64
echo.c
View File

@ -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;

20
misc.c
View File

@ -29,6 +29,7 @@
#include <string.h>
#include <stdlib.h>
#include <malloc.h>
#include <shellapi.h>
#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;
}

3
misc.h
View File

@ -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