FreeRDP
Loading...
Searching...
No Matches
wf_client.c
1
22#include <freerdp/config.h>
23
24#include <winpr/windows.h>
25#include <winpr/library.h>
26
27#include <winpr/crt.h>
28#include <winpr/assert.h>
29
30#include <errno.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <tchar.h>
35#include <winpr/assert.h>
36#include <sys/types.h>
37#include <io.h>
38
39#ifdef WITH_PROGRESS_BAR
40#include <shobjidl.h>
41#endif
42
43#ifdef WITH_WINDOWS_CERT_STORE
44#include <wincrypt.h>
45#endif
46
47#include <freerdp/log.h>
48#include <freerdp/freerdp.h>
49#include <freerdp/constants.h>
50#include <freerdp/settings.h>
51
52#include <freerdp/locale/locale.h>
53#include <freerdp/locale/keyboard.h>
54#include <freerdp/codec/region.h>
55#include <freerdp/client/cmdline.h>
56#include <freerdp/client/channels.h>
57#include <freerdp/channels/channels.h>
58
59#include "wf_gdi.h"
60#include "wf_rail.h"
61#include "wf_channels.h"
62#include "wf_graphics.h"
63
64#include "resource/resource.h"
65
66#define TAG CLIENT_TAG("windows")
67
68#define WM_FREERDP_SHOWWINDOW (WM_USER + 100)
69
70static BOOL wf_has_console(void)
71{
72#ifdef WITH_WIN_CONSOLE
73 int file = _fileno(stdin);
74 int tty = _isatty(file);
75#else
76 int file = -1;
77 int tty = 0;
78#endif
79
80 WLog_INFO(TAG, "Detected stdin=%d -> %s mode", file, tty ? "console" : "gui");
81 return tty;
82}
83
84static BOOL wf_end_paint(rdpContext* context)
85{
86 rdpGdi* gdi;
87 int ninvalid;
88 RECT updateRect;
89 HGDI_RGN cinvalid;
90 REGION16 invalidRegion;
91 RECTANGLE_16 invalidRect;
92 const RECTANGLE_16* extents;
93 wfContext* wfc = (wfContext*)context;
94 gdi = context->gdi;
95 ninvalid = gdi->primary->hdc->hwnd->ninvalid;
96 cinvalid = gdi->primary->hdc->hwnd->cinvalid;
97
98 if (ninvalid < 1)
99 return TRUE;
100
101 region16_init(&invalidRegion);
102
103 for (int i = 0; i < ninvalid; i++)
104 {
105 invalidRect.left = cinvalid[i].x;
106 invalidRect.top = cinvalid[i].y;
107 invalidRect.right = cinvalid[i].x + cinvalid[i].w;
108 invalidRect.bottom = cinvalid[i].y + cinvalid[i].h;
109 region16_union_rect(&invalidRegion, &invalidRegion, &invalidRect);
110 }
111
112 if (!region16_is_empty(&invalidRegion))
113 {
114 extents = region16_extents(&invalidRegion);
115 updateRect.left = extents->left;
116 updateRect.top = extents->top;
117 updateRect.right = extents->right;
118 updateRect.bottom = extents->bottom;
119
120 wf_scale_rect(wfc, &updateRect);
121
122 InvalidateRect(wfc->hwnd, &updateRect, FALSE);
123
124 if (wfc->rail)
125 wf_rail_invalidate_region(wfc, &invalidRegion);
126 }
127
128 region16_uninit(&invalidRegion);
129
130 if (!wfc->is_shown)
131 {
132 wfc->is_shown = TRUE;
133
134#ifdef WITH_PROGRESS_BAR
135 if (wfc->taskBarList)
136 {
137 wfc->taskBarList->lpVtbl->SetProgressState(wfc->taskBarList, wfc->hwnd,
138 TBPF_NOPROGRESS);
139 }
140#endif
141
142 PostMessage(wfc->hwnd, WM_FREERDP_SHOWWINDOW, 0, 0);
143 WLog_INFO(TAG, "Window is shown!");
144 }
145 return TRUE;
146}
147
148static BOOL wf_begin_paint(rdpContext* context)
149{
150 HGDI_DC hdc;
151
152 if (!context || !context->gdi || !context->gdi->primary || !context->gdi->primary->hdc)
153 return FALSE;
154
155 hdc = context->gdi->primary->hdc;
156
157 if (!hdc || !hdc->hwnd || !hdc->hwnd->invalid)
158 return FALSE;
159
160 hdc->hwnd->invalid->null = TRUE;
161 hdc->hwnd->ninvalid = 0;
162 return TRUE;
163}
164
165static BOOL wf_desktop_resize(rdpContext* context)
166{
167 BOOL same;
168 RECT rect;
169 rdpSettings* settings;
170 wfContext* wfc = (wfContext*)context;
171
172 if (!context || !context->settings)
173 return FALSE;
174
175 settings = context->settings;
176
177 if (wfc->primary)
178 {
179 same = (wfc->primary == wfc->drawing) ? TRUE : FALSE;
180 wf_image_free(wfc->primary);
181 wfc->primary =
182 wf_image_new(wfc, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
183 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight),
184 context->gdi->dstFormat, NULL);
185 }
186
187 if (!gdi_resize_ex(context->gdi, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
188 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight), 0,
189 context->gdi->dstFormat, wfc->primary->pdata, NULL))
190 return FALSE;
191
192 if (same)
193 wfc->drawing = wfc->primary;
194
195 if (wfc->fullscreen != TRUE)
196 {
197 if (wfc->hwnd && !freerdp_settings_get_bool(settings, FreeRDP_SmartSizing))
198 SetWindowPos(wfc->hwnd, HWND_TOP, -1, -1,
199 freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) + wfc->diff.x,
200 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) + wfc->diff.y,
201 SWP_NOMOVE);
202 }
203 else
204 {
205 wf_update_offset(wfc);
206 GetWindowRect(wfc->hwnd, &rect);
207 InvalidateRect(wfc->hwnd, &rect, TRUE);
208 }
209
210 return TRUE;
211}
212
213static BOOL wf_pre_connect(freerdp* instance)
214{
215 WINPR_ASSERT(instance);
216 WINPR_ASSERT(instance->context);
217 WINPR_ASSERT(instance->context->settings);
218
219 rdpContext* context = instance->context;
220 wfContext* wfc = (wfContext*)instance->context;
221 rdpSettings* settings = context->settings;
222 if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMajorType, OSMAJORTYPE_WINDOWS))
223 return FALSE;
224 if (!freerdp_settings_set_uint32(settings, FreeRDP_OsMinorType, OSMINORTYPE_WINDOWS_NT))
225 return FALSE;
226 wfc->fullscreen = freerdp_settings_get_bool(settings, FreeRDP_Fullscreen);
227 wfc->fullscreen_toggle = freerdp_settings_get_bool(settings, FreeRDP_ToggleFullscreen);
228 UINT32 desktopWidth = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
229 UINT32 desktopHeight = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
230
231 if (wfc->percentscreen > 0)
232 {
233 desktopWidth = (GetSystemMetrics(SM_CXSCREEN) * wfc->percentscreen) / 100;
234 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, desktopWidth))
235 return FALSE;
236 desktopHeight = (GetSystemMetrics(SM_CYSCREEN) * wfc->percentscreen) / 100;
237 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, desktopHeight))
238 return FALSE;
239 }
240
241 if (wfc->fullscreen)
242 {
243 if (freerdp_settings_get_bool(settings, FreeRDP_UseMultimon))
244 {
245 desktopWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
246 desktopHeight = GetSystemMetrics(SM_CYVIRTUALSCREEN);
247 }
248 else
249 {
250 desktopWidth = GetSystemMetrics(SM_CXSCREEN);
251 desktopHeight = GetSystemMetrics(SM_CYSCREEN);
252 }
253 }
254
255 /* FIXME: desktopWidth has a limitation that it should be divisible by 4,
256 * otherwise the screen will crash when connecting to an XP desktop.*/
257 desktopWidth = (desktopWidth + 3) & (~3);
258
259 if (desktopWidth != freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth))
260 {
261 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopWidth, desktopWidth))
262 return FALSE;
263 }
264
265 if (desktopHeight != freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight))
266 {
267 if (!freerdp_settings_set_uint32(settings, FreeRDP_DesktopHeight, desktopHeight))
268 return FALSE;
269 }
270
271 uint32_t keyboardLayoutId = freerdp_settings_get_uint32(settings, FreeRDP_KeyboardLayout);
272
273 {
274 CHAR name[KL_NAMELENGTH + 1] = { 0 };
275 if (GetKeyboardLayoutNameA(name))
276 {
277 ULONG rc = 0;
278
279 errno = 0;
280 rc = strtoul(name, NULL, 16);
281 if (errno == 0)
282 keyboardLayoutId = rc;
283 }
284
285 if (keyboardLayoutId == 0)
286 {
287 const HKL layout = GetKeyboardLayout(0);
288 const uint32_t masked = (uint32_t)(((uintptr_t)layout >> 16) & 0xFFFF);
289 keyboardLayoutId = masked;
290 }
291 }
292
293 if (keyboardLayoutId == 0)
294 freerdp_detect_keyboard_layout_from_system_locale(&keyboardLayoutId);
295 if (keyboardLayoutId == 0)
296 keyboardLayoutId = ENGLISH_UNITED_STATES;
297 if (!freerdp_settings_set_uint32(settings, FreeRDP_KeyboardLayout, keyboardLayoutId))
298 return FALSE;
299 PubSub_SubscribeChannelConnected(instance->context->pubSub, wf_OnChannelConnectedEventHandler);
300 PubSub_SubscribeChannelDisconnected(instance->context->pubSub,
301 wf_OnChannelDisconnectedEventHandler);
302 return TRUE;
303}
304
305static void wf_append_item_to_system_menu(HMENU hMenu, UINT fMask, UINT wID, const wchar_t* text,
306 wfContext* wfc)
307{
308 MENUITEMINFO item_info = { 0 };
309 item_info.fMask = fMask;
310 item_info.cbSize = sizeof(MENUITEMINFO);
311 item_info.wID = wID;
312 item_info.fType = MFT_STRING;
313 item_info.dwTypeData = _wcsdup(text);
314 item_info.cch = (UINT)_wcslen(text);
315 if (wfc)
316 item_info.dwItemData = (ULONG_PTR)wfc;
317 InsertMenuItem(hMenu, wfc->systemMenuInsertPosition++, TRUE, &item_info);
318}
319
320static void wf_add_system_menu(wfContext* wfc)
321{
322 HMENU hMenu;
323
324 if (wfc->fullscreen && !wfc->fullscreen_toggle)
325 {
326 return;
327 }
328
329 if (freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_DynamicResolutionUpdate))
330 {
331 return;
332 }
333
334 hMenu = GetSystemMenu(wfc->hwnd, FALSE);
335
336 wf_append_item_to_system_menu(hMenu,
337 MIIM_CHECKMARKS | MIIM_FTYPE | MIIM_ID | MIIM_STRING | MIIM_DATA,
338 SYSCOMMAND_ID_SMARTSIZING, L"Smart sizing", wfc);
339
340 if (freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_SmartSizing))
341 {
342 CheckMenuItem(hMenu, SYSCOMMAND_ID_SMARTSIZING, MF_CHECKED);
343 }
344
345 if (freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_RemoteAssistanceMode))
346 wf_append_item_to_system_menu(hMenu, MIIM_FTYPE | MIIM_ID | MIIM_STRING,
347 SYSCOMMAND_ID_REQUEST_CONTROL, L"Request control", wfc);
348}
349
350static WCHAR* wf_window_get_title(rdpSettings* settings)
351{
352 BOOL port;
353 WCHAR* windowTitle = NULL;
354 size_t size;
355 WCHAR prefix[] = L"FreeRDP:";
356
357 if (!settings)
358 return NULL;
359
360 const char* name = freerdp_settings_get_string(settings, FreeRDP_ServerHostname);
361
362 if (freerdp_settings_get_string(settings, FreeRDP_WindowTitle))
363 return ConvertUtf8ToWCharAlloc(freerdp_settings_get_string(settings, FreeRDP_WindowTitle),
364 NULL);
365
366 port = (freerdp_settings_get_uint32(settings, FreeRDP_ServerPort) != 3389);
367 size = strlen(name) + 16 + wcslen(prefix);
368 windowTitle = calloc(size, sizeof(WCHAR));
369
370 if (!windowTitle)
371 return NULL;
372
373 if (!port)
374 _snwprintf_s(windowTitle, size, _TRUNCATE, L"%s %S", prefix, name);
375 else
376 _snwprintf_s(windowTitle, size, _TRUNCATE, L"%s %S:%u", prefix, name,
377 freerdp_settings_get_uint32(settings, FreeRDP_ServerPort));
378
379 return windowTitle;
380}
381
382static BOOL wf_post_connect(freerdp* instance)
383{
384 rdpGdi* gdi;
385 DWORD dwStyle;
386 rdpCache* cache;
387 wfContext* wfc;
388 rdpContext* context;
389 rdpSettings* settings;
390 EmbedWindowEventArgs e;
391 const UINT32 format = PIXEL_FORMAT_BGRX32;
392
393 WINPR_ASSERT(instance);
394
395 context = instance->context;
396 WINPR_ASSERT(context);
397
398 settings = context->settings;
399 WINPR_ASSERT(settings);
400
401 wfc = (wfContext*)instance->context;
402 WINPR_ASSERT(wfc);
403
404 wfc->primary =
405 wf_image_new(wfc, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
406 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight), format, NULL);
407
408 if (!gdi_init_ex(instance, format, 0, wfc->primary->pdata, NULL))
409 return FALSE;
410
411 cache = instance->context->cache;
412 WINPR_ASSERT(cache);
413
414 gdi = instance->context->gdi;
415
416 if (!freerdp_settings_get_bool(settings, FreeRDP_SoftwareGdi))
417 {
418 wf_gdi_register_update_callbacks(context->update);
419 }
420
421 wfc->window_title = wf_window_get_title(settings);
422
423 if (!wfc->window_title)
424 return FALSE;
425
426 if (freerdp_settings_get_bool(settings, FreeRDP_EmbeddedWindow))
427 {
428 if (!freerdp_settings_set_bool(settings, FreeRDP_Decorations, FALSE))
429 return FALSE;
430 }
431
432 if (wfc->fullscreen)
433 dwStyle = WS_POPUP;
434 else if (!freerdp_settings_get_bool(settings, FreeRDP_Decorations))
435 dwStyle = WS_CHILD | WS_BORDER;
436 else
437 dwStyle =
438 WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_MINIMIZEBOX | WS_SIZEBOX | WS_MAXIMIZEBOX;
439
440 if (!wfc->hwnd)
441 {
442 wfc->hwnd = CreateWindowEx(0, wfc->wndClassName, wfc->window_title, dwStyle, 0, 0, 0, 0,
443 wfc->hWndParent, NULL, wfc->hInstance, NULL);
444 SetWindowLongPtr(wfc->hwnd, GWLP_USERDATA, (LONG_PTR)wfc);
445 }
446
447 wf_resize_window(wfc);
448 wf_add_system_menu(wfc);
449 BitBlt(wfc->primary->hdc, 0, 0, freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth),
450 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight), NULL, 0, 0, BLACKNESS);
451 wfc->drawing = wfc->primary;
452 EventArgsInit(&e, "wfreerdp");
453 e.embed = FALSE;
454 e.handle = (void*)wfc->hwnd;
455 PubSub_OnEmbedWindow(context->pubSub, context, &e);
456#ifdef WITH_PROGRESS_BAR
457 if (wfc->taskBarList)
458 {
459 ShowWindow(wfc->hwnd, SW_SHOWMINIMIZED);
460 wfc->taskBarList->lpVtbl->SetProgressState(wfc->taskBarList, wfc->hwnd, TBPF_INDETERMINATE);
461 }
462#endif
463 UpdateWindow(wfc->hwnd);
464 context->update->BeginPaint = wf_begin_paint;
465 context->update->DesktopResize = wf_desktop_resize;
466 context->update->EndPaint = wf_end_paint;
467 wf_register_pointer(context->graphics);
468
469 wfc->floatbar = wf_floatbar_new(wfc, wfc->hInstance,
470 freerdp_settings_get_uint32(settings, FreeRDP_Floatbar));
471 return TRUE;
472}
473
474static void wf_post_disconnect(freerdp* instance)
475{
476 wfContext* wfc;
477
478 if (!instance || !instance->context)
479 return;
480
481 wfc = (wfContext*)instance->context;
482 free(wfc->window_title);
483}
484
485static CREDUI_INFOW wfUiInfo = { sizeof(CREDUI_INFOW), NULL, L"Enter your credentials",
486 L"Remote Desktop Security", NULL };
487
488static BOOL wf_authenticate_ex(freerdp* instance, char** username, char** password, char** domain,
489 rdp_auth_reason reason)
490{
491 wfContext* wfc;
492 BOOL fSave;
493 DWORD status;
494 DWORD dwFlags;
495 WCHAR UserNameW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
496 WCHAR UserW[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
497 WCHAR DomainW[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
498 WCHAR PasswordW[CREDUI_MAX_PASSWORD_LENGTH + 1] = { 0 };
499
500 WINPR_ASSERT(instance);
501 WINPR_ASSERT(instance->context);
502 WINPR_ASSERT(instance->context->settings);
503
504 wfc = (wfContext*)instance->context;
505 WINPR_ASSERT(wfc);
506
507 WINPR_ASSERT(username);
508 WINPR_ASSERT(domain);
509 WINPR_ASSERT(password);
510
511 const WCHAR auth[] = L"Target credentials requested";
512 const WCHAR authPin[] = L"PIN requested";
513 const WCHAR gwAuth[] = L"Gateway credentials requested";
514 const WCHAR* titleW = auth;
515
516 fSave = FALSE;
517 dwFlags = CREDUI_FLAGS_DO_NOT_PERSIST | CREDUI_FLAGS_EXCLUDE_CERTIFICATES |
518 CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
519 switch (reason)
520 {
521 case AUTH_NLA:
522 break;
523 case AUTH_TLS:
524 case AUTH_RDP:
525 if ((*username) && (*password))
526 return TRUE;
527 break;
528 case AUTH_SMARTCARD_PIN:
529 dwFlags &= ~CREDUI_FLAGS_USERNAME_TARGET_CREDENTIALS;
530 dwFlags |= CREDUI_FLAGS_PASSWORD_ONLY_OK | CREDUI_FLAGS_KEEP_USERNAME;
531 titleW = authPin;
532 if (*password)
533 return TRUE;
534 if (!(*username))
535 *username = _strdup("PIN");
536 break;
537 case GW_AUTH_HTTP:
538 case GW_AUTH_RDG:
539 case GW_AUTH_RPC:
540 titleW = gwAuth;
541 break;
542 default:
543 return FALSE;
544 }
545
546 if (*username)
547 {
548 (void)ConvertUtf8ToWChar(*username, UserNameW, ARRAYSIZE(UserNameW));
549 (void)ConvertUtf8ToWChar(*username, UserW, ARRAYSIZE(UserW));
550 }
551
552 if (*password)
553 (void)ConvertUtf8ToWChar(*password, PasswordW, ARRAYSIZE(PasswordW));
554
555 if (*domain)
556 (void)ConvertUtf8ToWChar(*domain, DomainW, ARRAYSIZE(DomainW));
557
558 if (_wcsnlen(PasswordW, ARRAYSIZE(PasswordW)) == 0)
559 {
560 if (!wfc->isConsole &&
561 freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_CredentialsFromStdin))
562 WLog_ERR(TAG, "Flag for stdin read present but stdin is redirected; using GUI");
563 if (wfc->isConsole &&
564 freerdp_settings_get_bool(wfc->common.context.settings, FreeRDP_CredentialsFromStdin))
565 status = CredUICmdLinePromptForCredentialsW(titleW, NULL, 0, UserNameW,
566 ARRAYSIZE(UserNameW), PasswordW,
567 ARRAYSIZE(PasswordW), &fSave, dwFlags);
568 else
569 status = CredUIPromptForCredentialsW(&wfUiInfo, titleW, NULL, 0, UserNameW,
570 ARRAYSIZE(UserNameW), PasswordW,
571 ARRAYSIZE(PasswordW), &fSave, dwFlags);
572 if (status != NO_ERROR)
573 {
574 WLog_ERR(TAG, "CredUIPromptForCredentials unexpected status: 0x%08lX", status);
575 return FALSE;
576 }
577
578 if ((dwFlags & CREDUI_FLAGS_KEEP_USERNAME) == 0)
579 {
580 status = CredUIParseUserNameW(UserNameW, UserW, ARRAYSIZE(UserW), DomainW,
581 ARRAYSIZE(DomainW));
582 if (status != NO_ERROR)
583 {
584 CHAR User[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
585 CHAR UserName[CREDUI_MAX_USERNAME_LENGTH + 1] = { 0 };
586 CHAR Domain[CREDUI_MAX_DOMAIN_TARGET_LENGTH + 1] = { 0 };
587
588 (void)ConvertWCharNToUtf8(UserNameW, ARRAYSIZE(UserNameW), UserName,
589 ARRAYSIZE(UserName));
590 (void)ConvertWCharNToUtf8(UserW, ARRAYSIZE(UserW), User, ARRAYSIZE(User));
591 (void)ConvertWCharNToUtf8(DomainW, ARRAYSIZE(DomainW), Domain, ARRAYSIZE(Domain));
592 WLog_ERR(TAG, "Failed to parse UserName: %s into User: %s Domain: %s", UserName,
593 User, Domain);
594 return FALSE;
595 }
596 }
597 }
598
599 *username = ConvertWCharNToUtf8Alloc(UserW, ARRAYSIZE(UserW), NULL);
600 if (!(*username))
601 {
602 WLog_ERR(TAG, "ConvertWCharNToUtf8Alloc failed", status);
603 return FALSE;
604 }
605
606 if (_wcsnlen(DomainW, ARRAYSIZE(DomainW)) > 0)
607 *domain = ConvertWCharNToUtf8Alloc(DomainW, ARRAYSIZE(DomainW), NULL);
608 else
609 *domain = _strdup("\0");
610
611 if (!(*domain))
612 {
613 free(*username);
614 WLog_ERR(TAG, "strdup failed", status);
615 return FALSE;
616 }
617
618 *password = ConvertWCharNToUtf8Alloc(PasswordW, ARRAYSIZE(PasswordW), NULL);
619 if (!(*password))
620 {
621 free(*username);
622 free(*domain);
623 return FALSE;
624 }
625
626 return TRUE;
627}
628
629static WCHAR* wf_format_text(const WCHAR* fmt, ...)
630{
631 int rc;
632 size_t size = 0;
633 WCHAR* buffer = NULL;
634
635 do
636 {
637 WCHAR* tmp;
638 va_list ap;
639 va_start(ap, fmt);
640 rc = _vsnwprintf(buffer, size, fmt, ap);
641 va_end(ap);
642 if (rc <= 0)
643 goto fail;
644
645 if ((size_t)rc < size)
646 return buffer;
647
648 size = (size_t)rc + 1;
649 tmp = realloc(buffer, size * sizeof(WCHAR));
650 if (!tmp)
651 goto fail;
652
653 buffer = tmp;
654 } while (TRUE);
655
656fail:
657 free(buffer);
658 return NULL;
659}
660
661#ifdef WITH_WINDOWS_CERT_STORE
662/* https://stackoverflow.com/questions/1231178/load-an-pem-encoded-x-509-certificate-into-windows-cryptoapi/3803333#3803333
663 */
664/* https://github.com/microsoft/Windows-classic-samples/blob/main/Samples/Win7Samples/security/cryptoapi/peertrust/cpp/peertrust.cpp
665 */
666/* https://stackoverflow.com/questions/7340504/whats-the-correct-way-to-verify-an-ssl-certificate-in-win32
667 */
668
669static void wf_report_error(char* wszMessage, DWORD dwErrCode)
670{
671 LPSTR pwszMsgBuf = NULL;
672
673 if (NULL != wszMessage && 0 != *wszMessage)
674 {
675 WLog_ERR(TAG, "%s", wszMessage);
676 }
677
678 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
679 NULL, // Location of message
680 // definition ignored
681 dwErrCode, // Message identifier for
682 // the requested message
683 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Language identifier for
684 // the requested message
685 (LPSTR)&pwszMsgBuf, // Buffer that receives
686 // the formatted message
687 0, // Size of output buffer
688 // not needed as allocate
689 // buffer flag is set
690 NULL // Array of insert values
691 );
692
693 if (NULL != pwszMsgBuf)
694 {
695 WLog_ERR(TAG, "Error: 0x%08x (%d) %s", dwErrCode, dwErrCode, pwszMsgBuf);
696 LocalFree(pwszMsgBuf);
697 }
698 else
699 {
700 WLog_ERR(TAG, "Error: 0x%08x (%d)", dwErrCode, dwErrCode);
701 }
702}
703
704static DWORD wf_is_x509_certificate_trusted(const char* common_name, const char* subject,
705 const char* issuer, const char* fingerprint)
706{
707 HRESULT hr = CRYPT_E_NOT_FOUND;
708
709 DWORD dwChainFlags = CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT;
710 PCCERT_CONTEXT pCert = NULL;
711 HCERTCHAINENGINE hChainEngine = NULL;
712 PCCERT_CHAIN_CONTEXT pChainContext = NULL;
713
714 CERT_ENHKEY_USAGE EnhkeyUsage = { 0 };
715 CERT_USAGE_MATCH CertUsage = { 0 };
716 CERT_CHAIN_PARA ChainPara = { 0 };
717 CERT_CHAIN_POLICY_PARA ChainPolicy = { 0 };
718 CERT_CHAIN_POLICY_STATUS PolicyStatus = { 0 };
719 CERT_CHAIN_ENGINE_CONFIG EngineConfig = { 0 };
720
721 DWORD derPubKeyLen = WINPR_ASSERTING_INT_CAST(uint32_t, strlen(fingerprint));
722 char* derPubKey = calloc(derPubKeyLen, sizeof(char));
723 if (NULL == derPubKey)
724 {
725 WLog_ERR(TAG, "Could not allocate derPubKey");
726 goto CleanUp;
727 }
728
729 /*
730 * Convert from PEM format to DER format - removes header and footer and decodes from base64
731 */
732 if (!CryptStringToBinaryA(fingerprint, 0, CRYPT_STRING_BASE64HEADER, derPubKey, &derPubKeyLen,
733 NULL, NULL))
734 {
735 WLog_ERR(TAG, "CryptStringToBinary failed. Err: %d", GetLastError());
736 goto CleanUp;
737 }
738
739 //---------------------------------------------------------
740 // Initialize data structures for chain building.
741
742 EnhkeyUsage.cUsageIdentifier = 0;
743 EnhkeyUsage.rgpszUsageIdentifier = NULL;
744
745 CertUsage.dwType = USAGE_MATCH_TYPE_AND;
746 CertUsage.Usage = EnhkeyUsage;
747
748 ChainPara.cbSize = sizeof(ChainPara);
749 ChainPara.RequestedUsage = CertUsage;
750
751 ChainPolicy.cbSize = sizeof(ChainPolicy);
752
753 PolicyStatus.cbSize = sizeof(PolicyStatus);
754
755 EngineConfig.cbSize = sizeof(EngineConfig);
756 EngineConfig.dwUrlRetrievalTimeout = 0;
757
758 pCert = CertCreateCertificateContext(X509_ASN_ENCODING, derPubKey, derPubKeyLen);
759 if (NULL == pCert)
760 {
761 WLog_ERR(TAG, "FAILED: Certificate could not be parsed.");
762 goto CleanUp;
763 }
764
765 dwChainFlags |= CERT_CHAIN_ENABLE_PEER_TRUST;
766
767 // When this flag is set, end entity certificates in the
768 // Trusted People store are trusted without doing any chain building
769 // This optimizes the chain building process.
770
771 //---------------------------------------------------------
772 // Create chain engine.
773
774 if (!CertCreateCertificateChainEngine(&EngineConfig, &hChainEngine))
775 {
776 hr = HRESULT_FROM_WIN32(GetLastError());
777 goto CleanUp;
778 }
779
780 //-------------------------------------------------------------------
781 // Build a chain using CertGetCertificateChain
782
783 if (!CertGetCertificateChain(hChainEngine, // use the default chain engine
784 pCert, // pointer to the end certificate
785 NULL, // use the default time
786 NULL, // search no additional stores
787 &ChainPara, // use AND logic and enhanced key usage
788 // as indicated in the ChainPara
789 // data structure
790 dwChainFlags,
791 NULL, // currently reserved
792 &pChainContext)) // return a pointer to the chain created
793 {
794 hr = HRESULT_FROM_WIN32(GetLastError());
795 goto CleanUp;
796 }
797
798 //---------------------------------------------------------------
799 // Verify that the chain complies with policy
800
801 if (!CertVerifyCertificateChainPolicy(CERT_CHAIN_POLICY_BASE, // use the base policy
802 pChainContext, // pointer to the chain
803 &ChainPolicy,
804 &PolicyStatus)) // return a pointer to the policy status
805 {
806 hr = HRESULT_FROM_WIN32(GetLastError());
807 goto CleanUp;
808 }
809
810 if (PolicyStatus.dwError != S_OK)
811 {
812 wf_report_error("CertVerifyCertificateChainPolicy: Chain Status", PolicyStatus.dwError);
813 hr = PolicyStatus.dwError;
814 // Instruction: If the PolicyStatus.dwError is CRYPT_E_NO_REVOCATION_CHECK or
815 // CRYPT_E_REVOCATION_OFFLINE, it indicates errors in obtaining
816 // revocation information. These can be ignored since the retrieval of
817 // revocation information depends on network availability
818
819 if (PolicyStatus.dwError == CRYPT_E_NO_REVOCATION_CHECK ||
820 PolicyStatus.dwError == CRYPT_E_REVOCATION_OFFLINE)
821 {
822 hr = S_OK;
823 }
824
825 goto CleanUp;
826 }
827
828 WLog_INFO(TAG, "CertVerifyCertificateChainPolicy succeeded for %s (%s) issued by %s",
829 common_name, subject, issuer);
830
831 hr = S_OK;
832CleanUp:
833
834 if (FAILED(hr))
835 {
836 WLog_INFO(TAG, "CertVerifyCertificateChainPolicy failed for %s (%s) issued by %s",
837 common_name, subject, issuer);
838 wf_report_error(NULL, hr);
839 }
840
841 free(derPubKey);
842
843 if (NULL != pChainContext)
844 {
845 CertFreeCertificateChain(pChainContext);
846 }
847
848 if (NULL != hChainEngine)
849 {
850 CertFreeCertificateChainEngine(hChainEngine);
851 }
852
853 if (NULL != pCert)
854 {
855 CertFreeCertificateContext(pCert);
856 }
857
858 return (DWORD)hr;
859}
860#endif
861
862static DWORD wf_cli_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
863 const char* common_name, const char* subject,
864 const char* issuer, const char* fingerprint, DWORD flags)
865{
866#ifdef WITH_WINDOWS_CERT_STORE
867 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
868 {
869 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
870 {
871 return 2;
872 }
873 }
874#endif
875
876 return client_cli_verify_certificate_ex(instance, host, port, common_name, subject, issuer,
877 fingerprint, flags);
878}
879
880static DWORD wf_verify_certificate_ex(freerdp* instance, const char* host, UINT16 port,
881 const char* common_name, const char* subject,
882 const char* issuer, const char* fingerprint, DWORD flags)
883{
884 WCHAR* buffer;
885 WCHAR* caption;
886 int what = IDCANCEL;
887
888#ifdef WITH_WINDOWS_CERT_STORE
889 if (flags & VERIFY_CERT_FLAG_FP_IS_PEM && !(flags & VERIFY_CERT_FLAG_MISMATCH))
890 {
891 if (wf_is_x509_certificate_trusted(common_name, subject, issuer, fingerprint) == S_OK)
892 {
893 return 2;
894 }
895 }
896#endif
897
898 buffer = wf_format_text(
899 L"Certificate details:\n"
900 L"\tCommonName: %S\n"
901 L"\tSubject: %S\n"
902 L"\tIssuer: %S\n"
903 L"\tThumbprint: %S\n"
904 L"\tHostMismatch: %S\n"
905 L"\n"
906 L"The above X.509 certificate could not be verified, possibly because you do not have "
907 L"the CA certificate in your certificate store, or the certificate has expired. "
908 L"Please look at the OpenSSL documentation on how to add a private CA to the store.\n"
909 L"\n"
910 L"YES\tAccept permanently\n"
911 L"NO\tAccept for this session only\n"
912 L"CANCEL\tAbort connection\n",
913 common_name, subject, issuer, fingerprint,
914 flags & VERIFY_CERT_FLAG_MISMATCH ? "Yes" : "No");
915 caption = wf_format_text(L"Verify certificate for %S:%hu", host, port);
916
917 WINPR_UNUSED(instance);
918
919 if (!buffer || !caption)
920 goto fail;
921
922 what = MessageBoxW(NULL, buffer, caption, MB_YESNOCANCEL);
923fail:
924 free(buffer);
925 free(caption);
926
927 /* return 1 to accept and store a certificate, 2 to accept
928 * a certificate only for this session, 0 otherwise */
929 switch (what)
930 {
931 case IDYES:
932 return 1;
933 case IDNO:
934 return 2;
935 default:
936 return 0;
937 }
938}
939
940static DWORD wf_verify_changed_certificate_ex(freerdp* instance, const char* host, UINT16 port,
941 const char* common_name, const char* subject,
942 const char* issuer, const char* new_fingerprint,
943 const char* old_subject, const char* old_issuer,
944 const char* old_fingerprint, DWORD flags)
945{
946 WCHAR* buffer;
947 WCHAR* caption;
948 int what = IDCANCEL;
949
950 buffer = wf_format_text(
951 L"New Certificate details:\n"
952 L"\tCommonName: %S\n"
953 L"\tSubject: %S\n"
954 L"\tIssuer: %S\n"
955 L"\tThumbprint: %S\n"
956 L"\tHostMismatch: %S\n"
957 L"\n"
958 L"Old Certificate details:\n"
959 L"\tSubject: %S\n"
960 L"\tIssuer: %S\n"
961 L"\tThumbprint: %S"
962 L"The above X.509 certificate could not be verified, possibly because you do not have "
963 L"the CA certificate in your certificate store, or the certificate has expired. "
964 L"Please look at the OpenSSL documentation on how to add a private CA to the store.\n"
965 L"\n"
966 L"YES\tAccept permanently\n"
967 L"NO\tAccept for this session only\n"
968 L"CANCEL\tAbort connection\n",
969 common_name, subject, issuer, new_fingerprint,
970 flags & VERIFY_CERT_FLAG_MISMATCH ? "Yes" : "No", old_subject, old_issuer, old_fingerprint);
971 caption = wf_format_text(L"Verify certificate change for %S:%hu", host, port);
972
973 WINPR_UNUSED(instance);
974 if (!buffer || !caption)
975 goto fail;
976
977 what = MessageBoxW(NULL, buffer, caption, MB_YESNOCANCEL);
978fail:
979 free(buffer);
980 free(caption);
981
982 /* return 1 to accept and store a certificate, 2 to accept
983 * a certificate only for this session, 0 otherwise */
984 switch (what)
985 {
986 case IDYES:
987 return 1;
988 case IDNO:
989 return 2;
990 default:
991 return 0;
992 }
993}
994
995static BOOL wf_present_gateway_message(freerdp* instance, UINT32 type, BOOL isDisplayMandatory,
996 BOOL isConsentMandatory, size_t length, const WCHAR* message)
997{
998 if (!isDisplayMandatory && !isConsentMandatory)
999 return TRUE;
1000
1001 /* special handling for consent messages (show modal dialog) */
1002 if (type == GATEWAY_MESSAGE_CONSENT && isConsentMandatory)
1003 {
1004 int mbRes;
1005 WCHAR* msg;
1006
1007 msg = wf_format_text(L"%.*s\n\nI understand and agree to the terms of this policy", length,
1008 message);
1009 mbRes = MessageBoxW(NULL, msg, L"Consent Message", MB_YESNO);
1010 free(msg);
1011
1012 if (mbRes != IDYES)
1013 return FALSE;
1014 }
1015 else
1016 return client_cli_present_gateway_message(instance, type, isDisplayMandatory,
1017 isConsentMandatory, length, message);
1018
1019 return TRUE;
1020}
1021
1022static DWORD WINAPI wf_client_thread(LPVOID lpParam)
1023{
1024 MSG msg = { 0 };
1025 int width = 0;
1026 int height = 0;
1027 BOOL msg_ret = FALSE;
1028 int quit_msg = 0;
1029 DWORD error = 0;
1030
1031 freerdp* instance = (freerdp*)lpParam;
1032 WINPR_ASSERT(instance);
1033
1034 if (!freerdp_connect(instance))
1035 goto end;
1036
1037 rdpContext* context = instance->context;
1038 WINPR_ASSERT(context);
1039
1040 wfContext* wfc = (wfContext*)instance->context;
1041 WINPR_ASSERT(wfc);
1042
1043 rdpChannels* channels = context->channels;
1044 WINPR_ASSERT(channels);
1045
1046 rdpSettings* settings = context->settings;
1047 WINPR_ASSERT(settings);
1048
1049 while (!freerdp_shall_disconnect_context(instance->context))
1050 {
1051 HANDLE handles[MAXIMUM_WAIT_OBJECTS] = { 0 };
1052 DWORD nCount = 0;
1053
1054 if (freerdp_focus_required(instance))
1055 {
1056 wf_event_focus_in(wfc);
1057 wf_event_focus_in(wfc);
1058 }
1059
1060 {
1061 DWORD tmp = freerdp_get_event_handles(context, &handles[nCount], 64 - nCount);
1062
1063 if (tmp == 0)
1064 {
1065 WLog_ERR(TAG, "freerdp_get_event_handles failed");
1066 break;
1067 }
1068
1069 nCount += tmp;
1070 }
1071
1072 DWORD status = MsgWaitForMultipleObjectsEx(nCount, handles, 5 * 1000, QS_ALLINPUT,
1073 MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
1074 if (status == WAIT_FAILED)
1075 {
1076 WLog_ERR(TAG, "wfreerdp_run: WaitForMultipleObjects failed: 0x%08lX", GetLastError());
1077 break;
1078 }
1079
1080 {
1081 if (!freerdp_check_event_handles(context))
1082 {
1083 if (client_auto_reconnect(instance))
1084 continue;
1085
1086 WLog_ERR(TAG, "Failed to check FreeRDP file descriptor");
1087 break;
1088 }
1089 }
1090
1091 if (freerdp_shall_disconnect_context(instance->context))
1092 break;
1093
1094 quit_msg = FALSE;
1095
1096 while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
1097 {
1098 msg_ret = GetMessage(&msg, NULL, 0, 0);
1099
1100 if (freerdp_settings_get_bool(settings, FreeRDP_EmbeddedWindow))
1101 {
1102 if ((msg.message == WM_SETFOCUS) && (msg.lParam == 1))
1103 {
1104 PostMessage(wfc->hwnd, WM_SETFOCUS, 0, 0);
1105 }
1106 else if ((msg.message == WM_KILLFOCUS) && (msg.lParam == 1))
1107 {
1108 PostMessage(wfc->hwnd, WM_KILLFOCUS, 0, 0);
1109 }
1110 }
1111
1112 switch (msg.message)
1113 {
1114 case WM_SIZE:
1115 {
1116 width = LOWORD(msg.lParam);
1117 height = HIWORD(msg.lParam);
1118 SetWindowPos(wfc->hwnd, HWND_TOP, 0, 0, width, height, SWP_FRAMECHANGED);
1119 break;
1120 }
1121 case WM_FREERDP_SHOWWINDOW:
1122 {
1123 ShowWindow(wfc->hwnd, SW_NORMAL);
1124 break;
1125 }
1126 default:
1127 break;
1128 }
1129
1130 if ((msg_ret == 0) || (msg_ret == -1))
1131 {
1132 quit_msg = TRUE;
1133 break;
1134 }
1135
1136 TranslateMessage(&msg);
1137 DispatchMessage(&msg);
1138 }
1139
1140 if (quit_msg)
1141 break;
1142 }
1143
1144 /* cleanup */
1145 freerdp_disconnect(instance);
1146
1147end:
1148 error = freerdp_get_last_error(instance->context);
1149 WLog_DBG(TAG, "Main thread exited with %" PRIu32, error);
1150 ExitThread(error);
1151 return error;
1152}
1153
1154static DWORD WINAPI wf_keyboard_thread(LPVOID lpParam)
1155{
1156 MSG msg;
1157 BOOL status;
1158 wfContext* wfc;
1159 HHOOK hook_handle;
1160 wfc = (wfContext*)lpParam;
1161 WINPR_ASSERT(NULL != wfc);
1162 hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, wf_ll_kbd_proc, wfc->hInstance, 0);
1163
1164 if (hook_handle)
1165 {
1166 while ((status = GetMessage(&msg, NULL, 0, 0)) != 0)
1167 {
1168 if (status == -1)
1169 {
1170 WLog_ERR(TAG, "keyboard thread error getting message");
1171 break;
1172 }
1173 else
1174 {
1175 TranslateMessage(&msg);
1176 DispatchMessage(&msg);
1177 }
1178 }
1179
1180 UnhookWindowsHookEx(hook_handle);
1181 }
1182 else
1183 {
1184 WLog_ERR(TAG, "failed to install keyboard hook");
1185 }
1186
1187 WLog_DBG(TAG, "Keyboard thread exited.");
1188 ExitThread(0);
1189 return 0;
1190}
1191
1192int freerdp_client_set_window_size(wfContext* wfc, int width, int height)
1193{
1194 WLog_DBG(TAG, "freerdp_client_set_window_size %d, %d", width, height);
1195
1196 if ((width != wfc->client_width) || (height != wfc->client_height))
1197 {
1198 PostThreadMessage(wfc->mainThreadId, WM_SIZE, SIZE_RESTORED,
1199 ((UINT)height << 16) | (UINT)width);
1200 }
1201
1202 return 0;
1203}
1204
1205void wf_size_scrollbars(wfContext* wfc, UINT32 client_width, UINT32 client_height)
1206{
1207 const rdpSettings* settings;
1208 WINPR_ASSERT(wfc);
1209
1210 settings = wfc->common.context.settings;
1211 WINPR_ASSERT(settings);
1212
1213 if (wfc->disablewindowtracking)
1214 return;
1215
1216 // prevent infinite message loop
1217 wfc->disablewindowtracking = TRUE;
1218
1219 if (freerdp_settings_get_bool(settings, FreeRDP_SmartSizing) ||
1220 freerdp_settings_get_bool(settings, FreeRDP_DynamicResolutionUpdate))
1221 {
1222 wfc->xCurrentScroll = 0;
1223 wfc->yCurrentScroll = 0;
1224
1225 if (wfc->xScrollVisible || wfc->yScrollVisible)
1226 {
1227 if (ShowScrollBar(wfc->hwnd, SB_BOTH, FALSE))
1228 {
1229 wfc->xScrollVisible = FALSE;
1230 wfc->yScrollVisible = FALSE;
1231 }
1232 }
1233 }
1234 else
1235 {
1236 SCROLLINFO si;
1237 BOOL horiz = wfc->xScrollVisible;
1238 BOOL vert = wfc->yScrollVisible;
1239
1240 if (!horiz && client_width < freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth))
1241 {
1242 horiz = TRUE;
1243 }
1244 else if (horiz &&
1245 client_width >=
1247 settings, FreeRDP_DesktopWidth) /* - GetSystemMetrics(SM_CXVSCROLL)*/)
1248 {
1249 horiz = FALSE;
1250 }
1251
1252 if (!vert && client_height < freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight))
1253 {
1254 vert = TRUE;
1255 }
1256 else if (vert &&
1257 client_height >=
1259 settings, FreeRDP_DesktopHeight) /* - GetSystemMetrics(SM_CYHSCROLL)*/)
1260 {
1261 vert = FALSE;
1262 }
1263
1264 if (horiz == vert && (horiz != wfc->xScrollVisible && vert != wfc->yScrollVisible))
1265 {
1266 if (ShowScrollBar(wfc->hwnd, SB_BOTH, horiz))
1267 {
1268 wfc->xScrollVisible = horiz;
1269 wfc->yScrollVisible = vert;
1270 }
1271 }
1272
1273 if (horiz != wfc->xScrollVisible)
1274 {
1275 if (ShowScrollBar(wfc->hwnd, SB_HORZ, horiz))
1276 {
1277 wfc->xScrollVisible = horiz;
1278 }
1279 }
1280
1281 if (vert != wfc->yScrollVisible)
1282 {
1283 if (ShowScrollBar(wfc->hwnd, SB_VERT, vert))
1284 {
1285 wfc->yScrollVisible = vert;
1286 }
1287 }
1288
1289 if (horiz)
1290 {
1291 // The horizontal scrolling range is defined by
1292 // (bitmap_width) - (client_width). The current horizontal
1293 // scroll value remains within the horizontal scrolling range.
1294 wfc->xMaxScroll =
1295 MAX(freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth) - client_width, 0);
1296 wfc->xCurrentScroll = MIN(wfc->xCurrentScroll, wfc->xMaxScroll);
1297 si.cbSize = sizeof(si);
1298 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1299 si.nMin = wfc->xMinScroll;
1300 si.nMax = freerdp_settings_get_uint32(settings, FreeRDP_DesktopWidth);
1301 si.nPage = client_width;
1302 si.nPos = wfc->xCurrentScroll;
1303 SetScrollInfo(wfc->hwnd, SB_HORZ, &si, TRUE);
1304 }
1305
1306 if (vert)
1307 {
1308 // The vertical scrolling range is defined by
1309 // (bitmap_height) - (client_height). The current vertical
1310 // scroll value remains within the vertical scrolling range.
1311 wfc->yMaxScroll = MAX(
1312 freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight) - client_height, 0);
1313 wfc->yCurrentScroll = MIN(wfc->yCurrentScroll, wfc->yMaxScroll);
1314 si.cbSize = sizeof(si);
1315 si.fMask = SIF_RANGE | SIF_PAGE | SIF_POS;
1316 si.nMin = wfc->yMinScroll;
1317 si.nMax = freerdp_settings_get_uint32(settings, FreeRDP_DesktopHeight);
1318 si.nPage = client_height;
1319 si.nPos = wfc->yCurrentScroll;
1320 SetScrollInfo(wfc->hwnd, SB_VERT, &si, TRUE);
1321 }
1322 }
1323
1324 wfc->disablewindowtracking = FALSE;
1325 wf_update_canvas_diff(wfc);
1326}
1327
1328static BOOL wfreerdp_client_global_init(void)
1329{
1330 WSADATA wsaData;
1331
1332 WSAStartup(0x101, &wsaData);
1333
1334 freerdp_register_addin_provider(freerdp_channels_load_static_addin_entry, 0);
1335
1336 return TRUE;
1337}
1338
1339static void wfreerdp_client_global_uninit(void)
1340{
1341 WSACleanup();
1342}
1343
1344static BOOL wfreerdp_client_new(freerdp* instance, rdpContext* context)
1345{
1346 wfContext* wfc = (wfContext*)context;
1347 if (!wfc)
1348 return FALSE;
1349
1350 // AttachConsole and stdin do not work well.
1351 // Use GUI input dialogs instead of command line ones.
1352 wfc->isConsole = wf_has_console();
1353
1354 if (!(wfreerdp_client_global_init()))
1355 return FALSE;
1356
1357 WINPR_ASSERT(instance);
1358 instance->PreConnect = wf_pre_connect;
1359 instance->PostConnect = wf_post_connect;
1360 instance->PostDisconnect = wf_post_disconnect;
1361 instance->AuthenticateEx = wf_authenticate_ex;
1362
1363#ifdef WITH_WINDOWS_CERT_STORE
1364 if (!freerdp_settings_set_bool(context->settings, FreeRDP_CertificateCallbackPreferPEM, TRUE))
1365 return FALSE;
1366#endif
1367
1368 if (!wfc->isConsole)
1369 {
1370 instance->VerifyCertificateEx = wf_verify_certificate_ex;
1371 instance->VerifyChangedCertificateEx = wf_verify_changed_certificate_ex;
1372 instance->PresentGatewayMessage = wf_present_gateway_message;
1373 }
1374
1375#ifdef WITH_PROGRESS_BAR
1376 CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
1377 CoCreateInstance(&CLSID_TaskbarList, NULL, CLSCTX_ALL, &IID_ITaskbarList3,
1378 (void**)&wfc->taskBarList);
1379#endif
1380
1381 return TRUE;
1382}
1383
1384static void wfreerdp_client_free(freerdp* instance, rdpContext* context)
1385{
1386 WINPR_UNUSED(instance);
1387 if (!context)
1388 return;
1389
1390#ifdef WITH_PROGRESS_BAR
1391 CoUninitialize();
1392#endif
1393}
1394
1395static int wfreerdp_client_start(rdpContext* context)
1396{
1397 wfContext* wfc = (wfContext*)context;
1398
1399 WINPR_ASSERT(context);
1400 WINPR_ASSERT(context->settings);
1401
1402 freerdp* instance = context->instance;
1403 WINPR_ASSERT(instance);
1404
1405 rdpSettings* settings = context->settings;
1406 WINPR_ASSERT(settings);
1407
1408 HINSTANCE hInstance = GetModuleHandle(NULL);
1409 HWND hWndParent = (HWND)freerdp_settings_get_uint64(settings, FreeRDP_ParentWindowId);
1410 if (!freerdp_settings_set_bool(settings, FreeRDP_EmbeddedWindow, (hWndParent) ? TRUE : FALSE))
1411 return -1;
1412
1413 wfc->hWndParent = hWndParent;
1414
1415 if (freerdp_settings_get_bool(settings, FreeRDP_EmbeddedWindow))
1416 {
1417 typedef UINT(WINAPI * GetDpiForWindow_t)(HWND hwnd);
1418 typedef BOOL(WINAPI * SetProcessDPIAware_t)(void);
1419
1420 HMODULE module = GetModuleHandle(_T("User32"));
1421 if (module)
1422 {
1423 GetDpiForWindow_t pGetDpiForWindow =
1424 GetProcAddressAs(module, "GetDpiForWindow", GetDpiForWindow_t);
1425 SetProcessDPIAware_t pSetProcessDPIAware =
1426 GetProcAddressAs(module, "SetProcessDPIAware", SetProcessDPIAware_t);
1427 if (pGetDpiForWindow && pSetProcessDPIAware)
1428 {
1429 const UINT dpiAwareness = pGetDpiForWindow(hWndParent);
1430 if (dpiAwareness != USER_DEFAULT_SCREEN_DPI)
1431 pSetProcessDPIAware();
1432 }
1433 FreeLibrary(module);
1434 }
1435 }
1436
1437 /* initial windows system item position where we will insert new menu item
1438 * after default 5 items (restore, move, size, minimize, maximize)
1439 * gets incremented each time wf_append_item_to_system_menu is called
1440 * or maybe could use GetMenuItemCount() to get initial item count ? */
1441 wfc->systemMenuInsertPosition = 6;
1442
1443 wfc->hInstance = hInstance;
1444 wfc->cursor = LoadCursor(NULL, IDC_ARROW);
1445 wfc->icon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_ICON1));
1446 wfc->wndClassName = _tcsdup(_T("FreeRDP"));
1447 wfc->wndClass.cbSize = sizeof(WNDCLASSEX);
1448 wfc->wndClass.style = CS_HREDRAW | CS_VREDRAW;
1449 wfc->wndClass.lpfnWndProc = wf_event_proc;
1450 wfc->wndClass.cbClsExtra = 0;
1451 wfc->wndClass.cbWndExtra = 0;
1452 wfc->wndClass.hCursor = NULL;
1453 wfc->wndClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
1454 wfc->wndClass.lpszMenuName = NULL;
1455 wfc->wndClass.lpszClassName = wfc->wndClassName;
1456 wfc->wndClass.hInstance = hInstance;
1457 wfc->wndClass.hIcon = wfc->icon;
1458 wfc->wndClass.hIconSm = wfc->icon;
1459 RegisterClassEx(&(wfc->wndClass));
1460 wfc->keyboardThread =
1461 CreateThread(NULL, 0, wf_keyboard_thread, (void*)wfc, 0, &wfc->keyboardThreadId);
1462
1463 if (!wfc->keyboardThread)
1464 return -1;
1465
1466 wfc->common.thread =
1467 CreateThread(NULL, 0, wf_client_thread, (void*)instance, 0, &wfc->mainThreadId);
1468
1469 if (!wfc->common.thread)
1470 return -1;
1471
1472 return 0;
1473}
1474
1475static int wfreerdp_client_stop(rdpContext* context)
1476{
1477 int rc;
1478 wfContext* wfc = (wfContext*)context;
1479
1480 WINPR_ASSERT(wfc);
1481 PostThreadMessage(wfc->mainThreadId, WM_QUIT, 0, 0);
1482 rc = freerdp_client_common_stop(context);
1483 wfc->mainThreadId = 0;
1484
1485 if (wfc->keyboardThread)
1486 {
1487 PostThreadMessage(wfc->keyboardThreadId, WM_QUIT, 0, 0);
1488 (void)WaitForSingleObject(wfc->keyboardThread, INFINITE);
1489 (void)CloseHandle(wfc->keyboardThread);
1490 wfc->keyboardThread = NULL;
1491 wfc->keyboardThreadId = 0;
1492 }
1493
1494 return 0;
1495}
1496
1497int RdpClientEntry(RDP_CLIENT_ENTRY_POINTS* pEntryPoints)
1498{
1499 pEntryPoints->Version = 1;
1500 pEntryPoints->Size = sizeof(RDP_CLIENT_ENTRY_POINTS_V1);
1501 pEntryPoints->GlobalInit = wfreerdp_client_global_init;
1502 pEntryPoints->GlobalUninit = wfreerdp_client_global_uninit;
1503 pEntryPoints->ContextSize = sizeof(wfContext);
1504 pEntryPoints->ClientNew = wfreerdp_client_new;
1505 pEntryPoints->ClientFree = wfreerdp_client_free;
1506 pEntryPoints->ClientStart = wfreerdp_client_start;
1507 pEntryPoints->ClientStop = wfreerdp_client_stop;
1508 return 0;
1509}
FREERDP_API UINT32 freerdp_settings_get_uint32(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id)
Returns a UINT32 settings value.
FREERDP_API BOOL freerdp_settings_get_bool(const rdpSettings *settings, FreeRDP_Settings_Keys_Bool id)
Returns a boolean settings value.
FREERDP_API UINT64 freerdp_settings_get_uint64(const rdpSettings *settings, FreeRDP_Settings_Keys_UInt64 id)
Returns a UINT64 settings value.
FREERDP_API BOOL freerdp_settings_set_uint32(rdpSettings *settings, FreeRDP_Settings_Keys_UInt32 id, UINT32 param)
Sets a UINT32 settings value.
FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.
FREERDP_API BOOL freerdp_settings_set_bool(rdpSettings *settings, FreeRDP_Settings_Keys_Bool id, BOOL param)
Sets a BOOL settings value.