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