diff --git a/CMakeLists.txt b/CMakeLists.txt index a0687025ae42..22c0347c4f05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,7 +85,7 @@ if ($ENV{BUILD_NUMBER}) endif() set(WITH_LIBRARY_VERSIONING "ON") -set(RAW_VERSION_STRING "2.4.1") +set(RAW_VERSION_STRING "2.5.0") if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag") file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) elseif(USE_VERSION_FROM_GIT_TAG) diff --git a/ChangeLog b/ChangeLog index 146e00bf771a..bf4e50034e10 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,18 +1,24 @@ -# 20xx-xx-xx Version 2.x.x +# 2022-01-12 Version 2.5.0 Noteworthy changes: * Fixed smartcard login in case a redirection occurs the pin was lost +* Backported windows client drawing fixes +* Backported improved macOS keyboard layout detection +* Backported TcpConnectTimeout * Backported LibreSSL compatibility patches * Backported signal handler backtrace * Backported OpenSSL 3.0 support Fixed issues: +* Backport #7539: Wayland client clipboard issues +* Backport #7509: Various fixes regarding registry emulation, addin loader + and updated locale detection * Backport #7466: Android android_register_pointer missing initialization Important notes: For a complete and detailed change log since the last release run: -git log 2.4.1..2.x.x +git log 2.4.1..2.5.0 # 2021-10-20 Version 2.4.1 diff --git a/channels/audin/client/opensles/audin_opensl_es.c b/channels/audin/client/opensles/audin_opensl_es.c index 4e3efdeb2f97..87393ad36397 100644 --- a/channels/audin/client/opensles/audin_opensl_es.c +++ b/channels/audin/client/opensles/audin_opensl_es.c @@ -248,7 +248,7 @@ static UINT audin_opensles_parse_addin_args(AudinOpenSLESDevice* device, ADDIN_A { UINT status; DWORD flags; - COMMAND_LINE_ARGUMENT_A* arg; + const COMMAND_LINE_ARGUMENT_A* arg; AudinOpenSLESDevice* opensles = (AudinOpenSLESDevice*)device; COMMAND_LINE_ARGUMENT_A audin_opensles_args[] = { { "dev", COMMAND_LINE_VALUE_REQUIRED, "", NULL, NULL, -1, NULL, diff --git a/channels/client/addin.c b/channels/client/addin.c index cd6f90e991c3..db32ff83cc28 100644 --- a/channels/client/addin.c +++ b/channels/client/addin.c @@ -231,10 +231,8 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS do { - char* p[5]; - FREERDP_ADDIN* pAddin; - nDashes = 0; - pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN)); + BOOL used = FALSE; + FREERDP_ADDIN* pAddin = (FREERDP_ADDIN*)calloc(1, sizeof(FREERDP_ADDIN)); if (!pAddin) { @@ -242,57 +240,116 @@ static FREERDP_ADDIN** freerdp_channels_list_dynamic_addins(LPCSTR pszName, LPCS goto error_out; } + nDashes = 0; for (index = 0; FindData.cFileName[index]; index++) nDashes += (FindData.cFileName[index] == '-') ? 1 : 0; if (nDashes == 1) { + size_t len; + char* p[2] = { 0 }; /* -client. */ p[0] = FindData.cFileName; p[1] = strchr(p[0], '-') + 1; - strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1); + + len = p[1] - p[0]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1)); + pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_NAME; ppAddins[nAddins++] = pAddin; + + used = TRUE; } else if (nDashes == 2) { + size_t len; + char* p[4] = { 0 }; /* -client-. */ p[0] = FindData.cFileName; p[1] = strchr(p[0], '-') + 1; p[2] = strchr(p[1], '-') + 1; p[3] = strchr(p[2], '.') + 1; - strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1); - strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1); + + len = p[1] - p[0]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1)); + + len = p[3] - p[2]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1)); + pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_NAME; pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM; ppAddins[nAddins++] = pAddin; + + used = TRUE; } else if (nDashes == 3) { + size_t len; + char* p[5] = { 0 }; /* -client--. */ p[0] = FindData.cFileName; p[1] = strchr(p[0], '-') + 1; p[2] = strchr(p[1], '-') + 1; p[3] = strchr(p[2], '-') + 1; p[4] = strchr(p[3], '.') + 1; - strncpy(pAddin->cName, p[0], (p[1] - p[0]) - 1); - strncpy(pAddin->cSubsystem, p[2], (p[3] - p[2]) - 1); - strncpy(pAddin->cType, p[3], (p[4] - p[3]) - 1); + + len = p[1] - p[0]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cName, p[0], MIN(ARRAYSIZE(pAddin->cName), len - 1)); + + len = p[3] - p[2]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cSubsystem, p[2], MIN(ARRAYSIZE(pAddin->cSubsystem), len - 1)); + + len = p[4] - p[3]; + if (len < 1) + { + WLog_WARN(TAG, "Skipping file '%s', invalid format", FindData.cFileName); + goto skip; + } + strncpy(pAddin->cType, p[3], MIN(ARRAYSIZE(pAddin->cType), len - 1)); + pAddin->dwFlags = FREERDP_ADDIN_CLIENT; pAddin->dwFlags |= FREERDP_ADDIN_DYNAMIC; pAddin->dwFlags |= FREERDP_ADDIN_NAME; pAddin->dwFlags |= FREERDP_ADDIN_SUBSYSTEM; pAddin->dwFlags |= FREERDP_ADDIN_TYPE; ppAddins[nAddins++] = pAddin; + + used = TRUE; } - else - { + + skip: + if (!used) free(pAddin); - } + } while (FindNextFileA(hFind, &FindData)); FindClose(hFind); diff --git a/client/Android/Studio/build.gradle b/client/Android/Studio/build.gradle index 2d0abd32d725..cb52a54d923f 100644 --- a/client/Android/Studio/build.gradle +++ b/client/Android/Studio/build.gradle @@ -29,7 +29,7 @@ def getVersionName = { -> ext { versionName = properties.get('VERSION_NAME', getVersionName()) - versionCode = properties.get('VERSION_CODE', 25) + versionCode = properties.get('VERSION_CODE', 26) compileApi = properties.get('COMPILE_API', 30) targetApi = properties.get('TARGET_API', 30) minApi = properties.get('MIN_API', 21) diff --git a/client/Android/android_freerdp.c b/client/Android/android_freerdp.c index 78205c7519c0..2cdc020c2f67 100644 --- a/client/Android/android_freerdp.c +++ b/client/Android/android_freerdp.c @@ -56,7 +56,7 @@ #define TAG CLIENT_TAG("android") /* Defines the JNI version supported by this library. */ -#define FREERDP_JNI_VERSION "2.4.1" +#define FREERDP_JNI_VERSION "2.5.0" static void android_OnChannelConnectedEventHandler(void* context, ChannelConnectedEventArgs* e) { diff --git a/client/Wayland/wlf_cliprdr.c b/client/Wayland/wlf_cliprdr.c index 0762cfa3deaf..0f0195d7cd52 100644 --- a/client/Wayland/wlf_cliprdr.c +++ b/client/Wayland/wlf_cliprdr.c @@ -80,6 +80,7 @@ struct wlf_clipboard FILE* responseFile; UINT32 responseFormat; const char* responseMime; + CRITICAL_SECTION lock; }; static BOOL wlf_mime_is_text(const char* mime) @@ -275,7 +276,7 @@ BOOL wlf_cliprdr_handle_event(wfClipboard* clipboard, const UwacClipboardEvent* return TRUE; case UWAC_EVENT_CLIPBOARD_OFFER: - WLog_Print(clipboard->log, WLOG_INFO, "client announces mime %s", event->mime); + WLog_Print(clipboard->log, WLOG_DEBUG, "client announces mime %s", event->mime); wlf_cliprdr_add_client_format(clipboard, event->mime); return TRUE; @@ -387,6 +388,8 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* wfClipboard* clipboard = (wfClipboard*)context; size_t x; WINPR_UNUSED(seat); + + EnterCriticalSection(&clipboard->lock); clipboard->responseMime = NULL; for (x = 0; x < ARRAYSIZE(mime_html); x++) @@ -427,6 +430,8 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* if (clipboard->responseMime != NULL) { + if (clipboard->responseFile != NULL) + fclose(clipboard->responseFile); clipboard->responseFile = fdopen(fd, "w"); if (clipboard->responseFile) @@ -436,6 +441,7 @@ static void wlf_cliprdr_transfer_data(UwacSeat* seat, void* context, const char* "failed to open clipboard file descriptor for MIME %s", clipboard->responseMime); } + LeaveCriticalSection(&clipboard->lock); } static void wlf_cliprdr_cancel_data(UwacSeat* seat, void* context) @@ -673,6 +679,8 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context, const WCHAR* wdata = (const WCHAR*)formatDataResponse->requestedFormatData; wfClipboard* clipboard = (wfClipboard*)context->custom; + EnterCriticalSection(&clipboard->lock); + if (size > INT_MAX * sizeof(WCHAR)) return ERROR_INTERNAL_ERROR; @@ -694,10 +702,16 @@ wlf_cliprdr_server_format_data_response(CliprdrClientContext* context, break; } - fwrite(data, 1, size, clipboard->responseFile); - fclose(clipboard->responseFile); + if (clipboard->responseFile) + { + fwrite(data, 1, size, clipboard->responseFile); + fclose(clipboard->responseFile); + clipboard->responseFile = NULL; + } rc = CHANNEL_RC_OK; free(cdata); + + LeaveCriticalSection(&clipboard->lock); return rc; } @@ -829,17 +843,24 @@ static UINT wlf_cliprdr_clipboard_file_range_failure(wClipboardDelegate* delegat wfClipboard* wlf_clipboard_new(wlfContext* wfc) { rdpChannels* channels; - wfClipboard* clipboard; + wfClipboard* clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard)); - if (!(clipboard = (wfClipboard*)calloc(1, sizeof(wfClipboard)))) - return NULL; + if (!clipboard) + goto fail; + InitializeCriticalSection(&clipboard->lock); clipboard->wfc = wfc; channels = wfc->context.channels; clipboard->log = WLog_Get(TAG); clipboard->channels = channels; clipboard->system = ClipboardCreate(); + if (!clipboard->system) + goto fail; + clipboard->delegate = ClipboardGetDelegate(clipboard->system); + if (!clipboard->delegate) + goto fail; + clipboard->delegate->custom = clipboard; /* TODO: set up a filesystem base path for local URI */ /* clipboard->delegate->basePath = "file:///tmp/foo/bar/gaga"; */ @@ -848,6 +869,10 @@ wfClipboard* wlf_clipboard_new(wlfContext* wfc) clipboard->delegate->ClipboardFileRangeSuccess = wlf_cliprdr_clipboard_file_range_success; clipboard->delegate->ClipboardFileRangeFailure = wlf_cliprdr_clipboard_file_range_failure; return clipboard; + +fail: + wlf_clipboard_free(clipboard); + return NULL; } void wlf_clipboard_free(wfClipboard* clipboard) @@ -858,6 +883,12 @@ void wlf_clipboard_free(wfClipboard* clipboard) wlf_cliprdr_free_server_formats(clipboard); wlf_cliprdr_free_client_formats(clipboard); ClipboardDestroy(clipboard->system); + + EnterCriticalSection(&clipboard->lock); + if (clipboard->responseFile) + fclose(clipboard->responseFile); + LeaveCriticalSection(&clipboard->lock); + DeleteCriticalSection(&clipboard->lock); free(clipboard); } diff --git a/client/Wayland/wlfreerdp.c b/client/Wayland/wlfreerdp.c index aca7342118c8..ac8e772306d3 100644 --- a/client/Wayland/wlfreerdp.c +++ b/client/Wayland/wlfreerdp.c @@ -331,12 +331,15 @@ static BOOL handle_uwac_events(freerdp* instance, UwacDisplay* display) break; case UWAC_EVENT_FRAME_DONE: + { + UwacReturnCode r; EnterCriticalSection(&context->critical); - rc = UwacWindowSubmitBuffer(context->window, false); + r = UwacWindowSubmitBuffer(context->window, false); LeaveCriticalSection(&context->critical); - if (rc != UWAC_SUCCESS) + if (r != UWAC_SUCCESS) return FALSE; - break; + } + break; case UWAC_EVENT_POINTER_ENTER: if (!wlf_handle_pointer_enter(instance, &event.mouse_enter_leave)) diff --git a/client/Windows/wf_client.c b/client/Windows/wf_client.c index f61f0c8d2517..5ee059402c14 100644 --- a/client/Windows/wf_client.c +++ b/client/Windows/wf_client.c @@ -118,6 +118,18 @@ static BOOL wf_end_paint(rdpContext* context) updateRect.top = extents->top; updateRect.right = extents->right; updateRect.bottom = extents->bottom; + + if (wfc->xScrollVisible) + { + updateRect.left -= MIN(updateRect.left, wfc->xCurrentScroll); + updateRect.right -= MIN(updateRect.right, wfc->xCurrentScroll); + } + if (wfc->yScrollVisible) + { + updateRect.top -= MIN(updateRect.top, wfc->yCurrentScroll); + updateRect.bottom -= MIN(updateRect.bottom, wfc->yCurrentScroll); + } + InvalidateRect(wfc->hwnd, &updateRect, FALSE); if (wfc->rail) diff --git a/include/freerdp/locale/locale.h b/include/freerdp/locale/locale.h index 54ab2c66033b..6647bb22c28f 100644 --- a/include/freerdp/locale/locale.h +++ b/include/freerdp/locale/locale.h @@ -62,6 +62,7 @@ #define BRETON 0x047E #define BULGARIAN 0x0402 #define CATALAN 0x0403 +#define CHEROKEE 0x045C #define CHINESE_TAIWAN 0x0404 #define CHINESE_PRC 0x0804 #define CHINESE_HONG_KONG 0x0C04 @@ -113,12 +114,14 @@ #define GREEK 0x0408 #define GREENLANDIC 0x046F #define GUJARATI 0x0447 +#define HAWAIIAN 0x0475 #define HEBREW 0x040D #define HINDI 0x0439 #define HUNGARIAN 0x040E #define ICELANDIC 0x040F #define IGBO 0x0470 #define INDONESIAN 0x0421 +#define INUKTITUT 0x045D #define IRISH 0x083C #define ITALIAN_STANDARD 0x0410 #define ITALIAN_SWISS 0x0810 @@ -146,6 +149,7 @@ #define MARATHI 0x044E #define MOHAWK 0x047C #define MONGOLIAN 0x0450 +#define MYANMAR 0x0455 #define NEPALI 0x0461 #define NORWEGIAN_BOKMAL 0x0414 #define NORWEGIAN_NYNORSK 0x0814 diff --git a/include/freerdp/settings.h b/include/freerdp/settings.h index 26adb624e292..3b7a13896c36 100644 --- a/include/freerdp/settings.h +++ b/include/freerdp/settings.h @@ -894,6 +894,7 @@ typedef struct _RDPDR_PARALLEL RDPDR_PARALLEL; #define FreeRDP_TcpKeepAliveDelay (5192) #define FreeRDP_TcpKeepAliveInterval (5193) #define FreeRDP_TcpAckTimeout (5194) +#define FreeRDP_TcpConnectTimeout (5197) /** * FreeRDP Settings Data Structure @@ -1342,20 +1343,20 @@ struct rdp_settings /* Input Capabilities */ ALIGN64 char* KeyboardRemappingList; /* 2622 */ - ALIGN64 UINT32 KeyboardCodePage; /* 2623 */ - ALIGN64 UINT32 KeyboardLayout; /* 2624 */ - ALIGN64 UINT32 KeyboardType; /* 2625 */ - ALIGN64 UINT32 KeyboardSubType; /* 2626 */ - ALIGN64 UINT32 KeyboardFunctionKey; /* 2627 */ - ALIGN64 char* ImeFileName; /* 2628 */ - ALIGN64 BOOL UnicodeInput; /* 2629 */ - ALIGN64 BOOL FastPathInput; /* 2630 */ - ALIGN64 BOOL MultiTouchInput; /* 2631 */ - ALIGN64 BOOL MultiTouchGestures; /* 2632 */ - ALIGN64 UINT32 KeyboardHook; /* 2633 */ - ALIGN64 BOOL HasHorizontalWheel; /* 2634 */ - ALIGN64 BOOL HasExtendedMouseEvent; /* 2635 */ - UINT64 padding2688[2688 - 2636]; /* 2636 */ + ALIGN64 UINT32 KeyboardCodePage; /* 2623 */ + ALIGN64 UINT32 KeyboardLayout; /* 2624 */ + ALIGN64 UINT32 KeyboardType; /* 2625 */ + ALIGN64 UINT32 KeyboardSubType; /* 2626 */ + ALIGN64 UINT32 KeyboardFunctionKey; /* 2627 */ + ALIGN64 char* ImeFileName; /* 2628 */ + ALIGN64 BOOL UnicodeInput; /* 2629 */ + ALIGN64 BOOL FastPathInput; /* 2630 */ + ALIGN64 BOOL MultiTouchInput; /* 2631 */ + ALIGN64 BOOL MultiTouchGestures; /* 2632 */ + ALIGN64 UINT32 KeyboardHook; /* 2633 */ + ALIGN64 BOOL HasHorizontalWheel; /* 2634 */ + ALIGN64 BOOL HasExtendedMouseEvent; /* 2635 */ + UINT64 padding2688[2688 - 2636]; /* 2636 */ /* Brush Capabilities */ ALIGN64 UINT32 BrushSupportLevel; /* 2688 */ @@ -1545,7 +1546,9 @@ struct rdp_settings ALIGN64 UINT32 TcpKeepAliveDelay; /* 5192 */ ALIGN64 UINT32 TcpKeepAliveInterval; /* 5193 */ ALIGN64 UINT32 TcpAckTimeout; /* 5194 */ - UINT64 padding5312[5312 - 5195]; /* 5195 */ + UINT64 padding5197[5197 - 5195]; /* 5195 */ + ALIGN64 UINT32 TcpConnectTimeout; /* 5197 */ + UINT64 padding5312[5312 - 5198]; /* 5198 */ /** * WARNING: End of ABI stable zone! diff --git a/include/freerdp/update.h b/include/freerdp/update.h index ec5336a26069..71cb15580e72 100644 --- a/include/freerdp/update.h +++ b/include/freerdp/update.h @@ -254,7 +254,6 @@ struct rdp_update wStream* us; UINT16 numberOrders; - size_t offsetOrders; /* the offset to patch numberOrders in the stream */ BOOL combineUpdates; rdpBounds currentBounds; rdpBounds previousBounds; @@ -264,6 +263,7 @@ struct rdp_update * fills BITMAP_DATA struct members: flags, cbCompMainBodySize and cbCompFirstRowSize. */ BOOL autoCalculateBitmapData; + size_t offsetOrders; /* the offset to patch numberOrders in the stream */ }; #endif /* FREERDP_UPDATE_H */ diff --git a/libfreerdp/common/settings_getters.c b/libfreerdp/common/settings_getters.c index 2a34bb80fca9..a3ce8e9611e5 100644 --- a/libfreerdp/common/settings_getters.c +++ b/libfreerdp/common/settings_getters.c @@ -1570,6 +1570,9 @@ UINT32 freerdp_settings_get_uint32(const rdpSettings* settings, size_t id) case FreeRDP_TcpAckTimeout: return settings->TcpAckTimeout; + case FreeRDP_TcpConnectTimeout: + return settings->TcpConnectTimeout; + case FreeRDP_TcpKeepAliveDelay: return settings->TcpKeepAliveDelay; @@ -2021,6 +2024,10 @@ BOOL freerdp_settings_set_uint32(rdpSettings* settings, size_t id, UINT32 val) settings->TcpAckTimeout = val; break; + case FreeRDP_TcpConnectTimeout: + settings->TcpConnectTimeout = val; + break; + case FreeRDP_TcpKeepAliveDelay: settings->TcpKeepAliveDelay = val; break; diff --git a/libfreerdp/common/settings_str.c b/libfreerdp/common/settings_str.c index b7b8def722e9..9a545f25d2d0 100644 --- a/libfreerdp/common/settings_str.c +++ b/libfreerdp/common/settings_str.c @@ -285,6 +285,7 @@ static const struct settings_str_entry settings_map[] = { { FreeRDP_StaticChannelCount, 3, "FreeRDP_StaticChannelCount" }, { FreeRDP_TargetNetAddressCount, 3, "FreeRDP_TargetNetAddressCount" }, { FreeRDP_TcpAckTimeout, 3, "FreeRDP_TcpAckTimeout" }, + { FreeRDP_TcpConnectTimeout, 3, "FreeRDP_TcpConnectTimeout" }, { FreeRDP_TcpKeepAliveDelay, 3, "FreeRDP_TcpKeepAliveDelay" }, { FreeRDP_TcpKeepAliveInterval, 3, "FreeRDP_TcpKeepAliveInterval" }, { FreeRDP_TcpKeepAliveRetries, 3, "FreeRDP_TcpKeepAliveRetries" }, diff --git a/libfreerdp/core/nego.c b/libfreerdp/core/nego.c index 8357ff9abe53..ac4e93f7c6ab 100644 --- a/libfreerdp/core/nego.c +++ b/libfreerdp/core/nego.c @@ -277,6 +277,9 @@ static BOOL nego_tcp_connect(rdpNego* nego) { if (!nego->TcpConnected) { + const UINT32 TcpConnectTimeout = freerdp_settings_get_uint32( + nego->transport->context->settings, FreeRDP_TcpConnectTimeout); + if (nego->GatewayEnabled) { if (nego->GatewayBypassLocal) @@ -286,20 +289,21 @@ static BOOL nego_tcp_connect(rdpNego* nego) "Detecting if host can be reached locally. - This might take some time."); WLog_INFO(TAG, "To disable auto detection use /gateway-usage-method:direct"); transport_set_gateway_enabled(nego->transport, FALSE); - nego->TcpConnected = - transport_connect(nego->transport, nego->hostname, nego->port, 1); + nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, + TcpConnectTimeout); } if (!nego->TcpConnected) { transport_set_gateway_enabled(nego->transport, TRUE); - nego->TcpConnected = - transport_connect(nego->transport, nego->hostname, nego->port, 15); + nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, + TcpConnectTimeout); } } else { - nego->TcpConnected = transport_connect(nego->transport, nego->hostname, nego->port, 15); + nego->TcpConnected = + transport_connect(nego->transport, nego->hostname, nego->port, TcpConnectTimeout); } } diff --git a/libfreerdp/core/settings.c b/libfreerdp/core/settings.c index eb425dd958cd..d0c2b56f3303 100644 --- a/libfreerdp/core/settings.c +++ b/libfreerdp/core/settings.c @@ -547,11 +547,13 @@ rdpSettings* freerdp_settings_new(DWORD flags) if (!settings->DynamicChannelArray) goto out_fail; - settings->TcpKeepAlive = TRUE; - settings->TcpKeepAliveRetries = 3; - settings->TcpKeepAliveDelay = 5; - settings->TcpKeepAliveInterval = 2; - settings->TcpAckTimeout = 9000; + if (!freerdp_settings_set_bool(settings, FreeRDP_TcpKeepAlive, TRUE) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveRetries, 3) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveDelay, 5) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpKeepAliveInterval, 2) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpAckTimeout, 9000) || + !freerdp_settings_set_uint32(settings, FreeRDP_TcpConnectTimeout, 15000)) + goto out_fail; if (!settings->ServerMode) { diff --git a/libfreerdp/core/tcp.c b/libfreerdp/core/tcp.c index ef5f7b1939f3..60b4211b59cb 100644 --- a/libfreerdp/core/tcp.c +++ b/libfreerdp/core/tcp.c @@ -801,14 +801,14 @@ static BOOL freerdp_tcp_is_hostname_resolvable(rdpContext* context, const char* } static BOOL freerdp_tcp_connect_timeout(rdpContext* context, int sockfd, struct sockaddr* addr, - socklen_t addrlen, int timeout) + socklen_t addrlen, UINT32 timeout) { BOOL rc = FALSE; HANDLE handles[2]; int status = 0; int count = 0; u_long arg = 0; - DWORD tout = (timeout > 0) ? (DWORD)timeout * 1000U : INFINITE; + DWORD tout = (timeout > 0) ? timeout : INFINITE; handles[count] = CreateEvent(NULL, TRUE, FALSE, NULL); @@ -894,7 +894,7 @@ static void peer_free(t_peer* peer) } static int freerdp_tcp_connect_multi(rdpContext* context, char** hostnames, UINT32* ports, - UINT32 count, int port, int timeout) + UINT32 count, UINT16 port, UINT32 timeout) { UINT32 index; UINT32 sindex = count; diff --git a/libfreerdp/core/test/settings_property_lists.h b/libfreerdp/core/test/settings_property_lists.h index ff35a7ec8801..db8e5ed8e290 100644 --- a/libfreerdp/core/test/settings_property_lists.h +++ b/libfreerdp/core/test/settings_property_lists.h @@ -282,6 +282,7 @@ static const size_t uint32_list_indices[] = { FreeRDP_StaticChannelCount, FreeRDP_TargetNetAddressCount, FreeRDP_TcpAckTimeout, + FreeRDP_TcpConnectTimeout, FreeRDP_TcpKeepAliveDelay, FreeRDP_TcpKeepAliveInterval, FreeRDP_TcpKeepAliveRetries, diff --git a/libfreerdp/locale/CMakeLists.txt b/libfreerdp/locale/CMakeLists.txt index 1a300f22f2ab..9c74e20dcd9c 100644 --- a/libfreerdp/locale/CMakeLists.txt +++ b/libfreerdp/locale/CMakeLists.txt @@ -38,10 +38,20 @@ set(${MODULE_PREFIX}_SUN_SRCS keyboard_sun.c keyboard_sun.h) +set(${MODULE_PREFIX}_APPLE_SRCS + keyboard_apple.c + keyboard_apple.h) + if(CMAKE_SYSTEM_NAME MATCHES Solaris) set(WITH_SUN true) endif() +if(APPLE AND (NOT IOS)) + set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} ${${MODULE_PREFIX}_APPLE_SRCS}) + find_library(CARBON Carbon) + freerdp_library_add(${CARBON}) +endif() + if(WITH_X11) freerdp_definition_add(-DWITH_X11) freerdp_include_directory_add(${X11_INCLUDE_DIRS}) diff --git a/libfreerdp/locale/keyboard.c b/libfreerdp/locale/keyboard.c index eca0d635cd8c..4c5ae03e49e9 100644 --- a/libfreerdp/locale/keyboard.c +++ b/libfreerdp/locale/keyboard.c @@ -32,6 +32,10 @@ #include "liblocale.h" +#if defined(__MACOSX__) +#include "keyboard_apple.h" +#endif + #ifdef WITH_X11 #include "keyboard_x11.h" @@ -64,6 +68,11 @@ int freerdp_detect_keyboard(DWORD* keyboardLayoutId) *keyboardLayoutId = ((DWORD)GetKeyboardLayout(0) >> 16) & 0x0000FFFF; #endif +#if defined(__MACOSX__) + if (*keyboardLayoutId == 0) + freerdp_detect_keyboard_layout_from_cf(keyboardLayoutId); +#endif + #ifdef WITH_X11 if (*keyboardLayoutId == 0) freerdp_detect_keyboard_layout_from_xkb(keyboardLayoutId); @@ -148,7 +157,7 @@ DWORD freerdp_keyboard_init(DWORD keyboardLayoutId) for (keycode = 0; keycode < ARRAYSIZE(VIRTUAL_SCANCODE_TO_X11_KEYCODE); keycode++) { VIRTUAL_SCANCODE_TO_X11_KEYCODE - [RDP_SCANCODE_CODE(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode])] + [RDP_SCANCODE_CODE(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode])] [RDP_SCANCODE_EXTENDED(X11_KEYCODE_TO_VIRTUAL_SCANCODE[keycode]) ? 1 : 0] = keycode; } diff --git a/libfreerdp/locale/keyboard_apple.c b/libfreerdp/locale/keyboard_apple.c new file mode 100644 index 000000000000..d0d59216e52e --- /dev/null +++ b/libfreerdp/locale/keyboard_apple.c @@ -0,0 +1,244 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Apple Core Foundation Keyboard Mapping + * + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include + +#include "liblocale.h" + +#include +#include + +#include "keyboard_apple.h" + +struct KEYBOARD_LAYOUT_MAPPING_ +{ + const char* inputSourceId; /* Apple input source id (com.apple.keylayout or inputmethod) */ + DWORD code; /* mapped rdp keyboard layout id */ +}; +typedef struct KEYBOARD_LAYOUT_MAPPING_ KEYBOARD_LAYOUT_MAPPING; + +static const KEYBOARD_LAYOUT_MAPPING KEYBOARD_MAPPING_TABLE[] = { + { "com.apple.inputmethod.Kotoeri.Japanese", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.FullWidthRoman", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.HalfWidthKana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Japanese.Katakana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Katakana", JAPANESE }, + { "com.apple.inputmethod.Kotoeri.Roman", JAPANESE }, + { "com.apple.inputmethod.kotoeri.Ainu", JAPANESE }, + { "com.apple.keylayout.2SetHangul", KOREAN }, + { "com.apple.keylayout.390Hangul", KOREAN }, + { "com.apple.keylayout.3SetHangul", KOREAN }, + { "com.apple.keylayout.AfghanDari", DARI }, + { "com.apple.keylayout.AfghanPashto", PASHTO }, + { "com.apple.keylayout.AfghanUzbek", UZBEK_LATIN }, + { "com.apple.keylayout.Arabic", ARABIC_EGYPT }, + { "com.apple.keylayout.Arabic-QWERTY", ARABIC_EGYPT }, + { "com.apple.keylayout.ArabicPC", ARABIC_EGYPT }, + { "com.apple.keylayout.Armenian-HMQWERTY", ARMENIAN }, + { "com.apple.keylayout.Armenian-WesternQWERTY", ARMENIAN }, + { "com.apple.keylayout.Australian", ENGLISH_AUSTRALIAN }, + { "com.apple.keylayout.Austrian", GERMAN_AUSTRIAN }, + { "com.apple.keylayout.Azeri", AZERI_LATIN }, + { "com.apple.keylayout.Bangla", BENGALI_INDIA }, + { "com.apple.keylayout.Bangla-QWERTY", BENGALI_INDIA }, + { "com.apple.keylayout.Belgian", DUTCH_BELGIAN }, + { "com.apple.keylayout.Brazilian", PORTUGUESE_BRAZILIAN }, + { "com.apple.keylayout.British", ENGLISH_UNITED_KINGDOM }, + { "com.apple.keylayout.British-PC", ENGLISH_UNITED_KINGDOM }, + { "com.apple.keylayout.Bulgarian", BULGARIAN }, + { "com.apple.keylayout.Bulgarian-Phonetic", BULGARIAN }, + { "com.apple.keylayout.Byelorussian", BELARUSIAN }, + { "com.apple.keylayout.Canadian", ENGLISH_CANADIAN }, + { "com.apple.keylayout.Canadian-CSA", FRENCH_CANADIAN }, + { "com.apple.keylayout.CangjieKeyboard", CHINESE_TAIWAN }, + { "com.apple.keylayout.Cherokee-Nation", CHEROKEE }, + { "com.apple.keylayout.Cherokee-QWERTY", CHEROKEE }, + { "com.apple.keylayout.Colemak", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Croatian", CROATIAN }, + { "com.apple.keylayout.Croatian-PC", CROATIAN }, + { "com.apple.keylayout.Czech", CZECH }, + { "com.apple.keylayout.Czech-QWERTY", CZECH }, + { "com.apple.keylayout.DVORAK-QWERTYCMD", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Danish", DANISH }, + { "com.apple.keylayout.Devanagari", HINDI }, + { "com.apple.keylayout.Devanagari-QWERTY", HINDI }, + { "com.apple.keylayout.Dutch", DUTCH_STANDARD }, + { "com.apple.keylayout.Dvorak", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Dvorak-Left", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Dvorak-Right", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Estonian", ESTONIAN }, + { "com.apple.keylayout.Faroese", FAEROESE }, + { "com.apple.keylayout.Finnish", FINNISH }, + { "com.apple.keylayout.FinnishExtended", FINNISH }, + { "com.apple.keylayout.FinnishSami-PC", FINNISH }, + { "com.apple.keylayout.French", FRENCH_STANDARD }, + { "com.apple.keylayout.French-PC", FRENCH_STANDARD }, + { "com.apple.keylayout.French-numerical", FRENCH_STANDARD }, + { "com.apple.keylayout.GJCRomaja", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Georgian-QWERTY", GEORGIAN }, + { "com.apple.keylayout.German", GERMAN_STANDARD }, + { "com.apple.keylayout.Greek", GREEK }, + { "com.apple.keylayout.GreekPolytonic", GREEK }, + { "com.apple.keylayout.Gujarati", GUJARATI }, + { "com.apple.keylayout.Gujarati-QWERTY", GUJARATI }, + { "com.apple.keylayout.Gurmukhi", PUNJABI }, + { "com.apple.keylayout.Gurmukhi-QWERTY", PUNJABI }, + { "com.apple.keylayout.HNCRomaja", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Hawaiian", HAWAIIAN }, + { "com.apple.keylayout.Hebrew", HEBREW }, + { "com.apple.keylayout.Hebrew-PC", HEBREW }, + { "com.apple.keylayout.Hebrew-QWERTY", HEBREW }, + { "com.apple.keylayout.Hungarian", HUNGARIAN }, + { "com.apple.keylayout.Hungarian-QWERTY", HUNGARIAN }, + { "com.apple.keylayout.Icelandic", ICELANDIC }, + { "com.apple.keylayout.Inuktitut-Nunavut", INUKTITUT }, + { "com.apple.keylayout.Inuktitut-Nutaaq", INUKTITUT }, + { "com.apple.keylayout.Inuktitut-QWERTY", INUKTITUT }, + { "com.apple.keylayout.InuttitutNunavik", INUKTITUT }, + { "com.apple.keylayout.Irish", ENGLISH_IRELAND }, + { "com.apple.keylayout.IrishExtended", IRISH }, + { "com.apple.keylayout.Italian", ITALIAN_STANDARD }, + { "com.apple.keylayout.Italian-Pro", ITALIAN_STANDARD }, + { "com.apple.keylayout.Jawi-QWERTY", ARABIC_SAUDI_ARABIA }, + { "com.apple.keylayout.Kannada", KANNADA }, + { "com.apple.keylayout.Kannada-QWERTY", KANNADA }, + { "com.apple.keylayout.Kazakh", KAZAKH }, + { "com.apple.keylayout.Khmer", KHMER }, + { "com.apple.keylayout.Latvian", LATVIAN }, + { "com.apple.keylayout.Lithuanian", LITHUANIAN }, + { "com.apple.keylayout.Macedonian", MACEDONIAN }, + { "com.apple.keylayout.Malayalam", MALAYALAM }, + { "com.apple.keylayout.Malayalam-QWERTY", MALAYALAM }, + { "com.apple.keylayout.Maltese", MALTESE }, + { "com.apple.keylayout.Maori", MAORI }, + { "com.apple.keylayout.Myanmar-QWERTY", MYANMAR }, + { "com.apple.keylayout.Nepali", NEPALI }, + { "com.apple.keylayout.NorthernSami", SAMI_NORTHERN_NORWAY }, + { "com.apple.keylayout.Norwegian", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.NorwegianExtended", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.NorwegianSami-PC", NORWEGIAN_BOKMAL }, + { "com.apple.keylayout.Oriya", ORIYA }, + { "com.apple.keylayout.Persian", FARSI }, + { "com.apple.keylayout.Persian-ISIRI2901", FARSI }, + { "com.apple.keylayout.Polish", POLISH }, + { "com.apple.keylayout.PolishPro", POLISH }, + { "com.apple.keylayout.Portuguese", PORTUGUESE_STANDARD }, + { "com.apple.keylayout.Romanian", ROMANIAN }, + { "com.apple.keylayout.Romanian-Standard", ROMANIAN }, + { "com.apple.keylayout.Russian", RUSSIAN }, + { "com.apple.keylayout.Russian-Phonetic", RUSSIAN }, + { "com.apple.keylayout.RussianWin", RUSSIAN }, + { "com.apple.keylayout.Sami-PC", SAMI_NORTHERN_SWEDEN }, + { "com.apple.keylayout.Serbian", SERBIAN_CYRILLIC_BOSNIA_HERZEGOVINA }, + { "com.apple.keylayout.Serbian-Latin", SERBIAN_LATIN_BOSNIA_HERZEGOVINA }, + { "com.apple.keylayout.Sinhala", SINHALA }, + { "com.apple.keylayout.Sinhala-QWERTY", SINHALA }, + { "com.apple.keylayout.Slovak", SLOVAK }, + { "com.apple.keylayout.Slovak-QWERTY", SLOVAK }, + { "com.apple.keylayout.Slovenian", SLOVENIAN }, + { "com.apple.keylayout.Spanish", SPANISH_TRADITIONAL_SORT }, + { "com.apple.keylayout.Spanish-ISO", SPANISH_MODERN_SORT }, + { "com.apple.keylayout.Swedish", SWEDISH }, + { "com.apple.keylayout.Swedish-Pro", SWEDISH }, + { "com.apple.keylayout.SwedishSami-PC", SWEDISH }, + { "com.apple.keylayout.SwissFrench", FRENCH_SWISS }, + { "com.apple.keylayout.SwissGerman", GERMAN_SWISS }, + { "com.apple.keylayout.Telugu", TELUGU }, + { "com.apple.keylayout.Telugu-QWERTY", TELUGU }, + { "com.apple.keylayout.Thai", THAI }, + { "com.apple.keylayout.Thai-PattaChote", THAI }, + { "com.apple.keylayout.Tibetan-QWERTY", TIBETAN_PRC }, + { "com.apple.keylayout.Tibetan-Wylie", TIBETAN_PRC }, + { "com.apple.keylayout.TibetanOtaniUS", TIBETAN_PRC }, + { "com.apple.keylayout.Turkish", TURKISH }, + { "com.apple.keylayout.Turkish-QWERTY", TURKISH }, + { "com.apple.keylayout.Turkish-QWERTY-PC", TURKISH }, + { "com.apple.keylayout.US", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.USExtended", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.USInternational-PC", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Ukrainian", UKRAINIAN }, + { "com.apple.keylayout.Ukrainian-PC", UKRAINIAN }, + { "com.apple.keylayout.UnicodeHexInput", ENGLISH_UNITED_STATES }, + { "com.apple.keylayout.Urdu", URDU }, + { "com.apple.keylayout.Uyghur", UIGHUR }, + { "com.apple.keylayout.Vietnamese", VIETNAMESE }, + { "com.apple.keylayout.Welsh", WELSH } +}; + +int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId) +{ + int i; + CFIndex length; + char* inputSourceId = NULL; + CFStringRef inputSourceIdRef; + TISInputSourceRef inputSrc = TISCopyCurrentKeyboardLayoutInputSource(); + if (!inputSrc) + { + DEBUG_KBD("Failed to get current keyboard layout input source!"); + return 0; + } + + /* get current input source id */ + inputSourceIdRef = (CFStringRef)TISGetInputSourceProperty(inputSrc, kTISPropertyInputSourceID); + if (!inputSourceIdRef) + { + DEBUG_KBD("Failed to get input source id!"); + goto done; + } + + /* convert it to a C-string */ + length = CFStringGetLength(inputSourceIdRef); + length = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8) + 1; + inputSourceId = (char*)malloc(length); + if (!inputSourceId) + { + DEBUG_KBD("Failed to allocate string buffer!"); + goto done; + } + + if (!CFStringGetCString(inputSourceIdRef, inputSourceId, length, kCFStringEncodingUTF8)) + { + DEBUG_KBD("Failed to convert CFString to C-string!"); + goto done; + } + + /* Search for the id in the mapping table */ + for (i = 0; i < ARRAYSIZE(KEYBOARD_MAPPING_TABLE); ++i) + { + if (strcmp(inputSourceId, KEYBOARD_MAPPING_TABLE[i].inputSourceId) == 0) + { + *keyboardLayoutId = KEYBOARD_MAPPING_TABLE[i].code; + break; + } + } + +done: + free(inputSourceId); + CFRelease(inputSrc); + if (*keyboardLayoutId > 0) + return *keyboardLayoutId; + + return 0; +} diff --git a/libfreerdp/locale/keyboard_apple.h b/libfreerdp/locale/keyboard_apple.h new file mode 100644 index 000000000000..cd3c3e7beca7 --- /dev/null +++ b/libfreerdp/locale/keyboard_apple.h @@ -0,0 +1,28 @@ +/** + * FreeRDP: A Remote Desktop Protocol Implementation + * Apple Core Foundation Keyboard Mapping + * + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FREERDP_LOCALE_KEYBOARD_APPLE_H +#define FREERDP_LOCALE_KEYBOARD_APPLE_H + +#include + +FREERDP_LOCAL int freerdp_detect_keyboard_layout_from_cf(DWORD* keyboardLayoutId); + +#endif /* FREERDP_LOCALE_KEYBOARD_APPLE_H */ diff --git a/libfreerdp/locale/locale.c b/libfreerdp/locale/locale.c index 97ae9e469645..e7c6d00782d9 100644 --- a/libfreerdp/locale/locale.c +++ b/libfreerdp/locale/locale.c @@ -3,6 +3,8 @@ * Microsoft Locales * * Copyright 2009-2012 Marc-Andre Moreau + * Copyright 2021 Thincast Technologies GmbH + * Copyright 2021 Martin Fleisz * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,22 +23,32 @@ #include "config.h" #endif +#if defined(__APPLE__) +#include +#include +#endif + #include #include #include #include +#include #include #include "liblocale.h" #include +#define LOCALE_LANGUAGE_LEN 6 +#define LOCALE_COUNTRY_LEN 10 + struct _SYSTEM_LOCALE { - char language[4]; /* Two or three letter language code */ - char country[10]; /* Two or three letter country code (Sometimes with Cyrl_ prefix) */ - DWORD code; /* 32-bit unsigned integer corresponding to the locale */ + char language[LOCALE_LANGUAGE_LEN]; /* Two or three letter language code */ + char country[LOCALE_COUNTRY_LEN]; /* Two or three letter country code (Sometimes with Cyrl_ + prefix) */ + DWORD code; /* 32-bit unsigned integer corresponding to the locale */ }; typedef struct _SYSTEM_LOCALE SYSTEM_LOCALE; @@ -642,77 +654,118 @@ static const LOCALE_KEYBOARD_LAYOUTS LOCALE_KEYBOARD_LAYOUTS_TABLE[] = { { XHOSA, { 0x00000409, 0x00000409, 0x0, 0x0, 0x0 } }, }; -static BOOL freerdp_get_system_language_and_country_codes(char* language, char* country) +static BOOL freerdp_get_system_language_and_country_codes(char* language, size_t languageLen, + char* country, size_t countryLen) { - int dot; - DWORD nSize; - int underscore; - char* env_lang = NULL; - LPCSTR lang = "LANG"; - /* LANG = _. */ - nSize = GetEnvironmentVariableA(lang, NULL, 0); - - if (!nSize) - return FALSE; /* LANG environment variable was not set */ - - env_lang = (char*)malloc(nSize); - - if (!env_lang) - return FALSE; + assert(language); + assert(languageLen > 0); + assert(country); + assert(countryLen); - if (GetEnvironmentVariableA(lang, env_lang, nSize) != - nSize - 1) /* Get locale from environment variable LANG */ +#if defined(__APPLE__) { - free(env_lang); - return FALSE; - } + CFIndex strSize; + CFStringRef langRef, countryRef; + CFLocaleRef localeRef = CFLocaleCopyCurrent(); + if (!localeRef) + return FALSE; + + langRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleLanguageCode); + countryRef = (CFStringRef)CFLocaleGetValue(localeRef, kCFLocaleCountryCode); + if (!langRef || !countryRef) + { + CFRelease(localeRef); + return FALSE; + } - underscore = strcspn(env_lang, "_"); + if (!CFStringGetCString(langRef, language, languageLen, kCFStringEncodingUTF8) || + !CFStringGetCString(countryRef, country, countryLen, kCFStringEncodingUTF8)) + { + CFRelease(localeRef); + return FALSE; + } - if (underscore > 3) - { - free(env_lang); - return FALSE; /* The language name should not be more than 3 letters long */ + CFRelease(localeRef); + return TRUE; } - else +#else { - /* Get language code */ - strncpy(language, env_lang, underscore); - language[underscore] = '\0'; - } + int dot; + DWORD nSize; + int underscore; + char* env_lang = NULL; + LPCSTR lang = "LANG"; + /* LANG = _. */ + nSize = GetEnvironmentVariableA(lang, NULL, 0); - dot = strcspn(env_lang, "."); + if (!nSize) + return FALSE; /* LANG environment variable was not set */ + + env_lang = (char*)malloc(nSize); + + if (!env_lang) + return FALSE; + + if (GetEnvironmentVariableA(lang, env_lang, nSize) != + nSize - 1) /* Get locale from environment variable LANG */ + { + free(env_lang); + return FALSE; + } + + underscore = strcspn(env_lang, "_"); + + if (underscore > 3) + { + free(env_lang); + return FALSE; /* The language name should not be more than 3 letters long */ + } + else + { + /* Get language code */ + size_t len = MIN(languageLen - 1, underscore); + strncpy(language, env_lang, len); + language[len] = '\0'; + } + + dot = strcspn(env_lang, "."); + + /* Get country code */ + if (dot > underscore) + { + size_t len = MIN(countryLen - 1, dot - underscore - 1); + strncpy(country, &env_lang[underscore + 1], len); + country[len] = '\0'; + } + else + { + free(env_lang); + return FALSE; /* Invalid locale */ + } - /* Get country code */ - if (dot > underscore) - { - strncpy(country, &env_lang[underscore + 1], dot - underscore - 1); - country[dot - underscore - 1] = '\0'; - } - else - { free(env_lang); - return FALSE; /* Invalid locale */ + return TRUE; } - - free(env_lang); - return TRUE; +#endif } -static SYSTEM_LOCALE* freerdp_detect_system_locale(void) +static const SYSTEM_LOCALE* freerdp_detect_system_locale(void) { size_t i; - char language[4]; - char country[10]; - SYSTEM_LOCALE* locale = NULL; - freerdp_get_system_language_and_country_codes(language, country); + char language[LOCALE_LANGUAGE_LEN] = { 0 }; + char country[LOCALE_COUNTRY_LEN] = { 0 }; + const SYSTEM_LOCALE* locale = NULL; + + freerdp_get_system_language_and_country_codes(language, ARRAYSIZE(language), country, + ARRAYSIZE(country)); for (i = 0; i < ARRAYSIZE(SYSTEM_LOCALE_TABLE); i++) { - if ((strcmp(language, SYSTEM_LOCALE_TABLE[i].language) == 0) && - (strcmp(country, SYSTEM_LOCALE_TABLE[i].country) == 0)) + const SYSTEM_LOCALE* current = &SYSTEM_LOCALE_TABLE[i]; + + if ((strcmp(language, current->language) == 0) && (strcmp(country, current->country) == 0)) { - locale = (SYSTEM_LOCALE*)&SYSTEM_LOCALE_TABLE[i]; + locale = current; break; } } @@ -722,7 +775,7 @@ static SYSTEM_LOCALE* freerdp_detect_system_locale(void) DWORD freerdp_get_system_locale_id(void) { - SYSTEM_LOCALE* locale; + const SYSTEM_LOCALE* locale; locale = freerdp_detect_system_locale(); if (locale != NULL) @@ -737,8 +790,10 @@ const char* freerdp_get_system_locale_name_from_id(DWORD localeId) for (index = 0; index < ARRAYSIZE(LOCALE_NAME_TABLE); index++) { - if (localeId == LOCALE_NAME_TABLE[index].localeId) - return LOCALE_NAME_TABLE[index].name; + const LOCALE_NAME* const current = &LOCALE_NAME_TABLE[index]; + + if (localeId == current->localeId) + return current->name; } return NULL; @@ -747,10 +802,12 @@ const char* freerdp_get_system_locale_name_from_id(DWORD localeId) int freerdp_detect_keyboard_layout_from_system_locale(DWORD* keyboardLayoutId) { size_t i, j; - char language[4]; - char country[10]; - SYSTEM_LOCALE* locale; - freerdp_get_system_language_and_country_codes(language, country); + char language[LOCALE_LANGUAGE_LEN] = { 0 }; + char country[LOCALE_COUNTRY_LEN] = { 0 }; + const SYSTEM_LOCALE* locale; + + freerdp_get_system_language_and_country_codes(language, ARRAYSIZE(language), country, + ARRAYSIZE(country)); if ((strcmp(language, "C") == 0) || (strcmp(language, "POSIX") == 0)) { @@ -767,22 +824,24 @@ int freerdp_detect_keyboard_layout_from_system_locale(DWORD* keyboardLayoutId) for (i = 0; i < ARRAYSIZE(LOCALE_KEYBOARD_LAYOUTS_TABLE); i++) { - if (LOCALE_KEYBOARD_LAYOUTS_TABLE[i].locale == locale->code) + const LOCALE_KEYBOARD_LAYOUTS* const current = &LOCALE_KEYBOARD_LAYOUTS_TABLE[i]; + + if (current->locale == locale->code) { /* Locale found in list of default keyboard layouts */ for (j = 0; j < 5; j++) { - if (LOCALE_KEYBOARD_LAYOUTS_TABLE[i].keyboardLayouts[j] == ENGLISH_UNITED_STATES) + if (current->keyboardLayouts[j] == ENGLISH_UNITED_STATES) { continue; /* Skip, try to get a more localized keyboard layout */ } - else if (LOCALE_KEYBOARD_LAYOUTS_TABLE[i].keyboardLayouts[j] == 0) + else if (current->keyboardLayouts[j] == 0) { break; /* No more keyboard layouts */ } else { - *keyboardLayoutId = LOCALE_KEYBOARD_LAYOUTS_TABLE[i].keyboardLayouts[j]; + *keyboardLayoutId = current->keyboardLayouts[j]; return 0; } } diff --git a/winpr/CMakeLists.txt b/winpr/CMakeLists.txt index 036b50f83325..620f9a78b161 100644 --- a/winpr/CMakeLists.txt +++ b/winpr/CMakeLists.txt @@ -51,7 +51,7 @@ if (NOT WIN32) endif() # Soname versioning -set(RAW_VERSION_STRING "2.4.1") +set(RAW_VERSION_STRING "2.5.0") if(EXISTS "${CMAKE_SOURCE_DIR}/.source_tag") file(READ ${CMAKE_SOURCE_DIR}/.source_tag RAW_VERSION_STRING) elseif(USE_VERSION_FROM_GIT_TAG) diff --git a/winpr/libwinpr/registry/registry.c b/winpr/libwinpr/registry/registry.c index 726233e3def2..e0c148b5a61b 100644 --- a/winpr/libwinpr/registry/registry.c +++ b/winpr/libwinpr/registry/registry.c @@ -34,18 +34,18 @@ #include #include #include + #include +#include #include "registry_reg.h" static Reg* instance = NULL; -static Reg* RegGetInstance() +static Reg* RegGetInstance(void) { if (!instance) - { instance = reg_open(1); - } return instance; } @@ -213,18 +213,23 @@ LONG RegOpenKeyExW(HKEY hKey, LPCWSTR lpSubKey, DWORD ulOptions, REGSAM samDesir LONG RegOpenKeyExA(HKEY hKey, LPCSTR lpSubKey, DWORD ulOptions, REGSAM samDesired, PHKEY phkResult) { - Reg* reg; RegKey* pKey; - reg = RegGetInstance(); + Reg* reg = RegGetInstance(); if (!reg) return -1; + if (hKey != HKEY_LOCAL_MACHINE) + return ERROR_FILE_NOT_FOUND; + + assert(reg->root_key); pKey = reg->root_key->subkeys; while (pKey != NULL) { - if (_stricmp(pKey->subname, lpSubKey) == 0) + assert(lpSubKey); + + if (pKey->subname && (_stricmp(pKey->subname, lpSubKey) == 0)) { *phkResult = (HKEY)pKey; return ERROR_SUCCESS; @@ -271,24 +276,48 @@ LONG RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD RegKey* key; RegVal* pValue; + WINPR_UNUSED(lpReserved); + key = (RegKey*)hKey; + assert(key); + pValue = key->values; while (pValue != NULL) { if (strcmp(pValue->name, lpValueName) == 0) { + if (lpType) + *lpType = pValue->type; + if (pValue->type == REG_DWORD) { DWORD* pData = (DWORD*)lpData; - if (pData != NULL) + if (lpcbData) { - *pData = pValue->data.dword; + DWORD size = *lpcbData; + *lpcbData = sizeof(DWORD); + if (pData) + { + if (size < *lpcbData) + return ERROR_MORE_DATA; + } } - *lpcbData = sizeof(DWORD); + if (pData != NULL) + { + DWORD size; + assert(lpcbData); + size = *lpcbData; + *lpcbData = sizeof(DWORD); + if (size < sizeof(DWORD)) + return ERROR_MORE_DATA; + *pData = pValue->data.dword; + } + else if (lpcbData != NULL) + *lpcbData = sizeof(DWORD); return ERROR_SUCCESS; } else if (pValue->type == REG_SZ) @@ -300,11 +329,18 @@ LONG RegQueryValueExA(HKEY hKey, LPCSTR lpValueName, LPDWORD lpReserved, LPDWORD if (pData != NULL) { + DWORD size; + assert(lpcbData); + + size = *lpcbData; + *lpcbData = length; + if (size < length) + return ERROR_MORE_DATA; memcpy(pData, pValue->data.string, length); pData[length] = '\0'; } - - *lpcbData = (UINT32)length; + else if (lpcbData) + *lpcbData = (UINT32)length; return ERROR_SUCCESS; } diff --git a/winpr/libwinpr/registry/registry_reg.c b/winpr/libwinpr/registry/registry_reg.c index 353e490c113f..511d92790705 100644 --- a/winpr/libwinpr/registry/registry_reg.c +++ b/winpr/libwinpr/registry/registry_reg.c @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -35,12 +36,14 @@ #include "../log.h" #define TAG WINPR_TAG("registry") +#define WINPR_ASSERT assert + #define WINPR_HKLM_HIVE "/etc/winpr/HKLM.reg" struct reg_data_type { char* tag; - int length; + size_t length; DWORD type; }; @@ -52,50 +55,71 @@ static struct reg_data_type REG_DATA_TYPE_TABLE[] = { { "\"", 1, REG_SZ }, { "hex:", 4, REG_BINARY }, { "hex(2):\"", 8, REG_EXPAND_SZ }, { "hex(7):\"", 8, REG_MULTI_SZ }, - { "hex(b):\"", 8, REG_QWORD }, - { NULL, 0, 0 } }; - -static char* REG_DATA_TYPE_STRINGS[] = { "REG_NONE", - "REG_SZ", - "REG_EXPAND_SZ", - "REG_BINARY", - "REG_DWORD", - "REG_DWORD_BIG_ENDIAN", - "REG_LINK", - "REG_MULTI_SZ", - "REG_RESOURCE_LIST", - "REG_FULL_RESOURCE_DESCRIPTOR", - "REG_RESOURCE_REQUIREMENTS_LIST", - "REG_QWORD" }; - -static void reg_load_start(Reg* reg) + { "hex(b):\"", 8, REG_QWORD } }; + +static char* reg_data_type_string(DWORD type) { + switch (type) + { + case REG_NONE: + return "REG_NONE"; + case REG_SZ: + return "REG_SZ"; + case REG_EXPAND_SZ: + return "REG_EXPAND_SZ"; + case REG_BINARY: + return "REG_BINARY"; + case REG_DWORD: + return "REG_DWORD"; + case REG_DWORD_BIG_ENDIAN: + return "REG_DWORD_BIG_ENDIAN"; + case REG_LINK: + return "REG_LINK"; + case REG_MULTI_SZ: + return "REG_MULTI_SZ"; + case REG_RESOURCE_LIST: + return "REG_RESOURCE_LIST"; + case REG_FULL_RESOURCE_DESCRIPTOR: + return "REG_FULL_RESOURCE_DESCRIPTOR"; + case REG_RESOURCE_REQUIREMENTS_LIST: + return "REG_RESOURCE_REQUIREMENTS_LIST"; + case REG_QWORD: + return "REG_QWORD"; + default: + return "REG_UNKNOWN"; + } +} + +static BOOL reg_load_start(Reg* reg) +{ + char* buffer; INT64 file_size; + + WINPR_ASSERT(reg); + WINPR_ASSERT(reg->fp); + _fseeki64(reg->fp, 0, SEEK_END); file_size = _ftelli64(reg->fp); _fseeki64(reg->fp, 0, SEEK_SET); reg->line = NULL; reg->next_line = NULL; - reg->buffer = NULL; if (file_size < 1) - return; + return FALSE; - reg->buffer = (char*)malloc(file_size + 2); + buffer = (char*)realloc(reg->buffer, (size_t)file_size + 2); - if (!reg->buffer) - return; + if (!buffer) + return FALSE; + reg->buffer = buffer; - if (fread(reg->buffer, file_size, 1, reg->fp) != 1) - { - free(reg->buffer); - reg->buffer = NULL; - return; - } + if (fread(reg->buffer, (size_t)file_size, 1, reg->fp) != 1) + return FALSE; reg->buffer[file_size] = '\n'; reg->buffer[file_size + 1] = '\0'; reg->next_line = strtok(reg->buffer, "\n"); + return TRUE; } static void reg_load_finish(Reg* reg) @@ -112,15 +136,23 @@ static void reg_load_finish(Reg* reg) static RegVal* reg_load_value(Reg* reg, RegKey* key) { - int index; - char* p[5]; - int length; - char* name; + size_t index; + char* p[5] = { 0 }; + size_t length; + char* name = NULL; char* type; char* data; - RegVal* value; + RegVal* value = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + WINPR_ASSERT(reg->line); + p[0] = reg->line + 1; p[1] = strstr(p[0], "\"="); + if (!p[1]) + return NULL; + p[2] = p[1] + 2; type = p[2]; @@ -129,67 +161,85 @@ static RegVal* reg_load_value(Reg* reg, RegKey* key) else p[3] = strchr(p[2], ':'); + if (!p[3]) + return NULL; + data = p[3] + 1; - length = p[1] - p[0]; - name = (char*)malloc(length + 1); + length = (size_t)(p[1] - p[0]); + if (length < 1) + goto fail; + + name = (char*)calloc(length + 1, sizeof(char)); if (!name) - return NULL; + goto fail; memcpy(name, p[0], length); - name[length] = '\0'; - value = (RegVal*)malloc(sizeof(RegVal)); + value = (RegVal*)calloc(1, sizeof(RegVal)); if (!value) - { - free(name); - return NULL; - } + goto fail; value->name = name; value->type = REG_NONE; - value->next = value->prev = NULL; - for (index = 0; REG_DATA_TYPE_TABLE[index].length > 0; index++) + for (index = 0; index < ARRAYSIZE(REG_DATA_TYPE_TABLE); index++) { - if (strncmp(type, REG_DATA_TYPE_TABLE[index].tag, REG_DATA_TYPE_TABLE[index].length) == 0) + const struct reg_data_type* current = ®_DATA_TYPE_TABLE[index]; + WINPR_ASSERT(current->tag); + WINPR_ASSERT(current->length > 0); + WINPR_ASSERT(current->type != REG_NONE); + + if (strncmp(type, current->tag, current->length) == 0) { - value->type = REG_DATA_TYPE_TABLE[index].type; + value->type = current->type; break; } } - if (value->type == REG_DWORD) + switch (value->type) { - unsigned long val; - errno = 0; - val = strtoul(data, NULL, 16); - - if ((errno != 0) || (val > UINT32_MAX)) + case REG_DWORD: { - free(value); - free(name); - return NULL; - } + unsigned long val; + errno = 0; + val = strtoul(data, NULL, 16); - value->data.dword = val; - } - else if (value->type == REG_SZ) - { - p[4] = strchr(data, '"'); - p[4][0] = '\0'; - value->data.string = _strdup(data); + if ((errno != 0) || (val > UINT32_MAX)) + goto fail; - if (!value->data.string) + value->data.dword = (DWORD)val; + } + break; + case REG_SZ: { - free(value); - free(name); - return NULL; + size_t len, cmp; + char* end; + char* start = strchr(data, '"'); + if (!start) + goto fail; + + /* Check for terminating quote, check it is the last symbol */ + len = strlen(start); + end = strchr(start + 1, '"'); + if (!end) + goto fail; + cmp = end - start + 1; + if (len != cmp) + goto fail; + if (start[0] == '"') + start++; + if (end[0] == '"') + end[0] = '\0'; + value->data.string = _strdup(start); + + if (!value->data.string) + goto fail; } - } - else - { - WLog_ERR(TAG, "unimplemented format: %s", REG_DATA_TYPE_STRINGS[value->type]); + break; + default: + WLog_ERR(TAG, "unimplemented format: %s", reg_data_type_string(value->type)); + break; } if (!key->values) @@ -210,6 +260,11 @@ static RegVal* reg_load_value(Reg* reg, RegKey* key) } return value; + +fail: + free(value); + free(name); + return NULL; } static BOOL reg_load_has_next_line(Reg* reg) @@ -233,15 +288,21 @@ static char* reg_load_get_next_line(Reg* reg) static char* reg_load_peek_next_line(Reg* reg) { + WINPR_ASSERT(reg); return reg->next_line; } static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey) { - char* name; - char* path; - char* save; - int length; + char* name = NULL; + char* path = NULL; + char* save = NULL; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + WINPR_ASSERT(subkey); + WINPR_ASSERT(subkey->name); + path = _strdup(subkey->name); if (!path) @@ -253,9 +314,8 @@ static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey) { if (strcmp(key->name, name) == 0) { - length = strlen(name); - name += length + 1; - subkey->subname = _strdup(name); + if (save) + subkey->subname = _strdup(save); /* TODO: free allocated memory in error case */ if (!subkey->subname) @@ -274,19 +334,24 @@ static void reg_insert_key(Reg* reg, RegKey* key, RegKey* subkey) static RegKey* reg_load_key(Reg* reg, RegKey* key) { char* p[2]; - int length; - char* line; + size_t length; RegKey* subkey; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + + WINPR_ASSERT(reg->line); p[0] = reg->line + 1; p[1] = strrchr(p[0], ']'); - subkey = (RegKey*)malloc(sizeof(RegKey)); + if (!p[1]) + return NULL; + + subkey = (RegKey*)calloc(1, sizeof(RegKey)); if (!subkey) return NULL; - subkey->values = NULL; - subkey->prev = subkey->next = NULL; - length = p[1] - p[0]; + length = (size_t)(p[1] - p[0]); subkey->name = (char*)malloc(length + 1); if (!subkey->name) @@ -300,7 +365,7 @@ static RegKey* reg_load_key(Reg* reg, RegKey* key) while (reg_load_has_next_line(reg)) { - line = reg_load_peek_next_line(reg); + char* line = reg_load_peek_next_line(reg); if (line[0] == '[') break; @@ -354,16 +419,16 @@ static void reg_load(Reg* reg) static void reg_unload_value(Reg* reg, RegVal* value) { - if (value->type == REG_DWORD) - { - } - else if (value->type == REG_SZ) - { - free(value->data.string); - } - else + WINPR_ASSERT(reg); + WINPR_ASSERT(value); + + switch (value->type) { - WLog_ERR(TAG, "unimplemented format: %s", REG_DATA_TYPE_STRINGS[value->type]); + case REG_SZ: + free(value->data.string); + break; + default: + break; } free(value); @@ -372,12 +437,15 @@ static void reg_unload_value(Reg* reg, RegVal* value) static void reg_unload_key(Reg* reg, RegKey* key) { RegVal* pValue; - RegVal* pValueNext; + + WINPR_ASSERT(reg); + WINPR_ASSERT(key); + pValue = key->values; while (pValue != NULL) { - pValueNext = pValue->next; + RegVal* pValueNext = pValue->next; reg_unload_value(reg, pValue); pValue = pValueNext; } @@ -389,23 +457,26 @@ static void reg_unload_key(Reg* reg, RegKey* key) static void reg_unload(Reg* reg) { RegKey* pKey; - RegKey* pKeyNext; - pKey = reg->root_key->subkeys; - while (pKey != NULL) + WINPR_ASSERT(reg); + if (reg->root_key) { - pKeyNext = pKey->next; - reg_unload_key(reg, pKey); - pKey = pKeyNext; - } + pKey = reg->root_key->subkeys; + + while (pKey != NULL) + { + RegKey* pKeyNext = pKey->next; + reg_unload_key(reg, pKey); + pKey = pKeyNext; + } - free(reg->root_key); + free(reg->root_key); + } } Reg* reg_open(BOOL read_only) { - Reg* reg; - reg = (Reg*)malloc(sizeof(Reg)); + Reg* reg = (Reg*)calloc(1, sizeof(Reg)); if (!reg) return NULL; @@ -414,9 +485,7 @@ Reg* reg_open(BOOL read_only) reg->filename = WINPR_HKLM_HIVE; if (reg->read_only) - { reg->fp = winpr_fopen(reg->filename, "r"); - } else { reg->fp = winpr_fopen(reg->filename, "r+"); @@ -426,25 +495,22 @@ Reg* reg_open(BOOL read_only) } if (!reg->fp) - { - free(reg); - return NULL; - } + goto fail; - reg->root_key = (RegKey*)malloc(sizeof(RegKey)); + reg->root_key = (RegKey*)calloc(1, sizeof(RegKey)); if (!reg->root_key) - { - fclose(reg->fp); - free(reg); - return NULL; - } + goto fail; reg->root_key->values = NULL; reg->root_key->subkeys = NULL; reg->root_key->name = "HKEY_LOCAL_MACHINE"; reg_load(reg); return reg; + +fail: + reg_close(reg); + return NULL; } void reg_close(Reg* reg) @@ -452,7 +518,8 @@ void reg_close(Reg* reg) if (reg) { reg_unload(reg); - fclose(reg->fp); + if (reg->fp) + fclose(reg->fp); free(reg); } } diff --git a/winpr/libwinpr/registry/registry_reg.h b/winpr/libwinpr/registry/registry_reg.h index 90db136b42cc..ab3b34f3b411 100644 --- a/winpr/libwinpr/registry/registry_reg.h +++ b/winpr/libwinpr/registry/registry_reg.h @@ -30,7 +30,7 @@ struct _reg FILE* fp; char* line; char* next_line; - int line_length; + size_t line_length; char* buffer; char* filename; BOOL read_only; @@ -44,7 +44,8 @@ struct _reg_val RegVal* prev; RegVal* next; - union reg_data { + union reg_data + { DWORD dword; char* string; } data;