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