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