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