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