Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,251 @@
# WinPR: Windows Portable Runtime
# libwinpr-utils cmake build script
#
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
#
# 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.
include(CheckFunctionExists)
include(CMakeDependentOption)
winpr_include_directory_add(${CMAKE_CURRENT_SOURCE_DIR})
option(WITH_DEBUG_UTILS_CMDLINE_DUMP "build with excessive command line parser logging" OFF)
if(WITH_DEBUG_UTILS_CMDLINE_DUMP)
winpr_definition_add(-DWITH_DEBUG_UTILS_CMDLINE_DUMP)
endif()
option(WITH_STREAMPOOL_DEBUG "build with extensive streampool logging" OFF)
if(WITH_STREAMPOOL_DEBUG)
winpr_definition_add(-DWITH_STREAMPOOL_DEBUG)
endif()
option(WITH_LODEPNG "build WinPR with PNG support" OFF)
if(WITH_LODEPNG)
find_package(lodepng REQUIRED)
winpr_definition_add(WITH_LODEPNG)
set(WINPR_WITH_PNG ON CACHE BOOL "build cache")
winpr_system_include_directory_add(${lodepng_INCLUDE_DIRS})
winpr_library_add_private(${lodepng_LIBRARIES})
endif()
option(WINPR_UTILS_IMAGE_DIBv5 "[experimental] Add DIBv5 <--> BMP conversion support to clipboard" OFF)
option(WINPR_UTILS_IMAGE_PNG "Add PNG <--> BMP conversion support to clipboard" OFF)
if(WINPR_UTILS_IMAGE_PNG)
find_package(PNG REQUIRED)
winpr_pc_add_requires_private("libpng")
set(WINPR_WITH_PNG ON CACHE BOOL "build cache")
winpr_system_include_directory_add(${PNG_INCLUDE_DIRS})
winpr_library_add_private(${PNG_LIBRARIES})
endif()
option(WINPR_UTILS_IMAGE_WEBP "Add WebP <--> BMP conversion support to clipboard" OFF)
if(WINPR_UTILS_IMAGE_WEBP)
find_package(PkgConfig REQUIRED)
pkg_check_modules(WEBP libwebp REQUIRED)
winpr_pc_add_requires_private("libwebp")
winpr_system_include_directory_add(${WEBP_INCLUDE_DIRS})
winpr_library_add_private(${WEBP_LIBRARIES})
endif()
option(WINPR_UTILS_IMAGE_JPEG "Add Jpeg <--> BMP conversion support to clipboard" OFF)
if(WINPR_UTILS_IMAGE_JPEG)
find_package(PkgConfig REQUIRED)
pkg_check_modules(JPEG libjpeg REQUIRED)
winpr_pc_add_requires_private("libjpeg")
winpr_system_include_directory_add(${JPEG_INCLUDE_DIRS})
winpr_library_add_private(${JPEG_LIBRARIES})
endif()
set(COLLECTIONS_SRCS
collections/Object.c
collections/Queue.c
collections/Stack.c
collections/PubSub.c
collections/BitStream.c
collections/ArrayList.c
collections/LinkedList.c
collections/HashTable.c
collections/ListDictionary.c
collections/CountdownEvent.c
collections/BufferPool.c
collections/ObjectPool.c
collections/StreamPool.c
collections/MessageQueue.c
collections/MessagePipe.c
)
if(WINPR_HAVE_SYSLOG_H)
set(SYSLOG_SRCS wlog/SyslogAppender.c wlog/SyslogAppender.h)
endif()
find_package(libsystemd)
option(WITH_SYSTEMD "allows to export wLog to systemd journal" ${libsystemd_FOUND})
if(WITH_SYSTEMD)
find_package(libsystemd REQUIRED)
winpr_pc_add_requires_private("libsystemd")
set(WINPR_HAVE_JOURNALD_H TRUE)
set(JOURNALD_SRCS wlog/JournaldAppender.c wlog/JournaldAppender.h)
winpr_system_include_directory_add(${LIBSYSTEMD_INCLUDE_DIR})
winpr_library_add_private(${LIBSYSTEMD_LIBRARY})
else()
unset(WINPR_HAVE_JOURNALD_H)
endif()
set(WLOG_SRCS
wlog/wlog.c
wlog/wlog.h
wlog/Layout.c
wlog/Layout.h
wlog/Message.c
wlog/Message.h
wlog/DataMessage.c
wlog/DataMessage.h
wlog/ImageMessage.c
wlog/ImageMessage.h
wlog/PacketMessage.c
wlog/PacketMessage.h
wlog/Appender.c
wlog/Appender.h
wlog/FileAppender.c
wlog/FileAppender.h
wlog/BinaryAppender.c
wlog/BinaryAppender.h
wlog/CallbackAppender.c
wlog/CallbackAppender.h
wlog/ConsoleAppender.c
wlog/ConsoleAppender.h
wlog/UdpAppender.c
wlog/UdpAppender.h
${SYSLOG_SRCS}
${JOURNALD_SRCS}
)
set(ASN1_SRCS asn1/asn1.c)
set(SRCS
ini.c
sam.c
ntlm.c
image.c
print.c
stream.h
stream.c
strlst.c
debug.c
winpr.c
cmdline.c
ssl.c
)
if(ANDROID)
list(APPEND SRCS android.h android.c)
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
if(NOT WINPR_HAVE_UNWIND_H)
message("[backtrace] android NDK without unwind.h, falling back to corkscrew")
set(WINPR_HAVE_CORKSCREW 1)
endif()
endif()
if(WINPR_HAVE_CORKSCREW)
list(APPEND SRCS corkscrew/debug.c corkscrew/debug.h)
endif()
if(WIN32)
list(APPEND SRCS windows/debug.c windows/debug.h)
endif()
if(WINPR_HAVE_EXECINFO_H)
option(USE_EXECINFO "Use execinfo.h to generate backtraces" ON)
if(USE_EXECINFO)
winpr_definition_add(USE_EXECINFO)
list(APPEND SRCS execinfo/debug.c execinfo/debug.h)
endif()
endif()
if(WINPR_HAVE_UNWIND_H)
option(USE_UNWIND "Use unwind.h to generate backtraces" ON)
if(USE_UNWIND)
winpr_definition_add(USE_UNWIND)
list(APPEND SRCS unwind/debug.c unwind/debug.h)
endif()
endif()
if(NOT WITH_JSON_DISABLED)
if(jansson_FOUND)
winpr_library_add_private(jansson::jansson)
winpr_pc_add_requires_private("jansson")
winpr_definition_add(WITH_JANSSON)
winpr_module_add(json/jansson.c)
set(WITH_WINPR_JSON ON CACHE INTERNAL "internal")
elseif(json-c_FOUND)
winpr_library_add_private(json-c::json-c)
winpr_pc_add_requires_private("json-c")
winpr_definition_add(WITH_JSONC)
winpr_module_add(json/json-c.c)
set(WITH_WINPR_JSON ON CACHE INTERNAL "internal")
elseif(cJSON_FOUND)
winpr_library_add_private(cjson)
winpr_pc_add_requires_private("libcjson")
winpr_definition_add(WITH_CJSON)
winpr_module_add(json/c-json.c)
set(WITH_WINPR_JSON ON CACHE INTERNAL "internal")
else()
winpr_module_add(json/json-stub.c)
endif()
else()
winpr_module_add(json/json-stub.c)
endif()
winpr_module_add(json/json.c)
winpr_module_add(${SRCS} ${COLLECTIONS_SRCS} ${WLOG_SRCS} ${ASN1_SRCS})
winpr_include_directory_add(".")
if(OPENSSL_FOUND)
winpr_system_include_directory_add(${OPENSSL_INCLUDE_DIR})
winpr_library_add_private(${OPENSSL_LIBRARIES})
endif()
if(MBEDTLS_FOUND)
winpr_system_include_directory_add(${MBEDTLS_INCLUDE_DIR})
winpr_library_add_private(${MBEDTLS_LIBRARIES})
endif()
if(UNIX)
winpr_library_add_private(m)
set(CMAKE_REQUIRED_INCLUDES backtrace.h)
check_function_exists(backtrace BACKTRACE)
if(NOT BACKTRACE)
set(CMAKE_REQUIRED_LIBRARIES execinfo)
check_function_exists(backtrace EXECINFO)
if(EXECINFO)
winpr_library_add_private(execinfo)
endif()
endif()
endif()
if(WIN32)
winpr_library_add_public(dbghelp)
endif()
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,7 @@
set(MINWIN_LAYER "0")
set(MINWIN_GROUP "none")
set(MINWIN_MAJOR_VERSION "0")
set(MINWIN_MINOR_VERSION "0")
set(MINWIN_SHORT_NAME "utils")
set(MINWIN_LONG_NAME "WinPR Utils")
set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}")

View File

@@ -0,0 +1,77 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Winpr android helpers
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
*
* 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.
*/
#include "android.h"
#include <jni.h>
#include <winpr/winpr.h>
#include <winpr/assert.h>
#include "../log.h"
#define TAG WINPR_TAG("android")
JavaVM* jniVm = nullptr;
WINPR_API jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
WLog_INFO(TAG, "Setting up JNI environment...");
jniVm = vm;
return JNI_VERSION_1_6;
}
WINPR_API void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
JNIEnv* env = nullptr;
WLog_INFO(TAG, "Tearing down JNI environment...");
if ((*jniVm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_6) != JNI_OK)
{
WLog_FATAL(TAG, "Failed to get the environment");
return;
}
}
jboolean winpr_jni_attach_thread(JNIEnv** env)
{
WINPR_ASSERT(jniVm);
if ((*jniVm)->GetEnv(jniVm, (void**)env, JNI_VERSION_1_4) != JNI_OK)
{
WLog_INFO(TAG, "android_java_callback: attaching current thread");
(*jniVm)->AttachCurrentThread(jniVm, env, nullptr);
if ((*jniVm)->GetEnv(jniVm, (void**)env, JNI_VERSION_1_4) != JNI_OK)
{
WLog_ERR(TAG, "android_java_callback: failed to obtain current JNI environment");
}
return JNI_TRUE;
}
return JNI_FALSE;
}
/* attach current thread to JVM */
void winpr_jni_detach_thread(void)
{
WINPR_ASSERT(jniVm);
(*jniVm)->DetachCurrentThread(jniVm);
}

View File

@@ -0,0 +1,30 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Winpr android helpers
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
*
* 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 WINPR_UTILS_ANDROID_PRIV_H
#define WINPR_UTILS_ANDROID_PRIV_H
#include <jni.h>
extern JavaVM* jniVm;
jboolean winpr_jni_attach_thread(JNIEnv** env);
void winpr_jni_detach_thread(void);
#endif /* WINPR_UTILS_ANDROID_PRIV_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,931 @@
/**
* WinPR: Windows Portable Runtime
* Command-Line Utils
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/cmdline.h>
#include "../log.h"
#define TAG WINPR_TAG("commandline")
/**
* Command-line syntax: some basic concepts:
* https://pythonconquerstheuniverse.wordpress.com/2010/07/25/command-line-syntax-some-basic-concepts/
*/
/**
* Command-Line Syntax:
*
* <sigil><keyword><separator><value>
*
* <sigil>: '/' or '-' or ('+' | '-')
*
* <keyword>: option, named argument, flag
*
* <separator>: ':' or '='
*
* <value>: argument value
*
*/
#if !defined(WITH_DEBUG_UTILS_CMDLINE_DUMP)
static const char censoredmessage[] =
"<censored: build with -DWITH_DEBUG_UTILS_CMDLINE_DUMP=ON for details>";
#endif
#define log_error(flags, msg, index, arg) \
log_error_((flags), (msg), (index), (arg), __FILE__, __func__, __LINE__)
static void log_error_(DWORD flags, LPCSTR message, int index, WINPR_ATTR_UNUSED LPCSTR argv,
const char* file, const char* fkt, size_t line)
{
if ((flags & COMMAND_LINE_SILENCE_PARSER) == 0)
{
const DWORD level = WLOG_ERROR;
static wLog* log = nullptr;
if (!log)
log = WLog_Get(TAG);
if (!WLog_IsLevelActive(log, level))
return;
WLog_PrintTextMessage(log, level, line, file, fkt, "Failed at index %d [%s]: %s", index,
#if defined(WITH_DEBUG_UTILS_CMDLINE_DUMP)
argv
#else
censoredmessage
#endif
,
message);
}
}
#define log_comma_error(msg, arg) log_comma_error_((msg), (arg), __FILE__, __func__, __LINE__)
static void log_comma_error_(const char* message, WINPR_ATTR_UNUSED const char* argument,
const char* file, const char* fkt, size_t line)
{
const DWORD level = WLOG_ERROR;
static wLog* log = nullptr;
if (!log)
log = WLog_Get(TAG);
if (!WLog_IsLevelActive(log, level))
return;
WLog_PrintTextMessage(log, level, line, file, fkt, "%s [%s]", message,
#if defined(WITH_DEBUG_UTILS_CMDLINE_DUMP)
argument
#else
censoredmessage
#endif
);
}
int CommandLineParseArgumentsA(int argc, LPSTR* argv, COMMAND_LINE_ARGUMENT_A* options, DWORD flags,
void* context, COMMAND_LINE_PRE_FILTER_FN_A preFilter,
COMMAND_LINE_POST_FILTER_FN_A postFilter)
{
int status = 0;
int count = 0;
BOOL notescaped = FALSE;
const char* sigil = nullptr;
size_t sigil_length = 0;
char* keyword = nullptr;
size_t keyword_index = 0;
char* separator = nullptr;
char* value = nullptr;
int toggle = 0;
if (!argv)
return status;
if (argc == 1)
{
if (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD)
status = 0;
else
status = COMMAND_LINE_STATUS_PRINT_HELP;
return status;
}
for (int i = 1; i < argc; i++)
{
size_t keyword_length = 0;
BOOL found = FALSE;
BOOL escaped = TRUE;
if (preFilter)
{
count = preFilter(context, i, argc, argv);
if (count < 0)
{
log_error(flags, "PreFilter rule could not be applied", i, argv[i]);
status = COMMAND_LINE_ERROR;
return status;
}
if (count > 0)
{
i += (count - 1);
continue;
}
}
sigil = argv[i];
size_t length = strlen(argv[i]);
if ((sigil[0] == '/') && (flags & COMMAND_LINE_SIGIL_SLASH))
{
sigil_length = 1;
}
else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_DASH))
{
sigil_length = 1;
if (length > 2)
{
if ((sigil[1] == '-') && (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH))
sigil_length = 2;
}
}
else if ((sigil[0] == '+') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
{
sigil_length = 1;
}
else if ((sigil[0] == '-') && (flags & COMMAND_LINE_SIGIL_PLUS_MINUS))
{
sigil_length = 1;
}
else if (flags & COMMAND_LINE_SIGIL_NONE)
{
sigil_length = 0;
}
else if (flags & COMMAND_LINE_SIGIL_NOT_ESCAPED)
{
if (notescaped)
{
log_error(flags, "Unescaped sigil", i, argv[i]);
return COMMAND_LINE_ERROR;
}
sigil_length = 0;
escaped = FALSE;
notescaped = TRUE;
}
else
{
log_error(flags, "Invalid sigil", i, argv[i]);
return COMMAND_LINE_ERROR;
}
if ((sigil_length > 0) || (flags & COMMAND_LINE_SIGIL_NONE) ||
(flags & COMMAND_LINE_SIGIL_NOT_ESCAPED))
{
if (length < (sigil_length + 1))
{
if ((flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD))
continue;
log_error(flags, "Unexpected keyword", i, argv[i]);
return COMMAND_LINE_ERROR_NO_KEYWORD;
}
keyword_index = sigil_length;
keyword = &argv[i][keyword_index];
toggle = -1;
if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
{
if (strncmp(keyword, "enable-", 7) == 0)
{
toggle = TRUE;
keyword_index += 7;
keyword = &argv[i][keyword_index];
}
else if (strncmp(keyword, "disable-", 8) == 0)
{
toggle = FALSE;
keyword_index += 8;
keyword = &argv[i][keyword_index];
}
}
separator = nullptr;
if ((flags & COMMAND_LINE_SEPARATOR_COLON) && (!separator))
separator = strchr(keyword, ':');
if ((flags & COMMAND_LINE_SEPARATOR_EQUAL) && (!separator))
separator = strchr(keyword, '=');
if (separator)
{
SSIZE_T separator_index = (separator - argv[i]);
SSIZE_T value_index = separator_index + 1;
keyword_length = WINPR_ASSERTING_INT_CAST(size_t, (separator - keyword));
value = &argv[i][value_index];
}
else
{
if (length < keyword_index)
{
log_error(flags, "Argument required", i, argv[i]);
return COMMAND_LINE_ERROR;
}
keyword_length = length - keyword_index;
value = nullptr;
}
if (!escaped)
continue;
for (size_t j = 0; options[j].Name != nullptr; j++)
{
COMMAND_LINE_ARGUMENT_A* cur = &options[j];
BOOL match = FALSE;
if (strncmp(cur->Name, keyword, keyword_length) == 0)
{
if (strlen(cur->Name) == keyword_length)
match = TRUE;
}
if ((!match) && (cur->Alias != nullptr))
{
if (strncmp(cur->Alias, keyword, keyword_length) == 0)
{
if (strlen(cur->Alias) == keyword_length)
match = TRUE;
}
}
if (!match)
continue;
found = match;
cur->Index = i;
if ((flags & COMMAND_LINE_SEPARATOR_SPACE) && ((i + 1) < argc))
{
BOOL argument = 0;
int value_present = 1;
if (flags & COMMAND_LINE_SIGIL_DASH)
{
if (strncmp(argv[i + 1], "-", 1) == 0)
value_present = 0;
}
if (flags & COMMAND_LINE_SIGIL_DOUBLE_DASH)
{
if (strncmp(argv[i + 1], "--", 2) == 0)
value_present = 0;
}
if (flags & COMMAND_LINE_SIGIL_SLASH)
{
if (strncmp(argv[i + 1], "/", 1) == 0)
value_present = 0;
}
argument = (((cur->Flags & COMMAND_LINE_VALUE_REQUIRED) != 0) ||
((cur->Flags & COMMAND_LINE_VALUE_OPTIONAL) != 0));
if (value_present && argument)
{
i++;
value = argv[i];
}
else if (!value_present && (cur->Flags & COMMAND_LINE_VALUE_OPTIONAL))
{
value = nullptr;
}
else if (!value_present && argument)
{
log_error(flags, "Argument required", i, argv[i]);
return COMMAND_LINE_ERROR;
}
}
if (!(flags & COMMAND_LINE_SEPARATOR_SPACE))
{
if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG))
{
log_error(flags, "Unexpected value", i, argv[i]);
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
}
}
else
{
if (value && (cur->Flags & COMMAND_LINE_VALUE_FLAG))
{
i--;
value = nullptr;
}
}
if (!value && (cur->Flags & COMMAND_LINE_VALUE_REQUIRED))
{
log_error(flags, "Missing value", i, argv[i]);
status = COMMAND_LINE_ERROR_MISSING_VALUE;
return status;
}
cur->Flags |= COMMAND_LINE_ARGUMENT_PRESENT;
if (value)
{
if (!(cur->Flags & (COMMAND_LINE_VALUE_OPTIONAL | COMMAND_LINE_VALUE_REQUIRED)))
{
log_error(flags, "Unexpected value", i, argv[i]);
return COMMAND_LINE_ERROR_UNEXPECTED_VALUE;
}
cur->Value = value;
cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
}
else
{
if (cur->Flags & COMMAND_LINE_VALUE_FLAG)
{
cur->Value = (LPSTR)1;
cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
}
else if (cur->Flags & COMMAND_LINE_VALUE_BOOL)
{
if (flags & COMMAND_LINE_SIGIL_ENABLE_DISABLE)
{
if (toggle == -1)
cur->Value = BoolValueTrue;
else if (!toggle)
cur->Value = BoolValueFalse;
else
cur->Value = BoolValueTrue;
}
else
{
if (sigil[0] == '+')
cur->Value = BoolValueTrue;
else if (sigil[0] == '-')
cur->Value = BoolValueFalse;
else
cur->Value = BoolValueTrue;
}
cur->Flags |= COMMAND_LINE_VALUE_PRESENT;
}
}
if (postFilter)
{
count = postFilter(context, &options[j]);
if (count < 0)
{
log_error(flags, "PostFilter rule could not be applied", i, argv[i]);
status = COMMAND_LINE_ERROR;
return status;
}
}
if (cur->Flags & COMMAND_LINE_PRINT)
return COMMAND_LINE_STATUS_PRINT;
else if (cur->Flags & COMMAND_LINE_PRINT_HELP)
return COMMAND_LINE_STATUS_PRINT_HELP;
else if (cur->Flags & COMMAND_LINE_PRINT_VERSION)
return COMMAND_LINE_STATUS_PRINT_VERSION;
else if (cur->Flags & COMMAND_LINE_PRINT_BUILDCONFIG)
return COMMAND_LINE_STATUS_PRINT_BUILDCONFIG;
}
if (!found && (flags & COMMAND_LINE_IGN_UNKNOWN_KEYWORD) == 0)
{
log_error(flags, "Unexpected keyword", i, argv[i]);
return COMMAND_LINE_ERROR_NO_KEYWORD;
}
}
}
return status;
}
int CommandLineParseArgumentsW(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED LPWSTR* argv,
WINPR_ATTR_UNUSED COMMAND_LINE_ARGUMENT_W* options,
WINPR_ATTR_UNUSED DWORD flags, WINPR_ATTR_UNUSED void* context,
WINPR_ATTR_UNUSED COMMAND_LINE_PRE_FILTER_FN_W preFilter,
WINPR_ATTR_UNUSED COMMAND_LINE_POST_FILTER_FN_W postFilter)
{
WLog_ERR("TODO", "TODO: implement");
return 0;
}
int CommandLineClearArgumentsA(COMMAND_LINE_ARGUMENT_A* options)
{
for (size_t i = 0; options[i].Name != nullptr; i++)
{
options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
options[i].Value = nullptr;
}
return 0;
}
int CommandLineClearArgumentsW(COMMAND_LINE_ARGUMENT_W* options)
{
for (int i = 0; options[i].Name != nullptr; i++)
{
options[i].Flags &= COMMAND_LINE_INPUT_FLAG_MASK;
options[i].Value = nullptr;
}
return 0;
}
const COMMAND_LINE_ARGUMENT_A* CommandLineFindArgumentA(const COMMAND_LINE_ARGUMENT_A* options,
LPCSTR Name)
{
WINPR_ASSERT(options);
WINPR_ASSERT(Name);
for (size_t i = 0; options[i].Name != nullptr; i++)
{
if (strcmp(options[i].Name, Name) == 0)
return &options[i];
if (options[i].Alias != nullptr)
{
if (strcmp(options[i].Alias, Name) == 0)
return &options[i];
}
}
return nullptr;
}
const COMMAND_LINE_ARGUMENT_W* CommandLineFindArgumentW(const COMMAND_LINE_ARGUMENT_W* options,
LPCWSTR Name)
{
WINPR_ASSERT(options);
WINPR_ASSERT(Name);
for (size_t i = 0; options[i].Name != nullptr; i++)
{
if (_wcscmp(options[i].Name, Name) == 0)
return &options[i];
if (options[i].Alias != nullptr)
{
if (_wcscmp(options[i].Alias, Name) == 0)
return &options[i];
}
}
return nullptr;
}
const COMMAND_LINE_ARGUMENT_A* CommandLineFindNextArgumentA(const COMMAND_LINE_ARGUMENT_A* argument)
{
const COMMAND_LINE_ARGUMENT_A* nextArgument = nullptr;
if (!argument || !argument->Name)
return nullptr;
nextArgument = &argument[1];
if (nextArgument->Name == nullptr)
return nullptr;
return nextArgument;
}
static int is_quoted(char c)
{
switch (c)
{
case '"':
return 1;
case '\'':
return -1;
default:
return 0;
}
}
static size_t get_element_count(const char* list, BOOL* failed, BOOL fullquoted)
{
size_t count = 0;
int quoted = 0;
bool escaped = false;
BOOL finished = FALSE;
BOOL first = TRUE;
const char* it = list;
if (!list)
return 0;
if (strlen(list) == 0)
return 0;
while (!finished)
{
BOOL nextFirst = FALSE;
const char cur = *it++;
/* Ignore the symbol that was escaped. */
if (escaped)
{
escaped = false;
continue;
}
switch (cur)
{
case '\0':
if (quoted != 0)
{
log_comma_error("Invalid argument (missing closing quote)", list);
*failed = TRUE;
return 0;
}
finished = TRUE;
break;
case '\\':
if (!escaped)
{
escaped = true;
continue;
}
break;
case '\'':
case '"':
if (!fullquoted)
{
int now = is_quoted(cur) && !escaped;
if (now == quoted)
quoted = 0;
else if (quoted == 0)
quoted = now;
}
break;
case ',':
if (first)
{
log_comma_error("Invalid argument (empty list elements)", list);
*failed = TRUE;
return 0;
}
if (quoted == 0)
{
nextFirst = TRUE;
count++;
}
break;
default:
break;
}
first = nextFirst;
}
return count + 1;
}
static char* get_next_comma(char* string, BOOL fullquoted)
{
const char* log = string;
int quoted = 0;
bool first = true;
bool escaped = false;
WINPR_ASSERT(string);
while (TRUE)
{
char* last = string;
const char cur = *string++;
if (escaped)
{
escaped = false;
continue;
}
switch (cur)
{
case '\0':
if (quoted != 0)
log_comma_error("Invalid quoted argument", log);
return nullptr;
case '\\':
if (!escaped)
{
escaped = true;
continue;
}
break;
case '\'':
case '"':
if (!fullquoted)
{
int now = is_quoted(cur);
if ((quoted == 0) && !first)
{
log_comma_error("Invalid quoted argument", log);
return nullptr;
}
if (now == quoted)
quoted = 0;
else if (quoted == 0)
quoted = now;
}
break;
case ',':
if (first)
{
log_comma_error("Invalid argument (empty list elements)", log);
return nullptr;
}
if (quoted == 0)
return last;
break;
default:
break;
}
first = FALSE;
}
}
static BOOL is_valid_fullquoted(const char* string)
{
char cur = '\0';
char last = '\0';
const char quote = *string++;
/* We did not start with a quote. */
if (is_quoted(quote) == 0)
return FALSE;
while ((cur = *string++) != '\0')
{
/* A quote is found. */
if (cur == quote)
{
/* If the quote was escaped, it is valid. */
if (last != '\\')
{
/* Only allow unescaped quote as last character in string. */
if (*string != '\0')
return FALSE;
}
/* If the last quote in the string is escaped, it is wrong. */
else if (*string != '\0')
return FALSE;
}
last = cur;
}
/* The string did not terminate with the same quote as it started. */
return (last == quote);
}
char** CommandLineParseCommaSeparatedValuesEx(const char* name, const char* list, size_t* count)
{
char** p = nullptr;
char* str = nullptr;
size_t nArgs = 0;
size_t prefix = 0;
size_t len = 0;
size_t namelen = 0;
BOOL failed = FALSE;
char* copy = nullptr;
char* unquoted = nullptr;
BOOL fullquoted = FALSE;
BOOL success = FALSE;
if (count == nullptr)
goto fail;
*count = 0;
if (list)
{
int start = 0;
int end = 0;
unquoted = copy = _strdup(list);
if (!copy)
goto fail;
len = strlen(unquoted);
if (len > 0)
{
start = is_quoted(unquoted[0]);
end = is_quoted(unquoted[len - 1]);
if ((start != 0) && (end != 0))
{
if (start != end)
{
log_comma_error("Invalid argument (quote mismatch)", list);
goto fail;
}
if (!is_valid_fullquoted(unquoted))
goto fail;
unquoted[len - 1] = '\0';
unquoted++;
len -= 2;
fullquoted = TRUE;
}
}
}
*count = get_element_count(unquoted, &failed, fullquoted);
if (failed)
goto fail;
if (*count == 0)
{
if (!name)
goto fail;
else
{
size_t clen = strlen(name);
p = (char**)calloc(2UL + clen, sizeof(char*));
if (p)
{
char* dst = (char*)&p[1];
p[0] = dst;
(void)sprintf_s(dst, clen + 1, "%s", name);
*count = 1;
success = TRUE;
goto fail;
}
}
}
nArgs = *count;
if (name)
nArgs++;
prefix = (nArgs + 1UL) * sizeof(char*);
if (name)
namelen = strlen(name);
p = (char**)calloc(len + prefix + 1 + namelen + 1, sizeof(char*));
if (!p)
goto fail;
str = &((char*)p)[prefix];
memcpy(str, unquoted, len);
if (name)
{
char* namestr = &((char*)p)[prefix + len + 1];
memcpy(namestr, name, namelen);
p[0] = namestr;
}
for (size_t index = name ? 1 : 0; index < nArgs; index++)
{
char* ptr = str;
const int quote = is_quoted(*ptr);
char* comma = get_next_comma(str, fullquoted);
if ((quote != 0) && !fullquoted)
ptr++;
p[index] = ptr;
if (comma)
{
char* last = comma - 1;
const int lastQuote = is_quoted(*last);
if (!fullquoted)
{
if (lastQuote != quote)
{
log_comma_error("Invalid argument (quote mismatch)", list);
goto fail;
}
else if (lastQuote != 0)
*last = '\0';
}
*comma = '\0';
str = comma + 1;
}
else if (quote)
{
char* end = strrchr(ptr, '"');
if (!end)
goto fail;
*end = '\0';
}
}
*count = nArgs;
success = TRUE;
fail:
free(copy);
if (!success)
{
if (count)
*count = 0;
free((void*)p);
return nullptr;
}
return p;
}
char** CommandLineParseCommaSeparatedValues(const char* list, size_t* count)
{
return CommandLineParseCommaSeparatedValuesEx(nullptr, list, count);
}
char* CommandLineToCommaSeparatedValues(int argc, char* argv[])
{
return CommandLineToCommaSeparatedValuesEx(argc, argv, nullptr, 0);
}
static const char* filtered(const char* arg, const char* filters[], size_t number)
{
if (number == 0)
return arg;
for (size_t x = 0; x < number; x++)
{
const char* filter = filters[x];
size_t len = strlen(filter);
if (_strnicmp(arg, filter, len) == 0)
return &arg[len];
}
return nullptr;
}
char* CommandLineToCommaSeparatedValuesEx(int argc, char* argv[], const char* filters[],
size_t number)
{
char* str = nullptr;
size_t offset = 0;
size_t size = WINPR_ASSERTING_INT_CAST(size_t, argc) + 1;
if ((argc <= 0) || !argv)
return nullptr;
for (int x = 0; x < argc; x++)
size += strlen(argv[x]);
str = calloc(size, sizeof(char));
if (!str)
return nullptr;
for (int x = 0; x < argc; x++)
{
int rc = 0;
const char* arg = filtered(argv[x], filters, number);
if (!arg)
continue;
rc = _snprintf(&str[offset], size - offset, "%s,", arg);
if (rc <= 0)
{
free(str);
return nullptr;
}
offset += (size_t)rc;
}
if (offset > 0)
str[offset - 1] = '\0';
return str;
}
void CommandLineParserFree(char** ptr)
{
union
{
char* p;
char** pp;
} uptr;
uptr.pp = ptr;
free(uptr.p);
}

View File

@@ -0,0 +1,614 @@
/**
* WinPR: Windows Portable Runtime
* System.Collections.ArrayList
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <stdarg.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/collections.h>
#if defined(_WIN32) && (_MSC_VER < 1800) && !defined(__MINGW32__)
#define va_copy(dest, src) (dest = src)
#endif
struct s_wArrayList
{
size_t capacity;
size_t growthFactor;
BOOL synchronized;
size_t size;
void** array;
CRITICAL_SECTION lock;
wObject object;
};
/**
* C equivalent of the C# ArrayList Class:
* http://msdn.microsoft.com/en-us/library/system.collections.arraylist.aspx
*/
/**
* Properties
*/
/**
* Gets or sets the number of elements that the ArrayList can contain.
*/
size_t ArrayList_Capacity(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
return arrayList->capacity;
}
/**
* Gets the number of elements actually contained in the ArrayList.
*/
size_t ArrayList_Count(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
return arrayList->size;
}
/**
* Gets the internal list of items contained in the ArrayList.
*/
size_t ArrayList_Items(wArrayList* arrayList, ULONG_PTR** ppItems)
{
WINPR_ASSERT(arrayList);
*ppItems = (ULONG_PTR*)arrayList->array;
return arrayList->size;
}
/**
* Gets a value indicating whether the ArrayList has a fixed size.
*/
BOOL ArrayList_IsFixedSized(WINPR_ATTR_UNUSED wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
return FALSE;
}
/**
* Gets a value indicating whether the ArrayList is read-only.
*/
BOOL ArrayList_IsReadOnly(WINPR_ATTR_UNUSED wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
return FALSE;
}
/**
* Gets a value indicating whether access to the ArrayList is synchronized (thread safe).
*/
BOOL ArrayList_IsSynchronized(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
return arrayList->synchronized;
}
/**
* Lock access to the ArrayList
*/
static void ArrayList_Lock_Conditional(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
if (arrayList->synchronized)
EnterCriticalSection(&arrayList->lock);
}
void ArrayList_Lock(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
EnterCriticalSection(&arrayList->lock);
}
/**
* Unlock access to the ArrayList
*/
static void ArrayList_Unlock_Conditional(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
if (arrayList->synchronized)
LeaveCriticalSection(&arrayList->lock);
}
void ArrayList_Unlock(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
LeaveCriticalSection(&arrayList->lock);
}
/**
* Gets the element at the specified index.
*/
void* ArrayList_GetItem(wArrayList* arrayList, size_t index)
{
void* obj = nullptr;
WINPR_ASSERT(arrayList);
if (index < arrayList->size)
{
obj = arrayList->array[index];
}
return obj;
}
/**
* Sets the element at the specified index.
*/
BOOL ArrayList_SetItem(wArrayList* arrayList, size_t index, const void* obj)
{
WINPR_ASSERT(arrayList);
if (index >= arrayList->size)
return FALSE;
if (arrayList->object.fnObjectNew)
{
arrayList->array[index] = arrayList->object.fnObjectNew(obj);
if (obj && !arrayList->array[index])
return FALSE;
}
else
{
union
{
const void* cpv;
void* pv;
} cnv;
cnv.cpv = obj;
arrayList->array[index] = cnv.pv;
}
return TRUE;
}
/**
* Methods
*/
static BOOL ArrayList_EnsureCapacity(wArrayList* arrayList, size_t count)
{
WINPR_ASSERT(arrayList);
WINPR_ASSERT(count > 0);
if (arrayList->size + count > arrayList->capacity)
{
void** newArray = nullptr;
size_t newCapacity = arrayList->capacity * arrayList->growthFactor;
if (newCapacity < arrayList->size + count)
newCapacity = arrayList->size + count;
newArray = (void**)realloc((void*)arrayList->array, sizeof(void*) * newCapacity);
if (!newArray)
return FALSE;
arrayList->array = newArray;
arrayList->capacity = newCapacity;
}
return TRUE;
}
/**
* Shift a section of the list.
*/
static BOOL ArrayList_Shift(wArrayList* arrayList, size_t index, SSIZE_T count)
{
WINPR_ASSERT(arrayList);
if (count > 0)
{
if (!ArrayList_EnsureCapacity(arrayList, (size_t)count))
return FALSE;
MoveMemory((void*)&arrayList->array[index + (size_t)count], (void*)&arrayList->array[index],
(arrayList->size - index) * sizeof(void*));
arrayList->size += (size_t)count;
}
else if (count < 0)
{
const size_t scount = WINPR_ASSERTING_INT_CAST(size_t, -count);
const size_t off = index + scount;
if (off < arrayList->size)
{
const size_t chunk = arrayList->size - off;
MoveMemory((void*)&arrayList->array[index], (void*)&arrayList->array[off],
chunk * sizeof(void*));
}
arrayList->size -= scount;
}
return TRUE;
}
/**
* Removes all elements from the ArrayList.
*/
void ArrayList_Clear(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
for (size_t index = 0; index < arrayList->size; index++)
{
if (arrayList->object.fnObjectFree)
arrayList->object.fnObjectFree(arrayList->array[index]);
arrayList->array[index] = nullptr;
}
arrayList->size = 0;
ArrayList_Unlock_Conditional(arrayList);
}
/**
* Determines whether an element is in the ArrayList.
*/
BOOL ArrayList_Contains(wArrayList* arrayList, const void* obj)
{
BOOL rc = FALSE;
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
for (size_t index = 0; index < arrayList->size; index++)
{
rc = arrayList->object.fnObjectEquals(arrayList->array[index], obj);
if (rc)
break;
}
ArrayList_Unlock_Conditional(arrayList);
return rc;
}
#if defined(WITH_WINPR_DEPRECATED)
int ArrayList_Add(wArrayList* arrayList, const void* obj)
{
WINPR_ASSERT(arrayList);
if (!ArrayList_Append(arrayList, obj))
return -1;
return (int)ArrayList_Count(arrayList) - 1;
}
#endif
/**
* Adds an object to the end of the ArrayList.
*/
BOOL ArrayList_Append(wArrayList* arrayList, const void* obj)
{
size_t index = 0;
BOOL rc = FALSE;
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
if (!ArrayList_EnsureCapacity(arrayList, 1))
goto out;
index = arrayList->size++;
rc = ArrayList_SetItem(arrayList, index, obj);
out:
ArrayList_Unlock_Conditional(arrayList);
return rc;
}
/*
* Inserts an element into the ArrayList at the specified index.
*/
BOOL ArrayList_Insert(wArrayList* arrayList, size_t index, const void* obj)
{
BOOL ret = TRUE;
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
if (index < arrayList->size)
{
if (!ArrayList_Shift(arrayList, index, 1))
{
ret = FALSE;
}
else
{
ArrayList_SetItem(arrayList, index, obj);
}
}
ArrayList_Unlock_Conditional(arrayList);
return ret;
}
/**
* Removes the first occurrence of a specific object from the ArrayList.
*/
BOOL ArrayList_Remove(wArrayList* arrayList, const void* obj)
{
BOOL found = FALSE;
BOOL ret = TRUE;
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
size_t index = 0;
for (; index < arrayList->size; index++)
{
if (arrayList->object.fnObjectEquals(arrayList->array[index], obj))
{
found = TRUE;
break;
}
}
if (found)
{
if (arrayList->object.fnObjectFree)
arrayList->object.fnObjectFree(arrayList->array[index]);
ret = ArrayList_Shift(arrayList, index, -1);
}
ArrayList_Unlock_Conditional(arrayList);
return ret;
}
/**
* Removes the element at the specified index of the ArrayList.
*/
BOOL ArrayList_RemoveAt(wArrayList* arrayList, size_t index)
{
BOOL ret = TRUE;
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
if (index < arrayList->size)
{
if (arrayList->object.fnObjectFree)
arrayList->object.fnObjectFree(arrayList->array[index]);
ret = ArrayList_Shift(arrayList, index, -1);
}
ArrayList_Unlock_Conditional(arrayList);
return ret;
}
/**
* Searches for the specified Object and returns the zero-based index of the first occurrence within
* the entire ArrayList.
*
* Searches for the specified Object and returns the zero-based index of the last occurrence within
* the range of elements in the ArrayList that extends from the first element to the specified
* index.
*
* Searches for the specified Object and returns the zero-based index of the last occurrence within
* the range of elements in the ArrayList that contains the specified number of elements and ends at
* the specified index.
*/
SSIZE_T ArrayList_IndexOf(wArrayList* arrayList, const void* obj, SSIZE_T startIndex, SSIZE_T count)
{
BOOL found = FALSE;
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
SSIZE_T sindex = startIndex;
if (startIndex < 0)
sindex = 0;
SSIZE_T index = sindex;
SSIZE_T cindex = count;
if (count < 0)
{
if (arrayList->size > SSIZE_MAX)
goto fail;
cindex = (SSIZE_T)arrayList->size;
}
for (; index < sindex + cindex; index++)
{
if (arrayList->object.fnObjectEquals(arrayList->array[index], obj))
{
found = TRUE;
break;
}
}
fail:
if (!found)
index = -1;
ArrayList_Unlock_Conditional(arrayList);
return index;
}
/**
* Searches for the specified Object and returns the zero-based index of the last occurrence within
* the entire ArrayList.
*
* Searches for the specified Object and returns the zero-based index of the last occurrence within
* the range of elements in the ArrayList that extends from the first element to the specified
* index.
*
* Searches for the specified Object and returns the zero-based index of the last occurrence within
* the range of elements in the ArrayList that contains the specified number of elements and ends at
* the specified index.
*/
SSIZE_T ArrayList_LastIndexOf(wArrayList* arrayList, const void* obj, SSIZE_T startIndex,
SSIZE_T count)
{
SSIZE_T sindex = 0;
SSIZE_T cindex = 0;
BOOL found = FALSE;
WINPR_ASSERT(arrayList);
ArrayList_Lock_Conditional(arrayList);
sindex = startIndex;
if (startIndex < 0)
sindex = 0;
cindex = count;
if (count < 0)
{
WINPR_ASSERT(arrayList->size <= SSIZE_MAX);
cindex = (SSIZE_T)arrayList->size;
}
SSIZE_T index = sindex + cindex;
for (; index > sindex; index--)
{
if (arrayList->object.fnObjectEquals(arrayList->array[index - 1], obj))
{
found = TRUE;
break;
}
}
if (!found)
index = -1;
ArrayList_Unlock_Conditional(arrayList);
return index;
}
static BOOL ArrayList_DefaultCompare(const void* objA, const void* objB)
{
return (objA == objB);
}
wObject* ArrayList_Object(wArrayList* arrayList)
{
WINPR_ASSERT(arrayList);
return &arrayList->object;
}
BOOL ArrayList_ForEach(wArrayList* arrayList, ArrayList_ForEachFkt fkt, ...)
{
BOOL rc = 0;
va_list ap = WINPR_C_ARRAY_INIT;
va_start(ap, fkt);
rc = ArrayList_ForEachAP(arrayList, fkt, ap);
va_end(ap);
return rc;
}
BOOL ArrayList_ForEachAP(wArrayList* arrayList, ArrayList_ForEachFkt fkt, va_list ap)
{
BOOL rc = FALSE;
va_list cap = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(arrayList);
WINPR_ASSERT(fkt);
ArrayList_Lock_Conditional(arrayList);
size_t count = ArrayList_Count(arrayList);
for (size_t index = 0; index < count; index++)
{
BOOL rs = 0;
void* obj = ArrayList_GetItem(arrayList, index);
va_copy(cap, ap);
rs = fkt(obj, index, cap);
va_end(cap);
if (!rs)
goto fail;
}
rc = TRUE;
fail:
ArrayList_Unlock_Conditional(arrayList);
return rc;
}
/**
* Construction, Destruction
*/
wArrayList* ArrayList_New(BOOL synchronized)
{
wObject* obj = nullptr;
wArrayList* arrayList = nullptr;
arrayList = (wArrayList*)calloc(1, sizeof(wArrayList));
if (!arrayList)
return nullptr;
arrayList->synchronized = synchronized;
arrayList->growthFactor = 2;
obj = ArrayList_Object(arrayList);
if (!obj)
goto fail;
obj->fnObjectEquals = ArrayList_DefaultCompare;
if (!ArrayList_EnsureCapacity(arrayList, 32))
goto fail;
if (!InitializeCriticalSectionAndSpinCount(&arrayList->lock, 4000))
goto fail;
return arrayList;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
ArrayList_Free(arrayList);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void ArrayList_Free(wArrayList* arrayList)
{
if (!arrayList)
return;
ArrayList_Clear(arrayList);
DeleteCriticalSection(&arrayList->lock);
free((void*)arrayList->array);
free(arrayList);
}

View File

@@ -0,0 +1,177 @@
/**
* WinPR: Windows Portable Runtime
* BitStream
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/assert.h>
#include <winpr/config.h>
#include <winpr/print.h>
#include <winpr/bitstream.h>
static const char* BYTE_BIT_STRINGS_LSB[256] = {
"00000000", "00000001", "00000010", "00000011", "00000100", "00000101", "00000110", "00000111",
"00001000", "00001001", "00001010", "00001011", "00001100", "00001101", "00001110", "00001111",
"00010000", "00010001", "00010010", "00010011", "00010100", "00010101", "00010110", "00010111",
"00011000", "00011001", "00011010", "00011011", "00011100", "00011101", "00011110", "00011111",
"00100000", "00100001", "00100010", "00100011", "00100100", "00100101", "00100110", "00100111",
"00101000", "00101001", "00101010", "00101011", "00101100", "00101101", "00101110", "00101111",
"00110000", "00110001", "00110010", "00110011", "00110100", "00110101", "00110110", "00110111",
"00111000", "00111001", "00111010", "00111011", "00111100", "00111101", "00111110", "00111111",
"01000000", "01000001", "01000010", "01000011", "01000100", "01000101", "01000110", "01000111",
"01001000", "01001001", "01001010", "01001011", "01001100", "01001101", "01001110", "01001111",
"01010000", "01010001", "01010010", "01010011", "01010100", "01010101", "01010110", "01010111",
"01011000", "01011001", "01011010", "01011011", "01011100", "01011101", "01011110", "01011111",
"01100000", "01100001", "01100010", "01100011", "01100100", "01100101", "01100110", "01100111",
"01101000", "01101001", "01101010", "01101011", "01101100", "01101101", "01101110", "01101111",
"01110000", "01110001", "01110010", "01110011", "01110100", "01110101", "01110110", "01110111",
"01111000", "01111001", "01111010", "01111011", "01111100", "01111101", "01111110", "01111111",
"10000000", "10000001", "10000010", "10000011", "10000100", "10000101", "10000110", "10000111",
"10001000", "10001001", "10001010", "10001011", "10001100", "10001101", "10001110", "10001111",
"10010000", "10010001", "10010010", "10010011", "10010100", "10010101", "10010110", "10010111",
"10011000", "10011001", "10011010", "10011011", "10011100", "10011101", "10011110", "10011111",
"10100000", "10100001", "10100010", "10100011", "10100100", "10100101", "10100110", "10100111",
"10101000", "10101001", "10101010", "10101011", "10101100", "10101101", "10101110", "10101111",
"10110000", "10110001", "10110010", "10110011", "10110100", "10110101", "10110110", "10110111",
"10111000", "10111001", "10111010", "10111011", "10111100", "10111101", "10111110", "10111111",
"11000000", "11000001", "11000010", "11000011", "11000100", "11000101", "11000110", "11000111",
"11001000", "11001001", "11001010", "11001011", "11001100", "11001101", "11001110", "11001111",
"11010000", "11010001", "11010010", "11010011", "11010100", "11010101", "11010110", "11010111",
"11011000", "11011001", "11011010", "11011011", "11011100", "11011101", "11011110", "11011111",
"11100000", "11100001", "11100010", "11100011", "11100100", "11100101", "11100110", "11100111",
"11101000", "11101001", "11101010", "11101011", "11101100", "11101101", "11101110", "11101111",
"11110000", "11110001", "11110010", "11110011", "11110100", "11110101", "11110110", "11110111",
"11111000", "11111001", "11111010", "11111011", "11111100", "11111101", "11111110", "11111111"
};
static const char* BYTE_BIT_STRINGS_MSB[256] = {
"00000000", "10000000", "01000000", "11000000", "00100000", "10100000", "01100000", "11100000",
"00010000", "10010000", "01010000", "11010000", "00110000", "10110000", "01110000", "11110000",
"00001000", "10001000", "01001000", "11001000", "00101000", "10101000", "01101000", "11101000",
"00011000", "10011000", "01011000", "11011000", "00111000", "10111000", "01111000", "11111000",
"00000100", "10000100", "01000100", "11000100", "00100100", "10100100", "01100100", "11100100",
"00010100", "10010100", "01010100", "11010100", "00110100", "10110100", "01110100", "11110100",
"00001100", "10001100", "01001100", "11001100", "00101100", "10101100", "01101100", "11101100",
"00011100", "10011100", "01011100", "11011100", "00111100", "10111100", "01111100", "11111100",
"00000010", "10000010", "01000010", "11000010", "00100010", "10100010", "01100010", "11100010",
"00010010", "10010010", "01010010", "11010010", "00110010", "10110010", "01110010", "11110010",
"00001010", "10001010", "01001010", "11001010", "00101010", "10101010", "01101010", "11101010",
"00011010", "10011010", "01011010", "11011010", "00111010", "10111010", "01111010", "11111010",
"00000110", "10000110", "01000110", "11000110", "00100110", "10100110", "01100110", "11100110",
"00010110", "10010110", "01010110", "11010110", "00110110", "10110110", "01110110", "11110110",
"00001110", "10001110", "01001110", "11001110", "00101110", "10101110", "01101110", "11101110",
"00011110", "10011110", "01011110", "11011110", "00111110", "10111110", "01111110", "11111110",
"00000001", "10000001", "01000001", "11000001", "00100001", "10100001", "01100001", "11100001",
"00010001", "10010001", "01010001", "11010001", "00110001", "10110001", "01110001", "11110001",
"00001001", "10001001", "01001001", "11001001", "00101001", "10101001", "01101001", "11101001",
"00011001", "10011001", "01011001", "11011001", "00111001", "10111001", "01111001", "11111001",
"00000101", "10000101", "01000101", "11000101", "00100101", "10100101", "01100101", "11100101",
"00010101", "10010101", "01010101", "11010101", "00110101", "10110101", "01110101", "11110101",
"00001101", "10001101", "01001101", "11001101", "00101101", "10101101", "01101101", "11101101",
"00011101", "10011101", "01011101", "11011101", "00111101", "10111101", "01111101", "11111101",
"00000011", "10000011", "01000011", "11000011", "00100011", "10100011", "01100011", "11100011",
"00010011", "10010011", "01010011", "11010011", "00110011", "10110011", "01110011", "11110011",
"00001011", "10001011", "01001011", "11001011", "00101011", "10101011", "01101011", "11101011",
"00011011", "10011011", "01011011", "11011011", "00111011", "10111011", "01111011", "11111011",
"00000111", "10000111", "01000111", "11000111", "00100111", "10100111", "01100111", "11100111",
"00010111", "10010111", "01010111", "11010111", "00110111", "10110111", "01110111", "11110111",
"00001111", "10001111", "01001111", "11001111", "00101111", "10101111", "01101111", "11101111",
"00011111", "10011111", "01011111", "11011111", "00111111", "10111111", "01111111", "11111111"
};
void BitDump(const char* tag, UINT32 level, const BYTE* buffer, UINT32 length, UINT32 flags)
{
const char** strs = (flags & BITDUMP_MSB_FIRST) ? BYTE_BIT_STRINGS_MSB : BYTE_BIT_STRINGS_LSB;
char pbuffer[64 * 8 + 1] = WINPR_C_ARRAY_INIT;
size_t pos = 0;
WINPR_ASSERT(tag);
WINPR_ASSERT(buffer || (length == 0));
DWORD i = 0;
for (; i < length; i += 8)
{
const char* str = strs[buffer[i / 8]];
const DWORD nbits = (length - i) > 8 ? 8 : (length - i);
WINPR_ASSERT(nbits <= INT32_MAX);
const int rc = _snprintf(&pbuffer[pos], length - pos, "%.*s ", (int)nbits, str);
if (rc < 0)
return;
pos += (size_t)rc;
if ((i % 64) == 0)
{
pos = 0;
WLog_LVL(tag, level, "%s", pbuffer);
}
}
if (i)
WLog_LVL(tag, level, "%s ", pbuffer);
}
UINT32 ReverseBits32(UINT32 bits, UINT32 nbits)
{
UINT32 rbits = 0;
do
{
rbits = (rbits | (bits & 1)) << 1;
bits >>= 1;
nbits--;
} while (nbits > 0);
rbits >>= 1;
return rbits;
}
void BitStream_Attach(wBitStream* bs, const BYTE* buffer, UINT32 capacity)
{
union
{
const BYTE* cpv;
BYTE* pv;
} cnv;
WINPR_ASSERT(bs);
WINPR_ASSERT(buffer);
cnv.cpv = buffer;
bs->position = 0;
bs->buffer = cnv.pv;
bs->offset = 0;
bs->accumulator = 0;
bs->pointer = cnv.pv;
bs->capacity = capacity;
bs->length = bs->capacity * 8;
}
wBitStream* BitStream_New(void)
{
wBitStream* bs = (wBitStream*)calloc(1, sizeof(wBitStream));
return bs;
}
void BitStream_Free(wBitStream* bs)
{
if (!bs)
return;
free(bs);
}

View File

@@ -0,0 +1,597 @@
/**
* WinPR: Windows Portable Runtime
* Buffer Pool
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/collections.h>
#ifndef MAX
#define MAX(a, b) ((a) > (b)) ? (a) : (b)
#endif
typedef struct
{
SSIZE_T size;
void* buffer;
} wBufferPoolItem;
struct s_wBufferPool
{
SSIZE_T fixedSize;
DWORD alignment;
BOOL synchronized;
CRITICAL_SECTION lock;
SSIZE_T size;
SSIZE_T capacity;
void** array;
SSIZE_T aSize;
SSIZE_T aCapacity;
wBufferPoolItem* aArray;
SSIZE_T uSize;
SSIZE_T uCapacity;
wBufferPoolItem* uArray;
};
static BOOL BufferPool_Lock(wBufferPool* pool)
{
if (!pool)
return FALSE;
if (pool->synchronized)
EnterCriticalSection(&pool->lock);
return TRUE;
}
static BOOL BufferPool_Unlock(wBufferPool* pool)
{
if (!pool)
return FALSE;
if (pool->synchronized)
LeaveCriticalSection(&pool->lock);
return TRUE;
}
/**
* C equivalent of the C# BufferManager Class:
* http://msdn.microsoft.com/en-us/library/ms405814.aspx
*/
/**
* Methods
*/
static BOOL BufferPool_ShiftAvailable(wBufferPool* pool, size_t index, int count)
{
if (count > 0)
{
if (pool->aSize + count > pool->aCapacity)
{
wBufferPoolItem* newArray = nullptr;
SSIZE_T newCapacity = pool->aSize + count;
newCapacity += (newCapacity + 2) / 2;
WINPR_ASSERT(newCapacity > 0);
if (pool->alignment > 0)
newArray = (wBufferPoolItem*)winpr_aligned_realloc(
pool->aArray,
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity),
pool->alignment);
else
newArray = (wBufferPoolItem*)realloc(
pool->aArray,
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity));
if (!newArray)
return FALSE;
pool->aArray = newArray;
pool->aCapacity = newCapacity;
}
MoveMemory(
&pool->aArray[index + WINPR_ASSERTING_INT_CAST(size_t, count)], &pool->aArray[index],
(WINPR_ASSERTING_INT_CAST(size_t, pool->aSize) - index) * sizeof(wBufferPoolItem));
pool->aSize += count;
}
else if (count < 0)
{
MoveMemory(
&pool->aArray[index], &pool->aArray[index + WINPR_ASSERTING_INT_CAST(size_t, -count)],
(WINPR_ASSERTING_INT_CAST(size_t, pool->aSize) - index) * sizeof(wBufferPoolItem));
pool->aSize += count;
}
return TRUE;
}
static BOOL BufferPool_ShiftUsed(wBufferPool* pool, SSIZE_T index, SSIZE_T count)
{
if (count > 0)
{
const SSIZE_T required = pool->uSize + count;
// check for overflow
if ((required < count) || (required < pool->uSize))
return FALSE;
if (required > pool->uCapacity)
{
SSIZE_T newUCapacity = pool->uCapacity;
do
{
if (newUCapacity > SSIZE_MAX - 128ll)
return FALSE;
newUCapacity += 128ll;
} while (newUCapacity <= required);
wBufferPoolItem* newUArray = nullptr;
if (pool->alignment > 0)
newUArray = (wBufferPoolItem*)winpr_aligned_realloc(
pool->uArray,
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newUCapacity),
pool->alignment);
else
newUArray = (wBufferPoolItem*)realloc(
pool->uArray,
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newUCapacity));
if (!newUArray)
return FALSE;
pool->uCapacity = newUCapacity;
pool->uArray = newUArray;
}
MoveMemory(&pool->uArray[index + count], &pool->uArray[index],
WINPR_ASSERTING_INT_CAST(size_t, pool->uSize - index) * sizeof(wBufferPoolItem));
pool->uSize += count;
}
else if (count < 0)
{
MoveMemory(&pool->uArray[index], &pool->uArray[index - count],
WINPR_ASSERTING_INT_CAST(size_t, pool->uSize - index) * sizeof(wBufferPoolItem));
pool->uSize += count;
}
return TRUE;
}
/**
* Get the buffer pool size
*/
SSIZE_T BufferPool_GetPoolSize(wBufferPool* pool)
{
SSIZE_T size = 0;
BufferPool_Lock(pool);
if (pool->fixedSize)
{
/* fixed size buffers */
size = pool->size;
}
else
{
/* variable size buffers */
size = pool->uSize;
}
BufferPool_Unlock(pool);
return size;
}
/**
* Get the size of a pooled buffer
*/
SSIZE_T BufferPool_GetBufferSize(wBufferPool* pool, const void* buffer)
{
SSIZE_T size = 0;
BOOL found = FALSE;
BufferPool_Lock(pool);
if (pool->fixedSize)
{
/* fixed size buffers */
size = pool->fixedSize;
found = TRUE;
}
else
{
/* variable size buffers */
for (SSIZE_T index = 0; index < pool->uSize; index++)
{
if (pool->uArray[index].buffer == buffer)
{
size = pool->uArray[index].size;
found = TRUE;
break;
}
}
}
BufferPool_Unlock(pool);
return (found) ? size : -1;
}
/**
* Gets a buffer of at least the specified size from the pool.
*/
void* BufferPool_Take(wBufferPool* pool, SSIZE_T size)
{
SSIZE_T maxSize = 0;
SSIZE_T maxIndex = 0;
SSIZE_T foundIndex = -1;
BOOL found = FALSE;
void* buffer = nullptr;
BufferPool_Lock(pool);
if (pool->fixedSize)
{
/* fixed size buffers */
if (pool->size > 0)
buffer = pool->array[--(pool->size)];
if (!buffer)
{
if (pool->alignment)
buffer = winpr_aligned_malloc(WINPR_ASSERTING_INT_CAST(size_t, pool->fixedSize),
pool->alignment);
else
buffer = malloc(WINPR_ASSERTING_INT_CAST(size_t, pool->fixedSize));
}
if (!buffer)
goto out_error;
}
else
{
/* variable size buffers */
maxSize = 0;
maxIndex = 0;
if (size < 1)
size = pool->fixedSize;
for (SSIZE_T index = 0; index < pool->aSize; index++)
{
if (pool->aArray[index].size > maxSize)
{
maxIndex = index;
maxSize = pool->aArray[index].size;
}
if (pool->aArray[index].size >= size)
{
foundIndex = index;
found = TRUE;
break;
}
}
if (!found && maxSize)
{
foundIndex = maxIndex;
found = TRUE;
}
if (!found)
{
if (!size)
buffer = nullptr;
else
{
if (pool->alignment)
buffer = winpr_aligned_malloc(WINPR_ASSERTING_INT_CAST(size_t, size),
pool->alignment);
else
buffer = malloc(WINPR_ASSERTING_INT_CAST(size_t, size));
if (!buffer)
goto out_error;
}
}
else
{
buffer = pool->aArray[foundIndex].buffer;
if (maxSize < size)
{
void* newBuffer = nullptr;
if (pool->alignment)
newBuffer = winpr_aligned_realloc(
buffer, WINPR_ASSERTING_INT_CAST(size_t, size), pool->alignment);
else
newBuffer = realloc(buffer, WINPR_ASSERTING_INT_CAST(size_t, size));
if (!newBuffer)
goto out_error_no_free;
buffer = newBuffer;
}
if (!BufferPool_ShiftAvailable(pool, WINPR_ASSERTING_INT_CAST(size_t, foundIndex), -1))
goto out_error;
}
if (!buffer)
goto out_error;
if (pool->uSize + 1 > pool->uCapacity)
{
size_t newUCapacity = WINPR_ASSERTING_INT_CAST(size_t, pool->uCapacity);
newUCapacity += (newUCapacity + 2) / 2;
if (newUCapacity > SSIZE_MAX)
goto out_error;
wBufferPoolItem* newUArray =
(wBufferPoolItem*)realloc(pool->uArray, sizeof(wBufferPoolItem) * newUCapacity);
if (!newUArray)
goto out_error;
pool->uCapacity = (SSIZE_T)newUCapacity;
pool->uArray = newUArray;
}
pool->uArray[pool->uSize].buffer = buffer;
pool->uArray[pool->uSize].size = size;
(pool->uSize)++;
}
BufferPool_Unlock(pool);
return buffer;
out_error:
if (pool->alignment)
winpr_aligned_free(buffer);
else
free(buffer);
out_error_no_free:
BufferPool_Unlock(pool);
return nullptr;
}
/**
* Returns a buffer to the pool.
*/
BOOL BufferPool_Return(wBufferPool* pool, void* buffer)
{
BOOL rc = FALSE;
SSIZE_T size = 0;
BOOL found = FALSE;
BufferPool_Lock(pool);
if (pool->fixedSize)
{
/* fixed size buffers */
if ((pool->size + 1) >= pool->capacity)
{
SSIZE_T newCapacity = MAX(2, pool->size + (pool->size + 2) / 2 + 1);
void** newArray = (void**)realloc(
(void*)pool->array, sizeof(void*) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity));
if (!newArray)
goto out_error;
pool->capacity = newCapacity;
pool->array = newArray;
}
pool->array[(pool->size)++] = buffer;
}
else
{
/* variable size buffers */
SSIZE_T index = 0;
for (; index < pool->uSize; index++)
{
if (pool->uArray[index].buffer == buffer)
{
found = TRUE;
break;
}
}
if (found)
{
size = pool->uArray[index].size;
if (!BufferPool_ShiftUsed(pool, index, -1))
goto out_error;
}
if (size)
{
if ((pool->aSize + 1) >= pool->aCapacity)
{
SSIZE_T newCapacity = MAX(2, pool->aSize + (pool->aSize + 2) / 2 + 1);
wBufferPoolItem* newArray = (wBufferPoolItem*)realloc(
pool->aArray,
sizeof(wBufferPoolItem) * WINPR_ASSERTING_INT_CAST(size_t, newCapacity));
if (!newArray)
goto out_error;
pool->aCapacity = newCapacity;
pool->aArray = newArray;
}
pool->aArray[pool->aSize].buffer = buffer;
pool->aArray[pool->aSize].size = size;
(pool->aSize)++;
}
}
rc = TRUE;
out_error:
BufferPool_Unlock(pool);
return rc;
}
/**
* Releases the buffers currently cached in the pool.
*/
void BufferPool_Clear(wBufferPool* pool)
{
BufferPool_Lock(pool);
if (pool->fixedSize)
{
/* fixed size buffers */
while (pool->size > 0)
{
(pool->size)--;
if (pool->alignment)
winpr_aligned_free(pool->array[pool->size]);
else
free(pool->array[pool->size]);
}
}
else
{
/* variable size buffers */
while (pool->aSize > 0)
{
(pool->aSize)--;
if (pool->alignment)
winpr_aligned_free(pool->aArray[pool->aSize].buffer);
else
free(pool->aArray[pool->aSize].buffer);
}
while (pool->uSize > 0)
{
(pool->uSize)--;
if (pool->alignment)
winpr_aligned_free(pool->uArray[pool->uSize].buffer);
else
free(pool->uArray[pool->uSize].buffer);
}
}
BufferPool_Unlock(pool);
}
/**
* Construction, Destruction
*/
wBufferPool* BufferPool_New(BOOL synchronized, SSIZE_T fixedSize, DWORD alignment)
{
wBufferPool* pool = nullptr;
pool = (wBufferPool*)calloc(1, sizeof(wBufferPool));
if (pool)
{
pool->fixedSize = fixedSize;
if (pool->fixedSize < 0)
pool->fixedSize = 0;
pool->alignment = alignment;
pool->synchronized = synchronized;
if (pool->synchronized)
{
if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
goto out_error;
}
if (pool->fixedSize)
{
/* fixed size buffers */
pool->size = 0;
pool->capacity = 32;
pool->array =
(void**)calloc(WINPR_ASSERTING_INT_CAST(size_t, pool->capacity), sizeof(void*));
if (!pool->array)
goto out_error;
}
else
{
/* variable size buffers */
pool->aSize = 0;
pool->aCapacity = 32;
pool->aArray = (wBufferPoolItem*)calloc(
WINPR_ASSERTING_INT_CAST(size_t, pool->aCapacity), sizeof(wBufferPoolItem));
if (!pool->aArray)
goto out_error;
pool->uSize = 0;
pool->uCapacity = 32;
pool->uArray = (wBufferPoolItem*)calloc(
WINPR_ASSERTING_INT_CAST(size_t, pool->uCapacity), sizeof(wBufferPoolItem));
if (!pool->uArray)
goto out_error;
}
}
return pool;
out_error:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
BufferPool_Free(pool);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void BufferPool_Free(wBufferPool* pool)
{
if (pool)
{
BufferPool_Clear(pool);
if (pool->synchronized)
DeleteCriticalSection(&pool->lock);
if (pool->fixedSize)
{
/* fixed size buffers */
free((void*)pool->array);
}
else
{
/* variable size buffers */
free(pool->aArray);
free(pool->uArray);
}
free(pool);
}
}

View File

@@ -0,0 +1,210 @@
/**
* WinPR: Windows Portable Runtime
* Countdown Event
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/collections.h>
struct CountdownEvent
{
size_t count;
CRITICAL_SECTION lock;
HANDLE event;
size_t initialCount;
};
/**
* C equivalent of the C# CountdownEvent Class
* http://msdn.microsoft.com/en-us/library/dd235708/
*/
/**
* Properties
*/
/**
* Gets the number of remaining signals required to set the event.
*/
size_t CountdownEvent_CurrentCount(wCountdownEvent* countdown)
{
WINPR_ASSERT(countdown);
EnterCriticalSection(&countdown->lock);
const size_t rc = countdown->count;
LeaveCriticalSection(&countdown->lock);
return rc;
}
/**
* Gets the numbers of signals initially required to set the event.
*/
size_t CountdownEvent_InitialCount(wCountdownEvent* countdown)
{
WINPR_ASSERT(countdown);
EnterCriticalSection(&countdown->lock);
const size_t rc = countdown->initialCount;
LeaveCriticalSection(&countdown->lock);
return rc;
}
/**
* Determines whether the event is set.
*/
BOOL CountdownEvent_IsSet(wCountdownEvent* countdown)
{
BOOL status = FALSE;
WINPR_ASSERT(countdown);
if (WaitForSingleObject(countdown->event, 0) == WAIT_OBJECT_0)
status = TRUE;
return status;
}
/**
* Gets a WaitHandle that is used to wait for the event to be set.
*/
HANDLE CountdownEvent_WaitHandle(wCountdownEvent* countdown)
{
WINPR_ASSERT(countdown);
return countdown->event;
}
/**
* Methods
*/
/**
* Increments the CountdownEvent's current count by a specified value.
*/
void CountdownEvent_AddCount(wCountdownEvent* countdown, size_t signalCount)
{
WINPR_ASSERT(countdown);
EnterCriticalSection(&countdown->lock);
const BOOL signalSet = countdown->count == 0;
countdown->count += signalCount;
if (signalSet)
(void)ResetEvent(countdown->event);
LeaveCriticalSection(&countdown->lock);
}
/**
* Registers multiple signals with the CountdownEvent, decrementing the value of CurrentCount by the
* specified amount.
*/
BOOL CountdownEvent_Signal(wCountdownEvent* countdown, size_t signalCount)
{
BOOL status = FALSE;
BOOL newStatus = FALSE;
BOOL oldStatus = FALSE;
WINPR_ASSERT(countdown);
EnterCriticalSection(&countdown->lock);
if (WaitForSingleObject(countdown->event, 0) == WAIT_OBJECT_0)
oldStatus = TRUE;
if (signalCount <= countdown->count)
countdown->count -= signalCount;
else
countdown->count = 0;
if (countdown->count == 0)
newStatus = TRUE;
if (newStatus && (!oldStatus))
{
(void)SetEvent(countdown->event);
status = TRUE;
}
LeaveCriticalSection(&countdown->lock);
return status;
}
/**
* Resets the InitialCount property to a specified value.
*/
void CountdownEvent_Reset(wCountdownEvent* countdown, size_t count)
{
WINPR_ASSERT(countdown);
countdown->initialCount = count;
}
/**
* Construction, Destruction
*/
wCountdownEvent* CountdownEvent_New(size_t initialCount)
{
wCountdownEvent* countdown = (wCountdownEvent*)calloc(1, sizeof(wCountdownEvent));
if (!countdown)
return nullptr;
countdown->count = initialCount;
countdown->initialCount = initialCount;
if (!InitializeCriticalSectionAndSpinCount(&countdown->lock, 4000))
goto fail;
countdown->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!countdown->event)
goto fail;
if (countdown->count == 0)
{
if (!SetEvent(countdown->event))
goto fail;
}
return countdown;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
CountdownEvent_Free(countdown);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void CountdownEvent_Free(wCountdownEvent* countdown)
{
if (!countdown)
return;
DeleteCriticalSection(&countdown->lock);
(void)CloseHandle(countdown->event);
free(countdown);
}

View File

@@ -0,0 +1,873 @@
/**
* WinPR: Windows Portable Runtime
* System.Collections.Hashtable
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/collections.h>
/**
* This implementation is based on the public domain
* hash table implementation made by Keith Pomakis:
*
* http://www.pomakis.com/hashtable/hashtable.c
* http://www.pomakis.com/hashtable/hashtable.h
*/
typedef struct s_wKeyValuePair wKeyValuePair;
struct s_wKeyValuePair
{
void* key;
void* value;
wKeyValuePair* next;
BOOL markedForRemove;
};
struct s_wHashTable
{
BOOL synchronized;
CRITICAL_SECTION lock;
size_t numOfBuckets;
size_t numOfElements;
float idealRatio;
float lowerRehashThreshold;
float upperRehashThreshold;
wKeyValuePair** bucketArray;
HASH_TABLE_HASH_FN hash;
wObject key;
wObject value;
DWORD foreachRecursionLevel;
DWORD pendingRemoves;
};
BOOL HashTable_PointerCompare(const void* pointer1, const void* pointer2)
{
return (pointer1 == pointer2);
}
UINT32 HashTable_PointerHash(const void* pointer)
{
return ((UINT32)(UINT_PTR)pointer) >> 4;
}
BOOL HashTable_StringCompare(const void* string1, const void* string2)
{
if (!string1 || !string2)
return (string1 == string2);
return (strcmp((const char*)string1, (const char*)string2) == 0);
}
UINT32 HashTable_StringHash(const void* key)
{
UINT32 c = 0;
UINT32 hash = 5381;
const BYTE* str = (const BYTE*)key;
/* djb2 algorithm */
while ((c = *str++) != '\0')
hash = (hash * 33) + c;
return hash;
}
void* HashTable_StringClone(const void* str)
{
return winpr_ObjectStringClone(str);
}
void HashTable_StringFree(void* str)
{
winpr_ObjectStringFree(str);
}
static inline BOOL HashTable_IsProbablePrime(size_t oddNumber)
{
for (size_t i = 3; i < 51; i += 2)
{
if (oddNumber == i)
return TRUE;
else if (oddNumber % i == 0)
return FALSE;
}
return TRUE; /* maybe */
}
static inline size_t HashTable_CalculateIdealNumOfBuckets(wHashTable* table)
{
WINPR_ASSERT(table);
const float numOfElements = (float)table->numOfElements;
const float tmp = (numOfElements / table->idealRatio);
size_t idealNumOfBuckets = (size_t)tmp;
if (idealNumOfBuckets < 5)
idealNumOfBuckets = 5;
else
idealNumOfBuckets |= 0x01;
while (!HashTable_IsProbablePrime(idealNumOfBuckets))
idealNumOfBuckets += 2;
return idealNumOfBuckets;
}
static inline void HashTable_Rehash(wHashTable* table, size_t numOfBuckets)
{
UINT32 hashValue = 0;
wKeyValuePair* nextPair = nullptr;
wKeyValuePair** newBucketArray = nullptr;
WINPR_ASSERT(table);
if (numOfBuckets == 0)
numOfBuckets = HashTable_CalculateIdealNumOfBuckets(table);
if (numOfBuckets == table->numOfBuckets)
return; /* already the right size! */
newBucketArray = (wKeyValuePair**)calloc(numOfBuckets, sizeof(wKeyValuePair*));
if (!newBucketArray)
{
/*
* Couldn't allocate memory for the new array.
* This isn't a fatal error; we just can't perform the rehash.
*/
return;
}
for (size_t index = 0; index < table->numOfBuckets; index++)
{
wKeyValuePair* pair = table->bucketArray[index];
while (pair)
{
nextPair = pair->next;
hashValue = table->hash(pair->key) % numOfBuckets;
pair->next = newBucketArray[hashValue];
newBucketArray[hashValue] = pair;
pair = nextPair;
}
}
free((void*)table->bucketArray);
table->bucketArray = newBucketArray;
table->numOfBuckets = numOfBuckets;
}
static inline BOOL HashTable_Equals(wHashTable* table, const wKeyValuePair* pair, const void* key)
{
WINPR_ASSERT(table);
WINPR_ASSERT(pair);
WINPR_ASSERT(key);
return table->key.fnObjectEquals(key, pair->key);
}
static inline wKeyValuePair* HashTable_Get(wHashTable* table, const void* key)
{
UINT32 hashValue = 0;
wKeyValuePair* pair = nullptr;
WINPR_ASSERT(table);
if (!key)
return nullptr;
hashValue = table->hash(key) % table->numOfBuckets;
pair = table->bucketArray[hashValue];
while (pair && !HashTable_Equals(table, pair, key))
pair = pair->next;
return pair;
}
static inline void disposeKey(wHashTable* table, void* key)
{
WINPR_ASSERT(table);
if (table->key.fnObjectFree)
table->key.fnObjectFree(key);
}
static inline void disposeValue(wHashTable* table, void* value)
{
WINPR_ASSERT(table);
if (table->value.fnObjectFree)
table->value.fnObjectFree(value);
}
static inline void disposePair(wHashTable* table, wKeyValuePair* pair)
{
WINPR_ASSERT(table);
if (!pair)
return;
disposeKey(table, pair->key);
disposeValue(table, pair->value);
free(pair);
}
static inline void setKey(wHashTable* table, wKeyValuePair* pair, const void* key)
{
WINPR_ASSERT(table);
if (!pair)
return;
disposeKey(table, pair->key);
if (table->key.fnObjectNew)
pair->key = table->key.fnObjectNew(key);
else
{
union
{
const void* cpv;
void* pv;
} cnv;
cnv.cpv = key;
pair->key = cnv.pv;
}
}
static inline void setValue(wHashTable* table, wKeyValuePair* pair, const void* value)
{
WINPR_ASSERT(table);
if (!pair)
return;
disposeValue(table, pair->value);
if (table->value.fnObjectNew)
pair->value = table->value.fnObjectNew(value);
else
{
union
{
const void* cpv;
void* pv;
} cnv;
cnv.cpv = value;
pair->value = cnv.pv;
}
}
/**
* C equivalent of the C# Hashtable Class:
* http://msdn.microsoft.com/en-us/library/system.collections.hashtable.aspx
*/
/**
* Properties
*/
/**
* Gets the number of key/value pairs contained in the HashTable.
*/
size_t HashTable_Count(wHashTable* table)
{
WINPR_ASSERT(table);
return table->numOfElements;
}
/**
* Methods
*/
/**
* Adds an element with the specified key and value into the HashTable.
*/
#if defined(WITH_WINPR_DEPRECATED)
int HashTable_Add(wHashTable* table, const void* key, const void* value)
{
if (!HashTable_Insert(table, key, value))
return -1;
return 0;
}
#endif
BOOL HashTable_Insert(wHashTable* table, const void* key, const void* value)
{
BOOL rc = FALSE;
UINT32 hashValue = 0;
wKeyValuePair* pair = nullptr;
wKeyValuePair* newPair = nullptr;
WINPR_ASSERT(table);
if (!key || !value)
return FALSE;
if (table->synchronized)
EnterCriticalSection(&table->lock);
hashValue = table->hash(key) % table->numOfBuckets;
pair = table->bucketArray[hashValue];
while (pair && !HashTable_Equals(table, pair, key))
pair = pair->next;
if (pair)
{
if (pair->markedForRemove)
{
/* this entry was set to be removed but will be recycled instead */
table->pendingRemoves--;
pair->markedForRemove = FALSE;
table->numOfElements++;
}
if (pair->key != key)
{
setKey(table, pair, key);
}
if (pair->value != value)
{
setValue(table, pair, value);
}
rc = TRUE;
}
else
{
newPair = (wKeyValuePair*)calloc(1, sizeof(wKeyValuePair));
if (newPair)
{
setKey(table, newPair, key);
setValue(table, newPair, value);
newPair->next = table->bucketArray[hashValue];
newPair->markedForRemove = FALSE;
table->bucketArray[hashValue] = newPair;
table->numOfElements++;
if (!table->foreachRecursionLevel && table->upperRehashThreshold > table->idealRatio)
{
float elementToBucketRatio =
(float)table->numOfElements / (float)table->numOfBuckets;
if (elementToBucketRatio > table->upperRehashThreshold)
HashTable_Rehash(table, 0);
}
rc = TRUE;
}
}
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return rc;
}
/**
* Removes the element with the specified key from the HashTable.
*/
BOOL HashTable_Remove(wHashTable* table, const void* key)
{
UINT32 hashValue = 0;
BOOL status = TRUE;
wKeyValuePair* pair = nullptr;
wKeyValuePair* previousPair = nullptr;
WINPR_ASSERT(table);
if (!key)
return FALSE;
if (table->synchronized)
EnterCriticalSection(&table->lock);
hashValue = table->hash(key) % table->numOfBuckets;
pair = table->bucketArray[hashValue];
while (pair && !HashTable_Equals(table, pair, key))
{
previousPair = pair;
pair = pair->next;
}
if (!pair)
{
status = FALSE;
goto out;
}
if (table->foreachRecursionLevel)
{
/* if we are running a HashTable_Foreach, just mark the entry for removal */
pair->markedForRemove = TRUE;
table->pendingRemoves++;
table->numOfElements--;
goto out;
}
if (previousPair)
previousPair->next = pair->next;
else
table->bucketArray[hashValue] = pair->next;
disposePair(table, pair);
table->numOfElements--;
if (!table->foreachRecursionLevel && table->lowerRehashThreshold > 0.0f)
{
float elementToBucketRatio = (float)table->numOfElements / (float)table->numOfBuckets;
if (elementToBucketRatio < table->lowerRehashThreshold)
HashTable_Rehash(table, 0);
}
out:
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return status;
}
/**
* Get an item value using key
*/
void* HashTable_GetItemValue(wHashTable* table, const void* key)
{
void* value = nullptr;
wKeyValuePair* pair = nullptr;
WINPR_ASSERT(table);
if (!key)
return nullptr;
if (table->synchronized)
EnterCriticalSection(&table->lock);
pair = HashTable_Get(table, key);
if (pair && !pair->markedForRemove)
value = pair->value;
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return value;
}
/**
* Set an item value using key
*/
BOOL HashTable_SetItemValue(wHashTable* table, const void* key, const void* value)
{
BOOL status = TRUE;
wKeyValuePair* pair = nullptr;
WINPR_ASSERT(table);
if (!key)
return FALSE;
if (table->synchronized)
EnterCriticalSection(&table->lock);
pair = HashTable_Get(table, key);
if (!pair || pair->markedForRemove)
status = FALSE;
else
{
setValue(table, pair, value);
}
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return status;
}
/**
* Removes all elements from the HashTable.
*/
void HashTable_Clear(wHashTable* table)
{
wKeyValuePair* nextPair = nullptr;
WINPR_ASSERT(table);
if (table->synchronized)
EnterCriticalSection(&table->lock);
for (size_t index = 0; index < table->numOfBuckets; index++)
{
wKeyValuePair* pair = table->bucketArray[index];
while (pair)
{
nextPair = pair->next;
if (table->foreachRecursionLevel)
{
/* if we're in a foreach we just mark the entry for removal */
pair->markedForRemove = TRUE;
table->pendingRemoves++;
}
else
{
disposePair(table, pair);
pair = nextPair;
}
}
table->bucketArray[index] = nullptr;
}
table->numOfElements = 0;
if (table->foreachRecursionLevel == 0)
HashTable_Rehash(table, 5);
if (table->synchronized)
LeaveCriticalSection(&table->lock);
}
/**
* Gets the list of keys as an array
*/
size_t HashTable_GetKeys(wHashTable* table, ULONG_PTR** ppKeys)
{
size_t iKey = 0;
size_t count = 0;
ULONG_PTR* pKeys = nullptr;
wKeyValuePair* nextPair = nullptr;
WINPR_ASSERT(table);
if (table->synchronized)
EnterCriticalSection(&table->lock);
iKey = 0;
count = table->numOfElements;
if (ppKeys)
*ppKeys = nullptr;
if (count < 1)
{
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return 0;
}
pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR));
if (!pKeys)
{
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return 0;
}
for (size_t index = 0; index < table->numOfBuckets; index++)
{
wKeyValuePair* pair = table->bucketArray[index];
while (pair)
{
nextPair = pair->next;
if (!pair->markedForRemove)
pKeys[iKey++] = (ULONG_PTR)pair->key;
pair = nextPair;
}
}
if (table->synchronized)
LeaveCriticalSection(&table->lock);
if (ppKeys)
*ppKeys = pKeys;
else
free(pKeys);
return count;
}
BOOL HashTable_Foreach(wHashTable* table, HASH_TABLE_FOREACH_FN fn, VOID* arg)
{
BOOL ret = TRUE;
WINPR_ASSERT(table);
WINPR_ASSERT(fn);
if (table->synchronized)
EnterCriticalSection(&table->lock);
table->foreachRecursionLevel++;
for (size_t index = 0; index < table->numOfBuckets; index++)
{
for (wKeyValuePair* pair = table->bucketArray[index]; pair; pair = pair->next)
{
if (!pair->markedForRemove && !fn(pair->key, pair->value, arg))
{
ret = FALSE;
goto out;
}
}
}
table->foreachRecursionLevel--;
if (!table->foreachRecursionLevel && table->pendingRemoves)
{
/* if we're the last recursive foreach call, let's do the cleanup if needed */
wKeyValuePair** prevPtr = nullptr;
for (size_t index = 0; index < table->numOfBuckets; index++)
{
wKeyValuePair* nextPair = nullptr;
prevPtr = &table->bucketArray[index];
for (wKeyValuePair* pair = table->bucketArray[index]; pair;)
{
nextPair = pair->next;
if (pair->markedForRemove)
{
disposePair(table, pair);
*prevPtr = nextPair;
}
else
{
prevPtr = &pair->next;
}
pair = nextPair;
}
}
table->pendingRemoves = 0;
}
out:
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return ret;
}
/**
* Determines whether the HashTable contains a specific key.
*/
BOOL HashTable_Contains(wHashTable* table, const void* key)
{
BOOL status = 0;
wKeyValuePair* pair = nullptr;
WINPR_ASSERT(table);
if (!key)
return FALSE;
if (table->synchronized)
EnterCriticalSection(&table->lock);
pair = HashTable_Get(table, key);
status = (pair && !pair->markedForRemove);
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return status;
}
/**
* Determines whether the HashTable contains a specific key.
*/
BOOL HashTable_ContainsKey(wHashTable* table, const void* key)
{
BOOL status = 0;
wKeyValuePair* pair = nullptr;
WINPR_ASSERT(table);
if (!key)
return FALSE;
if (table->synchronized)
EnterCriticalSection(&table->lock);
pair = HashTable_Get(table, key);
status = (pair && !pair->markedForRemove);
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return status;
}
/**
* Determines whether the HashTable contains a specific value.
*/
BOOL HashTable_ContainsValue(wHashTable* table, const void* value)
{
BOOL status = FALSE;
WINPR_ASSERT(table);
if (!value)
return FALSE;
if (table->synchronized)
EnterCriticalSection(&table->lock);
for (size_t index = 0; index < table->numOfBuckets; index++)
{
wKeyValuePair* pair = table->bucketArray[index];
while (pair)
{
if (!pair->markedForRemove && HashTable_Equals(table, pair, value))
{
status = TRUE;
break;
}
pair = pair->next;
}
if (status)
break;
}
if (table->synchronized)
LeaveCriticalSection(&table->lock);
return status;
}
/**
* Construction, Destruction
*/
wHashTable* HashTable_New(BOOL synchronized)
{
wHashTable* table = (wHashTable*)calloc(1, sizeof(wHashTable));
if (!table)
goto fail;
table->synchronized = synchronized;
if (!InitializeCriticalSectionAndSpinCount(&(table->lock), 4000))
goto fail;
table->numOfBuckets = 64;
table->numOfElements = 0;
table->bucketArray = (wKeyValuePair**)calloc(table->numOfBuckets, sizeof(wKeyValuePair*));
if (!table->bucketArray)
goto fail;
table->idealRatio = 3.0f;
table->lowerRehashThreshold = 0.0f;
table->upperRehashThreshold = 15.0f;
table->hash = HashTable_PointerHash;
table->key.fnObjectEquals = HashTable_PointerCompare;
table->value.fnObjectEquals = HashTable_PointerCompare;
return table;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
HashTable_Free(table);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void HashTable_Free(wHashTable* table)
{
wKeyValuePair* pair = nullptr;
wKeyValuePair* nextPair = nullptr;
if (!table)
return;
if (table->bucketArray)
{
for (size_t index = 0; index < table->numOfBuckets; index++)
{
pair = table->bucketArray[index];
while (pair)
{
nextPair = pair->next;
disposePair(table, pair);
pair = nextPair;
}
}
free((void*)table->bucketArray);
}
DeleteCriticalSection(&(table->lock));
free(table);
}
void HashTable_Lock(wHashTable* table)
{
WINPR_ASSERT(table);
EnterCriticalSection(&table->lock);
}
void HashTable_Unlock(wHashTable* table)
{
WINPR_ASSERT(table);
LeaveCriticalSection(&table->lock);
}
wObject* HashTable_KeyObject(wHashTable* table)
{
WINPR_ASSERT(table);
return &table->key;
}
wObject* HashTable_ValueObject(wHashTable* table)
{
WINPR_ASSERT(table);
return &table->value;
}
BOOL HashTable_SetHashFunction(wHashTable* table, HASH_TABLE_HASH_FN fn)
{
WINPR_ASSERT(table);
table->hash = fn;
return fn != nullptr;
}
BOOL HashTable_SetupForStringData(wHashTable* table, BOOL stringValues)
{
wObject* obj = nullptr;
if (!HashTable_SetHashFunction(table, HashTable_StringHash))
return FALSE;
obj = HashTable_KeyObject(table);
obj->fnObjectEquals = HashTable_StringCompare;
obj->fnObjectNew = HashTable_StringClone;
obj->fnObjectFree = HashTable_StringFree;
if (stringValues)
{
obj = HashTable_ValueObject(table);
obj->fnObjectEquals = HashTable_StringCompare;
obj->fnObjectNew = HashTable_StringClone;
obj->fnObjectFree = HashTable_StringFree;
}
return TRUE;
}

View File

@@ -0,0 +1,385 @@
/**
* WinPR: Windows Portable Runtime
* System.Collections.Generic.LinkedList<T>
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/collections.h>
#include <winpr/assert.h>
typedef struct s_wLinkedListItem wLinkedListNode;
struct s_wLinkedListItem
{
void* value;
wLinkedListNode* prev;
wLinkedListNode* next;
};
struct s_wLinkedList
{
size_t count;
int initial;
wLinkedListNode* head;
wLinkedListNode* tail;
wLinkedListNode* current;
wObject object;
};
/**
* C equivalent of the C# LinkedList<T> Class:
* http://msdn.microsoft.com/en-us/library/he2s3bh7.aspx
*
* Internal implementation uses a doubly-linked list
*/
/**
* Properties
*/
/**
* Gets the number of nodes actually contained in the LinkedList.
*/
size_t LinkedList_Count(wLinkedList* list)
{
WINPR_ASSERT(list);
return list->count;
}
/**
* Gets the first node of the LinkedList.
*/
void* LinkedList_First(wLinkedList* list)
{
WINPR_ASSERT(list);
if (list->head)
return list->head->value;
else
return nullptr;
}
/**
* Gets the last node of the LinkedList.
*/
void* LinkedList_Last(wLinkedList* list)
{
WINPR_ASSERT(list);
if (list->tail)
return list->tail->value;
else
return nullptr;
}
/**
* Methods
*/
/**
* Determines whether the LinkedList contains a specific value.
*/
BOOL LinkedList_Contains(wLinkedList* list, const void* value)
{
wLinkedListNode* item = nullptr;
OBJECT_EQUALS_FN keyEquals = nullptr;
WINPR_ASSERT(list);
if (!list->head)
return FALSE;
item = list->head;
keyEquals = list->object.fnObjectEquals;
while (item)
{
if (keyEquals(item->value, value))
break;
item = item->next;
}
return (item != nullptr);
}
static wLinkedListNode* LinkedList_FreeNode(wLinkedList* list, wLinkedListNode* node)
{
wLinkedListNode* next = nullptr;
wLinkedListNode* prev = nullptr;
WINPR_ASSERT(list);
WINPR_ASSERT(node);
next = node->next;
prev = node->prev;
if (prev)
prev->next = next;
if (next)
next->prev = prev;
if (node == list->head)
list->head = node->next;
if (node == list->tail)
list->tail = node->prev;
if (list->object.fnObjectUninit)
list->object.fnObjectUninit(node);
if (list->object.fnObjectFree)
list->object.fnObjectFree(node);
free(node);
list->count--;
return next;
}
/**
* Removes all entries from the LinkedList.
*/
void LinkedList_Clear(wLinkedList* list)
{
wLinkedListNode* node = nullptr;
WINPR_ASSERT(list);
if (!list->head)
return;
node = list->head;
while (node)
node = LinkedList_FreeNode(list, node);
list->head = list->tail = nullptr;
list->count = 0;
}
static wLinkedListNode* LinkedList_Create(wLinkedList* list, const void* value)
{
wLinkedListNode* node = nullptr;
WINPR_ASSERT(list);
node = (wLinkedListNode*)calloc(1, sizeof(wLinkedListNode));
if (!node)
return nullptr;
if (list->object.fnObjectNew)
node->value = list->object.fnObjectNew(value);
else
{
union
{
const void* cpv;
void* pv;
} cnv;
cnv.cpv = value;
node->value = cnv.pv;
}
if (list->object.fnObjectInit)
list->object.fnObjectInit(node);
return node;
}
/**
* Adds a new node containing the specified value at the start of the LinkedList.
*/
BOOL LinkedList_AddFirst(wLinkedList* list, const void* value)
{
wLinkedListNode* node = LinkedList_Create(list, value);
if (!node)
return FALSE;
if (!list->head)
{
list->tail = list->head = node;
}
else
{
list->head->prev = node;
node->next = list->head;
list->head = node;
}
list->count++;
return TRUE;
}
/**
* Adds a new node containing the specified value at the end of the LinkedList.
*/
BOOL LinkedList_AddLast(wLinkedList* list, const void* value)
{
wLinkedListNode* node = LinkedList_Create(list, value);
if (!node)
return FALSE;
if (!list->tail)
{
list->head = list->tail = node;
}
else
{
list->tail->next = node;
node->prev = list->tail;
list->tail = node;
}
list->count++;
return TRUE;
}
/**
* Removes the first occurrence of the specified value from the LinkedList.
*/
BOOL LinkedList_Remove(wLinkedList* list, const void* value)
{
wLinkedListNode* node = nullptr;
OBJECT_EQUALS_FN keyEquals = nullptr;
WINPR_ASSERT(list);
keyEquals = list->object.fnObjectEquals;
node = list->head;
while (node)
{
if (keyEquals(node->value, value))
{
LinkedList_FreeNode(list, node);
return TRUE;
}
node = node->next;
}
return FALSE;
}
/**
* Removes the node at the start of the LinkedList.
*/
void LinkedList_RemoveFirst(wLinkedList* list)
{
WINPR_ASSERT(list);
if (list->head)
LinkedList_FreeNode(list, list->head);
}
/**
* Removes the node at the end of the LinkedList.
*/
void LinkedList_RemoveLast(wLinkedList* list)
{
WINPR_ASSERT(list);
if (list->tail)
LinkedList_FreeNode(list, list->tail);
}
/**
* Sets the enumerator to its initial position, which is before the first element in the collection.
*/
void LinkedList_Enumerator_Reset(wLinkedList* list)
{
WINPR_ASSERT(list);
list->initial = 1;
list->current = list->head;
}
/*
* Gets the element at the current position of the enumerator.
*/
void* LinkedList_Enumerator_Current(wLinkedList* list)
{
WINPR_ASSERT(list);
if (list->initial)
return nullptr;
if (list->current)
return list->current->value;
else
return nullptr;
}
/*
* Advances the enumerator to the next element of the LinkedList.
*/
BOOL LinkedList_Enumerator_MoveNext(wLinkedList* list)
{
WINPR_ASSERT(list);
if (list->initial)
list->initial = 0;
else if (list->current)
list->current = list->current->next;
if (!list->current)
return FALSE;
return TRUE;
}
static BOOL default_equal_function(const void* objA, const void* objB)
{
return objA == objB;
}
/**
* Construction, Destruction
*/
wLinkedList* LinkedList_New(void)
{
wLinkedList* list = nullptr;
list = (wLinkedList*)calloc(1, sizeof(wLinkedList));
if (list)
{
list->object.fnObjectEquals = default_equal_function;
}
return list;
}
void LinkedList_Free(wLinkedList* list)
{
if (list)
{
LinkedList_Clear(list);
free(list);
}
}
wObject* LinkedList_Object(wLinkedList* list)
{
WINPR_ASSERT(list);
return &list->object;
}

View File

@@ -0,0 +1,562 @@
/**
* WinPR: Windows Portable Runtime
* System.Collections.Specialized.ListDictionary
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/collections.h>
typedef struct s_wListDictionaryItem wListDictionaryItem;
struct s_wListDictionaryItem
{
void* key;
void* value;
wListDictionaryItem* next;
};
struct s_wListDictionary
{
BOOL synchronized;
CRITICAL_SECTION lock;
wListDictionaryItem* head;
wObject objectKey;
wObject objectValue;
};
/**
* C equivalent of the C# ListDictionary Class:
* http://msdn.microsoft.com/en-us/library/system.collections.specialized.listdictionary.aspx
*
* Internal implementation uses a singly-linked list
*/
WINPR_API wObject* ListDictionary_KeyObject(wListDictionary* listDictionary)
{
WINPR_ASSERT(listDictionary);
return &listDictionary->objectKey;
}
WINPR_API wObject* ListDictionary_ValueObject(wListDictionary* listDictionary)
{
WINPR_ASSERT(listDictionary);
return &listDictionary->objectValue;
}
/**
* Properties
*/
/**
* Gets the number of key/value pairs contained in the ListDictionary.
*/
size_t ListDictionary_Count(wListDictionary* listDictionary)
{
size_t count = 0;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
if (listDictionary->head)
{
wListDictionaryItem* item = listDictionary->head;
while (item)
{
count++;
item = item->next;
}
}
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return count;
}
/**
* Lock access to the ListDictionary
*/
void ListDictionary_Lock(wListDictionary* listDictionary)
{
WINPR_ASSERT(listDictionary);
EnterCriticalSection(&listDictionary->lock);
}
/**
* Unlock access to the ListDictionary
*/
void ListDictionary_Unlock(wListDictionary* listDictionary)
{
WINPR_ASSERT(listDictionary);
LeaveCriticalSection(&listDictionary->lock);
}
/**
* Methods
*/
/**
* Gets the list of keys as an array
*/
size_t ListDictionary_GetKeys(wListDictionary* listDictionary, ULONG_PTR** ppKeys)
{
ULONG_PTR* pKeys = nullptr;
WINPR_ASSERT(listDictionary);
if (!ppKeys)
return 0;
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
size_t count = 0;
if (listDictionary->head)
{
wListDictionaryItem* item = listDictionary->head;
while (item)
{
count++;
item = item->next;
}
}
if (count > 0)
{
pKeys = (ULONG_PTR*)calloc(count, sizeof(ULONG_PTR));
if (!pKeys)
{
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return 0;
}
}
size_t index = 0;
if (listDictionary->head)
{
wListDictionaryItem* item = listDictionary->head;
while (item)
{
pKeys[index++] = (ULONG_PTR)item->key;
item = item->next;
}
}
*ppKeys = pKeys;
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return count;
}
static void item_free(wListDictionary* listDictionary, wListDictionaryItem* item)
{
WINPR_ASSERT(listDictionary);
if (item)
{
if (listDictionary->objectKey.fnObjectFree)
listDictionary->objectKey.fnObjectFree(item->key);
if (listDictionary->objectValue.fnObjectFree)
listDictionary->objectValue.fnObjectFree(item->value);
}
free(item);
}
static void item_set(wListDictionary* listDictionary, wListDictionaryItem* item, const void* value)
{
WINPR_ASSERT(listDictionary);
WINPR_ASSERT(item);
if (listDictionary->objectValue.fnObjectFree)
listDictionary->objectValue.fnObjectFree(item->value);
if (listDictionary->objectValue.fnObjectNew)
item->value = listDictionary->objectValue.fnObjectNew(value);
else
item->value = (void*)(uintptr_t)value;
}
static wListDictionaryItem* new_item(wListDictionary* listDictionary, const void* key,
const void* value)
{
wListDictionaryItem* item = (wListDictionaryItem*)calloc(1, sizeof(wListDictionaryItem));
if (!item)
return nullptr;
if (listDictionary->objectKey.fnObjectNew)
item->key = listDictionary->objectKey.fnObjectNew(key);
else
item->key = (void*)(uintptr_t)key;
if (!item->key)
goto fail;
item_set(listDictionary, item, value);
if (value && !item->value)
goto fail;
return item;
fail:
item_free(listDictionary, item);
return nullptr;
}
/**
* Adds an entry with the specified key and value into the ListDictionary.
*/
BOOL ListDictionary_Add(wListDictionary* listDictionary, const void* key, const void* value)
{
BOOL ret = FALSE;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
wListDictionaryItem* item = new_item(listDictionary, key, value);
if (!item)
goto out_error;
if (!listDictionary->head)
{
listDictionary->head = item;
}
else
{
wListDictionaryItem* lastItem = listDictionary->head;
while (lastItem->next)
lastItem = lastItem->next;
lastItem->next = item;
}
ret = TRUE;
out_error:
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return ret;
}
/**
* Removes all entries from the ListDictionary.
*/
void ListDictionary_Clear(wListDictionary* listDictionary)
{
wListDictionaryItem* item = nullptr;
wListDictionaryItem* nextItem = nullptr;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
if (listDictionary->head)
{
item = listDictionary->head;
while (item)
{
nextItem = item->next;
item_free(listDictionary, item);
item = nextItem;
}
listDictionary->head = nullptr;
}
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
}
/**
* Determines whether the ListDictionary contains a specific key.
*/
BOOL ListDictionary_Contains(wListDictionary* listDictionary, const void* key)
{
wListDictionaryItem* item = nullptr;
OBJECT_EQUALS_FN keyEquals = nullptr;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
keyEquals = listDictionary->objectKey.fnObjectEquals;
item = listDictionary->head;
while (item)
{
if (keyEquals(item->key, key))
break;
item = item->next;
}
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return (item != nullptr);
}
/**
* Removes the entry with the specified key from the ListDictionary.
*/
static void* ListDictionary_RemoveOrTake(wListDictionary* listDictionary, const void* key,
BOOL take)
{
void* value = nullptr;
wListDictionaryItem* item = nullptr;
wListDictionaryItem* prevItem = nullptr;
OBJECT_EQUALS_FN keyEquals = nullptr;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
keyEquals = listDictionary->objectKey.fnObjectEquals;
item = listDictionary->head;
prevItem = nullptr;
while (item)
{
if (keyEquals(item->key, key))
{
if (!prevItem)
listDictionary->head = item->next;
else
prevItem->next = item->next;
if (take)
{
value = item->value;
item->value = nullptr;
}
item_free(listDictionary, item);
break;
}
prevItem = item;
item = item->next;
}
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return value;
}
void ListDictionary_Remove(wListDictionary* listDictionary, const void* key)
{
ListDictionary_RemoveOrTake(listDictionary, key, FALSE);
}
void* ListDictionary_Take(wListDictionary* listDictionary, const void* key)
{
return ListDictionary_RemoveOrTake(listDictionary, key, TRUE);
}
/**
* Removes the first (head) entry from the list
*/
static void* ListDictionary_Remove_Or_Take_Head(wListDictionary* listDictionary, BOOL take)
{
wListDictionaryItem* item = nullptr;
void* value = nullptr;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
if (listDictionary->head)
{
item = listDictionary->head;
listDictionary->head = listDictionary->head->next;
if (take)
{
value = item->value;
item->value = nullptr;
}
item_free(listDictionary, item);
}
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return value;
}
void ListDictionary_Remove_Head(wListDictionary* listDictionary)
{
ListDictionary_Remove_Or_Take_Head(listDictionary, FALSE);
}
void* ListDictionary_Take_Head(wListDictionary* listDictionary)
{
return ListDictionary_Remove_Or_Take_Head(listDictionary, TRUE);
}
/**
* Get an item value using key
*/
void* ListDictionary_GetItemValue(wListDictionary* listDictionary, const void* key)
{
void* value = nullptr;
wListDictionaryItem* item = nullptr;
OBJECT_EQUALS_FN keyEquals = nullptr;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
keyEquals = listDictionary->objectKey.fnObjectEquals;
if (listDictionary->head)
{
item = listDictionary->head;
while (item)
{
if (keyEquals(item->key, key))
break;
item = item->next;
}
}
value = (item) ? item->value : nullptr;
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return value;
}
/**
* Set an item value using key
*/
BOOL ListDictionary_SetItemValue(wListDictionary* listDictionary, const void* key,
const void* value)
{
BOOL status = FALSE;
wListDictionaryItem* item = nullptr;
OBJECT_EQUALS_FN keyEquals = nullptr;
WINPR_ASSERT(listDictionary);
if (listDictionary->synchronized)
ListDictionary_Lock(listDictionary);
keyEquals = listDictionary->objectKey.fnObjectEquals;
if (listDictionary->head)
{
item = listDictionary->head;
while (item)
{
if (keyEquals(item->key, key))
break;
item = item->next;
}
if (item)
item_set(listDictionary, item, value);
status = (item != nullptr);
}
if (listDictionary->synchronized)
ListDictionary_Unlock(listDictionary);
return status;
}
static BOOL default_equal_function(const void* obj1, const void* obj2)
{
return (obj1 == obj2);
}
/**
* Construction, Destruction
*/
wListDictionary* ListDictionary_New(BOOL synchronized)
{
wListDictionary* listDictionary = (wListDictionary*)calloc(1, sizeof(wListDictionary));
if (!listDictionary)
return nullptr;
listDictionary->synchronized = synchronized;
if (!InitializeCriticalSectionAndSpinCount(&(listDictionary->lock), 4000))
{
free(listDictionary);
return nullptr;
}
listDictionary->objectKey.fnObjectEquals = default_equal_function;
listDictionary->objectValue.fnObjectEquals = default_equal_function;
return listDictionary;
}
void ListDictionary_Free(wListDictionary* listDictionary)
{
if (listDictionary)
{
ListDictionary_Clear(listDictionary);
DeleteCriticalSection(&listDictionary->lock);
free(listDictionary);
}
}

View File

@@ -0,0 +1,80 @@
/**
* WinPR: Windows Portable Runtime
* Message Pipe
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/sysinfo.h>
#include <winpr/collections.h>
/**
* Properties
*/
/**
* Methods
*/
void MessagePipe_PostQuit(wMessagePipe* pipe, int nExitCode)
{
MessageQueue_PostQuit(pipe->In, nExitCode);
MessageQueue_PostQuit(pipe->Out, nExitCode);
}
/**
* Construction, Destruction
*/
wMessagePipe* MessagePipe_New(void)
{
wMessagePipe* pipe = nullptr;
pipe = (wMessagePipe*)malloc(sizeof(wMessagePipe));
if (!pipe)
return nullptr;
pipe->In = MessageQueue_New(nullptr);
if (!pipe->In)
goto error_in;
pipe->Out = MessageQueue_New(nullptr);
if (!pipe->Out)
goto error_out;
return pipe;
error_out:
MessageQueue_Free(pipe->In);
error_in:
free(pipe);
return nullptr;
}
void MessagePipe_Free(wMessagePipe* pipe)
{
if (pipe)
{
MessageQueue_Free(pipe->In);
MessageQueue_Free(pipe->Out);
free(pipe);
}
}

View File

@@ -0,0 +1,354 @@
/**
* WinPR: Windows Portable Runtime
* Message Queue
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/sysinfo.h>
#include <winpr/assert.h>
#include <winpr/collections.h>
struct s_wMessageQueue
{
size_t head;
size_t tail;
size_t size;
size_t capacity;
BOOL closed;
wMessage* array;
CRITICAL_SECTION lock;
HANDLE event;
wObject object;
};
/**
* Message Queue inspired from Windows:
* http://msdn.microsoft.com/en-us/library/ms632590/
*/
/**
* Properties
*/
wObject* MessageQueue_Object(wMessageQueue* queue)
{
WINPR_ASSERT(queue);
return &queue->object;
}
/**
* Gets an event which is set when the queue is non-empty
*/
HANDLE MessageQueue_Event(wMessageQueue* queue)
{
WINPR_ASSERT(queue);
return queue->event;
}
/**
* Gets the queue size
*/
size_t MessageQueue_Size(wMessageQueue* queue)
{
WINPR_ASSERT(queue);
EnterCriticalSection(&queue->lock);
const size_t ret = queue->size;
LeaveCriticalSection(&queue->lock);
return ret;
}
size_t MessageQueue_Capacity(wMessageQueue* queue)
{
WINPR_ASSERT(queue);
EnterCriticalSection(&queue->lock);
const size_t ret = queue->capacity;
LeaveCriticalSection(&queue->lock);
return ret;
}
/**
* Methods
*/
BOOL MessageQueue_Wait(wMessageQueue* queue)
{
BOOL status = FALSE;
WINPR_ASSERT(queue);
if (WaitForSingleObject(queue->event, INFINITE) == WAIT_OBJECT_0)
status = TRUE;
return status;
}
static BOOL MessageQueue_EnsureCapacity(wMessageQueue* queue, size_t count)
{
const size_t increment = 128;
WINPR_ASSERT(queue);
const size_t required = queue->size + count;
// check for overflow
if ((required < queue->size) || (required < count) ||
(required > (SIZE_MAX - increment) / sizeof(wMessage)))
return FALSE;
if (required > queue->capacity)
{
const size_t old_capacity = queue->capacity;
const size_t new_capacity = required + increment;
wMessage* new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity);
if (!new_arr)
return FALSE;
queue->array = new_arr;
queue->capacity = new_capacity;
ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(wMessage));
/* rearrange wrapped entries:
* fill up the newly available space and move tail
* back by the amount of elements that have been moved to the newly
* allocated space.
*/
if (queue->tail <= queue->head)
{
size_t tocopy = queue->tail;
size_t slots = new_capacity - old_capacity;
const size_t batch = (tocopy < slots) ? tocopy : slots;
CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(wMessage));
/* Tail is decremented. if the whole thing is appended
* just move the existing tail by old_capacity */
if (tocopy < slots)
{
ZeroMemory(queue->array, batch * sizeof(wMessage));
queue->tail += old_capacity;
}
else
{
const size_t remain = queue->tail - batch;
const size_t movesize = remain * sizeof(wMessage);
memmove_s(queue->array, queue->tail * sizeof(wMessage), &queue->array[batch],
movesize);
const size_t zerooffset = remain;
const size_t zerosize = (queue->tail - remain) * sizeof(wMessage);
ZeroMemory(&queue->array[zerooffset], zerosize);
queue->tail -= batch;
}
}
}
return TRUE;
}
BOOL MessageQueue_Dispatch(wMessageQueue* queue, const wMessage* message)
{
wMessage* dst = nullptr;
BOOL ret = FALSE;
WINPR_ASSERT(queue);
if (!message)
return FALSE;
WINPR_ASSERT(queue);
EnterCriticalSection(&queue->lock);
if (queue->closed)
goto out;
if (!MessageQueue_EnsureCapacity(queue, 1))
goto out;
dst = &(queue->array[queue->tail]);
*dst = *message;
dst->time = GetTickCount64();
queue->tail = (queue->tail + 1) % queue->capacity;
queue->size++;
if (queue->size > 0)
(void)SetEvent(queue->event);
if (message->id == WMQ_QUIT)
queue->closed = TRUE;
ret = TRUE;
out:
LeaveCriticalSection(&queue->lock);
return ret;
}
BOOL MessageQueue_Post(wMessageQueue* queue, void* context, UINT32 type, void* wParam, void* lParam)
{
wMessage message = WINPR_C_ARRAY_INIT;
message.context = context;
message.id = type;
message.wParam = wParam;
message.lParam = lParam;
message.Free = nullptr;
return MessageQueue_Dispatch(queue, &message);
}
BOOL MessageQueue_PostQuit(wMessageQueue* queue, int nExitCode)
{
return MessageQueue_Post(queue, nullptr, WMQ_QUIT, (void*)(size_t)nExitCode, nullptr);
}
int MessageQueue_Get(wMessageQueue* queue, wMessage* message)
{
int status = -1;
if (!MessageQueue_Wait(queue))
return status;
EnterCriticalSection(&queue->lock);
if (queue->size > 0)
{
CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage));
ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage));
queue->head = (queue->head + 1) % queue->capacity;
queue->size--;
if (queue->size < 1)
(void)ResetEvent(queue->event);
status = (message->id != WMQ_QUIT) ? 1 : 0;
}
LeaveCriticalSection(&queue->lock);
return status;
}
int MessageQueue_Peek(wMessageQueue* queue, wMessage* message, BOOL remove)
{
int status = 0;
WINPR_ASSERT(queue);
EnterCriticalSection(&queue->lock);
if (queue->size > 0)
{
CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage));
status = 1;
if (remove)
{
ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage));
queue->head = (queue->head + 1) % queue->capacity;
queue->size--;
if (queue->size < 1)
(void)ResetEvent(queue->event);
}
}
LeaveCriticalSection(&queue->lock);
return status;
}
/**
* Construction, Destruction
*/
wMessageQueue* MessageQueue_New(const wObject* callback)
{
wMessageQueue* queue = nullptr;
queue = (wMessageQueue*)calloc(1, sizeof(wMessageQueue));
if (!queue)
return nullptr;
if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
goto fail;
if (!MessageQueue_EnsureCapacity(queue, 32))
goto fail;
queue->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!queue->event)
goto fail;
if (callback)
queue->object = *callback;
return queue;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
MessageQueue_Free(queue);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void MessageQueue_Free(wMessageQueue* queue)
{
if (!queue)
return;
if (queue->event)
MessageQueue_Clear(queue);
(void)CloseHandle(queue->event);
DeleteCriticalSection(&queue->lock);
free(queue->array);
free(queue);
}
int MessageQueue_Clear(wMessageQueue* queue)
{
int status = 0;
WINPR_ASSERT(queue);
WINPR_ASSERT(queue->event);
EnterCriticalSection(&queue->lock);
while (queue->size > 0)
{
wMessage* msg = &(queue->array[queue->head]);
/* Free resources of message. */
if (queue->object.fnObjectUninit)
queue->object.fnObjectUninit(msg);
if (queue->object.fnObjectFree)
queue->object.fnObjectFree(msg);
ZeroMemory(msg, sizeof(wMessage));
queue->head = (queue->head + 1) % queue->capacity;
queue->size--;
}
(void)ResetEvent(queue->event);
queue->closed = FALSE;
LeaveCriticalSection(&queue->lock);
return status;
}

View File

@@ -0,0 +1,42 @@
/**
* WinPR: Windows Portable Runtime
* Collections
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 Thincast Technologies GmbH
*
* 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.
*/
#include <winpr/string.h>
#include <winpr/collections.h>
void* winpr_ObjectStringClone(const void* pvstr)
{
const char* str = pvstr;
if (!str)
return nullptr;
return _strdup(str);
}
void* winpr_ObjectWStringClone(const void* pvstr)
{
const WCHAR* str = pvstr;
if (!str)
return nullptr;
return _wcsdup(str);
}
void winpr_ObjectStringFree(void* pvstr)
{
free(pvstr);
}

View File

@@ -0,0 +1,194 @@
/**
* WinPR: Windows Portable Runtime
* Object Pool
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/collections.h>
struct s_wObjectPool
{
size_t size;
size_t capacity;
void** array;
CRITICAL_SECTION lock;
wObject object;
BOOL synchronized;
};
/**
* C Object Pool similar to C# BufferManager Class:
* http://msdn.microsoft.com/en-us/library/ms405814.aspx
*/
/**
* Methods
*/
static void ObjectPool_Lock(wObjectPool* pool)
{
WINPR_ASSERT(pool);
if (pool->synchronized)
EnterCriticalSection(&pool->lock);
}
static void ObjectPool_Unlock(wObjectPool* pool)
{
WINPR_ASSERT(pool);
if (pool->synchronized)
LeaveCriticalSection(&pool->lock);
}
/**
* Gets an object from the pool.
*/
void* ObjectPool_Take(wObjectPool* pool)
{
void* obj = nullptr;
ObjectPool_Lock(pool);
if (pool->size > 0)
obj = pool->array[--(pool->size)];
if (!obj)
{
if (pool->object.fnObjectNew)
obj = pool->object.fnObjectNew(nullptr);
}
if (pool->object.fnObjectInit)
pool->object.fnObjectInit(obj);
ObjectPool_Unlock(pool);
return obj;
}
static BOOL ObjectPool_EnsureCapacity(wObjectPool* pool, size_t add)
{
WINPR_ASSERT(pool->size < SIZE_MAX - add);
const size_t blocksize = 128ull;
const size_t required = pool->size + add;
if (required >= pool->capacity)
{
const size_t new_cap = required + blocksize - required % blocksize;
void** new_arr = (void**)realloc((void*)pool->array, sizeof(void*) * new_cap);
if (!new_arr)
return FALSE;
pool->array = new_arr;
pool->capacity = new_cap;
}
return TRUE;
}
/**
* Returns an object to the pool.
*/
void ObjectPool_Return(wObjectPool* pool, void* obj)
{
ObjectPool_Lock(pool);
if (!ObjectPool_EnsureCapacity(pool, 1))
goto out;
pool->array[(pool->size)++] = obj;
if (pool->object.fnObjectUninit)
pool->object.fnObjectUninit(obj);
out:
ObjectPool_Unlock(pool);
}
wObject* ObjectPool_Object(wObjectPool* pool)
{
WINPR_ASSERT(pool);
return &pool->object;
}
/**
* Releases the buffers currently cached in the pool.
*/
void ObjectPool_Clear(wObjectPool* pool)
{
ObjectPool_Lock(pool);
while (pool->size > 0)
{
(pool->size)--;
if (pool->object.fnObjectFree)
pool->object.fnObjectFree(pool->array[pool->size]);
}
ObjectPool_Unlock(pool);
}
/**
* Construction, Destruction
*/
wObjectPool* ObjectPool_New(BOOL synchronized)
{
wObjectPool* pool = (wObjectPool*)calloc(1, sizeof(wObjectPool));
if (!pool)
goto fail;
pool->synchronized = synchronized;
if (pool->synchronized)
{
if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
goto fail;
}
if (!ObjectPool_EnsureCapacity(pool, 32))
goto fail;
return pool;
fail:
ObjectPool_Free(pool);
return nullptr;
}
void ObjectPool_Free(wObjectPool* pool)
{
if (!pool)
return;
ObjectPool_Clear(pool);
if (pool->synchronized)
DeleteCriticalSection(&pool->lock);
free((void*)pool->array);
free(pool);
}

View File

@@ -0,0 +1,273 @@
/**
* WinPR: Windows Portable Runtime
* Publisher/Subscriber Pattern
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/collections.h>
/**
* Events (C# Programming Guide)
* http://msdn.microsoft.com/en-us/library/awbftdfh.aspx
*/
struct s_wPubSub
{
CRITICAL_SECTION lock;
BOOL synchronized;
size_t size;
size_t count;
wEventType* events;
};
/**
* Properties
*/
wEventType* PubSub_GetEventTypes(wPubSub* pubSub, size_t* count)
{
WINPR_ASSERT(pubSub);
if (count)
*count = pubSub->count;
return pubSub->events;
}
/**
* Methods
*/
void PubSub_Lock(wPubSub* pubSub)
{
WINPR_ASSERT(pubSub);
if (pubSub->synchronized)
EnterCriticalSection(&pubSub->lock);
}
void PubSub_Unlock(wPubSub* pubSub)
{
WINPR_ASSERT(pubSub);
if (pubSub->synchronized)
LeaveCriticalSection(&pubSub->lock);
}
wEventType* PubSub_FindEventType(wPubSub* pubSub, const char* EventName)
{
wEventType* event = nullptr;
WINPR_ASSERT(pubSub);
WINPR_ASSERT(EventName);
for (size_t index = 0; index < pubSub->count; index++)
{
if (strcmp(pubSub->events[index].EventName, EventName) == 0)
{
event = &(pubSub->events[index]);
break;
}
}
return event;
}
void PubSub_AddEventTypes(wPubSub* pubSub, wEventType* events, size_t count)
{
WINPR_ASSERT(pubSub);
WINPR_ASSERT(events || (count == 0));
if (pubSub->synchronized)
PubSub_Lock(pubSub);
const size_t required = pubSub->count + count;
WINPR_ASSERT((required >= pubSub->count) && (required >= count));
if (required >= pubSub->size)
{
size_t new_size = pubSub->size;
do
{
WINPR_ASSERT(new_size <= SIZE_MAX - 128ull);
new_size += 128ull;
} while (new_size <= required);
wEventType* new_event = (wEventType*)realloc(pubSub->events, new_size * sizeof(wEventType));
if (!new_event)
goto fail;
pubSub->size = new_size;
pubSub->events = new_event;
}
CopyMemory(&pubSub->events[pubSub->count], events, count * sizeof(wEventType));
pubSub->count += count;
fail:
if (pubSub->synchronized)
PubSub_Unlock(pubSub);
}
int PubSub_Subscribe(wPubSub* pubSub, const char* EventName, ...)
{
wEventType* event = nullptr;
int status = -1;
WINPR_ASSERT(pubSub);
va_list ap = WINPR_C_ARRAY_INIT;
va_start(ap, EventName);
pEventHandler EventHandler = va_arg(ap, pEventHandler);
if (pubSub->synchronized)
PubSub_Lock(pubSub);
event = PubSub_FindEventType(pubSub, EventName);
if (event)
{
status = 0;
if (event->EventHandlerCount < MAX_EVENT_HANDLERS)
event->EventHandlers[event->EventHandlerCount++] = EventHandler;
else
status = -1;
}
if (pubSub->synchronized)
PubSub_Unlock(pubSub);
va_end(ap);
return status;
}
int PubSub_Unsubscribe(wPubSub* pubSub, const char* EventName, ...)
{
wEventType* event = nullptr;
int status = -1;
WINPR_ASSERT(pubSub);
WINPR_ASSERT(EventName);
va_list ap = WINPR_C_ARRAY_INIT;
va_start(ap, EventName);
pEventHandler EventHandler = va_arg(ap, pEventHandler);
if (pubSub->synchronized)
PubSub_Lock(pubSub);
event = PubSub_FindEventType(pubSub, EventName);
if (event)
{
status = 0;
for (size_t index = 0; index < event->EventHandlerCount; index++)
{
if (event->EventHandlers[index] == EventHandler)
{
event->EventHandlers[index] = nullptr;
event->EventHandlerCount--;
MoveMemory((void*)&event->EventHandlers[index],
(void*)&event->EventHandlers[index + 1],
(MAX_EVENT_HANDLERS - index - 1) * sizeof(pEventHandler));
status = 1;
}
}
}
if (pubSub->synchronized)
PubSub_Unlock(pubSub);
va_end(ap);
return status;
}
int PubSub_OnEvent(wPubSub* pubSub, const char* EventName, void* context, const wEventArgs* e)
{
wEventType* event = nullptr;
int status = -1;
if (!pubSub)
return -1;
WINPR_ASSERT(e);
if (pubSub->synchronized)
PubSub_Lock(pubSub);
event = PubSub_FindEventType(pubSub, EventName);
if (pubSub->synchronized)
PubSub_Unlock(pubSub);
if (event)
{
status = 0;
for (size_t index = 0; index < event->EventHandlerCount; index++)
{
if (event->EventHandlers[index])
{
event->EventHandlers[index](context, e);
status++;
}
}
}
return status;
}
/**
* Construction, Destruction
*/
wPubSub* PubSub_New(BOOL synchronized)
{
wPubSub* pubSub = (wPubSub*)calloc(1, sizeof(wPubSub));
if (!pubSub)
return nullptr;
pubSub->synchronized = synchronized;
if (pubSub->synchronized && !InitializeCriticalSectionAndSpinCount(&pubSub->lock, 4000))
goto fail;
pubSub->count = 0;
pubSub->size = 64;
pubSub->events = (wEventType*)calloc(pubSub->size, sizeof(wEventType));
if (!pubSub->events)
goto fail;
return pubSub;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
PubSub_Free(pubSub);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void PubSub_Free(wPubSub* pubSub)
{
if (pubSub)
{
if (pubSub->synchronized)
DeleteCriticalSection(&pubSub->lock);
free(pubSub->events);
free(pubSub);
}
}

View File

@@ -0,0 +1,405 @@
/**
* WinPR: Windows Portable Runtime
* System.Collections.Queue
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/collections.h>
struct s_wQueue
{
size_t capacity;
size_t growthFactor;
BOOL synchronized;
BYTE padding[4];
size_t head;
size_t tail;
size_t size;
uintptr_t* array;
CRITICAL_SECTION lock;
HANDLE event;
wObject object;
BOOL haveLock;
BYTE padding2[4];
};
static inline void* uptr2void(uintptr_t ptr)
{
return (void*)ptr;
}
static inline uintptr_t void2uptr(const void* ptr)
{
return (uintptr_t)ptr;
}
/**
* C equivalent of the C# Queue Class:
* http://msdn.microsoft.com/en-us/library/system.collections.queue.aspx
*/
/**
* Properties
*/
/**
* Gets the number of elements contained in the Queue.
*/
size_t Queue_Count(wQueue* queue)
{
size_t ret = 0;
Queue_Lock(queue);
ret = queue->size;
Queue_Unlock(queue);
return ret;
}
size_t Queue_Capacity(wQueue* queue)
{
WINPR_ASSERT(queue);
Queue_Lock(queue);
const size_t ret = queue->capacity;
Queue_Unlock(queue);
return ret;
}
/**
* Lock access to the ArrayList
*/
void Queue_Lock(wQueue* queue)
{
WINPR_ASSERT(queue);
if (queue->synchronized)
EnterCriticalSection(&queue->lock);
}
/**
* Unlock access to the ArrayList
*/
void Queue_Unlock(wQueue* queue)
{
WINPR_ASSERT(queue);
if (queue->synchronized)
LeaveCriticalSection(&queue->lock);
}
/**
* Gets an event which is set when the queue is non-empty
*/
HANDLE Queue_Event(wQueue* queue)
{
WINPR_ASSERT(queue);
return queue->event;
}
wObject* Queue_Object(wQueue* queue)
{
WINPR_ASSERT(queue);
return &queue->object;
}
/**
* Methods
*/
/**
* Removes all objects from the Queue.
*/
void Queue_Clear(wQueue* queue)
{
Queue_Lock(queue);
for (size_t index = queue->head; index != queue->tail; index = (index + 1) % queue->capacity)
{
if (queue->object.fnObjectFree)
{
void* obj = uptr2void(queue->array[index]);
queue->object.fnObjectFree(obj);
}
queue->array[index] = 0;
}
queue->size = 0;
queue->head = queue->tail = 0;
(void)ResetEvent(queue->event);
Queue_Unlock(queue);
}
/**
* Determines whether an element is in the Queue.
*/
BOOL Queue_Contains(wQueue* queue, const void* obj)
{
BOOL found = FALSE;
Queue_Lock(queue);
for (size_t index = 0; index < queue->tail; index++)
{
void* ptr = uptr2void(queue->array[index]);
if (queue->object.fnObjectEquals(ptr, obj))
{
found = TRUE;
break;
}
}
Queue_Unlock(queue);
return found;
}
static BOOL Queue_EnsureCapacity(wQueue* queue, size_t count)
{
const size_t blocksize = 32ull;
WINPR_ASSERT(queue);
if (queue->growthFactor > SIZE_MAX / blocksize)
return FALSE;
const size_t increment = blocksize * queue->growthFactor;
if (queue->size > SIZE_MAX - count)
return FALSE;
const size_t required = queue->size + count;
if (required > queue->capacity)
{
const size_t old_capacity = queue->capacity;
if (required > SIZE_MAX - increment)
return FALSE;
const size_t new_capacity = required + increment - required % increment;
if (new_capacity > SIZE_MAX / sizeof(BYTE*))
return FALSE;
uintptr_t* newArray = (uintptr_t*)realloc(queue->array, sizeof(uintptr_t) * new_capacity);
if (!newArray)
return FALSE;
queue->capacity = new_capacity;
queue->array = newArray;
ZeroMemory(&(queue->array[old_capacity]),
(new_capacity - old_capacity) * sizeof(uintptr_t));
/* rearrange wrapped entries */
if (queue->tail <= queue->head)
{
const size_t tocopy = queue->tail;
const size_t slots = new_capacity - old_capacity;
const size_t batch = (tocopy < slots) ? tocopy : slots;
CopyMemory(&(queue->array[old_capacity]), queue->array, batch * sizeof(uintptr_t));
/* Tail is decremented. if the whole thing is appended
* just move the existing tail by old_capacity */
if (tocopy < slots)
{
ZeroMemory(queue->array, batch * sizeof(uintptr_t));
queue->tail += old_capacity;
}
else
{
const size_t remain = queue->tail - batch;
const size_t movesize = remain * sizeof(uintptr_t);
memmove_s(queue->array, queue->tail * sizeof(uintptr_t), &queue->array[batch],
movesize);
const size_t zerooffset = remain;
const size_t zerosize = (queue->tail - remain) * sizeof(uintptr_t);
ZeroMemory(&queue->array[zerooffset], zerosize);
queue->tail -= batch;
}
}
}
return TRUE;
}
/**
* Adds an object to the end of the Queue.
*/
BOOL Queue_Enqueue(wQueue* queue, const void* obj)
{
BOOL ret = TRUE;
Queue_Lock(queue);
if (!Queue_EnsureCapacity(queue, 1))
goto out;
if (queue->object.fnObjectNew)
queue->array[queue->tail] = void2uptr(queue->object.fnObjectNew(obj));
else
queue->array[queue->tail] = void2uptr(obj);
queue->tail = (queue->tail + 1) % queue->capacity;
{
const BOOL signalSet = queue->size == 0;
queue->size++;
if (signalSet)
(void)SetEvent(queue->event);
}
out:
Queue_Unlock(queue);
return ret;
}
/**
* Removes and returns the object at the beginning of the Queue.
*/
void* Queue_Dequeue(wQueue* queue)
{
void* obj = nullptr;
Queue_Lock(queue);
if (queue->size > 0)
{
obj = uptr2void(queue->array[queue->head]);
queue->array[queue->head] = 0;
queue->head = (queue->head + 1) % queue->capacity;
queue->size--;
}
if (queue->size < 1)
(void)ResetEvent(queue->event);
Queue_Unlock(queue);
return obj;
}
/**
* Returns the object at the beginning of the Queue without removing it.
*/
void* Queue_Peek(wQueue* queue)
{
void* obj = nullptr;
Queue_Lock(queue);
if (queue->size > 0)
obj = uptr2void(queue->array[queue->head]);
Queue_Unlock(queue);
return obj;
}
void Queue_Discard(wQueue* queue)
{
void* obj = nullptr;
Queue_Lock(queue);
obj = Queue_Dequeue(queue);
if (queue->object.fnObjectFree)
queue->object.fnObjectFree(obj);
Queue_Unlock(queue);
}
static BOOL default_queue_equals(const void* obj1, const void* obj2)
{
return (obj1 == obj2);
}
/**
* Construction, Destruction
*/
wQueue* Queue_New(BOOL synchronized, SSIZE_T capacity, SSIZE_T growthFactor)
{
wQueue* queue = (wQueue*)calloc(1, sizeof(wQueue));
if (!queue)
return nullptr;
queue->synchronized = synchronized;
queue->growthFactor = 2;
if (growthFactor > 0)
queue->growthFactor = (size_t)growthFactor;
if (capacity <= 0)
capacity = 32;
if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
goto fail;
queue->haveLock = TRUE;
if (!Queue_EnsureCapacity(queue, (size_t)capacity))
goto fail;
queue->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
if (!queue->event)
goto fail;
{
wObject* obj = Queue_Object(queue);
obj->fnObjectEquals = default_queue_equals;
}
return queue;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
Queue_Free(queue);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void Queue_Free(wQueue* queue)
{
if (!queue)
return;
if (queue->haveLock)
{
Queue_Clear(queue);
DeleteCriticalSection(&queue->lock);
}
(void)CloseHandle(queue->event);
free(queue->array);
free(queue);
}

View File

@@ -0,0 +1,258 @@
/**
* WinPR: Windows Portable Runtime
* System.Collections.Stack
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/collections.h>
#include <winpr/assert.h>
struct s_wStack
{
size_t size;
size_t capacity;
void** array;
CRITICAL_SECTION lock;
BOOL synchronized;
wObject object;
};
/**
* C equivalent of the C# Stack Class:
* http://msdn.microsoft.com/en-us/library/system.collections.stack.aspx
*/
/**
* Properties
*/
/**
* Gets the number of elements contained in the Stack.
*/
size_t Stack_Count(wStack* stack)
{
size_t ret = 0;
WINPR_ASSERT(stack);
if (stack->synchronized)
EnterCriticalSection(&stack->lock);
ret = stack->size;
if (stack->synchronized)
LeaveCriticalSection(&stack->lock);
return ret;
}
/**
* Gets a value indicating whether access to the Stack is synchronized (thread safe).
*/
BOOL Stack_IsSynchronized(wStack* stack)
{
WINPR_ASSERT(stack);
return stack->synchronized;
}
wObject* Stack_Object(wStack* stack)
{
WINPR_ASSERT(stack);
return &stack->object;
}
/**
* Methods
*/
/**
* Removes all objects from the Stack.
*/
void Stack_Clear(wStack* stack)
{
WINPR_ASSERT(stack);
if (stack->synchronized)
EnterCriticalSection(&stack->lock);
for (size_t index = 0; index < stack->size; index++)
{
if (stack->object.fnObjectFree)
stack->object.fnObjectFree(stack->array[index]);
stack->array[index] = nullptr;
}
stack->size = 0;
if (stack->synchronized)
LeaveCriticalSection(&stack->lock);
}
/**
* Determines whether an element is in the Stack.
*/
BOOL Stack_Contains(wStack* stack, const void* obj)
{
BOOL found = FALSE;
WINPR_ASSERT(stack);
if (stack->synchronized)
EnterCriticalSection(&stack->lock);
for (size_t i = 0; i < stack->size; i++)
{
if (stack->object.fnObjectEquals(stack->array[i], obj))
{
found = TRUE;
break;
}
}
if (stack->synchronized)
LeaveCriticalSection(&stack->lock);
return found;
}
/**
* Inserts an object at the top of the Stack.
*/
void Stack_Push(wStack* stack, void* obj)
{
WINPR_ASSERT(stack);
if (stack->synchronized)
EnterCriticalSection(&stack->lock);
WINPR_ASSERT(stack->size < SIZE_MAX);
if ((stack->size + 1ull) >= stack->capacity)
{
size_t new_cap = stack->capacity;
do
{
WINPR_ASSERT(new_cap <= SIZE_MAX - 128ull);
new_cap += 128ull;
} while (new_cap <= stack->size + 1ull);
void** new_arr = (void**)realloc((void*)stack->array, sizeof(void*) * new_cap);
if (!new_arr)
goto end;
stack->array = new_arr;
stack->capacity = new_cap;
}
stack->array[(stack->size)++] = obj;
end:
if (stack->synchronized)
LeaveCriticalSection(&stack->lock);
}
/**
* Removes and returns the object at the top of the Stack.
*/
void* Stack_Pop(wStack* stack)
{
void* obj = nullptr;
WINPR_ASSERT(stack);
if (stack->synchronized)
EnterCriticalSection(&stack->lock);
if (stack->size > 0)
obj = stack->array[--(stack->size)];
if (stack->synchronized)
LeaveCriticalSection(&stack->lock);
return obj;
}
/**
* Returns the object at the top of the Stack without removing it.
*/
void* Stack_Peek(wStack* stack)
{
void* obj = nullptr;
WINPR_ASSERT(stack);
if (stack->synchronized)
EnterCriticalSection(&stack->lock);
if (stack->size > 0)
obj = stack->array[stack->size - 1];
if (stack->synchronized)
LeaveCriticalSection(&stack->lock);
return obj;
}
static BOOL default_stack_equals(const void* obj1, const void* obj2)
{
return (obj1 == obj2);
}
/**
* Construction, Destruction
*/
wStack* Stack_New(BOOL synchronized)
{
wStack* stack = nullptr;
stack = (wStack*)calloc(1, sizeof(wStack));
if (!stack)
return nullptr;
stack->object.fnObjectEquals = default_stack_equals;
stack->synchronized = synchronized;
stack->capacity = 32;
stack->array = (void**)calloc(stack->capacity, sizeof(void*));
if (!stack->array)
goto out_free;
if (stack->synchronized && !InitializeCriticalSectionAndSpinCount(&stack->lock, 4000))
goto out_free;
return stack;
out_free:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
Stack_Free(stack);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void Stack_Free(wStack* stack)
{
if (stack)
{
if (stack->synchronized)
DeleteCriticalSection(&stack->lock);
free((void*)stack->array);
free(stack);
}
}

View File

@@ -0,0 +1,531 @@
/**
* WinPR: Windows Portable Runtime
* Object Pool
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/wlog.h>
#include <winpr/collections.h>
#include "../stream.h"
#include "../log.h"
#define TAG WINPR_TAG("utils.streampool")
struct s_StreamPoolEntry
{
#if defined(WITH_STREAMPOOL_DEBUG)
char** msg;
size_t lines;
#endif
wStream* s;
};
struct s_wStreamPool
{
size_t aSize;
size_t aCapacity;
struct s_StreamPoolEntry* aArray;
size_t uSize;
size_t uCapacity;
struct s_StreamPoolEntry* uArray;
CRITICAL_SECTION lock;
BOOL synchronized;
size_t defaultSize;
};
static void discard_entry(struct s_StreamPoolEntry* entry, BOOL discardStream)
{
if (!entry)
return;
#if defined(WITH_STREAMPOOL_DEBUG)
free((void*)entry->msg);
#endif
if (discardStream && entry->s)
Stream_Free(entry->s, entry->s->isAllocatedStream);
const struct s_StreamPoolEntry empty = WINPR_C_ARRAY_INIT;
*entry = empty;
}
static struct s_StreamPoolEntry add_entry(wStream* s)
{
struct s_StreamPoolEntry entry = WINPR_C_ARRAY_INIT;
#if defined(WITH_STREAMPOOL_DEBUG)
void* stack = winpr_backtrace(20);
if (stack)
entry.msg = winpr_backtrace_symbols(stack, &entry.lines);
winpr_backtrace_free(stack);
#endif
entry.s = s;
return entry;
}
/**
* Lock the stream pool
*/
static inline void StreamPool_Lock(wStreamPool* pool)
{
WINPR_ASSERT(pool);
if (pool->synchronized)
EnterCriticalSection(&pool->lock);
}
/**
* Unlock the stream pool
*/
static inline void StreamPool_Unlock(wStreamPool* pool)
{
WINPR_ASSERT(pool);
if (pool->synchronized)
LeaveCriticalSection(&pool->lock);
}
static BOOL StreamPool_EnsureCapacity(wStreamPool* pool, size_t count, BOOL usedOrAvailable)
{
WINPR_ASSERT(pool);
size_t* cap = (usedOrAvailable) ? &pool->uCapacity : &pool->aCapacity;
size_t* size = (usedOrAvailable) ? &pool->uSize : &pool->aSize;
struct s_StreamPoolEntry** array = (usedOrAvailable) ? &pool->uArray : &pool->aArray;
size_t new_cap = 0;
if (*cap == 0)
new_cap = *size + count;
else if (*size + count > *cap)
new_cap = (*size + count + 2) / 2 * 3;
else if ((*size + count) < *cap / 3)
new_cap = *cap / 2;
if (new_cap > 0)
{
struct s_StreamPoolEntry* new_arr = nullptr;
if (*cap < *size + count)
*cap += count;
new_arr =
(struct s_StreamPoolEntry*)realloc(*array, sizeof(struct s_StreamPoolEntry) * new_cap);
if (!new_arr)
return FALSE;
*cap = new_cap;
*array = new_arr;
}
return TRUE;
}
/**
* Methods
*/
static void StreamPool_ShiftUsed(wStreamPool* pool, size_t index)
{
WINPR_ASSERT(pool);
const size_t pcount = 1;
const size_t off = index + pcount;
if (pool->uSize >= off)
{
for (size_t x = 0; x < pcount; x++)
{
struct s_StreamPoolEntry* cur = &pool->uArray[index + x];
discard_entry(cur, FALSE);
}
MoveMemory(&pool->uArray[index], &pool->uArray[index + pcount],
(pool->uSize - index - pcount) * sizeof(struct s_StreamPoolEntry));
pool->uSize -= pcount;
}
}
/**
* Adds a used stream to the pool.
*/
static void StreamPool_AddUsed(wStreamPool* pool, wStream* s)
{
StreamPool_EnsureCapacity(pool, 1, TRUE);
pool->uArray[pool->uSize] = add_entry(s);
pool->uSize++;
}
/**
* Removes a used stream from the pool.
*/
static void StreamPool_RemoveUsed(wStreamPool* pool, wStream* s)
{
WINPR_ASSERT(pool);
for (size_t index = 0; index < pool->uSize; index++)
{
struct s_StreamPoolEntry* cur = &pool->uArray[index];
if (cur->s == s)
{
StreamPool_ShiftUsed(pool, index);
break;
}
}
}
static void StreamPool_ShiftAvailable(wStreamPool* pool, size_t index)
{
WINPR_ASSERT(pool);
const size_t pcount = 1;
const size_t off = index + pcount;
if (pool->aSize >= off)
{
for (size_t x = 0; x < pcount; x++)
{
struct s_StreamPoolEntry* cur = &pool->aArray[index + x];
discard_entry(cur, FALSE);
}
MoveMemory(&pool->aArray[index], &pool->aArray[index + pcount],
(pool->aSize - index - pcount) * sizeof(struct s_StreamPoolEntry));
pool->aSize -= pcount;
}
}
/**
* Gets a stream from the pool.
*/
wStream* StreamPool_Take(wStreamPool* pool, size_t size)
{
BOOL found = FALSE;
size_t foundIndex = 0;
wStream* s = nullptr;
StreamPool_Lock(pool);
if (size == 0)
size = pool->defaultSize;
for (size_t index = 0; index < pool->aSize; index++)
{
struct s_StreamPoolEntry* cur = &pool->aArray[index];
s = cur->s;
if (Stream_Capacity(s) >= size)
{
found = TRUE;
foundIndex = index;
break;
}
}
if (!found)
{
s = Stream_New(nullptr, size);
if (!s)
goto out_fail;
}
else if (s)
{
Stream_ResetPosition(s);
Stream_SetLength(s, Stream_Capacity(s));
StreamPool_ShiftAvailable(pool, foundIndex);
}
if (s)
{
s->pool = pool;
s->count = 1;
StreamPool_AddUsed(pool, s);
}
out_fail:
StreamPool_Unlock(pool);
return s;
}
/**
* Returns an object to the pool.
*/
static void StreamPool_Remove(wStreamPool* pool, wStream* s)
{
StreamPool_EnsureCapacity(pool, 1, FALSE);
Stream_EnsureValidity(s);
for (size_t x = 0; x < pool->aSize; x++)
{
wStream* cs = pool->aArray[x].s;
if (cs == s)
return;
}
pool->aArray[(pool->aSize)++] = add_entry(s);
StreamPool_RemoveUsed(pool, s);
}
static void StreamPool_ReleaseOrReturn(wStreamPool* pool, wStream* s)
{
StreamPool_Lock(pool);
StreamPool_Remove(pool, s);
StreamPool_Unlock(pool);
}
void StreamPool_Return(wStreamPool* pool, wStream* s)
{
WINPR_ASSERT(pool);
if (!s)
return;
StreamPool_Lock(pool);
StreamPool_Remove(pool, s);
StreamPool_Unlock(pool);
}
/**
* Increment stream reference count
*/
void Stream_AddRef(wStream* s)
{
WINPR_ASSERT(s);
s->count++;
}
/**
* Decrement stream reference count
*/
void Stream_Release(wStream* s)
{
WINPR_ASSERT(s);
if (s->count > 0)
s->count--;
if (s->count == 0)
{
if (s->pool)
StreamPool_ReleaseOrReturn(s->pool, s);
else
Stream_Free(s, TRUE);
}
}
/**
* Find stream in pool using pointer inside buffer
*/
wStream* StreamPool_Find(wStreamPool* pool, const BYTE* ptr)
{
wStream* s = nullptr;
StreamPool_Lock(pool);
for (size_t index = 0; index < pool->uSize; index++)
{
struct s_StreamPoolEntry* cur = &pool->uArray[index];
if ((ptr >= Stream_Buffer(cur->s)) &&
(ptr < (Stream_Buffer(cur->s) + Stream_Capacity(cur->s))))
{
s = cur->s;
break;
}
}
StreamPool_Unlock(pool);
return s;
}
/**
* Releases the streams currently cached in the pool.
*/
void StreamPool_Clear(wStreamPool* pool)
{
StreamPool_Lock(pool);
for (size_t x = 0; x < pool->aSize; x++)
{
struct s_StreamPoolEntry* cur = &pool->aArray[x];
discard_entry(cur, TRUE);
}
pool->aSize = 0;
if (pool->uSize > 0)
{
WLog_WARN(TAG, "Clearing StreamPool, but there are %" PRIuz " streams currently in use",
pool->uSize);
for (size_t x = 0; x < pool->uSize; x++)
{
struct s_StreamPoolEntry* cur = &pool->uArray[x];
discard_entry(cur, TRUE);
}
pool->uSize = 0;
}
StreamPool_Unlock(pool);
}
size_t StreamPool_UsedCount(wStreamPool* pool)
{
StreamPool_Lock(pool);
size_t usize = pool->uSize;
StreamPool_Unlock(pool);
return usize;
}
/**
* Construction, Destruction
*/
wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
{
wStreamPool* pool = nullptr;
pool = (wStreamPool*)calloc(1, sizeof(wStreamPool));
if (pool)
{
pool->synchronized = synchronized;
pool->defaultSize = defaultSize;
if (!StreamPool_EnsureCapacity(pool, 32, FALSE))
goto fail;
if (!StreamPool_EnsureCapacity(pool, 32, TRUE))
goto fail;
if (!InitializeCriticalSectionAndSpinCount(&pool->lock, 4000))
goto fail;
}
return pool;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
StreamPool_Free(pool);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
void StreamPool_Free(wStreamPool* pool)
{
if (pool)
{
StreamPool_Clear(pool);
DeleteCriticalSection(&pool->lock);
free(pool->aArray);
free(pool->uArray);
free(pool);
}
}
char* StreamPool_GetStatistics(wStreamPool* pool, char* buffer, size_t size)
{
WINPR_ASSERT(pool);
if (!buffer || (size < 1))
return nullptr;
size_t used = 0;
int offset = _snprintf(buffer, size - 1,
"aSize =%" PRIuz ", uSize =%" PRIuz ", aCapacity=%" PRIuz
", uCapacity=%" PRIuz,
pool->aSize, pool->uSize, pool->aCapacity, pool->uCapacity);
if ((offset > 0) && ((size_t)offset < size))
used += (size_t)offset;
#if defined(WITH_STREAMPOOL_DEBUG)
StreamPool_Lock(pool);
offset = _snprintf(&buffer[used], size - 1 - used, "\n-- dump used array take locations --\n");
if ((offset > 0) && ((size_t)offset < size - used))
used += (size_t)offset;
for (size_t x = 0; x < pool->uSize; x++)
{
const struct s_StreamPoolEntry* cur = &pool->uArray[x];
WINPR_ASSERT(cur->msg || (cur->lines == 0));
for (size_t y = 0; y < cur->lines; y++)
{
offset = _snprintf(&buffer[used], size - 1 - used, "[%" PRIuz " | %" PRIuz "]: %s\n", x,
y, cur->msg[y]);
if ((offset > 0) && ((size_t)offset < size - used))
used += (size_t)offset;
}
}
offset = _snprintf(&buffer[used], size - 1 - used, "\n-- statistics called from --\n");
if ((offset > 0) && ((size_t)offset < size - used))
used += (size_t)offset;
struct s_StreamPoolEntry entry = WINPR_C_ARRAY_INIT;
void* stack = winpr_backtrace(20);
if (stack)
entry.msg = winpr_backtrace_symbols(stack, &entry.lines);
winpr_backtrace_free(stack);
for (size_t x = 0; x < entry.lines; x++)
{
const char* msg = entry.msg[x];
offset = _snprintf(&buffer[used], size - 1 - used, "[%" PRIuz "]: %s\n", x, msg);
if ((offset > 0) && ((size_t)offset < size - used))
used += (size_t)offset;
}
free((void*)entry.msg);
StreamPool_Unlock(pool);
#endif
buffer[used] = '\0';
return buffer;
}
BOOL StreamPool_WaitForReturn(wStreamPool* pool, UINT32 timeoutMS)
{
wLog* log = WLog_Get(TAG);
/* HACK: We disconnected the transport above, now wait without a read or write lock until all
* streams in use have been returned to the pool. */
while (timeoutMS > 0)
{
const size_t used = StreamPool_UsedCount(pool);
if (used == 0)
return TRUE;
WLog_Print(log, WLOG_DEBUG, "%" PRIuz " streams still in use, sleeping...", used);
char buffer[4096] = WINPR_C_ARRAY_INIT;
StreamPool_GetStatistics(pool, buffer, sizeof(buffer));
WLog_Print(log, WLOG_TRACE, "Pool statistics: %s", buffer);
UINT32 diff = 10;
if (timeoutMS != INFINITE)
{
diff = timeoutMS > 10 ? 10 : timeoutMS;
timeoutMS -= diff;
}
Sleep(diff);
}
return FALSE;
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
/* A stack unwinder. */
#ifndef _CORKSCREW_BACKTRACE_H
#define _CORKSCREW_BACKTRACE_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <sys/types.h>
#include <corkscrew/ptrace.h>
#include <corkscrew/map_info.h>
#include <corkscrew/symbol_table.h>
/*
* Describes a single frame of a backtrace.
*/
typedef struct
{
uintptr_t absolute_pc; /* absolute PC offset */
uintptr_t stack_top; /* top of stack for this frame */
size_t stack_size; /* size of this stack frame */
} backtrace_frame_t;
/*
* Describes the symbols associated with a backtrace frame.
*/
typedef struct
{
uintptr_t relative_pc; /* relative frame PC offset from the start of the library,
or the absolute PC if the library is unknown */
uintptr_t relative_symbol_addr; /* relative offset of the symbol from the start of the
library or 0 if the library is unknown */
char* map_name; /* executable or library name, or nullptr if unknown */
char* symbol_name; /* symbol name, or nullptr if unknown */
char* demangled_name; /* demangled symbol name, or nullptr if unknown */
} backtrace_symbol_t;
/*
* Unwinds the call stack for the current thread of execution.
* Populates the backtrace array with the program counters from the call stack.
* Returns the number of frames collected, or -1 if an error occurred.
*/
ssize_t unwind_backtrace(backtrace_frame_t* backtrace, size_t ignore_depth, size_t max_depth);
/*
* Unwinds the call stack for a thread within this process.
* Populates the backtrace array with the program counters from the call stack.
* Returns the number of frames collected, or -1 if an error occurred.
*
* The task is briefly suspended while the backtrace is being collected.
*/
ssize_t unwind_backtrace_thread(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth,
size_t max_depth);
/*
* Unwinds the call stack of a task within a remote process using ptrace().
* Populates the backtrace array with the program counters from the call stack.
* Returns the number of frames collected, or -1 if an error occurred.
*/
ssize_t unwind_backtrace_ptrace(pid_t tid, const ptrace_context_t* context,
backtrace_frame_t* backtrace, size_t ignore_depth,
size_t max_depth);
/*
* Gets the symbols for each frame of a backtrace.
* The symbols array must be big enough to hold one symbol record per frame.
* The symbols must later be freed using free_backtrace_symbols.
*/
void get_backtrace_symbols(const backtrace_frame_t* backtrace, size_t frames,
backtrace_symbol_t* backtrace_symbols);
/*
* Gets the symbols for each frame of a backtrace from a remote process.
* The symbols array must be big enough to hold one symbol record per frame.
* The symbols must later be freed using free_backtrace_symbols.
*/
void get_backtrace_symbols_ptrace(const ptrace_context_t* context,
const backtrace_frame_t* backtrace, size_t frames,
backtrace_symbol_t* backtrace_symbols);
/*
* Frees the storage associated with backtrace symbols.
*/
void free_backtrace_symbols(backtrace_symbol_t* backtrace_symbols, size_t frames);
enum
{
// A hint for how big to make the line buffer for format_backtrace_line
MAX_BACKTRACE_LINE_LENGTH = 800,
};
/**
* Formats a line from a backtrace as a zero-terminated string into the specified buffer.
*/
void format_backtrace_line(unsigned frameNumber, const backtrace_frame_t* frame,
const backtrace_symbol_t* symbol, char* buffer, size_t bufferSize);
#ifdef __cplusplus
}
#endif
#endif // _CORKSCREW_BACKTRACE_H

View File

@@ -0,0 +1,260 @@
/**
* WinPR: Windows Portable Runtime
* Debugging Utils
*
* Copyright 2014 Armin Novak <armin.novak@thincast.com>
* Copyright 2014 Thincast Technologies GmbH
*
* 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.
*/
#include <pthread.h>
#include <dlfcn.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <winpr/string.h>
#include <corkscrew/backtrace.h>
#include <winpr/crt.h>
#include <winpr/wlog.h>
#include "debug.h"
#define TAG "com.winpr.utils.debug"
#define LOGT(...) \
do \
{ \
WLog_Print(WLog_Get(TAG), WLOG_TRACE, __VA_ARGS__); \
} while (0)
#define LOGD(...) \
do \
{ \
WLog_Print(WLog_Get(TAG), WLOG_DEBUG, __VA_ARGS__); \
} while (0)
#define LOGI(...) \
do \
{ \
WLog_Print(WLog_Get(TAG), WLOG_INFO, __VA_ARGS__); \
} while (0)
#define LOGW(...) \
do \
{ \
WLog_Print(WLog_Get(TAG), WLOG_WARN, __VA_ARGS__); \
} while (0)
#define LOGE(...) \
do \
{ \
WLog_Print(WLog_Get(TAG), WLOG_ERROR, __VA_ARGS__); \
} while (0)
#define LOGF(...) \
do \
{ \
WLog_Print(WLog_Get(TAG), WLOG_FATAL, __VA_ARGS__); \
} while (0)
static const char* support_msg = "Invalid stacktrace buffer! check if platform is supported!";
typedef struct
{
backtrace_frame_t* buffer;
size_t max;
size_t used;
} t_corkscrew_data;
typedef struct
{
void* hdl;
ssize_t (*unwind_backtrace)(backtrace_frame_t* backtrace, size_t ignore_depth,
size_t max_depth);
ssize_t (*unwind_backtrace_thread)(pid_t tid, backtrace_frame_t* backtrace, size_t ignore_depth,
size_t max_depth);
ssize_t (*unwind_backtrace_ptrace)(pid_t tid, const ptrace_context_t* context,
backtrace_frame_t* backtrace, size_t ignore_depth,
size_t max_depth);
void (*get_backtrace_symbols)(const backtrace_frame_t* backtrace, size_t frames,
backtrace_symbol_t* backtrace_symbols);
void (*get_backtrace_symbols_ptrace)(const ptrace_context_t* context,
const backtrace_frame_t* backtrace, size_t frames,
backtrace_symbol_t* backtrace_symbols);
void (*free_backtrace_symbols)(backtrace_symbol_t* backtrace_symbols, size_t frames);
void (*format_backtrace_line)(unsigned frameNumber, const backtrace_frame_t* frame,
const backtrace_symbol_t* symbol, char* buffer,
size_t bufferSize);
} t_corkscrew;
static pthread_once_t initialized = PTHREAD_ONCE_INIT;
static t_corkscrew* fkt = nullptr;
void load_library(void)
{
static t_corkscrew lib;
{
lib.hdl = dlopen("libcorkscrew.so", RTLD_LAZY);
if (!lib.hdl)
{
LOGF("dlopen error %s", dlerror());
goto fail;
}
lib.unwind_backtrace = dlsym(lib.hdl, "unwind_backtrace");
if (!lib.unwind_backtrace)
{
LOGF("dlsym error %s", dlerror());
goto fail;
}
lib.unwind_backtrace_thread = dlsym(lib.hdl, "unwind_backtrace_thread");
if (!lib.unwind_backtrace_thread)
{
LOGF("dlsym error %s", dlerror());
goto fail;
}
lib.unwind_backtrace_ptrace = dlsym(lib.hdl, "unwind_backtrace_ptrace");
if (!lib.unwind_backtrace_ptrace)
{
LOGF("dlsym error %s", dlerror());
goto fail;
}
lib.get_backtrace_symbols = dlsym(lib.hdl, "get_backtrace_symbols");
if (!lib.get_backtrace_symbols)
{
LOGF("dlsym error %s", dlerror());
goto fail;
}
lib.get_backtrace_symbols_ptrace = dlsym(lib.hdl, "get_backtrace_symbols_ptrace");
if (!lib.get_backtrace_symbols_ptrace)
{
LOGF("dlsym error %s", dlerror());
goto fail;
}
lib.free_backtrace_symbols = dlsym(lib.hdl, "free_backtrace_symbols");
if (!lib.free_backtrace_symbols)
{
LOGF("dlsym error %s", dlerror());
goto fail;
}
lib.format_backtrace_line = dlsym(lib.hdl, "format_backtrace_line");
if (!lib.format_backtrace_line)
{
LOGF("dlsym error %s", dlerror());
goto fail;
}
fkt = &lib;
return;
}
fail:
{
if (lib.hdl)
dlclose(lib.hdl);
fkt = nullptr;
}
}
void winpr_corkscrew_backtrace_free(void* buffer)
{
t_corkscrew_data* data = (t_corkscrew_data*)buffer;
if (!data)
return;
free(data->buffer);
free(data);
}
void* winpr_corkscrew_backtrace(DWORD size)
{
t_corkscrew_data* data = calloc(1, sizeof(t_corkscrew_data));
if (!data)
return nullptr;
data->buffer = calloc(size, sizeof(backtrace_frame_t));
if (!data->buffer)
{
free(data);
return nullptr;
}
pthread_once(&initialized, load_library);
data->max = size;
data->used = fkt->unwind_backtrace(data->buffer, 0, size);
return data;
}
char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used)
{
t_corkscrew_data* data = (t_corkscrew_data*)buffer;
if (used)
*used = 0;
if (!data)
return nullptr;
pthread_once(&initialized, load_library);
if (!fkt)
{
LOGF(support_msg);
return nullptr;
}
else
{
size_t line_len = (data->max > 1024) ? data->max : 1024;
size_t array_size = data->used * sizeof(char*);
size_t lines_size = data->used * line_len;
char** vlines = calloc(1, array_size + lines_size);
backtrace_symbol_t* symbols = calloc(data->used, sizeof(backtrace_symbol_t));
if (!vlines || !symbols)
{
free(vlines);
free(symbols);
return nullptr;
}
/* Set the pointers in the allocated buffer's initial array section */
for (size_t i = 0; i < data->used; i++)
vlines[i] = (char*)vlines + array_size + i * line_len;
fkt->get_backtrace_symbols(data->buffer, data->used, symbols);
for (size_t i = 0; i < data->used; i++)
fkt->format_backtrace_line(i, &data->buffer[i], &symbols[i], vlines[i], line_len);
fkt->free_backtrace_symbols(symbols, data->used);
free(symbols);
if (used)
*used = data->used;
return vlines;
}
}

View File

@@ -0,0 +1,40 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Debugging helpers
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* 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 WINPR_DEBUG_CORKSCREW_H
#define WINPR_DEBUG_CORKSCREW_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <winpr/wtypes.h>
void* winpr_corkscrew_backtrace(DWORD size);
void winpr_corkscrew_backtrace_free(void* buffer);
char** winpr_corkscrew_backtrace_symbols(void* buffer, size_t* used);
void winpr_corkscrew_backtrace_symbols_fd(void* buffer, int fd);
#ifdef __cplusplus
}
#endif
#endif /* WINPR_DEBUG_CORKSCREW_H */

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
/* C++ symbol name demangling. */
#ifndef _CORKSCREW_DEMANGLE_H
#define _CORKSCREW_DEMANGLE_H
#include <sys/types.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C"
{
#endif
/*
* Demangles a C++ symbol name.
* If name is nullptr or if the name cannot be demangled, returns nullptr.
* Otherwise, returns a newly allocated string that contains the demangled name.
*
* The caller must free the returned string using free().
*/
char* demangle_symbol_name(const char* name);
#ifdef __cplusplus
}
#endif
#endif // _CORKSCREW_DEMANGLE_H

View File

@@ -0,0 +1,76 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
/* Process memory map. */
#ifndef _CORKSCREW_MAP_INFO_H
#define _CORKSCREW_MAP_INFO_H
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct
{
struct map_info* next;
uintptr_t start;
uintptr_t end;
bool is_readable;
bool is_writable;
bool is_executable;
void* data; // arbitrary data associated with the map by the user, initially nullptr
char name[];
} map_info_t;
/* Loads memory map from /proc/<tid>/maps. */
map_info_t* load_map_info_list(pid_t tid);
/* Frees memory map. */
void free_map_info_list(map_info_t* milist);
/* Finds the memory map that contains the specified address. */
const map_info_t* find_map_info(const map_info_t* milist, uintptr_t addr);
/* Returns true if the addr is in a readable map. */
bool is_readable_map(const map_info_t* milist, uintptr_t addr);
/* Returns true if the addr is in a writable map. */
bool is_writable_map(const map_info_t* milist, uintptr_t addr);
/* Returns true if the addr is in an executable map. */
bool is_executable_map(const map_info_t* milist, uintptr_t addr);
/* Acquires a reference to the memory map for this process.
* The result is cached and refreshed automatically.
* Make sure to release the map info when done. */
map_info_t* acquire_my_map_info_list();
/* Releases a reference to the map info for this process that was
* previous acquired using acquire_my_map_info_list(). */
void release_my_map_info_list(map_info_t* milist);
/* Flushes the cached memory map so the next call to
* acquire_my_map_info_list() gets fresh data. */
void flush_my_map_info_list();
#ifdef __cplusplus
}
#endif
#endif // _CORKSCREW_MAP_INFO_H

View File

@@ -0,0 +1,139 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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.
*/
/* Useful ptrace() utility functions. */
#ifndef _CORKSCREW_PTRACE_H
#define _CORKSCREW_PTRACE_H
#include <corkscrew/map_info.h>
#include <corkscrew/symbol_table.h>
#include <sys/types.h>
#include <stdbool.h>
#include <stdint.h>
#ifdef __cplusplus
extern "C"
{
#endif
/* Stores information about a process that is used for several different
* ptrace() based operations. */
typedef struct
{
map_info_t* map_info_list;
} ptrace_context_t;
/* Describes how to access memory from a process. */
typedef struct
{
pid_t tid;
const map_info_t* map_info_list;
} memory_t;
#ifdef __i386__
/* ptrace() register context. */
typedef struct pt_regs_x86
{
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
uint32_t eax;
uint32_t xds;
uint32_t xes;
uint32_t xfs;
uint32_t xgs;
uint32_t orig_eax;
uint32_t eip;
uint32_t xcs;
uint32_t eflags;
uint32_t esp;
uint32_t xss;
} pt_regs_x86_t;
#endif
#ifdef __mips__
/* ptrace() GET_REGS context. */
typedef struct pt_regs_mips
{
uint64_t regs[32];
uint64_t lo;
uint64_t hi;
uint64_t cp0_epc;
uint64_t cp0_badvaddr;
uint64_t cp0_status;
uint64_t cp0_cause;
} pt_regs_mips_t;
#endif
/*
* Initializes a memory structure for accessing memory from this process.
*/
void init_memory(memory_t* memory, const map_info_t* map_info_list);
/*
* Initializes a memory structure for accessing memory from another process
* using ptrace().
*/
void init_memory_ptrace(memory_t* memory, pid_t tid);
/*
* Reads a word of memory safely.
* If the memory is local, ensures that the address is readable before dereferencing it.
* Returns false and a value of 0xffffffff if the word could not be read.
*/
bool try_get_word(const memory_t* memory, uintptr_t ptr, uint32_t* out_value);
/*
* Reads a word of memory safely using ptrace().
* Returns false and a value of 0xffffffff if the word could not be read.
*/
bool try_get_word_ptrace(pid_t tid, uintptr_t ptr, uint32_t* out_value);
/*
* Loads information needed for examining a remote process using ptrace().
* The caller must already have successfully attached to the process
* using ptrace().
*
* The context can be used for any threads belonging to that process
* assuming ptrace() is attached to them before performing the actual
* unwinding. The context can continue to be used to decode backtraces
* even after ptrace() has been detached from the process.
*/
ptrace_context_t* load_ptrace_context(pid_t pid);
/*
* Frees a ptrace context.
*/
void free_ptrace_context(ptrace_context_t* context);
/*
* Finds a symbol using ptrace.
* Returns the containing map and information about the symbol, or
* nullptr if one or the other is not available.
*/
void find_symbol_ptrace(const ptrace_context_t* context, uintptr_t addr,
const map_info_t** out_map_info, const symbol_t** out_symbol);
#ifdef __cplusplus
}
#endif
#endif // _CORKSCREW_PTRACE_H

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2011 The Android Open Source Project
*
* 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 _CORKSCREW_SYMBOL_TABLE_H
#define _CORKSCREW_SYMBOL_TABLE_H
#include <stdint.h>
#include <sys/types.h>
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct
{
uintptr_t start;
uintptr_t end;
char* name;
} symbol_t;
typedef struct
{
symbol_t* symbols;
size_t num_symbols;
} symbol_table_t;
/*
* Loads a symbol table from a given file.
* Returns nullptr on error.
*/
symbol_table_t* load_symbol_table(const char* filename);
/*
* Frees a symbol table.
*/
void free_symbol_table(symbol_table_t* table);
/*
* Finds a symbol associated with an address in the symbol table.
* Returns nullptr if not found.
*/
const symbol_t* find_symbol(const symbol_table_t* table, uintptr_t addr);
#ifdef __cplusplus
}
#endif
#endif // _CORKSCREW_SYMBOL_TABLE_H

View File

@@ -0,0 +1,211 @@
/**
* WinPR: Windows Portable Runtime
* Debugging Utils
*
* Copyright 2014 Armin Novak <armin.novak@thincast.com>
* Copyright 2014 Thincast Technologies GmbH
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/platform.h>
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO
#define __STDC_WANT_LIB_EXT1__ 1 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
WINPR_PRAGMA_DIAG_POP
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <winpr/crt.h>
#include <winpr/string.h>
#if defined(USE_EXECINFO)
#include <execinfo/debug.h>
#endif
#if defined(USE_UNWIND)
#include <unwind/debug.h>
#endif
#if defined(WINPR_HAVE_CORKSCREW)
#include <corkscrew/debug.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
#include <io.h>
#include <windows/debug.h>
#endif
#include <winpr/wlog.h>
#include <winpr/debug.h>
#define TAG "com.winpr.utils.debug"
#define LINE_LENGTH_MAX 2048
static const char support_msg[] = "Invalid stacktrace buffer! check if platform is supported!";
void winpr_backtrace_free(void* buffer)
{
if (!buffer)
return;
#if defined(USE_UNWIND)
winpr_unwind_backtrace_free(buffer);
#elif defined(USE_EXECINFO)
winpr_execinfo_backtrace_free(buffer);
#elif defined(WINPR_HAVE_CORKSCREW)
winpr_corkscrew_backtrace_free(buffer);
#elif defined(_WIN32) || defined(_WIN64)
winpr_win_backtrace_free(buffer);
#else
free(buffer);
WLog_FATAL(TAG, "%s", support_msg);
#endif
}
void* winpr_backtrace(DWORD size)
{
#if defined(USE_UNWIND)
return winpr_unwind_backtrace(size);
#elif defined(USE_EXECINFO)
return winpr_execinfo_backtrace(size);
#elif defined(WINPR_HAVE_CORKSCREW)
return winpr_corkscrew_backtrace(size);
#elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP)
return winpr_win_backtrace(size);
#else
WLog_FATAL(TAG, "%s", support_msg);
/* return a non nullptr buffer to allow the backtrace function family to succeed without failing
*/
return strndup(support_msg, sizeof(support_msg));
#endif
}
char** winpr_backtrace_symbols(void* buffer, size_t* used)
{
if (used)
*used = 0;
if (!buffer)
{
WLog_FATAL(TAG, "%s", support_msg);
return nullptr;
}
#if defined(USE_UNWIND)
return winpr_unwind_backtrace_symbols(buffer, used);
#elif defined(USE_EXECINFO)
return winpr_execinfo_backtrace_symbols(buffer, used);
#elif defined(WINPR_HAVE_CORKSCREW)
return winpr_corkscrew_backtrace_symbols(buffer, used);
#elif (defined(_WIN32) || defined(_WIN64)) && !defined(_UWP)
return winpr_win_backtrace_symbols(buffer, used);
#else
WLog_FATAL(TAG, "%s", support_msg);
/* We return a char** on heap that is compatible with free:
*
* 1. We allocate sizeof(char*) + strlen + 1 bytes.
* 2. The first sizeof(char*) bytes contain the pointer to the string following the pointer.
* 3. The at data + sizeof(char*) contains the actual string
*/
size_t len = strnlen(support_msg, sizeof(support_msg));
char* ppmsg = calloc(sizeof(char*) + len + 1, sizeof(char));
if (!ppmsg)
return nullptr;
char** msgptr = (char**)ppmsg;
char* msg = &ppmsg[sizeof(char*)];
*msgptr = msg;
strncpy(msg, support_msg, len);
*used = 1;
return msgptr;
#endif
}
void winpr_backtrace_symbols_fd(void* buffer, int fd)
{
if (!buffer)
{
WLog_FATAL(TAG, "%s", support_msg);
return;
}
#if defined(USE_EXECINFO) && !defined(USE_UNWIND)
winpr_execinfo_backtrace_symbols_fd(buffer, fd);
#elif !defined(ANDROID)
{
size_t used = 0;
char** lines = winpr_backtrace_symbols(buffer, &used);
if (!lines)
return;
for (size_t i = 0; i < used; i++)
(void)_write(fd, lines[i], (unsigned)strnlen(lines[i], LINE_LENGTH_MAX));
free((void*)lines);
}
#else
WLog_FATAL(TAG, "%s", support_msg);
#endif
}
void winpr_log_backtrace(const char* tag, DWORD level, DWORD size)
{
winpr_log_backtrace_ex(WLog_Get(tag), level, size);
}
void winpr_log_backtrace_ex(wLog* log, DWORD level, WINPR_ATTR_UNUSED DWORD size)
{
size_t used = 0;
char** msg = nullptr;
void* stack = winpr_backtrace(20);
if (!stack)
{
WLog_Print(log, WLOG_ERROR, "winpr_backtrace failed!\n");
goto fail;
}
msg = winpr_backtrace_symbols(stack, &used);
if (msg)
{
for (size_t x = 0; x < used; x++)
WLog_Print(log, level, "%" PRIuz ": %s", x, msg[x]);
}
free((void*)msg);
fail:
winpr_backtrace_free(stack);
}
char* winpr_strerror(INT32 dw, char* dmsg, size_t size)
{
#ifdef __STDC_LIB_EXT1__
(void)strerror_s(dw, dmsg, size);
#elif defined(WINPR_HAVE_STRERROR_R)
(void)strerror_r(dw, dmsg, size);
#else
(void)_snprintf(dmsg, size, "%s", strerror(dw));
#endif
return dmsg;
}

View File

@@ -0,0 +1,98 @@
/**
* WinPR: Windows Portable Runtime
* Debugging Utils
*
* Copyright 2014 Armin Novak <armin.novak@thincast.com>
* Copyright 2014 Thincast Technologies GmbH
*
* 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.
*/
#include <assert.h>
#include <stdlib.h>
#include <fcntl.h>
#include <execinfo.h>
#include "debug.h"
typedef struct
{
void** buffer;
size_t max;
size_t used;
} t_execinfo;
void winpr_execinfo_backtrace_free(void* buffer)
{
t_execinfo* data = (t_execinfo*)buffer;
if (!data)
return;
free((void*)data->buffer);
free(data);
}
void* winpr_execinfo_backtrace(DWORD size)
{
t_execinfo* data = calloc(1, sizeof(t_execinfo));
if (!data)
return nullptr;
data->buffer = (void**)calloc(size, sizeof(void*));
if (!data->buffer)
{
free(data);
return nullptr;
}
assert(size <= INT32_MAX);
const int rc = backtrace(data->buffer, (int)size);
if (rc < 0)
{
free(data);
return nullptr;
}
data->max = size;
data->used = (size_t)rc;
return data;
}
char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used)
{
t_execinfo* data = (t_execinfo*)buffer;
if (used)
*used = 0;
if (!data)
return nullptr;
if (used)
*used = data->used;
assert(data->used < INT32_MAX);
return backtrace_symbols(data->buffer, (int)data->used);
}
void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd)
{
t_execinfo* data = (t_execinfo*)buffer;
if (!data)
return;
assert(data->used <= INT32_MAX);
backtrace_symbols_fd(data->buffer, (int)data->used, fd);
}

View File

@@ -0,0 +1,40 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Debugging helpers
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* 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 WINPR_DEBUG_EXECINFO_H
#define WINPR_DEBUG_EXECINFO_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <winpr/wtypes.h>
void* winpr_execinfo_backtrace(DWORD size);
void winpr_execinfo_backtrace_free(void* buffer);
char** winpr_execinfo_backtrace_symbols(void* buffer, size_t* used);
void winpr_execinfo_backtrace_symbols_fd(void* buffer, int fd);
#ifdef __cplusplus
}
#endif
#endif /* WINPR_DEBUG_EXECINFO_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,34 @@
/*
* WinPR: Windows Portable Runtime
* Image Utils
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 Thincast Technologies GmbH
*
* 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 LIBWINPR_UTILS_IMAGE_H
#define LIBWINPR_UTILS_IMAGE_H
#include <winpr/wtypes.h>
#include <winpr/stream.h>
#include <winpr/image.h>
BOOL readBitmapFileHeader(wStream* s, WINPR_BITMAP_FILE_HEADER* bf);
BOOL writeBitmapFileHeader(wStream* s, const WINPR_BITMAP_FILE_HEADER* bf);
BOOL readBitmapInfoHeader(wStream* s, WINPR_BITMAP_INFO_HEADER* bi, size_t* poffset);
BOOL writeBitmapInfoHeader(wStream* s, const WINPR_BITMAP_INFO_HEADER* bi);
#endif /* LIBWINPR_UTILS_IMAGE_H */

View File

@@ -0,0 +1,887 @@
/**
* WinPR: Windows Portable Runtime
* .ini config file
*
* Copyright 2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/file.h>
#include <winpr/ini.h>
typedef struct
{
char* name;
char* value;
} wIniFileKey;
typedef struct
{
char* name;
size_t nKeys;
size_t cKeys;
wIniFileKey** keys;
} wIniFileSection;
struct s_wIniFile
{
char* line;
char* nextLine;
size_t lineLength;
char* tokctx;
char* buffer;
size_t buffersize;
char* filename;
BOOL readOnly;
size_t nSections;
size_t cSections;
wIniFileSection** sections;
};
static BOOL IniFile_Load_NextLine(wIniFile* ini, char* str)
{
size_t length = 0;
WINPR_ASSERT(ini);
ini->nextLine = strtok_s(str, "\n", &ini->tokctx);
if (ini->nextLine)
length = strlen(ini->nextLine);
if (length > 0)
{
if (ini->nextLine[length - 1] == '\r')
{
ini->nextLine[length - 1] = '\0';
length--;
}
if (length < 1)
ini->nextLine = nullptr;
}
return (ini->nextLine != nullptr);
}
static BOOL IniFile_BufferResize(wIniFile* ini, size_t size)
{
WINPR_ASSERT(ini);
if (size > ini->buffersize)
{
const size_t diff = size - ini->buffersize;
char* tmp = realloc(ini->buffer, size);
if (!tmp)
return FALSE;
memset(&tmp[ini->buffersize], 0, diff * sizeof(char));
ini->buffer = tmp;
ini->buffersize = size;
}
return TRUE;
}
static BOOL IniFile_Load_String(wIniFile* ini, const char* iniString)
{
size_t fileSize = 0;
WINPR_ASSERT(ini);
if (!iniString)
return FALSE;
ini->line = nullptr;
ini->nextLine = nullptr;
fileSize = strlen(iniString);
if (fileSize < 1)
return FALSE;
if (!IniFile_BufferResize(ini, fileSize + 2))
return FALSE;
CopyMemory(ini->buffer, iniString, fileSize);
ini->buffer[fileSize] = '\n';
IniFile_Load_NextLine(ini, ini->buffer);
return TRUE;
}
static void IniFile_Close_File(FILE* fp)
{
if (fp)
(void)fclose(fp);
}
static FILE* IniFile_Open_File(wIniFile* ini, const char* filename)
{
WINPR_ASSERT(ini);
if (!filename)
return nullptr;
if (ini->readOnly)
return winpr_fopen(filename, "rb");
else
return winpr_fopen(filename, "w+b");
}
static BOOL IniFile_Load_File(wIniFile* ini, const char* filename)
{
BOOL rc = FALSE;
WINPR_ASSERT(ini);
FILE* fp = IniFile_Open_File(ini, filename);
if (!fp)
return FALSE;
if (_fseeki64(fp, 0, SEEK_END) < 0)
goto out_file;
{
const INT64 fileSize = _ftelli64(fp);
if (fileSize < 0)
goto out_file;
if (_fseeki64(fp, 0, SEEK_SET) < 0)
goto out_file;
ini->line = nullptr;
ini->nextLine = nullptr;
if (fileSize < 1)
goto out_file;
if (!IniFile_BufferResize(ini, (size_t)fileSize + 2))
goto out_file;
if (fread(ini->buffer, (size_t)fileSize, 1ul, fp) != 1)
goto out_file;
ini->buffer[fileSize] = '\n';
ini->buffer[fileSize + 1] = '\0';
}
IniFile_Load_NextLine(ini, ini->buffer);
rc = TRUE;
out_file:
IniFile_Close_File(fp);
return rc;
}
static BOOL IniFile_Load_HasNextLine(wIniFile* ini)
{
WINPR_ASSERT(ini);
return (ini->nextLine != nullptr);
}
static char* IniFile_Load_GetNextLine(wIniFile* ini)
{
WINPR_ASSERT(ini);
ini->line = ini->nextLine;
ini->lineLength = strlen(ini->line);
IniFile_Load_NextLine(ini, nullptr);
return ini->line;
}
static void IniFile_Key_Free(wIniFileKey* key)
{
if (!key)
return;
free(key->name);
free(key->value);
free(key);
}
static wIniFileKey* IniFile_Key_New(const char* name, const char* value)
{
if (!name || !value)
return nullptr;
wIniFileKey* key = calloc(1, sizeof(wIniFileKey));
if (key)
{
key->name = _strdup(name);
key->value = _strdup(value);
if (!key->name || !key->value)
{
IniFile_Key_Free(key);
return nullptr;
}
}
return key;
}
static void IniFile_Section_Free(wIniFileSection* section)
{
if (!section)
return;
free(section->name);
for (size_t index = 0; index < section->nKeys; index++)
{
IniFile_Key_Free(section->keys[index]);
}
free((void*)section->keys);
free(section);
}
static BOOL IniFile_SectionKeysResize(wIniFileSection* section, size_t count)
{
WINPR_ASSERT(section);
if (section->nKeys + count >= section->cKeys)
{
const size_t new_size = section->cKeys + count + 1024;
const size_t diff = new_size - section->cKeys;
wIniFileKey** new_keys =
(wIniFileKey**)realloc((void*)section->keys, sizeof(wIniFileKey*) * new_size);
if (!new_keys)
return FALSE;
memset((void*)&new_keys[section->cKeys], 0, diff * sizeof(wIniFileKey*));
section->cKeys = new_size;
section->keys = new_keys;
}
return TRUE;
}
static wIniFileSection* IniFile_Section_New(const char* name)
{
if (!name)
return nullptr;
wIniFileSection* section = calloc(1, sizeof(wIniFileSection));
if (!section)
goto fail;
section->name = _strdup(name);
if (!section->name)
goto fail;
if (!IniFile_SectionKeysResize(section, 64))
goto fail;
return section;
fail:
IniFile_Section_Free(section);
return nullptr;
}
static wIniFileSection* IniFile_GetSection(wIniFile* ini, const char* name)
{
wIniFileSection* section = nullptr;
WINPR_ASSERT(ini);
if (!name)
return nullptr;
for (size_t index = 0; index < ini->nSections; index++)
{
if (_stricmp(name, ini->sections[index]->name) == 0)
{
section = ini->sections[index];
break;
}
}
return section;
}
static BOOL IniFile_SectionResize(wIniFile* ini, size_t count)
{
WINPR_ASSERT(ini);
if (ini->nSections + count >= ini->cSections)
{
const size_t new_size = ini->cSections + count + 1024;
const size_t diff = new_size - ini->cSections;
wIniFileSection** new_sect =
(wIniFileSection**)realloc((void*)ini->sections, sizeof(wIniFileSection*) * new_size);
if (!new_sect)
return FALSE;
memset((void*)&new_sect[ini->cSections], 0, diff * sizeof(wIniFileSection*));
ini->cSections = new_size;
ini->sections = new_sect;
}
return TRUE;
}
static wIniFileSection* IniFile_AddToSection(wIniFile* ini, const char* name)
{
WINPR_ASSERT(ini);
if (!name)
return nullptr;
wIniFileSection* section = IniFile_GetSection(ini, name);
if (!section)
{
if (!IniFile_SectionResize(ini, 1))
return nullptr;
section = IniFile_Section_New(name);
if (!section)
return nullptr;
ini->sections[ini->nSections++] = section;
}
return section;
}
static wIniFileKey* IniFile_GetKey(wIniFileSection* section, const char* name)
{
wIniFileKey* key = nullptr;
WINPR_ASSERT(section);
if (!name)
return nullptr;
for (size_t index = 0; index < section->nKeys; index++)
{
if (_stricmp(name, section->keys[index]->name) == 0)
{
key = section->keys[index];
break;
}
}
return key;
}
static wIniFileKey* IniFile_AddKey(wIniFileSection* section, const char* name, const char* value)
{
WINPR_ASSERT(section);
if (!name || !value)
return nullptr;
wIniFileKey* key = IniFile_GetKey(section, name);
if (!key)
{
if (!IniFile_SectionKeysResize(section, 1))
return nullptr;
key = IniFile_Key_New(name, value);
if (!key)
return nullptr;
section->keys[section->nKeys++] = key;
}
else
{
free(key->value);
key->value = _strdup(value);
if (!key->value)
return nullptr;
}
return key;
}
static int IniFile_Load(wIniFile* ini)
{
char* name = nullptr;
char* value = nullptr;
char* separator = nullptr;
char* beg = nullptr;
char* end = nullptr;
wIniFileSection* section = nullptr;
WINPR_ASSERT(ini);
while (IniFile_Load_HasNextLine(ini))
{
char* line = IniFile_Load_GetNextLine(ini);
if (line[0] == ';')
continue;
if (line[0] == '[')
{
beg = &line[1];
end = strchr(line, ']');
if (!end)
return -1;
*end = '\0';
IniFile_AddToSection(ini, beg);
section = ini->sections[ini->nSections - 1];
}
else
{
separator = strchr(line, '=');
if (separator == nullptr)
return -1;
end = separator;
while ((&end[-1] > line) && ((end[-1] == ' ') || (end[-1] == '\t')))
end--;
*end = '\0';
name = line;
beg = separator + 1;
while (*beg && ((*beg == ' ') || (*beg == '\t')))
beg++;
if (*beg == '"')
beg++;
end = &line[ini->lineLength];
while ((end > beg) && ((end[-1] == ' ') || (end[-1] == '\t')))
end--;
if (end[-1] == '"')
end[-1] = '\0';
value = beg;
if (!IniFile_AddKey(section, name, value))
return -1;
}
}
return 1;
}
static BOOL IniFile_SetFilename(wIniFile* ini, const char* name)
{
WINPR_ASSERT(ini);
free(ini->filename);
ini->filename = nullptr;
if (!name)
return TRUE;
ini->filename = _strdup(name);
return ini->filename != nullptr;
}
int IniFile_ReadBuffer(wIniFile* ini, const char* buffer)
{
BOOL status = 0;
WINPR_ASSERT(ini);
if (!buffer)
return -1;
ini->readOnly = TRUE;
status = IniFile_Load_String(ini, buffer);
if (!status)
return -1;
return IniFile_Load(ini);
}
int IniFile_ReadFile(wIniFile* ini, const char* filename)
{
WINPR_ASSERT(ini);
ini->readOnly = TRUE;
if (!IniFile_SetFilename(ini, filename))
return -1;
if (!ini->filename)
return -1;
if (!IniFile_Load_File(ini, filename))
return -1;
return IniFile_Load(ini);
}
char** IniFile_GetSectionNames(wIniFile* ini, size_t* count)
{
WINPR_ASSERT(ini);
if (!count)
return nullptr;
if (ini->nSections > INT_MAX)
return nullptr;
size_t length = (sizeof(char*) * ini->nSections) + sizeof(char);
for (size_t index = 0; index < ini->nSections; index++)
{
wIniFileSection* section = ini->sections[index];
const size_t nameLength = strlen(section->name);
length += (nameLength + 1);
}
char** sectionNames = (char**)calloc(length, sizeof(char*));
if (!sectionNames)
return nullptr;
char* p = (char*)&((BYTE*)sectionNames)[sizeof(char*) * ini->nSections];
for (size_t index = 0; index < ini->nSections; index++)
{
sectionNames[index] = p;
wIniFileSection* section = ini->sections[index];
const size_t nameLength = strlen(section->name);
CopyMemory(p, section->name, nameLength + 1);
p += (nameLength + 1);
}
*p = '\0';
*count = ini->nSections;
return sectionNames;
}
char** IniFile_GetSectionKeyNames(wIniFile* ini, const char* section, size_t* count)
{
WINPR_ASSERT(ini);
if (!section || !count)
return nullptr;
wIniFileSection* pSection = IniFile_GetSection(ini, section);
if (!pSection)
return nullptr;
if (pSection->nKeys > INT_MAX)
return nullptr;
size_t length = (sizeof(char*) * pSection->nKeys) + sizeof(char);
for (size_t index = 0; index < pSection->nKeys; index++)
{
wIniFileKey* pKey = pSection->keys[index];
const size_t nameLength = strlen(pKey->name);
length += (nameLength + 1);
}
char** keyNames = (char**)calloc(length, sizeof(char*));
if (!keyNames)
return nullptr;
char* p = (char*)&((BYTE*)keyNames)[sizeof(char*) * pSection->nKeys];
for (size_t index = 0; index < pSection->nKeys; index++)
{
keyNames[index] = p;
wIniFileKey* pKey = pSection->keys[index];
const size_t nameLength = strlen(pKey->name);
CopyMemory(p, pKey->name, nameLength + 1);
p += (nameLength + 1);
}
*p = '\0';
*count = pSection->nKeys;
return keyNames;
}
const char* IniFile_GetKeyValueString(wIniFile* ini, const char* section, const char* key)
{
const char* value = nullptr;
wIniFileKey* pKey = nullptr;
wIniFileSection* pSection = nullptr;
WINPR_ASSERT(ini);
pSection = IniFile_GetSection(ini, section);
if (!pSection)
return nullptr;
pKey = IniFile_GetKey(pSection, key);
if (!pKey)
return nullptr;
value = (const char*)pKey->value;
return value;
}
int IniFile_GetKeyValueInt(wIniFile* ini, const char* section, const char* key)
{
int err = 0;
long value = 0;
wIniFileKey* pKey = nullptr;
wIniFileSection* pSection = nullptr;
WINPR_ASSERT(ini);
pSection = IniFile_GetSection(ini, section);
if (!pSection)
return 0;
pKey = IniFile_GetKey(pSection, key);
if (!pKey)
return 0;
err = errno;
errno = 0;
value = strtol(pKey->value, nullptr, 0);
if ((value < INT_MIN) || (value > INT_MAX) || (errno != 0))
{
errno = err;
return 0;
}
return (int)value;
}
int IniFile_SetKeyValueString(wIniFile* ini, const char* section, const char* key,
const char* value)
{
wIniFileKey* pKey = nullptr;
WINPR_ASSERT(ini);
wIniFileSection* pSection = IniFile_GetSection(ini, section);
if (!pSection)
pSection = IniFile_AddToSection(ini, section);
if (!pSection)
return -1;
pKey = IniFile_AddKey(pSection, key, value);
if (!pKey)
return -1;
return 1;
}
int IniFile_SetKeyValueInt(wIniFile* ini, const char* section, const char* key, int value)
{
char strVal[128] = WINPR_C_ARRAY_INIT;
wIniFileKey* pKey = nullptr;
wIniFileSection* pSection = nullptr;
WINPR_ASSERT(ini);
(void)sprintf_s(strVal, sizeof(strVal), "%d", value);
pSection = IniFile_GetSection(ini, section);
if (!pSection)
pSection = IniFile_AddToSection(ini, section);
if (!pSection)
return -1;
pKey = IniFile_AddKey(pSection, key, strVal);
if (!pKey)
return -1;
return 1;
}
char* IniFile_WriteBuffer(wIniFile* ini)
{
size_t offset = 0;
size_t size = 0;
char* buffer = nullptr;
WINPR_ASSERT(ini);
for (size_t i = 0; i < ini->nSections; i++)
{
wIniFileSection* section = ini->sections[i];
size += (strlen(section->name) + 3);
for (size_t j = 0; j < section->nKeys; j++)
{
wIniFileKey* key = section->keys[j];
size += (strlen(key->name) + strlen(key->value) + 2);
}
size += 1;
}
size += 1;
buffer = calloc(size + 1, sizeof(char));
if (!buffer)
return nullptr;
offset = 0;
for (size_t i = 0; i < ini->nSections; i++)
{
wIniFileSection* section = ini->sections[i];
(void)sprintf_s(&buffer[offset], size - offset, "[%s]\n", section->name);
offset += (strlen(section->name) + 3);
for (size_t j = 0; j < section->nKeys; j++)
{
wIniFileKey* key = section->keys[j];
(void)sprintf_s(&buffer[offset], size - offset, "%s=%s\n", key->name, key->value);
offset += (strlen(key->name) + strlen(key->value) + 2);
}
(void)sprintf_s(&buffer[offset], size - offset, "\n");
offset += 1;
}
return buffer;
}
int IniFile_WriteFile(wIniFile* ini, const char* filename)
{
int ret = -1;
WINPR_ASSERT(ini);
char* buffer = IniFile_WriteBuffer(ini);
if (!buffer)
return -1;
const size_t length = strlen(buffer);
ini->readOnly = FALSE;
if (!filename)
filename = ini->filename;
FILE* fp = IniFile_Open_File(ini, filename);
if (!fp)
goto fail;
if (fwrite((void*)buffer, length, 1, fp) != 1)
goto fail;
ret = 1;
fail:
IniFile_Close_File(fp);
free(buffer);
return ret;
}
void IniFile_Free(wIniFile* ini)
{
if (!ini)
return;
IniFile_SetFilename(ini, nullptr);
for (size_t index = 0; index < ini->nSections; index++)
IniFile_Section_Free(ini->sections[index]);
free((void*)ini->sections);
free(ini->buffer);
free(ini);
}
wIniFile* IniFile_New(void)
{
wIniFile* ini = (wIniFile*)calloc(1, sizeof(wIniFile));
if (!ini)
goto fail;
if (!IniFile_SectionResize(ini, 64))
goto fail;
return ini;
fail:
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
IniFile_Free(ini);
WINPR_PRAGMA_DIAG_POP
return nullptr;
}
wIniFile* IniFile_Clone(const wIniFile* ini)
{
if (!ini)
return nullptr;
wIniFile* copy = IniFile_New();
if (!copy)
goto fail;
copy->lineLength = ini->lineLength;
if (!IniFile_SetFilename(copy, ini->filename))
goto fail;
if (ini->buffersize > 0)
{
if (!IniFile_BufferResize(copy, ini->buffersize))
goto fail;
memcpy(copy->buffer, ini->buffer, copy->buffersize);
}
copy->readOnly = ini->readOnly;
for (size_t x = 0; x < ini->nSections; x++)
{
const wIniFileSection* cur = ini->sections[x];
if (!cur)
goto fail;
wIniFileSection* scopy = IniFile_AddToSection(copy, cur->name);
if (!scopy)
goto fail;
for (size_t y = 0; y < cur->nKeys; y++)
{
const wIniFileKey* key = cur->keys[y];
if (!key)
goto fail;
IniFile_AddKey(scopy, key->name, key->value);
}
}
return copy;
fail:
IniFile_Free(copy);
return nullptr;
}

View File

@@ -0,0 +1,304 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* JSON parser wrapper
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 Thincast Technologies GmbH
*
* 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.
*/
#include <math.h>
#include <errno.h>
#include <winpr/file.h>
#include <winpr/json.h>
#include <winpr/assert.h>
#if !defined(WITH_CJSON)
#error "This file must only be compiled when cJSON is enabled"
#endif
#include <cjson/cJSON.h>
#if defined(WITH_CJSON)
#if CJSON_VERSION_MAJOR == 1
#if (CJSON_VERSION_MINOR < 7) || ((CJSON_VERSION_MINOR == 7) && (CJSON_VERSION_PATCH < 13))
#define USE_CJSON_COMPAT
#endif
#endif
#endif
#if defined(USE_CJSON_COMPAT)
static double cJSON_GetNumberValue(const cJSON* prop)
{
#ifndef NAN
#ifdef _WIN32
#define NAN sqrt(-1.0)
#define COMPAT_NAN_UNDEF
#else
#define NAN 0.0 / 0.0
#define COMPAT_NAN_UNDEF
#endif
#endif
if (!cJSON_IsNumber(prop))
return NAN;
char* val = cJSON_GetStringValue(prop);
if (!val)
return NAN;
errno = 0;
char* endptr = nullptr;
double dval = strtod(val, &endptr);
if (val == endptr)
return NAN;
if (endptr != nullptr)
return NAN;
if (errno != 0)
return NAN;
return dval;
#ifdef COMPAT_NAN_UNDEF
#undef NAN
#endif
}
static cJSON* cJSON_ParseWithLength(const char* value, size_t buffer_length)
{
// Check for string '\0' termination.
const size_t slen = strnlen(value, buffer_length);
if (slen >= buffer_length)
{
if (value[buffer_length] != '\0')
return nullptr;
}
return cJSON_Parse(value);
}
#endif
int WINPR_JSON_version(char* buffer, size_t len)
{
return _snprintf(buffer, len, "cJSON %s", cJSON_Version());
}
WINPR_JSON* WINPR_JSON_Parse(const char* value)
{
return cJSON_Parse(value);
}
WINPR_JSON* WINPR_JSON_ParseWithLength(const char* value, size_t buffer_length)
{
return cJSON_ParseWithLength(value, buffer_length);
}
void WINPR_JSON_Delete(WINPR_JSON* item)
{
cJSON_Delete((cJSON*)item);
}
WINPR_JSON* WINPR_JSON_GetArrayItem(const WINPR_JSON* array, size_t index)
{
WINPR_ASSERT(index <= INT_MAX);
return cJSON_GetArrayItem((const cJSON*)array, (INT)index);
}
size_t WINPR_JSON_GetArraySize(const WINPR_JSON* array)
{
const int rc = cJSON_GetArraySize((const cJSON*)array);
if (rc <= 0)
return 0;
return (size_t)rc;
}
WINPR_JSON* WINPR_JSON_GetObjectItem(const WINPR_JSON* object, const char* string)
{
return cJSON_GetObjectItem((const cJSON*)object, string);
}
WINPR_JSON* WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON* object, const char* string)
{
return cJSON_GetObjectItemCaseSensitive((const cJSON*)object, string);
}
BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON* object, const char* string)
{
return cJSON_HasObjectItem((const cJSON*)object, string);
}
const char* WINPR_JSON_GetErrorPtr(void)
{
return cJSON_GetErrorPtr();
}
const char* WINPR_JSON_GetStringValue(WINPR_JSON* item)
{
return cJSON_GetStringValue((cJSON*)item);
}
double WINPR_JSON_GetNumberValue(const WINPR_JSON* item)
{
return cJSON_GetNumberValue((const cJSON*)item);
}
BOOL WINPR_JSON_IsInvalid(const WINPR_JSON* item)
{
return cJSON_IsInvalid((const cJSON*)item);
}
BOOL WINPR_JSON_IsFalse(const WINPR_JSON* item)
{
return cJSON_IsFalse((const cJSON*)item);
}
BOOL WINPR_JSON_IsTrue(const WINPR_JSON* item)
{
return cJSON_IsTrue((const cJSON*)item);
}
BOOL WINPR_JSON_IsBool(const WINPR_JSON* item)
{
return cJSON_IsBool((const cJSON*)item);
}
BOOL WINPR_JSON_IsNull(const WINPR_JSON* item)
{
return cJSON_IsNull((const cJSON*)item);
}
BOOL WINPR_JSON_IsNumber(const WINPR_JSON* item)
{
return cJSON_IsNumber((const cJSON*)item);
}
BOOL WINPR_JSON_IsString(const WINPR_JSON* item)
{
return cJSON_IsString((const cJSON*)item);
}
BOOL WINPR_JSON_IsArray(const WINPR_JSON* item)
{
return cJSON_IsArray((const cJSON*)item);
}
BOOL WINPR_JSON_IsObject(const WINPR_JSON* item)
{
return cJSON_IsObject((const cJSON*)item);
}
WINPR_JSON* WINPR_JSON_CreateNull(void)
{
return cJSON_CreateNull();
}
WINPR_JSON* WINPR_JSON_CreateTrue(void)
{
return cJSON_CreateTrue();
}
WINPR_JSON* WINPR_JSON_CreateFalse(void)
{
return cJSON_CreateFalse();
}
WINPR_JSON* WINPR_JSON_CreateBool(BOOL boolean)
{
return cJSON_CreateBool(boolean);
}
WINPR_JSON* WINPR_JSON_CreateNumber(double num)
{
return cJSON_CreateNumber(num);
}
WINPR_JSON* WINPR_JSON_CreateString(const char* string)
{
return cJSON_CreateString(string);
}
WINPR_JSON* WINPR_JSON_CreateArray(void)
{
return cJSON_CreateArray();
}
WINPR_JSON* WINPR_JSON_CreateObject(void)
{
return cJSON_CreateObject();
}
WINPR_JSON* WINPR_JSON_AddNullToObject(WINPR_JSON* object, const char* name)
{
return cJSON_AddNullToObject((cJSON*)object, name);
}
WINPR_JSON* WINPR_JSON_AddTrueToObject(WINPR_JSON* object, const char* name)
{
return cJSON_AddTrueToObject((cJSON*)object, name);
}
WINPR_JSON* WINPR_JSON_AddFalseToObject(WINPR_JSON* object, const char* name)
{
return cJSON_AddFalseToObject((cJSON*)object, name);
}
WINPR_JSON* WINPR_JSON_AddBoolToObject(WINPR_JSON* object, const char* name, BOOL boolean)
{
return cJSON_AddBoolToObject((cJSON*)object, name, boolean);
}
WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name, double number)
{
return cJSON_AddNumberToObject((cJSON*)object, name, number);
}
WINPR_JSON* WINPR_JSON_AddIntegerToObject(WINPR_JSON* object, const char* name, int64_t number)
{
char str[64] = WINPR_C_ARRAY_INIT;
(void)_snprintf(str, sizeof(str), "%" PRId64, number);
return cJSON_AddRawToObject((cJSON*)object, name, str);
}
WINPR_JSON* WINPR_JSON_AddStringToObject(WINPR_JSON* object, const char* name, const char* string)
{
return cJSON_AddStringToObject((cJSON*)object, name, string);
}
WINPR_JSON* WINPR_JSON_AddObjectToObject(WINPR_JSON* object, const char* name)
{
return cJSON_AddObjectToObject((cJSON*)object, name);
}
BOOL WINPR_JSON_AddItemToArray(WINPR_JSON* array, WINPR_JSON* item)
{
#if defined(USE_CJSON_COMPAT)
if ((array == nullptr) || (item == nullptr))
return FALSE;
cJSON_AddItemToArray((cJSON*)array, (cJSON*)item);
return TRUE;
#else
return cJSON_AddItemToArray((cJSON*)array, (cJSON*)item);
#endif
}
WINPR_JSON* WINPR_JSON_AddArrayToObject(WINPR_JSON* object, const char* name)
{
return cJSON_AddArrayToObject((cJSON*)object, name);
}
char* WINPR_JSON_Print(WINPR_JSON* item)
{
return cJSON_Print((const cJSON*)item);
}
char* WINPR_JSON_PrintUnformatted(WINPR_JSON* item)
{
return cJSON_PrintUnformatted((const cJSON*)item);
}

View File

@@ -0,0 +1,355 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* JSON parser wrapper
*
* Copyright 2025 Armin Novak <anovak@thincast.com>
* Copyright 2025 Thincast Technologies GmbH
*
* 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.
*/
#include <winpr/file.h>
#include <winpr/json.h>
#include <winpr/assert.h>
#if !defined(WITH_JANSSON)
#error "This file must only be compiled if jansson library is linked in"
#endif
#include <jansson.h>
#if !defined(JANSSON_VERSION_HEX) || (JANSSON_VERSION_HEX < 0x020d00)
#error "The library detected is too old, need >= 2.13.0"
#endif
static WINPR_TLS char lasterror[256] = WINPR_C_ARRAY_INIT;
#if defined(WITH_DEBUG_JANSSON)
#include "../log.h"
#define TAG WINPR_TAG("jansson")
#define ccast(json) ccast_((json), __func__)
static const json_t* ccast_(const WINPR_JSON* json, const char* fkt)
{
const json_t* jansson = (const json_t*)json;
if (!jansson)
WLog_DBG(TAG, "%s: nullptr", fkt);
else
{
WLog_DBG(TAG, "%s: %" PRIuz, fkt, jansson->refcount);
}
return jansson;
}
#define cast(json) cast_((json), __func__)
static json_t* cast_(WINPR_JSON* json, const char* fkt)
{
json_t* jansson = (json_t*)json;
if (!jansson)
WLog_DBG(TAG, "%s: nullptr", fkt);
else
{
WLog_DBG(TAG, "%s: %" PRIuz, fkt, jansson->refcount);
}
return jansson;
}
#define revcast(json) revcast_((json), __func__)
static WINPR_JSON* revcast_(json_t* json, const char* fkt)
{
json_t* jansson = (json_t*)json;
if (!jansson)
WLog_DBG(TAG, "%s: nullptr", fkt);
else
{
WLog_DBG(TAG, "%s: %" PRIuz, fkt, jansson->refcount);
}
return jansson;
}
#else
static inline const json_t* ccast(const WINPR_JSON* json)
{
return WINPR_CXX_COMPAT_CAST(const json_t*, json);
}
static inline json_t* cast(WINPR_JSON* json)
{
return WINPR_CXX_COMPAT_CAST(json_t*, json);
}
static inline WINPR_JSON* revcast(json_t* json)
{
return WINPR_CXX_COMPAT_CAST(WINPR_JSON*, json);
}
#endif
int WINPR_JSON_version(char* buffer, size_t len)
{
return _snprintf(buffer, len, "jansson %s", jansson_version_str());
}
static WINPR_JSON* updateError(WINPR_JSON* json, const json_error_t* error)
{
lasterror[0] = '\0';
if (!json)
(void)_snprintf(lasterror, sizeof(lasterror), "[%d:%d:%d] %s [%s]", error->line,
error->column, error->position, error->text, error->source);
return json;
}
WINPR_JSON* WINPR_JSON_Parse(const char* value)
{
json_error_t error = WINPR_C_ARRAY_INIT;
WINPR_JSON* json = revcast(json_loads(value, JSON_DECODE_ANY, &error));
return updateError(json, &error);
}
WINPR_JSON* WINPR_JSON_ParseWithLength(const char* value, size_t buffer_length)
{
if (!value || (buffer_length == 0))
return nullptr;
json_error_t error = WINPR_C_ARRAY_INIT;
const size_t slen = strnlen(value, buffer_length);
WINPR_JSON* json = revcast(json_loadb(value, slen, JSON_DECODE_ANY, &error));
return updateError(json, &error);
}
void WINPR_JSON_Delete(WINPR_JSON* item)
{
json_delete(cast(item));
}
WINPR_JSON* WINPR_JSON_GetArrayItem(const WINPR_JSON* array, size_t index)
{
return revcast(json_array_get(ccast(array), index));
}
size_t WINPR_JSON_GetArraySize(const WINPR_JSON* array)
{
return json_array_size(ccast(array));
}
WINPR_JSON* WINPR_JSON_GetObjectItem(const WINPR_JSON* object, const char* string)
{
json_t* json = cast(WINPR_CAST_CONST_PTR_AWAY(object, WINPR_JSON*));
void* it = json_object_iter(json);
while (it)
{
const char* name = json_object_iter_key(it);
if (_stricmp(name, string) == 0)
return revcast(json_object_iter_value(it));
it = json_object_iter_next(json, it);
}
return nullptr;
}
WINPR_JSON* WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON* object, const char* string)
{
return revcast(json_object_get(ccast(object), string));
}
BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON* object, const char* string)
{
return json_object_get(ccast(object), string) != nullptr;
}
const char* WINPR_JSON_GetErrorPtr(void)
{
return lasterror;
}
const char* WINPR_JSON_GetStringValue(WINPR_JSON* item)
{
return json_string_value(cast(item));
}
double WINPR_JSON_GetNumberValue(const WINPR_JSON* item)
{
return json_number_value(ccast(item));
}
BOOL WINPR_JSON_IsInvalid(const WINPR_JSON* item)
{
const json_t* jitem = ccast(item);
if (WINPR_JSON_IsArray(jitem))
return FALSE;
if (WINPR_JSON_IsObject(jitem))
return FALSE;
if (WINPR_JSON_IsNull(jitem))
return FALSE;
if (WINPR_JSON_IsNumber(jitem))
return FALSE;
if (WINPR_JSON_IsBool(jitem))
return FALSE;
if (WINPR_JSON_IsString(jitem))
return FALSE;
return TRUE;
}
BOOL WINPR_JSON_IsFalse(const WINPR_JSON* item)
{
return json_is_false(ccast(item));
}
BOOL WINPR_JSON_IsTrue(const WINPR_JSON* item)
{
return json_is_true(ccast(item));
}
BOOL WINPR_JSON_IsBool(const WINPR_JSON* item)
{
return json_is_boolean(ccast(item));
}
BOOL WINPR_JSON_IsNull(const WINPR_JSON* item)
{
return json_is_null(ccast(item));
}
BOOL WINPR_JSON_IsNumber(const WINPR_JSON* item)
{
return json_is_number(ccast(item));
}
BOOL WINPR_JSON_IsString(const WINPR_JSON* item)
{
return json_is_string(ccast(item));
}
BOOL WINPR_JSON_IsArray(const WINPR_JSON* item)
{
return json_is_array(ccast(item));
}
BOOL WINPR_JSON_IsObject(const WINPR_JSON* item)
{
return json_is_object(ccast(item));
}
WINPR_JSON* WINPR_JSON_CreateNull(void)
{
return revcast(json_null());
}
WINPR_JSON* WINPR_JSON_CreateTrue(void)
{
return revcast(json_true());
}
WINPR_JSON* WINPR_JSON_CreateFalse(void)
{
return revcast(json_false());
}
WINPR_JSON* WINPR_JSON_CreateBool(BOOL boolean)
{
return revcast(json_boolean(boolean));
}
WINPR_JSON* WINPR_JSON_CreateNumber(double num)
{
return revcast(json_real(num));
}
WINPR_JSON* WINPR_JSON_CreateString(const char* string)
{
return revcast(json_string(string));
}
WINPR_JSON* WINPR_JSON_CreateArray(void)
{
return revcast(json_array());
}
WINPR_JSON* WINPR_JSON_CreateObject(void)
{
return revcast(json_object());
}
static WINPR_JSON* add_to_object(WINPR_JSON* object, const char* name, json_t* obj)
{
if (!obj)
return nullptr;
const int rc = json_object_set_new(cast(object), name, obj);
if (rc != 0)
return nullptr;
return revcast(obj);
}
WINPR_JSON* WINPR_JSON_AddNullToObject(WINPR_JSON* object, const char* name)
{
json_t* obj = json_null();
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddTrueToObject(WINPR_JSON* object, const char* name)
{
json_t* obj = json_true();
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddFalseToObject(WINPR_JSON* object, const char* name)
{
json_t* obj = json_false();
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddBoolToObject(WINPR_JSON* object, const char* name, BOOL boolean)
{
json_t* obj = json_boolean(boolean);
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name, double number)
{
json_t* obj = json_real(number);
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddIntegerToObject(WINPR_JSON* object, const char* name, int64_t number)
{
json_t* obj = json_integer(number);
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddStringToObject(WINPR_JSON* object, const char* name, const char* string)
{
json_t* obj = json_string(string);
return add_to_object(object, name, obj);
}
WINPR_JSON* WINPR_JSON_AddObjectToObject(WINPR_JSON* object, const char* name)
{
json_t* obj = json_object();
return add_to_object(object, name, obj);
}
BOOL WINPR_JSON_AddItemToArray(WINPR_JSON* array, WINPR_JSON* item)
{
return json_array_append_new(cast(array), item) == 0;
}
WINPR_JSON* WINPR_JSON_AddArrayToObject(WINPR_JSON* object, const char* name)
{
json_t* obj = json_array();
return add_to_object(object, name, obj);
}
char* WINPR_JSON_Print(WINPR_JSON* item)
{
return json_dumps(cast(item), JSON_INDENT(2) | JSON_ENSURE_ASCII | JSON_SORT_KEYS);
}
char* WINPR_JSON_PrintUnformatted(WINPR_JSON* item)
{
return json_dumps(cast(item), JSON_COMPACT | JSON_ENSURE_ASCII | JSON_SORT_KEYS);
}

View File

@@ -0,0 +1,343 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* JSON parser wrapper
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 Thincast Technologies GmbH
*
* 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.
*/
#include <math.h>
#include <errno.h>
#include <winpr/file.h>
#include <winpr/json.h>
#include <winpr/assert.h>
#if !defined(WITH_JSONC)
#error "This file must only be compiled when json-c is enabled"
#endif
#include <json.h>
#if JSON_C_MAJOR_VERSION == 0
#if JSON_C_MINOR_VERSION < 14
static struct json_object* json_object_new_null(void)
{
return nullptr;
}
#endif
#endif
int WINPR_JSON_version(char* buffer, size_t len)
{
return _snprintf(buffer, len, "json-c %s", json_c_version());
}
WINPR_JSON* WINPR_JSON_Parse(const char* value)
{
return json_tokener_parse(value);
}
WINPR_JSON* WINPR_JSON_ParseWithLength(const char* value, size_t buffer_length)
{
WINPR_ASSERT(buffer_length <= INT_MAX);
json_tokener* tok = json_tokener_new();
if (!tok)
return nullptr;
json_object* obj = json_tokener_parse_ex(tok, value, (int)buffer_length);
json_tokener_free(tok);
return obj;
}
void WINPR_JSON_Delete(WINPR_JSON* item)
{
json_object_put((json_object*)item);
}
WINPR_JSON* WINPR_JSON_GetArrayItem(const WINPR_JSON* array, size_t index)
{
return json_object_array_get_idx((const json_object*)array, index);
}
size_t WINPR_JSON_GetArraySize(const WINPR_JSON* array)
{
return json_object_array_length((const json_object*)array);
}
WINPR_JSON* WINPR_JSON_GetObjectItem(const WINPR_JSON* object, const char* string)
{
struct json_object_iterator it = json_object_iter_begin((const json_object*)object);
struct json_object_iterator itEnd = json_object_iter_end((const json_object*)object);
while (!json_object_iter_equal(&it, &itEnd))
{
const char* key = json_object_iter_peek_name(&it);
if (_stricmp(key, string) == 0)
{
return json_object_iter_peek_value(&it);
}
json_object_iter_next(&it);
}
return nullptr;
}
WINPR_JSON* WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON* object, const char* string)
{
return json_object_object_get((const json_object*)object, string);
}
BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON* object, const char* string)
{
return json_object_object_get_ex((const json_object*)object, string, nullptr);
}
const char* WINPR_JSON_GetErrorPtr(void)
{
return json_util_get_last_err();
}
const char* WINPR_JSON_GetStringValue(WINPR_JSON* item)
{
return json_object_get_string((json_object*)item);
}
double WINPR_JSON_GetNumberValue(const WINPR_JSON* item)
{
return json_object_get_double((const json_object*)item);
}
BOOL WINPR_JSON_IsInvalid(const WINPR_JSON* item)
{
if (WINPR_JSON_IsArray(item))
return FALSE;
if (WINPR_JSON_IsObject(item))
return FALSE;
if (WINPR_JSON_IsNull(item))
return FALSE;
if (WINPR_JSON_IsNumber(item))
return FALSE;
if (WINPR_JSON_IsBool(item))
return FALSE;
if (WINPR_JSON_IsString(item))
return FALSE;
return TRUE;
}
BOOL WINPR_JSON_IsFalse(const WINPR_JSON* item)
{
if (!json_object_is_type((const json_object*)item, json_type_boolean))
return FALSE;
json_bool val = json_object_get_boolean((const json_object*)item);
return val == 0;
}
BOOL WINPR_JSON_IsTrue(const WINPR_JSON* item)
{
if (!json_object_is_type((const json_object*)item, json_type_boolean))
return FALSE;
json_bool val = json_object_get_boolean((const json_object*)item);
return val != 0;
}
BOOL WINPR_JSON_IsBool(const WINPR_JSON* item)
{
return json_object_is_type((const json_object*)item, json_type_boolean);
}
BOOL WINPR_JSON_IsNull(const WINPR_JSON* item)
{
return json_object_is_type((const json_object*)item, json_type_null);
}
BOOL WINPR_JSON_IsNumber(const WINPR_JSON* item)
{
return json_object_is_type((const json_object*)item, json_type_int) ||
json_object_is_type((const json_object*)item, json_type_double);
}
BOOL WINPR_JSON_IsString(const WINPR_JSON* item)
{
return json_object_is_type((const json_object*)item, json_type_string);
}
BOOL WINPR_JSON_IsArray(const WINPR_JSON* item)
{
return json_object_is_type((const json_object*)item, json_type_array);
}
BOOL WINPR_JSON_IsObject(const WINPR_JSON* item)
{
return json_object_is_type((const json_object*)item, json_type_object);
}
WINPR_JSON* WINPR_JSON_CreateNull(void)
{
return json_object_new_null();
}
WINPR_JSON* WINPR_JSON_CreateTrue(void)
{
return json_object_new_boolean(TRUE);
}
WINPR_JSON* WINPR_JSON_CreateFalse(void)
{
return json_object_new_boolean(FALSE);
}
WINPR_JSON* WINPR_JSON_CreateBool(BOOL boolean)
{
return json_object_new_boolean(boolean);
}
WINPR_JSON* WINPR_JSON_CreateNumber(double num)
{
return json_object_new_double(num);
}
WINPR_JSON* WINPR_JSON_CreateString(const char* string)
{
return json_object_new_string(string);
}
WINPR_JSON* WINPR_JSON_CreateArray(void)
{
return json_object_new_array();
}
WINPR_JSON* WINPR_JSON_CreateObject(void)
{
return json_object_new_object();
}
WINPR_JSON* WINPR_JSON_AddNullToObject(WINPR_JSON* object, const char* name)
{
struct json_object* obj = json_object_new_null();
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddTrueToObject(WINPR_JSON* object, const char* name)
{
struct json_object* obj = json_object_new_boolean(TRUE);
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddFalseToObject(WINPR_JSON* object, const char* name)
{
struct json_object* obj = json_object_new_boolean(FALSE);
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddBoolToObject(WINPR_JSON* object, const char* name, BOOL boolean)
{
struct json_object* obj = json_object_new_boolean(boolean);
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name, double number)
{
struct json_object* obj = json_object_new_double(number);
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddIntegerToObject(WINPR_JSON* object, const char* name, int64_t number)
{
struct json_object* obj = json_object_new_int64(number);
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddStringToObject(WINPR_JSON* object, const char* name, const char* string)
{
struct json_object* obj = json_object_new_string(string);
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
WINPR_JSON* WINPR_JSON_AddObjectToObject(WINPR_JSON* object, const char* name)
{
struct json_object* obj = json_object_new_object();
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
BOOL WINPR_JSON_AddItemToArray(WINPR_JSON* array, WINPR_JSON* item)
{
const int rc = json_object_array_add((json_object*)array, (json_object*)item);
if (rc != 0)
return FALSE;
return TRUE;
}
WINPR_JSON* WINPR_JSON_AddArrayToObject(WINPR_JSON* object, const char* name)
{
struct json_object* obj = json_object_new_array();
if (json_object_object_add((json_object*)object, name, obj) != 0)
{
json_object_put(obj);
return nullptr;
}
return obj;
}
char* WINPR_JSON_Print(WINPR_JSON* item)
{
const char* str = json_object_to_json_string_ext((json_object*)item, JSON_C_TO_STRING_PRETTY);
if (!str)
return nullptr;
return _strdup(str);
}
char* WINPR_JSON_PrintUnformatted(WINPR_JSON* item)
{
const char* str = json_object_to_json_string_ext((json_object*)item, JSON_C_TO_STRING_PLAIN);
if (!str)
return nullptr;
return _strdup(str);
}

View File

@@ -0,0 +1,275 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* JSON parser wrapper
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 Thincast Technologies GmbH
*
* 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.
*/
#include <math.h>
#include <errno.h>
#include <winpr/file.h>
#include <winpr/json.h>
#include <winpr/assert.h>
int WINPR_JSON_version(char* buffer, size_t len)
{
(void)_snprintf(buffer, len, "JSON support not available");
return -1;
}
WINPR_JSON* WINPR_JSON_Parse(const char* value)
{
WINPR_UNUSED(value);
return nullptr;
}
WINPR_JSON* WINPR_JSON_ParseWithLength(const char* value, size_t buffer_length)
{
WINPR_UNUSED(value);
WINPR_UNUSED(buffer_length);
return nullptr;
}
void WINPR_JSON_Delete(WINPR_JSON* item)
{
WINPR_UNUSED(item);
}
WINPR_JSON* WINPR_JSON_GetArrayItem(const WINPR_JSON* array, size_t index)
{
WINPR_UNUSED(array);
WINPR_UNUSED(index);
return nullptr;
}
size_t WINPR_JSON_GetArraySize(const WINPR_JSON* array)
{
WINPR_UNUSED(array);
return 0;
}
WINPR_JSON* WINPR_JSON_GetObjectItem(const WINPR_JSON* object, const char* string)
{
WINPR_UNUSED(object);
WINPR_UNUSED(string);
return nullptr;
}
WINPR_JSON* WINPR_JSON_GetObjectItemCaseSensitive(const WINPR_JSON* object, const char* string)
{
WINPR_UNUSED(object);
WINPR_UNUSED(string);
return nullptr;
}
BOOL WINPR_JSON_HasObjectItem(const WINPR_JSON* object, const char* string)
{
WINPR_UNUSED(object);
WINPR_UNUSED(string);
return FALSE;
}
const char* WINPR_JSON_GetErrorPtr(void)
{
return nullptr;
}
const char* WINPR_JSON_GetStringValue(WINPR_JSON* item)
{
WINPR_UNUSED(item);
return nullptr;
}
double WINPR_JSON_GetNumberValue(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return nan("");
}
BOOL WINPR_JSON_IsInvalid(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return TRUE;
}
BOOL WINPR_JSON_IsFalse(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
BOOL WINPR_JSON_IsTrue(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
BOOL WINPR_JSON_IsBool(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
BOOL WINPR_JSON_IsNull(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
BOOL WINPR_JSON_IsNumber(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
BOOL WINPR_JSON_IsString(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
BOOL WINPR_JSON_IsArray(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
BOOL WINPR_JSON_IsObject(const WINPR_JSON* item)
{
WINPR_UNUSED(item);
return FALSE;
}
WINPR_JSON* WINPR_JSON_CreateNull(void)
{
return nullptr;
}
WINPR_JSON* WINPR_JSON_CreateTrue(void)
{
return nullptr;
}
WINPR_JSON* WINPR_JSON_CreateFalse(void)
{
return nullptr;
}
WINPR_JSON* WINPR_JSON_CreateBool(BOOL boolean)
{
WINPR_UNUSED(boolean);
return nullptr;
}
WINPR_JSON* WINPR_JSON_CreateNumber(double num)
{
WINPR_UNUSED(num);
return nullptr;
}
WINPR_JSON* WINPR_JSON_CreateString(const char* string)
{
WINPR_UNUSED(string);
return nullptr;
}
WINPR_JSON* WINPR_JSON_CreateArray(void)
{
return nullptr;
}
WINPR_JSON* WINPR_JSON_CreateObject(void)
{
return nullptr;
}
WINPR_JSON* WINPR_JSON_AddNullToObject(WINPR_JSON* object, const char* name)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
return nullptr;
}
WINPR_JSON* WINPR_JSON_AddTrueToObject(WINPR_JSON* object, const char* name)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
return nullptr;
}
WINPR_JSON* WINPR_JSON_AddFalseToObject(WINPR_JSON* object, const char* name)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
return nullptr;
}
WINPR_JSON* WINPR_JSON_AddBoolToObject(WINPR_JSON* object, const char* name, BOOL boolean)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
WINPR_UNUSED(boolean);
return nullptr;
}
WINPR_JSON* WINPR_JSON_AddNumberToObject(WINPR_JSON* object, const char* name, double number)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
WINPR_UNUSED(number);
return nullptr;
}
WINPR_JSON* WINPR_JSON_AddStringToObject(WINPR_JSON* object, const char* name, const char* string)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
WINPR_UNUSED(string);
return nullptr;
}
WINPR_JSON* WINPR_JSON_AddObjectToObject(WINPR_JSON* object, const char* name)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
return nullptr;
}
BOOL WINPR_JSON_AddItemToArray(WINPR_JSON* array, WINPR_JSON* item)
{
WINPR_UNUSED(array);
WINPR_UNUSED(item);
return FALSE;
}
WINPR_JSON* WINPR_JSON_AddArrayToObject(WINPR_JSON* object, const char* name)
{
WINPR_UNUSED(object);
WINPR_UNUSED(name);
return nullptr;
}
char* WINPR_JSON_Print(WINPR_JSON* item)
{
WINPR_UNUSED(item);
return nullptr;
}
char* WINPR_JSON_PrintUnformatted(WINPR_JSON* item)
{
WINPR_UNUSED(item);
return nullptr;
}

View File

@@ -0,0 +1,63 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* JSON parser wrapper
*
* Copyright 2024 Armin Novak <anovak@thincast.com>
* Copyright 2024 Thincast Technologies GmbH
*
* 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.
*/
#include <math.h>
#include <errno.h>
#include <winpr/file.h>
#include <winpr/json.h>
#include <winpr/assert.h>
WINPR_JSON* WINPR_JSON_ParseFromFile(const char* filename)
{
FILE* fp = winpr_fopen(filename, "r");
if (!fp)
return nullptr;
WINPR_JSON* json = WINPR_JSON_ParseFromFileFP(fp);
(void)fclose(fp);
return json;
}
WINPR_JSON* WINPR_JSON_ParseFromFileFP(FILE* fp)
{
if (!fp)
return nullptr;
if (fseek(fp, 0, SEEK_END) != 0)
return nullptr;
const INT64 size = _ftelli64(fp);
if (size < 0)
return nullptr;
if (fseek(fp, 0, SEEK_SET) != 0)
return nullptr;
const size_t usize = WINPR_ASSERTING_INT_CAST(size_t, size);
char* str = calloc(usize + 1, sizeof(char));
if (!str)
return nullptr;
WINPR_JSON* json = nullptr;
const size_t s = fread(str, sizeof(char), usize, fp);
if (s == usize)
json = WINPR_JSON_ParseWithLength(str, usize);
free(str);
return json;
}

View File

@@ -0,0 +1,182 @@
/**
* WinPR: Windows Portable Runtime
* NTLM Utils
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/ntlm.h>
#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/crypto.h>
/**
* Define NTOWFv1(Password, User, Domain) as
* MD4(UNICODE(Password))
* EndDefine
*/
BOOL NTOWFv1W(LPWSTR Password, UINT32 PasswordLength, BYTE* NtHash)
{
if (!Password || !NtHash)
return FALSE;
if (!winpr_Digest(WINPR_MD_MD4, (BYTE*)Password, (size_t)PasswordLength, NtHash,
WINPR_MD4_DIGEST_LENGTH))
return FALSE;
return TRUE;
}
BOOL NTOWFv1A(LPSTR Password, UINT32 PasswordLength, BYTE* NtHash)
{
LPWSTR PasswordW = nullptr;
BOOL result = FALSE;
size_t pwdCharLength = 0;
if (!NtHash)
return FALSE;
PasswordW = ConvertUtf8NToWCharAlloc(Password, PasswordLength, &pwdCharLength);
if (!PasswordW)
return FALSE;
if (!NTOWFv1W(PasswordW, (UINT32)pwdCharLength * sizeof(WCHAR), NtHash))
goto out_fail;
result = TRUE;
out_fail:
free(PasswordW);
return result;
}
/**
* Define NTOWFv2(Password, User, Domain) as
* HMAC_MD5(MD4(UNICODE(Password)),
* UNICODE(ConcatenationOf(UpperCase(User), Domain)))
* EndDefine
*/
BOOL NTOWFv2W(LPWSTR Password, UINT32 PasswordLength, LPWSTR User, UINT32 UserLength, LPWSTR Domain,
UINT32 DomainLength, BYTE* NtHash)
{
BYTE NtHashV1[WINPR_MD5_DIGEST_LENGTH];
if ((!User) || (!Password) || (!NtHash))
return FALSE;
if (!NTOWFv1W(Password, PasswordLength, NtHashV1))
return FALSE;
return NTOWFv2FromHashW(NtHashV1, User, UserLength, Domain, DomainLength, NtHash);
}
BOOL NTOWFv2A(LPSTR Password, UINT32 PasswordLength, LPSTR User, UINT32 UserLength, LPSTR Domain,
UINT32 DomainLength, BYTE* NtHash)
{
LPWSTR UserW = nullptr;
LPWSTR DomainW = nullptr;
LPWSTR PasswordW = nullptr;
BOOL result = FALSE;
size_t userCharLength = 0;
size_t domainCharLength = 0;
size_t pwdCharLength = 0;
if (!NtHash)
return FALSE;
UserW = ConvertUtf8NToWCharAlloc(User, UserLength, &userCharLength);
DomainW = ConvertUtf8NToWCharAlloc(Domain, DomainLength, &domainCharLength);
PasswordW = ConvertUtf8NToWCharAlloc(Password, PasswordLength, &pwdCharLength);
if (!UserW || !DomainW || !PasswordW)
goto out_fail;
if (!NTOWFv2W(PasswordW, (UINT32)pwdCharLength * sizeof(WCHAR), UserW,
(UINT32)userCharLength * sizeof(WCHAR), DomainW,
(UINT32)domainCharLength * sizeof(WCHAR), NtHash))
goto out_fail;
result = TRUE;
out_fail:
free(UserW);
free(DomainW);
free(PasswordW);
return result;
}
BOOL NTOWFv2FromHashW(BYTE* NtHashV1, LPWSTR User, UINT32 UserLength, LPWSTR Domain,
UINT32 DomainLength, BYTE* NtHash)
{
BYTE* buffer = nullptr;
BYTE result = FALSE;
if (!User || !NtHash)
return FALSE;
if (!(buffer = (BYTE*)malloc(UserLength + DomainLength)))
return FALSE;
/* Concatenate(UpperCase(User), Domain) */
CopyMemory(buffer, User, UserLength);
CharUpperBuffW((LPWSTR)buffer, UserLength / 2);
if (DomainLength > 0)
{
CopyMemory(&buffer[UserLength], Domain, DomainLength);
}
/* Compute the HMAC-MD5 hash of the above value using the NTLMv1 hash as the key, the result is
* the NTLMv2 hash */
if (!winpr_HMAC(WINPR_MD_MD5, NtHashV1, 16, buffer, UserLength + DomainLength, NtHash,
WINPR_MD5_DIGEST_LENGTH))
goto out_fail;
result = TRUE;
out_fail:
free(buffer);
return result;
}
BOOL NTOWFv2FromHashA(BYTE* NtHashV1, LPSTR User, UINT32 UserLength, LPSTR Domain,
UINT32 DomainLength, BYTE* NtHash)
{
LPWSTR UserW = nullptr;
LPWSTR DomainW = nullptr;
BOOL result = FALSE;
size_t userCharLength = 0;
size_t domainCharLength = 0;
if (!NtHash)
return FALSE;
UserW = ConvertUtf8NToWCharAlloc(User, UserLength, &userCharLength);
DomainW = ConvertUtf8NToWCharAlloc(Domain, DomainLength, &domainCharLength);
if (!UserW || !DomainW)
goto out_fail;
if (!NTOWFv2FromHashW(NtHashV1, UserW, (UINT32)userCharLength * sizeof(WCHAR), DomainW,
(UINT32)domainCharLength * sizeof(WCHAR), NtHash))
goto out_fail;
result = TRUE;
out_fail:
free(UserW);
free(DomainW);
return result;
}

View File

@@ -0,0 +1,262 @@
/**
* WinPR: Windows Portable Runtime
* Print Utils
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/debug.h>
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <winpr/crt.h>
#include <winpr/print.h>
#include "../log.h"
#ifndef MIN
#define MIN(a, b) (a) < (b) ? (a) : (b)
#endif
void winpr_HexDump(const char* tag, UINT32 level, const void* data, size_t length)
{
wLog* log = WLog_Get(tag);
winpr_HexLogDump(log, level, data, length);
}
void winpr_HexLogDump(wLog* log, UINT32 level, const void* data, size_t length)
{
const BYTE* p = data;
size_t line = 0;
size_t offset = 0;
const size_t maxlen = 20; /* 64bit SIZE_MAX as decimal */
/* String line length:
* prefix '[1234] '
* hexdump '01 02 03 04'
* separator ' '
* ASIC line 'ab..cd'
* zero terminator '\0'
*/
const size_t blen = (maxlen + 3ULL) + (WINPR_HEXDUMP_LINE_LENGTH * 3ULL) + 3ULL +
WINPR_HEXDUMP_LINE_LENGTH + 1ULL;
size_t pos = 0;
char* buffer = nullptr;
if (!WLog_IsLevelActive(log, level))
return;
if (!log)
return;
buffer = malloc(blen);
if (!buffer)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_Print(log, WLOG_ERROR, "malloc(%" PRIuz ") failed with [%d] %s", blen, errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
return;
}
while (offset < length)
{
int rc = _snprintf(&buffer[pos], blen - pos, "%04" PRIuz " ", offset);
if (rc < 0)
goto fail;
pos += (size_t)rc;
line = length - offset;
if (line > WINPR_HEXDUMP_LINE_LENGTH)
line = WINPR_HEXDUMP_LINE_LENGTH;
size_t i = 0;
for (; i < line; i++)
{
rc = _snprintf(&buffer[pos], blen - pos, "%02" PRIx8 " ", p[i]);
if (rc < 0)
goto fail;
pos += (size_t)rc;
}
for (; i < WINPR_HEXDUMP_LINE_LENGTH; i++)
{
rc = _snprintf(&buffer[pos], blen - pos, " ");
if (rc < 0)
goto fail;
pos += (size_t)rc;
}
for (size_t j = 0; j < line; j++)
{
rc = _snprintf(&buffer[pos], blen - pos, "%c",
(p[j] >= 0x20 && p[j] < 0x7F) ? (char)p[j] : '.');
if (rc < 0)
goto fail;
pos += (size_t)rc;
}
WLog_Print(log, level, "%s", buffer);
offset += line;
p += line;
pos = 0;
}
WLog_Print(log, level, "[length=%" PRIuz "] ", length);
fail:
free(buffer);
}
void winpr_CArrayDump(const char* tag, UINT32 level, const void* data, size_t length, size_t width)
{
const BYTE* p = data;
size_t offset = 0;
const size_t llen = ((length > width) ? width : length) * 4ull + 1ull;
size_t pos = 0;
char* buffer = malloc(llen);
if (!buffer)
{
char ebuffer[256] = WINPR_C_ARRAY_INIT;
WLog_ERR(tag, "malloc(%" PRIuz ") failed with [%d] %s", llen, errno,
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
return;
}
while (offset < length)
{
size_t line = length - offset;
if (line > width)
line = width;
pos = 0;
for (size_t i = 0; i < line; i++)
{
const int rc = _snprintf(&buffer[pos], llen - pos, "\\x%02" PRIX8 "", p[i]);
if (rc < 0)
goto fail;
pos += (size_t)rc;
}
WLog_LVL(tag, level, "%s", buffer);
offset += line;
p += line;
}
fail:
free(buffer);
}
static BYTE value(char c)
{
if ((c >= '0') && (c <= '9'))
return (c - '0') & 0xFF;
if ((c >= 'A') && (c <= 'F'))
return (10 + c - 'A') & 0xFF;
if ((c >= 'a') && (c <= 'f'))
return (10 + c - 'a') & 0xFF;
return 0;
}
size_t winpr_HexStringToBinBuffer(const char* str, size_t strLength, BYTE* data, size_t dataLength)
{
size_t y = 0;
size_t maxStrLen = 0;
if (!str || !data || (strLength == 0) || (dataLength == 0))
return 0;
maxStrLen = strnlen(str, strLength);
for (size_t x = 0; x < maxStrLen;)
{
BYTE val = value(str[x++]);
if (x < maxStrLen)
val = (BYTE)(val << 4) | (value(str[x++]));
if (x < maxStrLen)
{
if (str[x] == ' ')
x++;
}
data[y++] = val;
if (y >= dataLength)
return y;
}
return y;
}
size_t winpr_BinToHexStringBuffer(const BYTE* data, size_t length, char* dstStr, size_t dstSize,
BOOL space)
{
const size_t n = space ? 3 : 2;
const char bin2hex[] = "0123456789ABCDEF";
const size_t maxLength = MIN(length, dstSize / n);
if (!data || !dstStr || (length == 0) || (dstSize == 0))
return 0;
for (size_t i = 0; i < maxLength; i++)
{
const int ln = data[i] & 0xF;
const int hn = (data[i] >> 4) & 0xF;
char* dst = &dstStr[i * n];
dst[0] = bin2hex[hn];
dst[1] = bin2hex[ln];
if (space)
dst[2] = ' ';
}
if (space && (maxLength > 0))
{
dstStr[maxLength * n - 1] = '\0';
return maxLength * n - 1;
}
dstStr[maxLength * n] = '\0';
return maxLength * n;
}
char* winpr_BinToHexString(const BYTE* data, size_t length, BOOL space)
{
size_t rc = 0;
const size_t n = space ? 3 : 2;
const size_t size = (length + 1ULL) * n;
char* p = (char*)malloc(size);
if (!p)
return nullptr;
rc = winpr_BinToHexStringBuffer(data, length, p, size, space);
if (rc == 0)
{
free(p);
return nullptr;
}
return p;
}

View File

@@ -0,0 +1,385 @@
/**
* WinPR: Windows Portable Runtime
* Security Accounts Manager (SAM)
*
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/path.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/wtypes.h>
#include <winpr/crt.h>
#include <winpr/sam.h>
#include <winpr/cast.h>
#include <winpr/print.h>
#include <winpr/file.h>
#include "../log.h"
#ifdef WINPR_HAVE_UNISTD_H
#include <unistd.h>
#endif
#define TAG WINPR_TAG("utils")
struct winpr_sam
{
FILE* fp;
char* line;
char* buffer;
char* context;
BOOL readOnly;
};
static WINPR_SAM_ENTRY* SamEntryFromDataA(LPCSTR User, DWORD UserLength, LPCSTR Domain,
DWORD DomainLength)
{
WINPR_SAM_ENTRY* entry = calloc(1, sizeof(WINPR_SAM_ENTRY));
if (!entry)
return nullptr;
if (User && (UserLength > 0))
entry->User = _strdup(User);
entry->UserLength = UserLength;
if (Domain && (DomainLength > 0))
entry->Domain = _strdup(Domain);
entry->DomainLength = DomainLength;
return entry;
}
static BOOL SamAreEntriesEqual(const WINPR_SAM_ENTRY* a, const WINPR_SAM_ENTRY* b)
{
if (!a || !b)
return FALSE;
if (a->UserLength != b->UserLength)
return FALSE;
if (a->DomainLength != b->DomainLength)
return FALSE;
if (a->UserLength > 0)
{
if (!a->User || !b->User)
return FALSE;
if (strncmp(a->User, b->User, a->UserLength) != 0)
return FALSE;
}
if (a->DomainLength > 0)
{
if (!a->Domain || !b->Domain)
return FALSE;
if (strncmp(a->Domain, b->Domain, a->DomainLength) != 0)
return FALSE;
}
return TRUE;
}
WINPR_SAM* SamOpen(const char* filename, BOOL readOnly)
{
FILE* fp = nullptr;
WINPR_SAM* sam = nullptr;
char* allocatedFileName = nullptr;
if (!filename)
{
allocatedFileName = winpr_GetConfigFilePath(TRUE, "SAM");
filename = allocatedFileName;
}
if (readOnly)
fp = winpr_fopen(filename, "r");
else
{
fp = winpr_fopen(filename, "r+");
if (!fp)
fp = winpr_fopen(filename, "w+");
}
free(allocatedFileName);
if (fp)
{
sam = (WINPR_SAM*)calloc(1, sizeof(WINPR_SAM));
if (!sam)
{
(void)fclose(fp);
return nullptr;
}
sam->readOnly = readOnly;
sam->fp = fp;
}
else
{
WLog_DBG(TAG, "Could not open SAM file!");
return nullptr;
}
return sam;
}
static BOOL SamLookupStart(WINPR_SAM* sam)
{
size_t readSize = 0;
INT64 fileSize = 0;
if (!sam || !sam->fp)
return FALSE;
if (_fseeki64(sam->fp, 0, SEEK_END) != 0)
return FALSE;
fileSize = _ftelli64(sam->fp);
if (_fseeki64(sam->fp, 0, SEEK_SET) != 0)
return FALSE;
if (fileSize < 1)
return FALSE;
sam->context = nullptr;
sam->buffer = (char*)calloc((size_t)fileSize + 2, 1);
if (!sam->buffer)
return FALSE;
readSize = fread(sam->buffer, (size_t)fileSize, 1, sam->fp);
if (!readSize)
{
if (!ferror(sam->fp))
readSize = (size_t)fileSize;
}
if (readSize < 1)
{
free(sam->buffer);
sam->buffer = nullptr;
return FALSE;
}
sam->buffer[fileSize] = '\n';
sam->buffer[fileSize + 1] = '\0';
sam->line = strtok_s(sam->buffer, "\n", &sam->context);
return TRUE;
}
static void SamLookupFinish(WINPR_SAM* sam)
{
free(sam->buffer);
sam->buffer = nullptr;
sam->line = nullptr;
}
static BOOL SamReadEntry(WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
{
char* p[5] = WINPR_C_ARRAY_INIT;
size_t count = 0;
if (!sam || !entry || !sam->line)
return FALSE;
char* cur = sam->line;
while ((cur = strchr(cur, ':')) != nullptr)
{
count++;
cur++;
}
if (count < 4)
return FALSE;
p[0] = sam->line;
p[1] = strchr(p[0], ':') + 1;
p[2] = strchr(p[1], ':') + 1;
p[3] = strchr(p[2], ':') + 1;
p[4] = strchr(p[3], ':') + 1;
const size_t LmHashLength = WINPR_ASSERTING_INT_CAST(size_t, (p[3] - p[2] - 1));
const size_t NtHashLength = WINPR_ASSERTING_INT_CAST(size_t, (p[4] - p[3] - 1));
if ((LmHashLength != 0) && (LmHashLength != 32))
return FALSE;
if ((NtHashLength != 0) && (NtHashLength != 32))
return FALSE;
entry->UserLength = (UINT32)(p[1] - p[0] - 1);
entry->User = (LPSTR)malloc(entry->UserLength + 1);
if (!entry->User)
return FALSE;
entry->User[entry->UserLength] = '\0';
entry->DomainLength = (UINT32)(p[2] - p[1] - 1);
memcpy(entry->User, p[0], entry->UserLength);
if (entry->DomainLength > 0)
{
entry->Domain = (LPSTR)malloc(entry->DomainLength + 1);
if (!entry->Domain)
{
free(entry->User);
entry->User = nullptr;
return FALSE;
}
memcpy(entry->Domain, p[1], entry->DomainLength);
entry->Domain[entry->DomainLength] = '\0';
}
else
entry->Domain = nullptr;
if (LmHashLength == 32)
{
if (winpr_HexStringToBinBuffer(p[2], LmHashLength, entry->LmHash, sizeof(entry->LmHash)) !=
32)
return FALSE;
}
if (NtHashLength == 32)
{
if (winpr_HexStringToBinBuffer(p[3], NtHashLength, (BYTE*)entry->NtHash,
sizeof(entry->NtHash)) != 32)
return FALSE;
}
return TRUE;
}
void SamFreeEntry(WINPR_ATTR_UNUSED WINPR_SAM* sam, WINPR_SAM_ENTRY* entry)
{
if (entry)
{
if (entry->UserLength > 0)
free(entry->User);
if (entry->DomainLength > 0)
free(entry->Domain);
free(entry);
}
}
void SamResetEntry(WINPR_SAM_ENTRY* entry)
{
if (!entry)
return;
if (entry->UserLength)
{
free(entry->User);
entry->User = nullptr;
}
if (entry->DomainLength)
{
free(entry->Domain);
entry->Domain = nullptr;
}
ZeroMemory(entry->LmHash, sizeof(entry->LmHash));
ZeroMemory(entry->NtHash, sizeof(entry->NtHash));
}
WINPR_SAM_ENTRY* SamLookupUserA(WINPR_SAM* sam, LPCSTR User, UINT32 UserLength, LPCSTR Domain,
UINT32 DomainLength)
{
size_t length = 0;
BOOL found = FALSE;
WINPR_SAM_ENTRY* search = SamEntryFromDataA(User, UserLength, Domain, DomainLength);
WINPR_SAM_ENTRY* entry = (WINPR_SAM_ENTRY*)calloc(1, sizeof(WINPR_SAM_ENTRY));
if (!entry || !search)
goto fail;
if (!SamLookupStart(sam))
goto fail;
while (sam->line != nullptr)
{
length = strlen(sam->line);
if (length > 1)
{
if (sam->line[0] != '#')
{
if (!SamReadEntry(sam, entry))
{
goto out_fail;
}
if (SamAreEntriesEqual(entry, search))
{
found = 1;
break;
}
}
}
SamResetEntry(entry);
sam->line = strtok_s(nullptr, "\n", &sam->context);
}
out_fail:
SamLookupFinish(sam);
fail:
SamFreeEntry(sam, search);
if (!found)
{
SamFreeEntry(sam, entry);
return nullptr;
}
return entry;
}
WINPR_SAM_ENTRY* SamLookupUserW(WINPR_SAM* sam, LPCWSTR User, UINT32 UserLength, LPCWSTR Domain,
UINT32 DomainLength)
{
WINPR_SAM_ENTRY* entry = nullptr;
char* utfUser = nullptr;
char* utfDomain = nullptr;
size_t userCharLen = 0;
size_t domainCharLen = 0;
utfUser = ConvertWCharNToUtf8Alloc(User, UserLength / sizeof(WCHAR), &userCharLen);
if (!utfUser)
goto fail;
if (DomainLength > 0)
{
utfDomain = ConvertWCharNToUtf8Alloc(Domain, DomainLength / sizeof(WCHAR), &domainCharLen);
if (!utfDomain)
goto fail;
}
entry = SamLookupUserA(sam, utfUser, (UINT32)userCharLen, utfDomain, (UINT32)domainCharLen);
fail:
free(utfUser);
free(utfDomain);
return entry;
}
void SamClose(WINPR_SAM* sam)
{
if (sam != nullptr)
{
if (sam->fp)
(void)fclose(sam->fp);
free(sam);
}
}

View File

@@ -0,0 +1,448 @@
/**
* WinPR: Windows Portable Runtime
* OpenSSL Library Initialization
*
* Copyright 2014 Thincast Technologies GmbH
* Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/ssl.h>
#include <winpr/thread.h>
#include <winpr/crypto.h>
#ifdef WITH_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
#include <openssl/provider.h>
#endif
#include "../log.h"
#define TAG WINPR_TAG("utils.ssl")
static BOOL g_winpr_openssl_initialized_by_winpr = FALSE;
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
static OSSL_PROVIDER* s_winpr_openssl_provider_fips = nullptr;
static OSSL_PROVIDER* s_winpr_openssl_provider_legacy = nullptr;
static OSSL_PROVIDER* s_winpr_openssl_provider_default = nullptr;
#endif
/**
* Note from OpenSSL 1.1.0 "CHANGES":
* OpenSSL now uses a new threading API. It is no longer necessary to
* set locking callbacks to use OpenSSL in a multi-threaded environment.
*/
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
#define WINPR_OPENSSL_LOCKING_REQUIRED 1
static int g_winpr_openssl_num_locks = 0;
static HANDLE* g_winpr_openssl_locks = nullptr;
struct CRYPTO_dynlock_value
{
HANDLE mutex;
};
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
static unsigned long _winpr_openssl_id(void)
{
return (unsigned long)GetCurrentThreadId();
}
#endif
static void _winpr_openssl_locking(int mode, int type, const char* file, int line)
{
if (mode & CRYPTO_LOCK)
{
(void)WaitForSingleObject(g_winpr_openssl_locks[type], INFINITE);
}
else
{
(void)ReleaseMutex(g_winpr_openssl_locks[type]);
}
}
static struct CRYPTO_dynlock_value* _winpr_openssl_dynlock_create(const char* file, int line)
{
struct CRYPTO_dynlock_value* dynlock;
if (!(dynlock = (struct CRYPTO_dynlock_value*)malloc(sizeof(struct CRYPTO_dynlock_value))))
return nullptr;
if (!(dynlock->mutex = CreateMutex(nullptr, FALSE, nullptr)))
{
free(dynlock);
return nullptr;
}
return dynlock;
}
static void _winpr_openssl_dynlock_lock(int mode, struct CRYPTO_dynlock_value* dynlock,
const char* file, int line)
{
if (mode & CRYPTO_LOCK)
{
(void)WaitForSingleObject(dynlock->mutex, INFINITE);
}
else
{
(void)ReleaseMutex(dynlock->mutex);
}
}
static void _winpr_openssl_dynlock_destroy(struct CRYPTO_dynlock_value* dynlock, const char* file,
int line)
{
(void)CloseHandle(dynlock->mutex);
free(dynlock);
}
static BOOL _winpr_openssl_initialize_locking(void)
{
int count;
/* OpenSSL static locking */
if (CRYPTO_get_locking_callback())
{
WLog_WARN(TAG, "OpenSSL static locking callback is already set");
}
else
{
if ((count = CRYPTO_num_locks()) > 0)
{
HANDLE* locks;
if (!(locks = calloc(count, sizeof(HANDLE))))
{
WLog_ERR(TAG, "error allocating lock table");
return FALSE;
}
for (int i = 0; i < count; i++)
{
if (!(locks[i] = CreateMutex(nullptr, FALSE, nullptr)))
{
WLog_ERR(TAG, "error creating lock #%d", i);
while (i--)
{
if (locks[i])
(void)CloseHandle(locks[i]);
}
free(locks);
return FALSE;
}
}
g_winpr_openssl_locks = locks;
g_winpr_openssl_num_locks = count;
CRYPTO_set_locking_callback(_winpr_openssl_locking);
}
}
/* OpenSSL dynamic locking */
if (CRYPTO_get_dynlock_create_callback() || CRYPTO_get_dynlock_lock_callback() ||
CRYPTO_get_dynlock_destroy_callback())
{
WLog_WARN(TAG, "dynamic locking callbacks are already set");
}
else
{
CRYPTO_set_dynlock_create_callback(_winpr_openssl_dynlock_create);
CRYPTO_set_dynlock_lock_callback(_winpr_openssl_dynlock_lock);
CRYPTO_set_dynlock_destroy_callback(_winpr_openssl_dynlock_destroy);
}
/* Use the deprecated CRYPTO_get_id_callback() if building against OpenSSL < 1.0.0 */
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
if (CRYPTO_get_id_callback())
{
WLog_WARN(TAG, "OpenSSL id_callback is already set");
}
else
{
CRYPTO_set_id_callback(_winpr_openssl_id);
}
#endif
return TRUE;
}
static BOOL _winpr_openssl_cleanup_locking(void)
{
/* undo our static locking modifications */
if (CRYPTO_get_locking_callback() == _winpr_openssl_locking)
{
CRYPTO_set_locking_callback(nullptr);
for (int i = 0; i < g_winpr_openssl_num_locks; i++)
{
(void)CloseHandle(g_winpr_openssl_locks[i]);
}
g_winpr_openssl_num_locks = 0;
free(g_winpr_openssl_locks);
g_winpr_openssl_locks = nullptr;
}
/* unset our dynamic locking callbacks */
if (CRYPTO_get_dynlock_create_callback() == _winpr_openssl_dynlock_create)
{
CRYPTO_set_dynlock_create_callback(nullptr);
}
if (CRYPTO_get_dynlock_lock_callback() == _winpr_openssl_dynlock_lock)
{
CRYPTO_set_dynlock_lock_callback(nullptr);
}
if (CRYPTO_get_dynlock_destroy_callback() == _winpr_openssl_dynlock_destroy)
{
CRYPTO_set_dynlock_destroy_callback(nullptr);
}
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
if (CRYPTO_get_id_callback() == _winpr_openssl_id)
{
CRYPTO_set_id_callback(nullptr);
}
#endif
return TRUE;
}
#endif /* OpenSSL < 1.1.0 */
static BOOL winpr_enable_fips(DWORD flags)
{
if (flags & WINPR_SSL_INIT_ENABLE_FIPS)
{
#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
WLog_ERR(TAG, "Openssl fips mode not available on openssl versions less than 1.0.1!");
return FALSE;
#else
WLog_DBG(TAG, "Ensuring openssl fips mode is enabled");
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
s_winpr_openssl_provider_fips = OSSL_PROVIDER_load(nullptr, "fips");
if (s_winpr_openssl_provider_fips == nullptr)
{
WLog_WARN(TAG, "OpenSSL FIPS provider failed to load");
}
if (!EVP_default_properties_is_fips_enabled(nullptr))
#else
if (FIPS_mode() != 1)
#endif
{
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
if (EVP_set_default_properties(nullptr, "fips=yes"))
#else
if (FIPS_mode_set(1))
#endif
WLog_INFO(TAG, "Openssl fips mode enabled!");
else
{
WLog_ERR(TAG, "Openssl fips mode enable failed!");
return FALSE;
}
}
#endif
}
return TRUE;
}
static void winpr_openssl_cleanup(void)
{
winpr_CleanupSSL(WINPR_SSL_INIT_DEFAULT);
}
static BOOL CALLBACK winpr_openssl_initialize(WINPR_ATTR_UNUSED PINIT_ONCE once, PVOID param,
WINPR_ATTR_UNUSED PVOID* context)
{
DWORD flags = param ? *(PDWORD)param : WINPR_SSL_INIT_DEFAULT;
if (flags & WINPR_SSL_INIT_ALREADY_INITIALIZED)
{
return TRUE;
}
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
if (flags & WINPR_SSL_INIT_ENABLE_LOCKING)
{
if (!_winpr_openssl_initialize_locking())
{
return FALSE;
}
}
#endif
/* SSL_load_error_strings() is void */
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
SSL_load_error_strings();
/* SSL_library_init() always returns "1" */
SSL_library_init();
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
#else
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_SSL_STRINGS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS |
OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS |
OPENSSL_INIT_ENGINE_ALL_BUILTIN,
nullptr) != 1)
return FALSE;
#endif
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
/* The legacy provider is needed for MD4. */
s_winpr_openssl_provider_legacy = OSSL_PROVIDER_load(nullptr, "legacy");
if (s_winpr_openssl_provider_legacy == nullptr)
{
WLog_WARN(TAG, "OpenSSL LEGACY provider failed to load, no md4 support available!");
}
s_winpr_openssl_provider_default = OSSL_PROVIDER_load(nullptr, "default");
if (s_winpr_openssl_provider_default == nullptr)
{
WLog_WARN(TAG, "OpenSSL DEFAULT provider failed to load");
}
#endif
(void)atexit(winpr_openssl_cleanup);
g_winpr_openssl_initialized_by_winpr = TRUE;
return TRUE;
}
/* exported functions */
BOOL winpr_InitializeSSL(DWORD flags)
{
static INIT_ONCE once = INIT_ONCE_STATIC_INIT;
if (!InitOnceExecuteOnce(&once, winpr_openssl_initialize, &flags, nullptr))
return FALSE;
return winpr_enable_fips(flags);
}
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
static int unload(OSSL_PROVIDER* provider, WINPR_ATTR_UNUSED void* data)
{
if (!provider)
return 1;
const char* name = OSSL_PROVIDER_get0_name(provider);
if (!name)
return 1;
OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
const int rc = OSSL_PROVIDER_available(ctx, name);
if (rc < 1)
return 1;
OSSL_PROVIDER_unload(provider);
return 1;
}
#endif
BOOL winpr_CleanupSSL(DWORD flags)
{
if (flags & WINPR_SSL_CLEANUP_GLOBAL)
{
if (!g_winpr_openssl_initialized_by_winpr)
{
WLog_WARN(TAG, "ssl was not initialized by winpr");
return FALSE;
}
g_winpr_openssl_initialized_by_winpr = FALSE;
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
_winpr_openssl_cleanup_locking();
#endif
#if (OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER)
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
EVP_cleanup();
#endif
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
flags |= WINPR_SSL_CLEANUP_THREAD;
#endif
}
#ifdef WINPR_OPENSSL_LOCKING_REQUIRED
if (flags & WINPR_SSL_CLEANUP_THREAD)
{
#if (OPENSSL_VERSION_NUMBER < 0x10000000L) || defined(LIBRESSL_VERSION_NUMBER)
ERR_remove_state(0);
#else
ERR_remove_thread_state(nullptr);
#endif
}
#endif
#if defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
OSSL_LIB_CTX* ctx = OSSL_LIB_CTX_get0_global_default();
OSSL_PROVIDER_do_all(ctx, unload, nullptr);
#endif
return TRUE;
}
BOOL winpr_FIPSMode(void)
{
#if (OPENSSL_VERSION_NUMBER < 0x10001000L) || defined(LIBRESSL_VERSION_NUMBER)
return FALSE;
#elif defined(OPENSSL_VERSION_MAJOR) && (OPENSSL_VERSION_MAJOR >= 3)
return (EVP_default_properties_is_fips_enabled(nullptr) == 1);
#else
return (FIPS_mode() == 1);
#endif
}
#else
BOOL winpr_InitializeSSL(DWORD flags)
{
return TRUE;
}
BOOL winpr_CleanupSSL(DWORD flags)
{
return TRUE;
}
BOOL winpr_FIPSMode(void)
{
return FALSE;
}
#endif

View File

@@ -0,0 +1,516 @@
/*
* WinPR: Windows Portable Runtime
* Stream Utils
*
* Copyright 2011 Vic Lee
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/assert.h>
#include <winpr/crt.h>
#include <winpr/stream.h>
#include "stream.h"
#include "../log.h"
#define STREAM_TAG WINPR_TAG("wStream")
#define STREAM_ASSERT(cond) \
do \
{ \
if (!(cond)) \
{ \
WLog_FATAL(STREAM_TAG, "%s [%s:%s:%" PRIuz "]", #cond, __FILE__, __func__, \
(size_t)__LINE__); \
winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20); \
abort(); \
} \
} while (0)
BOOL Stream_EnsureCapacity(wStream* s, size_t size)
{
WINPR_ASSERT(s);
if (s->capacity >= size)
return TRUE;
const size_t increment = 128ull;
const size_t old_capacity = s->capacity;
const size_t new_capacity = size + increment - size % increment;
const size_t position = Stream_GetPosition(s);
BYTE* new_buf = nullptr;
if (!s->isOwner)
{
new_buf = (BYTE*)malloc(new_capacity);
if (!new_buf)
return FALSE;
CopyMemory(new_buf, s->buffer, s->capacity);
s->isOwner = TRUE;
}
else
{
new_buf = (BYTE*)realloc(s->buffer, new_capacity);
if (!new_buf)
return FALSE;
}
s->buffer = new_buf;
s->capacity = new_capacity;
s->length = new_capacity;
ZeroMemory(&s->buffer[old_capacity], s->capacity - old_capacity);
return Stream_SetPosition(s, position);
}
BOOL Stream_EnsureRemainingCapacity(wStream* s, size_t size)
{
if (Stream_GetPosition(s) + size > Stream_Capacity(s))
return Stream_EnsureCapacity(s, Stream_Capacity(s) + size);
return TRUE;
}
wStream* Stream_New(BYTE* buffer, size_t size)
{
wStream* s = nullptr;
if (!buffer && !size)
return nullptr;
s = calloc(1, sizeof(wStream));
if (!s)
return nullptr;
if (buffer)
s->buffer = buffer;
else
s->buffer = (BYTE*)malloc(size);
if (!s->buffer)
{
free(s);
return nullptr;
}
s->pointer = s->buffer;
s->capacity = size;
s->length = size;
s->pool = nullptr;
s->count = 1;
s->isAllocatedStream = TRUE;
s->isOwner = TRUE;
return s;
}
wStream* Stream_StaticConstInit(wStream* s, const BYTE* buffer, size_t size)
{
union
{
BYTE* b;
const BYTE* cb;
} cnv;
cnv.cb = buffer;
return Stream_StaticInit(s, cnv.b, size);
}
wStream* Stream_StaticInit(wStream* s, BYTE* buffer, size_t size)
{
const wStream empty = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(s);
WINPR_ASSERT(buffer);
*s = empty;
s->buffer = s->pointer = buffer;
s->capacity = s->length = size;
s->pool = nullptr;
s->count = 1;
s->isAllocatedStream = FALSE;
s->isOwner = FALSE;
return s;
}
void Stream_EnsureValidity(wStream* s)
{
size_t cur = 0;
STREAM_ASSERT(s);
STREAM_ASSERT(s->pointer >= s->buffer);
cur = (size_t)(s->pointer - s->buffer);
STREAM_ASSERT(cur <= s->capacity);
STREAM_ASSERT(s->length <= s->capacity);
}
void Stream_Free(wStream* s, BOOL bFreeBuffer)
{
if (s)
{
Stream_EnsureValidity(s);
if (bFreeBuffer && s->isOwner)
free(s->buffer);
if (s->isAllocatedStream)
free(s);
}
}
BOOL Stream_SetLength(wStream* _s, size_t _l)
{
if ((_l) > Stream_Capacity(_s))
{
_s->length = 0;
return FALSE;
}
_s->length = _l;
return TRUE;
}
BOOL Stream_SetPosition(wStream* _s, size_t _p)
{
if ((_p) > Stream_Capacity(_s))
{
_s->pointer = _s->buffer;
return FALSE;
}
_s->pointer = _s->buffer + (_p);
return TRUE;
}
void Stream_SealLength(wStream* _s)
{
size_t cur = 0;
WINPR_ASSERT(_s);
WINPR_ASSERT(_s->buffer <= _s->pointer);
cur = (size_t)(_s->pointer - _s->buffer);
WINPR_ASSERT(cur <= _s->capacity);
if (cur <= _s->capacity)
_s->length = cur;
else
{
WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds");
winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
_s->length = 0;
}
}
#if defined(WITH_WINPR_DEPRECATED)
BOOL Stream_SetPointer(wStream* _s, BYTE* _p)
{
WINPR_ASSERT(_s);
if (!_p || (_s->buffer > _p) || (_s->buffer + _s->capacity < _p))
{
_s->pointer = _s->buffer;
return FALSE;
}
_s->pointer = _p;
return TRUE;
}
BOOL Stream_SetBuffer(wStream* _s, BYTE* _b)
{
WINPR_ASSERT(_s);
WINPR_ASSERT(_b);
_s->buffer = _b;
_s->pointer = _b;
return _s->buffer != nullptr;
}
void Stream_SetCapacity(wStream* _s, size_t _c)
{
WINPR_ASSERT(_s);
_s->capacity = _c;
}
#endif
size_t Stream_GetRemainingCapacity(const wStream* _s)
{
size_t cur = 0;
WINPR_ASSERT(_s);
WINPR_ASSERT(_s->buffer <= _s->pointer);
cur = (size_t)(_s->pointer - _s->buffer);
WINPR_ASSERT(cur <= _s->capacity);
if (cur > _s->capacity)
{
WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was written out of bounds");
winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
return 0;
}
return (_s->capacity - cur);
}
size_t Stream_GetRemainingLength(const wStream* _s)
{
size_t cur = 0;
WINPR_ASSERT(_s);
WINPR_ASSERT(_s->buffer <= _s->pointer);
WINPR_ASSERT(_s->length <= _s->capacity);
cur = (size_t)(_s->pointer - _s->buffer);
WINPR_ASSERT(cur <= _s->length);
if (cur > _s->length)
{
WLog_FATAL(STREAM_TAG, "wStream API misuse: stream was read out of bounds");
winpr_log_backtrace(STREAM_TAG, WLOG_FATAL, 20);
return 0;
}
return (_s->length - cur);
}
BOOL Stream_Write_UTF16_String(wStream* s, const WCHAR* src, size_t length)
{
WINPR_ASSERT(s);
WINPR_ASSERT(src || (length == 0));
if (!s || !src)
return FALSE;
if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, (s), length, sizeof(WCHAR)))
return FALSE;
for (size_t x = 0; x < length; x++)
Stream_Write_UINT16(s, src[x]);
return TRUE;
}
BOOL Stream_Read_UTF16_String(wStream* s, WCHAR* dst, size_t length)
{
WINPR_ASSERT(s);
WINPR_ASSERT(dst);
if (!Stream_CheckAndLogRequiredLengthOfSize(STREAM_TAG, s, length, sizeof(WCHAR)))
return FALSE;
for (size_t x = 0; x < length; x++)
Stream_Read_UINT16(s, dst[x]);
return TRUE;
}
BOOL Stream_CheckAndLogRequiredCapacityEx(const char* tag, DWORD level, wStream* s, size_t nmemb,
size_t size, const char* fmt, ...)
{
WINPR_ASSERT(size != 0);
const size_t actual = Stream_GetRemainingCapacity(s) / size;
if (actual < nmemb)
{
va_list args = WINPR_C_ARRAY_INIT;
va_start(args, fmt);
const BOOL rc =
Stream_CheckAndLogRequiredCapacityExVa(tag, level, s, nmemb, size, fmt, args);
va_end(args);
return rc;
}
return TRUE;
}
BOOL Stream_CheckAndLogRequiredCapacityExVa(const char* tag, DWORD level, wStream* s, size_t nmemb,
size_t size, const char* fmt, va_list args)
{
WINPR_ASSERT(size != 0);
const size_t actual = Stream_GetRemainingCapacity(s) / size;
if (actual < nmemb)
return Stream_CheckAndLogRequiredCapacityWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt,
args);
return TRUE;
}
WINPR_ATTR_FORMAT_ARG(6, 0)
BOOL Stream_CheckAndLogRequiredCapacityWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb,
size_t size, WINPR_FORMAT_ARG const char* fmt,
va_list args)
{
WINPR_ASSERT(size != 0);
const size_t actual = Stream_GetRemainingCapacity(s) / size;
if (actual < nmemb)
{
char prefix[1024] = WINPR_C_ARRAY_INIT;
(void)vsnprintf(prefix, sizeof(prefix), fmt, args);
WLog_Print(log, level,
"[%s] invalid remaining capacity, got %" PRIuz ", require at least %" PRIuz
" [element size=%" PRIuz "]",
prefix, actual, nmemb, size);
winpr_log_backtrace_ex(log, level, 20);
return FALSE;
}
return TRUE;
}
WINPR_ATTR_FORMAT_ARG(6, 7)
BOOL Stream_CheckAndLogRequiredCapacityWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb,
size_t size, WINPR_FORMAT_ARG const char* fmt, ...)
{
WINPR_ASSERT(size != 0);
const size_t actual = Stream_GetRemainingCapacity(s) / size;
if (actual < nmemb)
{
va_list args = WINPR_C_ARRAY_INIT;
va_start(args, fmt);
const BOOL rc =
Stream_CheckAndLogRequiredCapacityWLogExVa(log, level, s, nmemb, size, fmt, args);
va_end(args);
return rc;
}
return TRUE;
}
WINPR_ATTR_FORMAT_ARG(6, 7)
BOOL Stream_CheckAndLogRequiredLengthEx(const char* tag, DWORD level, wStream* s, size_t nmemb,
size_t size, WINPR_FORMAT_ARG const char* fmt, ...)
{
WINPR_ASSERT(size > 0);
const size_t actual = Stream_GetRemainingLength(s) / size;
if (actual < nmemb)
{
va_list args = WINPR_C_ARRAY_INIT;
va_start(args, fmt);
const BOOL rc = Stream_CheckAndLogRequiredLengthExVa(tag, level, s, nmemb, size, fmt, args);
va_end(args);
return rc;
}
return TRUE;
}
BOOL Stream_CheckAndLogRequiredLengthExVa(const char* tag, DWORD level, wStream* s, size_t nmemb,
size_t size, const char* fmt, va_list args)
{
WINPR_ASSERT(size > 0);
const size_t actual = Stream_GetRemainingLength(s) / size;
if (actual < nmemb)
return Stream_CheckAndLogRequiredLengthWLogExVa(WLog_Get(tag), level, s, nmemb, size, fmt,
args);
return TRUE;
}
BOOL Stream_CheckAndLogRequiredLengthWLogEx(wLog* log, DWORD level, wStream* s, size_t nmemb,
size_t size, const char* fmt, ...)
{
WINPR_ASSERT(size > 0);
const size_t actual = Stream_GetRemainingLength(s) / size;
if (actual < nmemb)
{
va_list args = WINPR_C_ARRAY_INIT;
va_start(args, fmt);
const BOOL rc =
Stream_CheckAndLogRequiredLengthWLogExVa(log, level, s, nmemb, size, fmt, args);
va_end(args);
return rc;
}
return TRUE;
}
WINPR_ATTR_FORMAT_ARG(6, 0)
BOOL Stream_CheckAndLogRequiredLengthWLogExVa(wLog* log, DWORD level, wStream* s, size_t nmemb,
size_t size, WINPR_FORMAT_ARG const char* fmt,
va_list args)
{
WINPR_ASSERT(size > 0);
const size_t actual = Stream_GetRemainingLength(s) / size;
if (actual < nmemb)
{
char prefix[1024] = WINPR_C_ARRAY_INIT;
(void)vsnprintf(prefix, sizeof(prefix), fmt, args);
WLog_Print(log, level,
"[%s] invalid length, got %" PRIuz ", require at least %" PRIuz
" [element size=%" PRIuz "]",
prefix, actual, nmemb, size);
winpr_log_backtrace_ex(log, level, 20);
return FALSE;
}
return TRUE;
}
SSIZE_T Stream_Write_UTF16_String_From_UTF8(wStream* s, size_t wcharLength, const char* src,
size_t length, BOOL fill)
{
SSIZE_T rc = 0;
WCHAR* str = Stream_PointerAs(s, WCHAR);
if (length != 0)
{
if (!Stream_CheckAndLogRequiredCapacityOfSize(STREAM_TAG, s, wcharLength, sizeof(WCHAR)))
return -1;
rc = ConvertUtf8NToWChar(src, length, str, wcharLength);
if (rc < 0)
return -1;
Stream_Seek(s, (size_t)rc * sizeof(WCHAR));
}
if (fill)
Stream_Zero(s, (wcharLength - (size_t)rc) * sizeof(WCHAR));
return rc;
}
char* Stream_Read_UTF16_String_As_UTF8(wStream* s, size_t wcharLength, size_t* pUtfCharLength)
{
const WCHAR* str = Stream_ConstPointer(s);
if (wcharLength > SIZE_MAX / sizeof(WCHAR))
return nullptr;
if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
return nullptr;
Stream_Seek(s, wcharLength * sizeof(WCHAR));
return ConvertWCharNToUtf8Alloc(str, wcharLength, pUtfCharLength);
}
SSIZE_T Stream_Read_UTF16_String_As_UTF8_Buffer(wStream* s, size_t wcharLength, char* utfBuffer,
size_t utfBufferCharLength)
{
const WCHAR* ptr = Stream_ConstPointer(s);
if (wcharLength > SIZE_MAX / sizeof(WCHAR))
return -1;
if (!Stream_CheckAndLogRequiredLength(STREAM_TAG, s, wcharLength * sizeof(WCHAR)))
return -1;
Stream_Seek(s, wcharLength * sizeof(WCHAR));
return ConvertWCharNToUtf8(ptr, wcharLength, utfBuffer, utfBufferCharLength);
}
BOOL Stream_SafeSeekEx(wStream* s, size_t size, const char* file, size_t line, const char* fkt)
{
if (!Stream_CheckAndLogRequiredLengthEx(STREAM_TAG, WLOG_WARN, s, size, 1, "%s(%s:%" PRIuz ")",
fkt, file, line))
return FALSE;
Stream_Seek(s, size);
return TRUE;
}

View File

@@ -0,0 +1,28 @@
/*
* WinPR: Windows Portable Runtime
* Stream Utils
*
* Copyright 2021 Armin Novak <anovak@thincast.com>
* Copyright 2021 Thincast Technologies GmbH
*
* 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 LIBWINPR_UTILS_STREAM_H
#define LIBWINPR_UTILS_STREAM_H
#include <winpr/stream.h>
void Stream_EnsureValidity(wStream* s);
#endif /* LIBWINPR_UTILS_STREAM_H */

View File

@@ -0,0 +1,74 @@
/*
* String List Utils
*
* Copyright 2018 Pascal Bourguignon <pjb@informatimago.com>
*
* 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.
*/
#include <winpr/config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/strlst.h>
#include <winpr/string.h>
void string_list_free(char** string_list)
{
for (size_t i = 0; string_list[i]; i++)
{
free(string_list[i]);
}
free((void*)string_list);
}
int string_list_length(const char* const* string_list)
{
int i = 0;
for (; string_list[i]; i++)
;
return i;
}
char** string_list_copy(const char* const* string_list)
{
int length = string_list_length(string_list);
char** copy = (char**)calloc(WINPR_ASSERTING_INT_CAST(size_t, length) + 1, sizeof(char*));
if (!copy)
{
return nullptr;
}
for (int i = 0; i < length; i++)
{
copy[i] = _strdup(string_list[i]);
}
copy[length] = nullptr;
return copy;
}
void string_list_print(FILE* out, const char* const* string_list)
{
for (int j = 0; string_list[j]; j++)
{
(void)fprintf(out, "[%2d]: %s\n", j, string_list[j]);
}
(void)fflush(out);
}

View File

@@ -0,0 +1,55 @@
set(MODULE_NAME "TestWinPRUtils")
set(MODULE_PREFIX "TEST_WINPR_UTILS")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestIni.c
TestVersion.c
TestImage.c
TestBacktrace.c
TestQueue.c
TestPrint.c
TestPubSub.c
TestStream.c
TestBitStream.c
TestArrayList.c
TestLinkedList.c
TestListDictionary.c
TestCmdLine.c
TestASN1.c
TestWLog.c
TestWLogCallback.c
TestHashTable.c
TestBufferPool.c
TestStreamPool.c
TestMessageQueue.c
TestMessagePipe.c
)
if(WITH_LODEPNG)
list(APPEND ${MODULES_PREFIX}_TESTS TestImage.c)
endif()
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
add_compile_definitions(TEST_SOURCE_PATH="${CMAKE_CURRENT_SOURCE_DIR}")
add_compile_definitions(TEST_BINARY_PATH="${CMAKE_CURRENT_BINARY_DIR}")
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
target_link_libraries(${MODULE_NAME} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
add_executable(img-cnv img-cnv.c)
target_link_libraries(img-cnv winpr)

View File

@@ -0,0 +1,347 @@
#include <winpr/asn1.h>
#include <winpr/print.h>
static const BYTE boolContent[] = { 0x01, 0x01, 0xFF };
static const BYTE badBoolContent[] = { 0x01, 0x04, 0xFF };
static const BYTE integerContent[] = { 0x02, 0x01, 0x02 };
static const BYTE badIntegerContent[] = { 0x02, 0x04, 0x02 };
static const BYTE positiveIntegerContent[] = { 0x02, 0x02, 0x00, 0xff };
static const BYTE negativeIntegerContent[] = { 0x02, 0x01, 0xff };
static const BYTE seqContent[] = { 0x30, 0x22, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x1B, 0x44,
0x69, 0x67, 0x69, 0x74, 0x61, 0x6C, 0x20, 0x53, 0x69, 0x67,
0x6E, 0x61, 0x74, 0x75, 0x72, 0x65, 0x20, 0x54, 0x72, 0x75,
0x73, 0x74, 0x20, 0x43, 0x6F, 0x2E, 0x31 };
static const BYTE contextualInteger[] = { 0xA0, 0x03, 0x02, 0x01, 0x02 };
static const BYTE oidContent[] = { 0x06, 0x03, 0x55, 0x04, 0x0A };
static const BYTE badOidContent[] = { 0x06, 0x89, 0x55, 0x04, 0x0A };
static const BYTE oidValue[] = { 0x55, 0x04, 0x0A };
static const BYTE ia5stringContent[] = { 0x16, 0x22, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F,
0x63, 0x70, 0x73, 0x2E, 0x72, 0x6F, 0x6F, 0x74, 0x2D,
0x78, 0x31, 0x2E, 0x6C, 0x65, 0x74, 0x73, 0x65, 0x6E,
0x63, 0x72, 0x79, 0x70, 0x74, 0x2E, 0x6F, 0x72, 0x67 };
static const BYTE utctimeContent[] = { 0x17, 0x0D, 0x32, 0x31, 0x30, 0x33, 0x31, 0x37,
0x31, 0x36, 0x34, 0x30, 0x34, 0x36, 0x5A };
int TestASN1Read(int argc, char* argv[])
{
WinPrAsn1Decoder decoder = WinPrAsn1Decoder_init();
WinPrAsn1Decoder seqDecoder = WinPrAsn1Decoder_init();
wStream staticS = WINPR_C_ARRAY_INIT;
WinPrAsn1_BOOL boolV = 0;
WinPrAsn1_INTEGER integerV = 0;
WinPrAsn1_OID oidV = WINPR_C_ARRAY_INIT;
WinPrAsn1_IA5STRING ia5stringV = nullptr;
WinPrAsn1_UTCTIME utctimeV = WINPR_C_ARRAY_INIT;
WinPrAsn1_tag tag = 0;
size_t len = 0;
BOOL error = 0;
/* ============== Test INTEGERs ================ */
Stream_StaticConstInit(&staticS, integerContent, sizeof(integerContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadInteger(&decoder, &integerV))
return -1;
Stream_StaticConstInit(&staticS, positiveIntegerContent, sizeof(positiveIntegerContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadInteger(&decoder, &integerV) && integerV != 0xff)
return -1;
Stream_StaticConstInit(&staticS, negativeIntegerContent, sizeof(negativeIntegerContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadInteger(&decoder, &integerV) && integerV != -1)
return -1;
Stream_StaticConstInit(&staticS, badIntegerContent, sizeof(badIntegerContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (WinPrAsn1DecReadInteger(&decoder, &integerV))
return -1;
/* ================ Test BOOL ================*/
Stream_StaticConstInit(&staticS, boolContent, sizeof(boolContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadBoolean(&decoder, &boolV))
return -10;
Stream_StaticConstInit(&staticS, badBoolContent, sizeof(badBoolContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (WinPrAsn1DecReadBoolean(&decoder, &boolV))
return -11;
/* ================ Test OID ================*/
Stream_StaticConstInit(&staticS, oidContent, sizeof(oidContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadOID(&decoder, &oidV, TRUE) || oidV.len != 3 ||
(memcmp(oidV.data, oidValue, oidV.len) != 0))
return -15;
WinPrAsn1FreeOID(&oidV);
Stream_StaticConstInit(&staticS, badOidContent, sizeof(badOidContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (WinPrAsn1DecReadOID(&decoder, &oidV, TRUE))
return -15;
WinPrAsn1FreeOID(&oidV);
Stream_StaticConstInit(&staticS, ia5stringContent, sizeof(ia5stringContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadIA5String(&decoder, &ia5stringV) ||
(strcmp(ia5stringV, "http://cps.root-x1.letsencrypt.org") != 0))
return -16;
free(ia5stringV);
/* ================ Test utc time ================*/
Stream_StaticConstInit(&staticS, utctimeContent, sizeof(utctimeContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadUtcTime(&decoder, &utctimeV) || utctimeV.year != 2021 ||
utctimeV.month != 3 || utctimeV.day != 17 || utctimeV.minute != 40 || utctimeV.tz != 'Z')
return -17;
/* ================ Test sequence ================*/
Stream_StaticConstInit(&staticS, seqContent, sizeof(seqContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadSequence(&decoder, &seqDecoder))
return -20;
Stream_StaticConstInit(&staticS, seqContent, sizeof(seqContent));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
if (!WinPrAsn1DecReadTagLenValue(&decoder, &tag, &len, &seqDecoder))
return -21;
if (tag != ER_TAG_SEQUENCE)
return -22;
if (!WinPrAsn1DecPeekTag(&seqDecoder, &tag) || tag != ER_TAG_OBJECT_IDENTIFIER)
return -23;
/* ================ Test contextual ================*/
Stream_StaticConstInit(&staticS, contextualInteger, sizeof(contextualInteger));
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
error = TRUE;
if (!WinPrAsn1DecReadContextualInteger(&decoder, 0, &error, &integerV) || error)
return -25;
/* test reading a contextual integer that is not there (index 1).
* that should not touch the decoder read head and we shall be able to extract contextual tag 0
* after that
*/
WinPrAsn1Decoder_Init(&decoder, WINPR_ASN1_DER, &staticS);
error = FALSE;
if (WinPrAsn1DecReadContextualInteger(&decoder, 1, &error, &integerV) || error)
return -26;
error = FALSE;
if (!WinPrAsn1DecReadContextualInteger(&decoder, 0, &error, &integerV) || error)
return -27;
return 0;
}
static BYTE oid1_val[] = { 1 };
static const WinPrAsn1_OID oid1 = { sizeof(oid1_val), oid1_val };
static BYTE oid2_val[] = { 2, 2 };
static WinPrAsn1_OID oid2 = { sizeof(oid2_val), oid2_val };
static BYTE oid3_val[] = { 3, 3, 3 };
static WinPrAsn1_OID oid3 = { sizeof(oid3_val), oid3_val };
static BYTE oid4_val[] = { 4, 4, 4, 4 };
static WinPrAsn1_OID oid4 = { sizeof(oid4_val), oid4_val };
int TestASN1Write(int argc, char* argv[])
{
wStream* s = nullptr;
size_t expectedOuputSz = 0;
int retCode = 100;
WinPrAsn1_UTCTIME utcTime;
WinPrAsn1_IA5STRING ia5string = nullptr;
WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
if (!enc)
goto out;
/* Let's encode something like:
* APP(3)
* SEQ2
* OID1
* OID2
* SEQ3
* OID3
* OID4
*
* [5] integer(200)
* [6] SEQ (empty)
* [7] UTC time (2016-03-17 16:40:41 UTC)
* [8] IA5String(test)
* [9] OctetString
* SEQ(empty)
*
*/
/* APP(3) */
retCode = 101;
if (!WinPrAsn1EncAppContainer(enc, 3))
goto out;
/* SEQ2 */
retCode = 102;
if (!WinPrAsn1EncSeqContainer(enc))
goto out;
retCode = 103;
if (WinPrAsn1EncOID(enc, &oid1) != 3)
goto out;
retCode = 104;
if (WinPrAsn1EncOID(enc, &oid2) != 4)
goto out;
retCode = 105;
if (WinPrAsn1EncEndContainer(enc) != 9)
goto out;
/* SEQ3 */
retCode = 110;
if (!WinPrAsn1EncSeqContainer(enc))
goto out;
retCode = 111;
if (WinPrAsn1EncOID(enc, &oid3) != 5)
goto out;
retCode = 112;
if (WinPrAsn1EncOID(enc, &oid4) != 6)
goto out;
retCode = 113;
if (WinPrAsn1EncEndContainer(enc) != 13)
goto out;
/* [5] integer(200) */
retCode = 114;
if (WinPrAsn1EncContextualInteger(enc, 5, 200) != 6)
goto out;
/* [6] SEQ (empty) */
retCode = 115;
if (!WinPrAsn1EncContextualSeqContainer(enc, 6))
goto out;
retCode = 116;
if (WinPrAsn1EncEndContainer(enc) != 4)
goto out;
/* [7] UTC time (2016-03-17 16:40:41 UTC) */
retCode = 117;
utcTime.year = 2016;
utcTime.month = 3;
utcTime.day = 17;
utcTime.hour = 16;
utcTime.minute = 40;
utcTime.second = 41;
utcTime.tz = 'Z';
if (WinPrAsn1EncContextualUtcTime(enc, 7, &utcTime) != 17)
goto out;
/* [8] IA5String(test) */
retCode = 118;
ia5string = "test";
if (!WinPrAsn1EncContextualContainer(enc, 8))
goto out;
retCode = 119;
if (WinPrAsn1EncIA5String(enc, ia5string) != 6)
goto out;
retCode = 120;
if (WinPrAsn1EncEndContainer(enc) != 8)
goto out;
/* [9] OctetString
* SEQ(empty)
*/
retCode = 121;
if (!WinPrAsn1EncContextualOctetStringContainer(enc, 9))
goto out;
retCode = 122;
if (!WinPrAsn1EncSeqContainer(enc))
goto out;
retCode = 123;
if (WinPrAsn1EncEndContainer(enc) != 2)
goto out;
retCode = 124;
if (WinPrAsn1EncEndContainer(enc) != 6)
goto out;
/* close APP */
expectedOuputSz = 24 + 6 + 4 + 17 + 8 + 6;
retCode = 200;
if (WinPrAsn1EncEndContainer(enc) != expectedOuputSz)
goto out;
/* let's output the result */
retCode = 201;
s = Stream_New(nullptr, 1024);
if (!s)
goto out;
retCode = 202;
if (!WinPrAsn1EncToStream(enc, s) || Stream_GetPosition(s) != expectedOuputSz)
goto out;
/* winpr_HexDump("", WLOG_ERROR, Stream_Buffer(s), Stream_GetPosition(s));*/
/*
* let's perform a mini-performance test, where we encode an ASN1 message with a big depth,
* so that we trigger reallocation routines in the encoder. We're gonna encode something like
* SEQ1
* SEQ2
* SEQ3
* ...
* SEQ1000
* INTEGER(2)
*
* As static chunks and containers are 50, a depth of 1000 should be enough
*
*/
WinPrAsn1Encoder_Reset(enc);
retCode = 203;
for (size_t i = 0; i < 1000; i++)
{
if (!WinPrAsn1EncSeqContainer(enc))
goto out;
}
retCode = 204;
if (WinPrAsn1EncInteger(enc, 2) != 3)
goto out;
retCode = 205;
for (size_t i = 0; i < 1000; i++)
{
if (!WinPrAsn1EncEndContainer(enc))
goto out;
}
retCode = 0;
out:
if (s)
Stream_Free(s, TRUE);
WinPrAsn1Encoder_Free(&enc);
return retCode;
}
int TestASN1(int argc, char* argv[])
{
int ret = TestASN1Read(argc, argv);
if (ret)
return ret;
return TestASN1Write(argc, argv);
}

View File

@@ -0,0 +1,84 @@
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/collections.h>
int TestArrayList(int argc, char* argv[])
{
int res = -1;
SSIZE_T rc = 0;
size_t val = 0;
const size_t elemsToInsert = 10;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
wArrayList* arrayList = ArrayList_New(TRUE);
if (!arrayList)
return -1;
for (size_t index = 0; index < elemsToInsert; index++)
{
if (!ArrayList_Append(arrayList, (void*)index))
goto fail;
}
size_t count = ArrayList_Count(arrayList);
printf("ArrayList count: %" PRIuz "\n", count);
SSIZE_T index = ArrayList_IndexOf(arrayList, (void*)(size_t)6, -1, -1);
printf("ArrayList index: %" PRIdz "\n", index);
if (index != 6)
goto fail;
ArrayList_Insert(arrayList, 5, (void*)(size_t)100);
index = ArrayList_IndexOf(arrayList, (void*)(size_t)6, -1, -1);
printf("ArrayList index: %" PRIdz "\n", index);
if (index != 7)
goto fail;
ArrayList_Remove(arrayList, (void*)(size_t)100);
rc = ArrayList_IndexOf(arrayList, (void*)(size_t)6, -1, -1);
printf("ArrayList index: %" PRIdz "\n", rc);
if (rc != 6)
goto fail;
for (size_t index = 0; index < elemsToInsert; index++)
{
val = (size_t)ArrayList_GetItem(arrayList, 0);
if (!ArrayList_RemoveAt(arrayList, 0))
goto fail;
if (val != index)
{
printf("ArrayList: shifted %" PRIdz " entries, expected value %" PRIdz ", got %" PRIdz
"\n",
index, index, val);
goto fail;
}
}
rc = ArrayList_IndexOf(arrayList, (void*)elemsToInsert, -1, -1);
printf("ArrayList index: %" PRIdz "\n", rc);
if (rc != -1)
goto fail;
count = ArrayList_Count(arrayList);
printf("ArrayList count: %" PRIuz "\n", count);
if (count != 0)
goto fail;
ArrayList_Clear(arrayList);
res = 0;
fail:
ArrayList_Free(arrayList);
return res;
}

View File

@@ -0,0 +1,34 @@
#include <stdio.h>
#include <winpr/debug.h>
int TestBacktrace(int argc, char* argv[])
{
int rc = -1;
size_t used = 0;
char** msg = nullptr;
void* stack = winpr_backtrace(20);
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!stack)
{
(void)fprintf(stderr, "winpr_backtrace failed!\n");
return -1;
}
msg = winpr_backtrace_symbols(stack, &used);
if (msg)
{
for (size_t x = 0; x < used; x++)
printf("%" PRIuz ": %s\n", x, msg[x]);
rc = 0;
}
winpr_backtrace_symbols_fd(stack, fileno(stdout));
winpr_backtrace_free(stack);
free((void*)msg);
return rc;
}

View File

@@ -0,0 +1,86 @@
#include <winpr/crt.h>
#include <winpr/print.h>
#include <winpr/stream.h>
#include <winpr/bitstream.h>
static void BitStrGen(void)
{
char str[64] = WINPR_C_ARRAY_INIT;
for (DWORD i = 0; i < 256;)
{
printf("\t");
for (DWORD j = 0; j < 4; j++)
{
if (0)
{
/* Least Significant Bit First */
str[0] = (i & (1 << 7)) ? '1' : '0';
str[1] = (i & (1 << 6)) ? '1' : '0';
str[2] = (i & (1 << 5)) ? '1' : '0';
str[3] = (i & (1 << 4)) ? '1' : '0';
str[4] = (i & (1 << 3)) ? '1' : '0';
str[5] = (i & (1 << 2)) ? '1' : '0';
str[6] = (i & (1 << 1)) ? '1' : '0';
str[7] = (i & (1 << 0)) ? '1' : '0';
str[8] = '\0';
}
else
{
/* Most Significant Bit First */
str[7] = (i & (1 << 7)) ? '1' : '0';
str[6] = (i & (1 << 6)) ? '1' : '0';
str[5] = (i & (1 << 5)) ? '1' : '0';
str[4] = (i & (1 << 4)) ? '1' : '0';
str[3] = (i & (1 << 3)) ? '1' : '0';
str[2] = (i & (1 << 2)) ? '1' : '0';
str[1] = (i & (1 << 1)) ? '1' : '0';
str[0] = (i & (1 << 0)) ? '1' : '0';
str[8] = '\0';
}
printf("\"%s\",%s", str, j == 3 ? "" : " ");
i++;
}
printf("\n");
}
}
int TestBitStream(int argc, char* argv[])
{
wBitStream* bs = nullptr;
BYTE buffer[1024] = WINPR_C_ARRAY_INIT;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
bs = BitStream_New();
if (!bs)
return 1;
BitStream_Attach(bs, buffer, sizeof(buffer));
BitStream_Write_Bits(bs, 0xAF, 8); /* 11110101 */
BitStream_Write_Bits(bs, 0xF, 4); /* 1111 */
BitStream_Write_Bits(bs, 0xA, 4); /* 0101 */
BitStream_Flush(bs);
BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST);
BitStream_Write_Bits(bs, 3, 2); /* 11 */
BitStream_Write_Bits(bs, 0, 3); /* 000 */
BitStream_Write_Bits(bs, 0x2D, 6); /* 101101 */
BitStream_Write_Bits(bs, 0x19, 5); /* 11001 */
// BitStream_Flush(bs); /* flush should be done automatically here (32 bits written) */
BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST);
BitStream_Write_Bits(bs, 3, 2); /* 11 */
BitStream_Flush(bs);
BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST);
BitStream_Write_Bits(bs, 00, 2); /* 00 */
BitStream_Write_Bits(bs, 0xF, 4); /* 1111 */
BitStream_Write_Bits(bs, 0, 20);
BitStream_Write_Bits(bs, 0xAFF, 12); /* 111111110101 */
BitStream_Flush(bs);
BitDump(__func__, WLOG_INFO, buffer, bs->position, BITDUMP_MSB_FIRST);
BitStream_Free(bs);
return 0;
}

View File

@@ -0,0 +1,68 @@
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <winpr/collections.h>
int TestBufferPool(int argc, char* argv[])
{
DWORD PoolSize = 0;
SSIZE_T BufferSize = 0;
wBufferPool* pool = nullptr;
BYTE* Buffers[10] = WINPR_C_ARRAY_INIT;
int DefaultSize = 1234;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
pool = BufferPool_New(TRUE, -1, 16);
if (!pool)
return -1;
Buffers[0] = BufferPool_Take(pool, DefaultSize);
Buffers[1] = BufferPool_Take(pool, DefaultSize);
Buffers[2] = BufferPool_Take(pool, 2048);
if (!Buffers[0] || !Buffers[1] || !Buffers[2])
return -1;
BufferSize = BufferPool_GetBufferSize(pool, Buffers[0]);
if (BufferSize != DefaultSize)
{
printf("BufferPool_GetBufferSize failure: Actual: %" PRIdz " Expected: %" PRIu32 "\n",
BufferSize, DefaultSize);
return -1;
}
BufferSize = BufferPool_GetBufferSize(pool, Buffers[1]);
if (BufferSize != DefaultSize)
{
printf("BufferPool_GetBufferSize failure: Actual: %" PRIdz " Expected: %" PRIu32 "\n",
BufferSize, DefaultSize);
return -1;
}
BufferSize = BufferPool_GetBufferSize(pool, Buffers[2]);
if (BufferSize != 2048)
{
printf("BufferPool_GetBufferSize failure: Actual: %" PRIdz " Expected: 2048\n", BufferSize);
return -1;
}
BufferPool_Return(pool, Buffers[1]);
PoolSize = BufferPool_GetPoolSize(pool);
if (PoolSize != 2)
{
printf("BufferPool_GetPoolSize failure: Actual: %" PRIu32 " Expected: 2\n", PoolSize);
return -1;
}
BufferPool_Clear(pool);
BufferPool_Free(pool);
return 0;
}

View File

@@ -0,0 +1,379 @@
#include <errno.h>
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/tchar.h>
#include <winpr/cmdline.h>
#include <winpr/strlst.h>
static const char* testArgv[] = { "mstsc.exe",
"+z",
"/w:1024",
"/h:768",
"/bpp:32",
"/admin",
"/multimon",
"+fonts",
"-wallpaper",
"/v:localhost:3389",
"/valuelist:value1,value2",
"/valuelist-empty:",
nullptr };
static const char testListAppName[] = "test app name";
static const char* testListArgs[] = {
"g:some.gateway.server,u:some\\\"user,p:some\\\"password,d:some\\\"domain,type:auto",
"a,b,c,d",
"a:,\"b:xxx, yyy\",c",
"a:,,,b",
"a:,\",b",
"\"a,b,c,d d d,fff\"",
"",
nullptr,
"'a,b,\",c'",
"\"a,b,',c\"",
"', a, ', b,c'",
"\"a,b,\",c\"",
};
static const char* testListArgs1[] = { testListAppName, "a", "b", "c", "d" };
static const char* testListArgs2[] = { testListAppName, "a:", "b:xxx, yyy", "c" };
// static const char* testListArgs3[] = {};
// static const char* testListArgs4[] = {};
static const char* testListArgs5[] = { testListAppName, "a", "b", "c", "d d d", "fff" };
static const char* testListArgs6[] = { testListAppName };
static const char* testListArgs7[] = { testListAppName };
static const char* testListArgs8[] = { testListAppName, "a", "b", "\"", "c" };
static const char* testListArgs9[] = { testListAppName, "a", "b", "'", "c" };
// static const char* testListArgs10[] = {};
// static const char* testListArgs11[] = {};
static const char* testListArgs12[] = { testListAppName, "g:some.gateway.server",
"u:some\\\"user", "p:some\\\"password",
"d:some\\\"domain", "type:auto" };
static const char** testListArgsResult[] = {
testListArgs12,
testListArgs1,
testListArgs2,
nullptr /* testListArgs3 */,
nullptr /* testListArgs4 */,
testListArgs5,
testListArgs6,
testListArgs7,
testListArgs8,
testListArgs9,
nullptr /* testListArgs10 */,
nullptr /* testListArgs11 */
};
static const size_t testListArgsCount[] = {
ARRAYSIZE(testListArgs12), ARRAYSIZE(testListArgs1),
ARRAYSIZE(testListArgs2), 0 /* ARRAYSIZE(testListArgs3) */,
0 /* ARRAYSIZE(testListArgs4) */, ARRAYSIZE(testListArgs5),
ARRAYSIZE(testListArgs6), ARRAYSIZE(testListArgs7),
ARRAYSIZE(testListArgs8), ARRAYSIZE(testListArgs9),
0 /* ARRAYSIZE(testListArgs10) */, 0 /* ARRAYSIZE(testListArgs11) */
};
static BOOL checkResult(size_t index, char** actual, size_t actualCount)
{
const char** result = testListArgsResult[index];
const size_t resultCount = testListArgsCount[index];
if (resultCount != actualCount)
return FALSE;
if (actualCount == 0)
{
return (actual == nullptr);
}
else
{
if (!actual)
return FALSE;
for (size_t x = 0; x < actualCount; x++)
{
const char* a = result[x];
const char* b = actual[x];
if (strcmp(a, b) != 0)
return FALSE;
}
}
return TRUE;
}
static BOOL TestCommandLineParseCommaSeparatedValuesEx(void)
{
WINPR_ASSERT(ARRAYSIZE(testListArgs) == ARRAYSIZE(testListArgsResult));
WINPR_ASSERT(ARRAYSIZE(testListArgs) == ARRAYSIZE(testListArgsCount));
for (size_t x = 0; x < ARRAYSIZE(testListArgs); x++)
{
union
{
char* p;
char** pp;
const char** ppc;
} ptr;
const char* list = testListArgs[x];
size_t count = 42;
ptr.pp = CommandLineParseCommaSeparatedValuesEx(testListAppName, list, &count);
BOOL valid = checkResult(x, ptr.pp, count);
free(ptr.p);
if (!valid)
return FALSE;
}
return TRUE;
}
int TestCmdLine(int argc, char* argv[])
{
int status = 0;
int ret = -1;
DWORD flags = 0;
long width = 0;
long height = 0;
const COMMAND_LINE_ARGUMENT_A* arg = nullptr;
int testArgc = 0;
char** command_line = nullptr;
COMMAND_LINE_ARGUMENT_A args[] = {
{ "v", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr,
"destination server" },
{ "port", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr,
"server port" },
{ "w", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr, "width" },
{ "h", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr, "height" },
{ "f", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr, "fullscreen" },
{ "bpp", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr,
"session bpp (color depth)" },
{ "admin", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, "console",
"admin (or console) session" },
{ "multimon", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"multi-monitor" },
{ "a", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, "addin", "addin" },
{ "u", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr, "username" },
{ "p", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr, "password" },
{ "d", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr, "domain" },
{ "z", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"compression" },
{ "audio", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr,
"audio output mode" },
{ "mic", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"audio input (microphone)" },
{ "fonts", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"smooth fonts (cleartype)" },
{ "aero", COMMAND_LINE_VALUE_BOOL, nullptr, nullptr, BoolValueFalse, -1, nullptr,
"desktop composition" },
{ "window-drag", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"full window drag" },
{ "menu-anims", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"menu animations" },
{ "themes", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"themes" },
{ "wallpaper", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"wallpaper" },
{ "codec", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr, "codec" },
{ "nego", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"protocol security negotiation" },
{ "sec", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr,
"force specific protocol security" },
#if defined(WITH_FREERDP_DEPRECATED)
{ "sec-rdp", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"rdp protocol security" },
{ "sec-tls", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"tls protocol security" },
{ "sec-nla", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueTrue, nullptr, -1, nullptr,
"nla protocol security" },
{ "sec-ext", COMMAND_LINE_VALUE_BOOL, nullptr, BoolValueFalse, nullptr, -1, nullptr,
"nla extended protocol security" },
{ "cert-name", COMMAND_LINE_VALUE_REQUIRED, nullptr, nullptr, nullptr, -1, nullptr,
"certificate name" },
{ "cert-ignore", COMMAND_LINE_VALUE_FLAG, nullptr, nullptr, nullptr, -1, nullptr,
"ignore certificate" },
#endif
{ "valuelist", COMMAND_LINE_VALUE_REQUIRED, "<val1>,<val2>", nullptr, nullptr, -1, nullptr,
"List of comma separated values." },
{ "valuelist-empty", COMMAND_LINE_VALUE_REQUIRED, "<val1>,<val2>", nullptr, nullptr, -1,
nullptr,
"List of comma separated values. Used to test correct behavior if an empty list was "
"passed." },
{ "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, nullptr, nullptr,
nullptr, -1, nullptr, "print version" },
{ "help", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_HELP, nullptr, nullptr, nullptr, -1,
"?", "print help" },
{ nullptr, 0, nullptr, nullptr, nullptr, -1, nullptr, nullptr }
};
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
flags = COMMAND_LINE_SIGIL_SLASH | COMMAND_LINE_SEPARATOR_COLON | COMMAND_LINE_SIGIL_PLUS_MINUS;
testArgc = string_list_length(testArgv);
command_line = string_list_copy(testArgv);
if (!command_line)
{
printf("Argument duplication failed (not enough memory?)\n");
return ret;
}
status =
CommandLineParseArgumentsA(testArgc, command_line, args, flags, nullptr, nullptr, nullptr);
if (status != 0)
{
printf("CommandLineParseArgumentsA failure: %d\n", status);
goto out;
}
arg = CommandLineFindArgumentA(args, "w");
if (strcmp("1024", arg->Value) != 0)
{
printf("CommandLineFindArgumentA: unexpected %s value %s\n", arg->Name, arg->Value);
goto out;
}
arg = CommandLineFindArgumentA(args, "h");
if (strcmp("768", arg->Value) != 0)
{
printf("CommandLineFindArgumentA: unexpected %s value %s\n", arg->Name, arg->Value);
goto out;
}
arg = CommandLineFindArgumentA(args, "f");
if (arg->Value)
{
printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name);
goto out;
}
arg = CommandLineFindArgumentA(args, "admin");
if (!arg->Value)
{
printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name);
goto out;
}
arg = CommandLineFindArgumentA(args, "multimon");
if (!arg->Value)
{
printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name);
goto out;
}
arg = CommandLineFindArgumentA(args, "v");
if (strcmp("localhost:3389", arg->Value) != 0)
{
printf("CommandLineFindArgumentA: unexpected %s value %s\n", arg->Name, arg->Value);
goto out;
}
arg = CommandLineFindArgumentA(args, "fonts");
if (!arg->Value)
{
printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name);
goto out;
}
arg = CommandLineFindArgumentA(args, "wallpaper");
if (arg->Value)
{
printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name);
goto out;
}
arg = CommandLineFindArgumentA(args, "help");
if (arg->Value)
{
printf("CommandLineFindArgumentA: unexpected %s value\n", arg->Name);
goto out;
}
arg = args;
errno = 0;
do
{
if (!(arg->Flags & COMMAND_LINE_VALUE_PRESENT))
continue;
printf("Argument: %s\n", arg->Name);
CommandLineSwitchStart(arg) CommandLineSwitchCase(arg, "v")
{
}
CommandLineSwitchCase(arg, "w")
{
width = strtol(arg->Value, nullptr, 0);
if (errno != 0)
goto out;
}
CommandLineSwitchCase(arg, "h")
{
height = strtol(arg->Value, nullptr, 0);
if (errno != 0)
goto out;
}
CommandLineSwitchCase(arg, "valuelist")
{
size_t count = 0;
char** p = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count);
free((void*)p);
if (!p || count != 3)
{
printf("CommandLineParseCommaSeparatedValuesEx: invalid p or count (%" PRIuz
"!=3)\n",
count);
goto out;
}
}
CommandLineSwitchCase(arg, "valuelist-empty")
{
size_t count = 0;
char** p = CommandLineParseCommaSeparatedValuesEx(arg->Name, arg->Value, &count);
free((void*)p);
if (!p || count != 1)
{
printf("CommandLineParseCommaSeparatedValuesEx: invalid p or count (%" PRIuz
"!=1)\n",
count);
goto out;
}
}
CommandLineSwitchDefault(arg)
{
}
CommandLineSwitchEnd(arg)
} while ((arg = CommandLineFindNextArgumentA(arg)) != nullptr);
if ((width != 1024) || (height != 768))
{
printf("Unexpected width and height: Actual: (%ldx%ld), Expected: (1024x768)\n", width,
height);
goto out;
}
ret = 0;
out:
string_list_free(command_line);
if (!TestCommandLineParseCommaSeparatedValuesEx())
return -1;
return ret;
}

View File

@@ -0,0 +1,446 @@
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/collections.h>
static char* key1 = "key1";
static char* key2 = "key2";
static char* key3 = "key3";
static char* val1 = "val1";
static char* val2 = "val2";
static char* val3 = "val3";
static int test_hash_table_pointer(void)
{
int rc = -1;
size_t count = 0;
char* value = nullptr;
wHashTable* table = nullptr;
table = HashTable_New(TRUE);
if (!table)
return -1;
if (!HashTable_Insert(table, key1, val1))
goto fail;
if (!HashTable_Insert(table, key2, val2))
goto fail;
if (!HashTable_Insert(table, key3, val3))
goto fail;
count = HashTable_Count(table);
if (count != 3)
{
printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Remove(table, key2))
goto fail;
count = HashTable_Count(table);
if (count != 2)
{
printf("HashTable_Count: Expected : 2, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Remove(table, key3))
goto fail;
count = HashTable_Count(table);
if (count != 1)
{
printf("HashTable_Count: Expected : 1, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Remove(table, key1))
goto fail;
count = HashTable_Count(table);
if (count != 0)
{
printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Insert(table, key1, val1))
goto fail;
if (!HashTable_Insert(table, key2, val2))
goto fail;
if (!HashTable_Insert(table, key3, val3))
goto fail;
count = HashTable_Count(table);
if (count != 3)
{
printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count);
goto fail;
}
value = (char*)HashTable_GetItemValue(table, key1);
if (strcmp(value, val1) != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val1, value);
goto fail;
}
value = (char*)HashTable_GetItemValue(table, key2);
if (strcmp(value, val2) != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val2, value);
goto fail;
}
value = (char*)HashTable_GetItemValue(table, key3);
if (strcmp(value, val3) != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val3, value);
goto fail;
}
if (!HashTable_SetItemValue(table, key2, "apple"))
goto fail;
value = (char*)HashTable_GetItemValue(table, key2);
if (strcmp(value, "apple") != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", "apple", value);
goto fail;
}
if (!HashTable_Contains(table, key2))
{
printf("HashTable_Contains: Expected : TRUE, Actual: FALSE\n");
goto fail;
}
if (!HashTable_Remove(table, key2))
{
printf("HashTable_Remove: Expected : TRUE, Actual: FALSE\n");
goto fail;
}
if (HashTable_Remove(table, key2))
{
printf("HashTable_Remove: Expected : FALSE, Actual: TRUE\n");
goto fail;
}
HashTable_Clear(table);
count = HashTable_Count(table);
if (count != 0)
{
printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count);
goto fail;
}
rc = 1;
fail:
HashTable_Free(table);
return rc;
}
static int test_hash_table_string(void)
{
int rc = -1;
size_t count = 0;
char* value = nullptr;
wHashTable* table = HashTable_New(TRUE);
if (!table)
return -1;
if (!HashTable_SetupForStringData(table, TRUE))
goto fail;
if (!HashTable_Insert(table, key1, val1))
goto fail;
if (!HashTable_Insert(table, key2, val2))
goto fail;
if (!HashTable_Insert(table, key3, val3))
goto fail;
count = HashTable_Count(table);
if (count != 3)
{
printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Remove(table, key2))
goto fail;
count = HashTable_Count(table);
if (count != 2)
{
printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Remove(table, key3))
goto fail;
count = HashTable_Count(table);
if (count != 1)
{
printf("HashTable_Count: Expected : 1, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Remove(table, key1))
goto fail;
count = HashTable_Count(table);
if (count != 0)
{
printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count);
goto fail;
}
if (!HashTable_Insert(table, key1, val1))
goto fail;
if (!HashTable_Insert(table, key2, val2))
goto fail;
if (!HashTable_Insert(table, key3, val3))
goto fail;
count = HashTable_Count(table);
if (count != 3)
{
printf("HashTable_Count: Expected : 3, Actual: %" PRIuz "\n", count);
goto fail;
}
value = (char*)HashTable_GetItemValue(table, key1);
if (strcmp(value, val1) != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val1, value);
goto fail;
}
value = (char*)HashTable_GetItemValue(table, key2);
if (strcmp(value, val2) != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val2, value);
goto fail;
}
value = (char*)HashTable_GetItemValue(table, key3);
if (strcmp(value, val3) != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", val3, value);
goto fail;
}
if (!HashTable_SetItemValue(table, key2, "apple"))
goto fail;
value = (char*)HashTable_GetItemValue(table, key2);
if (strcmp(value, "apple") != 0)
{
printf("HashTable_GetItemValue: Expected : %s, Actual: %s\n", "apple", value);
goto fail;
}
if (!HashTable_Contains(table, key2))
{
printf("HashTable_Contains: Expected : TRUE, Actual: FALSE\n");
goto fail;
}
if (!HashTable_Remove(table, key2))
{
printf("HashTable_Remove: Expected : TRUE, Actual: FALSE\n");
goto fail;
}
if (HashTable_Remove(table, key2))
{
printf("HashTable_Remove: Expected : FALSE, Actual: TRUE\n");
goto fail;
}
HashTable_Clear(table);
count = HashTable_Count(table);
if (count != 0)
{
printf("HashTable_Count: Expected : 0, Actual: %" PRIuz "\n", count);
goto fail;
}
rc = 1;
fail:
HashTable_Free(table);
return rc;
}
typedef struct
{
wHashTable* table;
size_t strlenCounter;
size_t foreachCalls;
BOOL test3error;
} ForeachData;
static BOOL foreachFn1(const void* key, void* value, void* arg)
{
ForeachData* d = (ForeachData*)arg;
WINPR_UNUSED(key);
d->strlenCounter += strlen((const char*)value);
return TRUE;
}
static BOOL foreachFn2(const void* key, void* value, void* arg)
{
ForeachData* d = (ForeachData*)arg;
WINPR_UNUSED(key);
WINPR_UNUSED(value);
d->foreachCalls++;
return (d->foreachCalls != 2);
}
static BOOL foreachFn3(const void* key, void* value, void* arg)
{
const char* keyStr = (const char*)key;
ForeachData* d = (ForeachData*)arg;
ForeachData d2;
WINPR_UNUSED(value);
WINPR_ASSERT(keyStr);
if (strcmp(keyStr, "key1") == 0)
{
/* when we pass on key1, let's remove key2 and check that the value is not
* visible anymore (even if has just been marked for removal)*/
HashTable_Remove(d->table, "key2");
if (HashTable_Contains(d->table, "key2"))
{
d->test3error = TRUE;
return FALSE;
}
if (HashTable_ContainsValue(d->table, "value2"))
{
d->test3error = TRUE;
return FALSE;
}
/* number of elements of the table shall be correct too */
if (HashTable_Count(d->table) != 2)
{
d->test3error = TRUE;
return FALSE;
}
/* we try recursive HashTable_Foreach */
d2.table = d->table;
d2.strlenCounter = 0;
if (!HashTable_Foreach(d->table, foreachFn1, &d2))
{
d->test3error = TRUE;
return FALSE;
}
if (d2.strlenCounter != 8)
{
d->test3error = TRUE;
return FALSE;
}
}
return TRUE;
}
static int test_hash_foreach(void)
{
ForeachData foreachData;
wHashTable* table = nullptr;
int retCode = 0;
foreachData.table = table = HashTable_New(TRUE);
if (!table)
return -1;
if (!HashTable_SetupForStringData(table, TRUE))
goto out;
if (HashTable_Insert(table, key1, val1) < 0 || HashTable_Insert(table, key2, val2) < 0 ||
HashTable_Insert(table, key3, val3) < 0)
{
retCode = -2;
goto out;
}
/* let's try a first trivial foreach */
foreachData.strlenCounter = 0;
if (!HashTable_Foreach(table, foreachFn1, &foreachData))
{
retCode = -10;
goto out;
}
if (foreachData.strlenCounter != 12)
{
retCode = -11;
goto out;
}
/* interrupted foreach */
foreachData.foreachCalls = 0;
if (HashTable_Foreach(table, foreachFn2, &foreachData))
{
retCode = -20;
goto out;
}
if (foreachData.foreachCalls != 2)
{
retCode = -21;
goto out;
}
/* let's try a foreach() call that will remove a value from the table in the callback */
foreachData.test3error = FALSE;
if (!HashTable_Foreach(table, foreachFn3, &foreachData))
{
retCode = -30;
goto out;
}
if (foreachData.test3error)
{
retCode = -31;
goto out;
}
out:
HashTable_Free(table);
return retCode;
}
int TestHashTable(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (test_hash_table_pointer() < 0)
return 1;
if (test_hash_table_string() < 0)
return 2;
if (test_hash_foreach() < 0)
return 3;
return 0;
}

View File

@@ -0,0 +1,232 @@
#include <stdio.h>
#include <winpr/string.h>
#include <winpr/assert.h>
#include <winpr/file.h>
#include <winpr/path.h>
#include <winpr/image.h>
static const char test_src_filename[] = TEST_SOURCE_PATH "/rgb";
static const char test_bin_filename[] = TEST_BINARY_PATH "/rgb";
static BOOL test_image_equal(const wImage* imageA, const wImage* imageB)
{
return winpr_image_equal(imageA, imageB,
WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
WINPR_IMAGE_CMP_FUZZY);
}
static BOOL test_equal_to(const wImage* bmp, const char* name, UINT32 format)
{
BOOL rc = FALSE;
wImage* cmp = winpr_image_new();
if (!cmp)
goto fail;
char path[MAX_PATH] = WINPR_C_ARRAY_INIT;
(void)_snprintf(path, sizeof(path), "%s.%s", name, winpr_image_format_extension(format));
const int cmpSize = winpr_image_read(cmp, path);
if (cmpSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, path);
goto fail;
}
rc = test_image_equal(bmp, cmp);
if (!rc)
(void)fprintf(stderr, "[%s] winpr_image_eqal failed", __func__);
fail:
winpr_image_free(cmp, TRUE);
return rc;
}
static BOOL test_equal(void)
{
BOOL rc = FALSE;
wImage* bmp = winpr_image_new();
if (!bmp)
goto fail;
char path[MAX_PATH] = WINPR_C_ARRAY_INIT;
(void)_snprintf(path, sizeof(path), "%s.bmp", test_src_filename);
PathCchConvertStyleA(path, sizeof(path), PATH_STYLE_NATIVE);
const int bmpSize = winpr_image_read(bmp, path);
if (bmpSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, path);
goto fail;
}
for (UINT32 x = 0; x < UINT8_MAX; x++)
{
if (!winpr_image_format_is_supported(x))
continue;
if (!test_equal_to(bmp, test_src_filename, x))
goto fail;
}
rc = TRUE;
fail:
winpr_image_free(bmp, TRUE);
return rc;
}
static BOOL test_read_write_compare(const char* tname, const char* tdst, UINT32 format)
{
BOOL rc = FALSE;
wImage* bmp1 = winpr_image_new();
wImage* bmp2 = winpr_image_new();
wImage* bmp3 = winpr_image_new();
if (!bmp1 || !bmp2 || !bmp3)
goto fail;
char spath[MAX_PATH] = WINPR_C_ARRAY_INIT;
char dpath[MAX_PATH] = WINPR_C_ARRAY_INIT;
char bpath1[MAX_PATH] = WINPR_C_ARRAY_INIT;
char bpath2[MAX_PATH] = WINPR_C_ARRAY_INIT;
(void)_snprintf(spath, sizeof(spath), "%s.%s", tname, winpr_image_format_extension(format));
(void)_snprintf(dpath, sizeof(dpath), "%s.%s", tdst, winpr_image_format_extension(format));
(void)_snprintf(bpath1, sizeof(bpath1), "%s.src.%s", dpath,
winpr_image_format_extension(WINPR_IMAGE_BITMAP));
(void)_snprintf(bpath2, sizeof(bpath2), "%s.bin.%s", dpath,
winpr_image_format_extension(WINPR_IMAGE_BITMAP));
PathCchConvertStyleA(spath, sizeof(spath), PATH_STYLE_NATIVE);
PathCchConvertStyleA(dpath, sizeof(dpath), PATH_STYLE_NATIVE);
PathCchConvertStyleA(bpath1, sizeof(bpath1), PATH_STYLE_NATIVE);
PathCchConvertStyleA(bpath2, sizeof(bpath2), PATH_STYLE_NATIVE);
const int bmpRSize = winpr_image_read(bmp1, spath);
if (bmpRSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, spath);
goto fail;
}
const int bmpWSize = winpr_image_write(bmp1, dpath);
if (bmpWSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_write failed for %s", __func__, dpath);
goto fail;
}
const int bmp2RSize = winpr_image_read(bmp2, dpath);
if (bmp2RSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, dpath);
goto fail;
}
const int bmpSrcWSize = winpr_image_write_ex(bmp1, WINPR_IMAGE_BITMAP, bpath1);
if (bmpSrcWSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_write_ex failed for %s", __func__, bpath1);
goto fail;
}
/* write a bitmap and read it back.
* this tests if we have the proper internal format */
const int bmpBinWSize = winpr_image_write_ex(bmp2, WINPR_IMAGE_BITMAP, bpath2);
if (bmpBinWSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_write_ex failed for %s", __func__, bpath2);
goto fail;
}
const int bmp3RSize = winpr_image_read(bmp3, bpath2);
if (bmp3RSize <= 0)
{
(void)fprintf(stderr, "[%s] winpr_image_read failed for %s", __func__, bpath2);
goto fail;
}
if (!winpr_image_equal(bmp1, bmp2,
WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
WINPR_IMAGE_CMP_FUZZY))
{
(void)fprintf(stderr, "[%s] winpr_image_eqal failed bmp1 bmp2", __func__);
goto fail;
}
rc = winpr_image_equal(bmp3, bmp2,
WINPR_IMAGE_CMP_IGNORE_DEPTH | WINPR_IMAGE_CMP_IGNORE_ALPHA |
WINPR_IMAGE_CMP_FUZZY);
if (!rc)
(void)fprintf(stderr, "[%s] winpr_image_eqal failed bmp3 bmp2", __func__);
fail:
winpr_image_free(bmp1, TRUE);
winpr_image_free(bmp2, TRUE);
winpr_image_free(bmp3, TRUE);
return rc;
}
static BOOL test_read_write(void)
{
BOOL rc = TRUE;
for (UINT32 x = 0; x < UINT8_MAX; x++)
{
if (!winpr_image_format_is_supported(x))
continue;
if (!test_read_write_compare(test_src_filename, test_bin_filename, x))
rc = FALSE;
}
return rc;
}
static BOOL test_load_file(const char* name)
{
BOOL rc = FALSE;
wImage* image = winpr_image_new();
if (!image || !name)
goto fail;
const int res = winpr_image_read(image, name);
rc = (res > 0);
fail:
winpr_image_free(image, TRUE);
return rc;
}
static BOOL test_load(void)
{
const char* names[] = {
"rgb.16a.bmp", "rgb.16a.nocolor.bmp", "rgb.16.bmp", "rgb.16.nocolor.bmp",
"rgb.16x.bmp", "rgb.16x.nocolor.bmp", "rgb.24.bmp", "rgb.24.nocolor.bmp",
"rgb.32.bmp", "rgb.32.nocolor.bmp", "rgb.32x.bmp", "rgb.32x.nocolor.bmp",
"rgb.bmp"
};
for (size_t x = 0; x < ARRAYSIZE(names); x++)
{
const char* name = names[x];
char* fname = GetCombinedPath(TEST_SOURCE_PATH, name);
const BOOL res = test_load_file(fname);
free(fname);
if (!res)
return FALSE;
}
return TRUE;
}
int TestImage(int argc, char* argv[])
{
int rc = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!test_equal())
rc -= 1;
if (!test_read_write())
rc -= 2;
if (!test_load())
rc -= 4;
return rc;
}

View File

@@ -0,0 +1,160 @@
#include <winpr/crt.h>
#include <winpr/ini.h>
static const char TEST_INI_01[] = "; This is a sample .ini config file\n"
"\n"
"[first_section]\n"
"one = 1\n"
"five = 5\n"
"animal = BIRD\n"
"\n"
"[second_section]\n"
"path = \"/usr/local/bin\"\n"
"URL = \"http://www.example.com/~username\"\n"
"\n";
static const char TEST_INI_02[] = "[FreeRDS]\n"
"prefix=\"/usr/local\"\n"
"bindir=\"bin\"\n"
"sbindir=\"sbin\"\n"
"libdir=\"lib\"\n"
"datarootdir=\"share\"\n"
"localstatedir=\"var\"\n"
"sysconfdir=\"etc\"\n"
"\n";
static const char TEST_INI_03[] = "[FreeRDS]\n"
"prefix=\"/usr/local\"\n"
"bindir=\"bin\"\n"
"# some illegal string\n"
"sbindir=\"sbin\"\n"
"libdir=\"lib\"\n"
"invalid key-value pair\n"
"datarootdir=\"share\"\n"
"localstatedir=\"var\"\n"
"sysconfdir=\"etc\"\n"
"\n";
int TestIni(int argc, char* argv[])
{
int rc = -1;
size_t nKeys = 0;
size_t nSections = 0;
UINT32 iValue = 0;
wIniFile* ini = nullptr;
const char* sValue = nullptr;
char** keyNames = nullptr;
char** sectionNames = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
/* First Sample */
ini = IniFile_New();
if (!ini)
goto fail;
if (IniFile_ReadBuffer(ini, TEST_INI_01) < 0)
goto fail;
free((void*)sectionNames);
sectionNames = IniFile_GetSectionNames(ini, &nSections);
if (!sectionNames && (nSections > 0))
goto fail;
for (size_t i = 0; i < nSections; i++)
{
free((void*)keyNames);
keyNames = IniFile_GetSectionKeyNames(ini, sectionNames[i], &nKeys);
printf("[%s]\n", sectionNames[i]);
if (!keyNames && (nKeys > 0))
goto fail;
for (size_t j = 0; j < nKeys; j++)
{
sValue = IniFile_GetKeyValueString(ini, sectionNames[i], keyNames[j]);
printf("%s = %s\n", keyNames[j], sValue);
}
}
iValue = IniFile_GetKeyValueInt(ini, "first_section", "one");
if (iValue != 1)
{
printf("IniFile_GetKeyValueInt failure\n");
goto fail;
}
iValue = IniFile_GetKeyValueInt(ini, "first_section", "five");
if (iValue != 5)
{
printf("IniFile_GetKeyValueInt failure\n");
goto fail;
}
sValue = IniFile_GetKeyValueString(ini, "first_section", "animal");
if (strcmp(sValue, "BIRD") != 0)
{
printf("IniFile_GetKeyValueString failure\n");
goto fail;
}
sValue = IniFile_GetKeyValueString(ini, "second_section", "path");
if (strcmp(sValue, "/usr/local/bin") != 0)
{
printf("IniFile_GetKeyValueString failure\n");
goto fail;
}
sValue = IniFile_GetKeyValueString(ini, "second_section", "URL");
if (strcmp(sValue, "http://www.example.com/~username") != 0)
{
printf("IniFile_GetKeyValueString failure\n");
goto fail;
}
IniFile_Free(ini);
/* Second Sample */
ini = IniFile_New();
if (!ini)
goto fail;
if (IniFile_ReadBuffer(ini, TEST_INI_02) < 0)
goto fail;
free((void*)sectionNames);
sectionNames = IniFile_GetSectionNames(ini, &nSections);
if (!sectionNames && (nSections > 0))
goto fail;
for (size_t i = 0; i < nSections; i++)
{
free((void*)keyNames);
keyNames = IniFile_GetSectionKeyNames(ini, sectionNames[i], &nKeys);
printf("[%s]\n", sectionNames[i]);
if (!keyNames && (nKeys > 0))
goto fail;
for (size_t j = 0; j < nKeys; j++)
{
sValue = IniFile_GetKeyValueString(ini, sectionNames[i], keyNames[j]);
printf("%s = %s\n", keyNames[j], sValue);
}
}
IniFile_Free(ini);
/* Third sample - invalid input */
ini = IniFile_New();
if (IniFile_ReadBuffer(ini, TEST_INI_03) != -1)
goto fail;
rc = 0;
fail:
free((void*)keyNames);
free((void*)sectionNames);
IniFile_Free(ini);
return rc;
}

View File

@@ -0,0 +1,135 @@
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/collections.h>
int TestLinkedList(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
wLinkedList* list = LinkedList_New();
if (!list)
return -1;
if (!LinkedList_AddFirst(list, (void*)(size_t)1))
return -1;
if (!LinkedList_AddLast(list, (void*)(size_t)2))
return -1;
if (!LinkedList_AddLast(list, (void*)(size_t)3))
return -1;
size_t count = LinkedList_Count(list);
if (count != 3)
{
printf("LinkedList_Count: expected 3, actual: %" PRIuz "\n", count);
return -1;
}
LinkedList_Enumerator_Reset(list);
while (LinkedList_Enumerator_MoveNext(list))
{
printf("\t%p\n", LinkedList_Enumerator_Current(list));
}
printf("\n");
printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list));
LinkedList_RemoveFirst(list);
LinkedList_RemoveLast(list);
count = LinkedList_Count(list);
if (count != 1)
{
printf("LinkedList_Count: expected 1, actual: %" PRIuz "\n", count);
return -1;
}
LinkedList_Enumerator_Reset(list);
while (LinkedList_Enumerator_MoveNext(list))
{
printf("\t%p\n", LinkedList_Enumerator_Current(list));
}
printf("\n");
printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list));
LinkedList_RemoveFirst(list);
LinkedList_RemoveLast(list);
count = LinkedList_Count(list);
if (count != 0)
{
printf("LinkedList_Count: expected 0, actual: %" PRIuz "\n", count);
return -1;
}
if (!LinkedList_AddFirst(list, (void*)(size_t)4))
return -1;
if (!LinkedList_AddLast(list, (void*)(size_t)5))
return -1;
if (!LinkedList_AddLast(list, (void*)(size_t)6))
return -1;
count = LinkedList_Count(list);
if (count != 3)
{
printf("LinkedList_Count: expected 3, actual: %" PRIuz "\n", count);
return -1;
}
LinkedList_Enumerator_Reset(list);
while (LinkedList_Enumerator_MoveNext(list))
{
printf("\t%p\n", LinkedList_Enumerator_Current(list));
}
printf("\n");
printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list));
if (!LinkedList_Remove(list, (void*)(size_t)5))
return -1;
LinkedList_Enumerator_Reset(list);
while (LinkedList_Enumerator_MoveNext(list))
{
printf("\t%p\n", LinkedList_Enumerator_Current(list));
}
printf("\n");
printf("LinkedList First: %p Last: %p\n", LinkedList_First(list), LinkedList_Last(list));
LinkedList_Free(list);
/* Test enumerator robustness */
/* enumerator on an empty list */
list = LinkedList_New();
if (!list)
return -1;
LinkedList_Enumerator_Reset(list);
while (LinkedList_Enumerator_MoveNext(list))
{
printf("\terror: %p\n", LinkedList_Enumerator_Current(list));
}
printf("\n");
LinkedList_Free(list);
/* Use an enumerator without reset */
list = LinkedList_New();
if (!list)
return -1;
if (!LinkedList_AddFirst(list, (void*)(size_t)4))
return -1;
if (!LinkedList_AddLast(list, (void*)(size_t)5))
return -1;
if (!LinkedList_AddLast(list, (void*)(size_t)6))
return -1;
while (LinkedList_Enumerator_MoveNext(list))
{
printf("\t%p\n", LinkedList_Enumerator_Current(list));
}
printf("\n");
LinkedList_Free(list);
return 0;
}

View File

@@ -0,0 +1,178 @@
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/collections.h>
static char* key1 = "key1";
static char* key2 = "key2";
static char* key3 = "key3";
static char* val1 = "val1";
static char* val2 = "val2";
static char* val3 = "val3";
int TestListDictionary(int argc, char* argv[])
{
size_t count = 0;
char* value = nullptr;
wListDictionary* list = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
list = ListDictionary_New(TRUE);
if (!list)
return -1;
if (!ListDictionary_Add(list, key1, val1) || !ListDictionary_Add(list, key2, val2) ||
!ListDictionary_Add(list, key3, val3))
return -1;
count = ListDictionary_Count(list);
if (count != 3)
{
printf("ListDictionary_Count: Expected : 3, Actual: %" PRIuz "\n", count);
return -1;
}
ListDictionary_Remove(list, key2);
count = ListDictionary_Count(list);
if (count != 2)
{
printf("ListDictionary_Count: Expected : 2, Actual: %" PRIuz "\n", count);
return -1;
}
ListDictionary_Remove(list, key3);
count = ListDictionary_Count(list);
if (count != 1)
{
printf("ListDictionary_Count: Expected : 1, Actual: %" PRIuz "\n", count);
return -1;
}
ListDictionary_Remove(list, key1);
count = ListDictionary_Count(list);
if (count != 0)
{
printf("ListDictionary_Count: Expected : 0, Actual: %" PRIuz "\n", count);
return -1;
}
if (!ListDictionary_Add(list, key1, val1) || !ListDictionary_Add(list, key2, val2) ||
!ListDictionary_Add(list, key3, val3))
return -1;
count = ListDictionary_Count(list);
if (count != 3)
{
printf("ListDictionary_Count: Expected : 3, Actual: %" PRIuz "\n", count);
return -1;
}
value = (char*)ListDictionary_GetItemValue(list, key1);
if (strcmp(value, val1) != 0)
{
printf("ListDictionary_GetItemValue: Expected : %" PRIuz ", Actual: %" PRIuz "\n",
(size_t)val1, (size_t)value);
return -1;
}
value = (char*)ListDictionary_GetItemValue(list, key2);
if (strcmp(value, val2) != 0)
{
printf("ListDictionary_GetItemValue: Expected : %" PRIuz ", Actual: %" PRIuz "\n",
(size_t)val2, (size_t)value);
return -1;
}
value = (char*)ListDictionary_GetItemValue(list, key3);
if (strcmp(value, val3) != 0)
{
printf("ListDictionary_GetItemValue: Expected : %" PRIuz ", Actual: %" PRIuz "\n",
(size_t)val3, (size_t)value);
return -1;
}
ListDictionary_SetItemValue(list, key2, "apple");
value = (char*)ListDictionary_GetItemValue(list, key2);
if (strcmp(value, "apple") != 0)
{
printf("ListDictionary_GetItemValue: Expected : %s, Actual: %s\n", "apple", value);
return -1;
}
if (!ListDictionary_Contains(list, key2))
{
printf("ListDictionary_Contains: Expected : TRUE, Actual: FALSE\n");
return -1;
}
if (!ListDictionary_Take(list, key2))
{
printf("ListDictionary_Remove: Expected : TRUE, Actual: FALSE\n");
return -1;
}
if (ListDictionary_Take(list, key2))
{
printf("ListDictionary_Remove: Expected : FALSE, Actual: TRUE\n");
return -1;
}
value = ListDictionary_Take_Head(list);
count = ListDictionary_Count(list);
if ((strncmp(value, val1, 4) != 0) || (count != 1))
{
printf("ListDictionary_Remove_Head: Expected : %s, Actual: %s Count: %" PRIuz "\n", val1,
value, count);
return -1;
}
value = ListDictionary_Take_Head(list);
count = ListDictionary_Count(list);
if ((strncmp(value, val3, 4) != 0) || (count != 0))
{
printf("ListDictionary_Remove_Head: Expected : %s, Actual: %s Count: %" PRIuz "\n", val3,
value, count);
return -1;
}
value = ListDictionary_Take_Head(list);
if (value)
{
printf("ListDictionary_Remove_Head: Expected : (null), Actual: %s\n", value);
return -1;
}
if (!ListDictionary_Add(list, key1, val1) || !ListDictionary_Add(list, key2, val2) ||
!ListDictionary_Add(list, key3, val3))
return -1;
ListDictionary_Clear(list);
count = ListDictionary_Count(list);
if (count != 0)
{
printf("ListDictionary_Count: Expected : 0, Actual: %" PRIuz "\n", count);
return -1;
}
ListDictionary_Free(list);
return 0;
}

View File

@@ -0,0 +1,105 @@
#include <winpr/crt.h>
#include <winpr/thread.h>
#include <winpr/collections.h>
static DWORD WINAPI message_echo_pipe_client_thread(LPVOID arg)
{
int index = 0;
wMessagePipe* pipe = (wMessagePipe*)arg;
while (index < 100)
{
wMessage message = WINPR_C_ARRAY_INIT;
int count = -1;
if (!MessageQueue_Post(pipe->In, nullptr, 0, (void*)(size_t)index, nullptr))
break;
if (!MessageQueue_Wait(pipe->Out))
break;
if (!MessageQueue_Peek(pipe->Out, &message, TRUE))
break;
if (message.id == WMQ_QUIT)
break;
count = (int)(size_t)message.wParam;
if (count != index)
printf("Echo count mismatch: Actual: %d, Expected: %d\n", count, index);
index++;
}
MessageQueue_PostQuit(pipe->In, 0);
return 0;
}
static DWORD WINAPI message_echo_pipe_server_thread(LPVOID arg)
{
wMessage message = WINPR_C_ARRAY_INIT;
wMessagePipe* pipe = (wMessagePipe*)arg;
while (MessageQueue_Wait(pipe->In))
{
if (MessageQueue_Peek(pipe->In, &message, TRUE))
{
if (message.id == WMQ_QUIT)
break;
if (!MessageQueue_Dispatch(pipe->Out, &message))
break;
}
}
return 0;
}
int TestMessagePipe(int argc, char* argv[])
{
HANDLE ClientThread = nullptr;
HANDLE ServerThread = nullptr;
wMessagePipe* EchoPipe = nullptr;
int ret = 1;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!(EchoPipe = MessagePipe_New()))
{
printf("failed to create message pipe\n");
goto out;
}
if (!(ClientThread = CreateThread(nullptr, 0, message_echo_pipe_client_thread, (void*)EchoPipe,
0, nullptr)))
{
printf("failed to create client thread\n");
goto out;
}
if (!(ServerThread = CreateThread(nullptr, 0, message_echo_pipe_server_thread, (void*)EchoPipe,
0, nullptr)))
{
printf("failed to create server thread\n");
goto out;
}
(void)WaitForSingleObject(ClientThread, INFINITE);
(void)WaitForSingleObject(ServerThread, INFINITE);
ret = 0;
out:
if (EchoPipe)
MessagePipe_Free(EchoPipe);
if (ClientThread)
(void)CloseHandle(ClientThread);
if (ServerThread)
(void)CloseHandle(ServerThread);
return ret;
}

View File

@@ -0,0 +1,192 @@
#include <winpr/crt.h>
#include <winpr/thread.h>
#include <winpr/collections.h>
static DWORD WINAPI message_queue_consumer_thread(LPVOID arg)
{
wMessage message = WINPR_C_ARRAY_INIT;
wMessageQueue* queue = (wMessageQueue*)arg;
while (MessageQueue_Wait(queue))
{
if (MessageQueue_Peek(queue, &message, TRUE))
{
if (message.id == WMQ_QUIT)
break;
printf("Message.Type: %" PRIu32 "\n", message.id);
}
}
return 0;
}
static bool wrap_test(bool (*fkt)(wMessageQueue* queue))
{
wMessageQueue* queue = MessageQueue_New(nullptr);
if (!queue)
return false;
WINPR_ASSERT(fkt);
const bool rc = fkt(queue);
MessageQueue_Free(queue);
return rc;
}
static bool check(const wMessage* message, size_t pos)
{
if (!message)
return false;
if (message->context != (void*)13)
return false;
if (message->id != pos)
return false;
if (message->wParam != (void*)23)
return false;
if (message->lParam != (void*)42)
return false;
if (message->Free != nullptr)
return false;
return true;
}
static bool append(wMessageQueue* queue, size_t pos)
{
const wMessage message = { .context = (void*)13,
.id = WINPR_ASSERTING_INT_CAST(DWORD, pos),
.wParam = (void*)23,
.lParam = (void*)42,
.Free = nullptr };
return MessageQueue_Dispatch(queue, &message);
}
static bool fill_capcity(wMessageQueue* queue, size_t* pos)
{
WINPR_ASSERT(pos);
size_t cpos = *pos;
const size_t capacity = MessageQueue_Capacity(queue);
while (MessageQueue_Size(queue) < capacity)
{
if (!append(queue, cpos++))
return false;
}
*pos = cpos;
return true;
}
static bool drain(wMessageQueue* queue, size_t expect)
{
wMessage message = WINPR_C_ARRAY_INIT;
if (MessageQueue_Get(queue, &message) < 0)
return false;
if (!check(&message, expect))
return false;
return true;
}
static bool drain_capcity(wMessageQueue* queue, size_t remain, size_t* pos)
{
WINPR_ASSERT(pos);
size_t cpos = *pos;
while (MessageQueue_Size(queue) > remain)
{
if (!drain(queue, cpos++))
return false;
}
*pos = cpos;
return true;
}
static bool test_growth_move(wMessageQueue* queue, bool big)
{
WINPR_ASSERT(queue);
const size_t cap = MessageQueue_Capacity(queue);
if (cap < 4)
return false;
size_t wpos = 0;
size_t rpos = 0;
if (!fill_capcity(queue, &wpos))
return false;
if (big)
{
if (!append(queue, wpos++))
return false;
}
if (!drain_capcity(queue, 3, &rpos))
return false;
if (!fill_capcity(queue, &wpos))
return false;
if (!append(queue, wpos++))
return false;
return drain_capcity(queue, 0, &rpos);
}
static bool test_growth_big_move(wMessageQueue* queue)
{
return test_growth_move(queue, true);
}
static bool test_growth_small_move(wMessageQueue* queue)
{
return test_growth_move(queue, false);
}
static bool test_operation_run(wMessageQueue* queue, HANDLE thread)
{
WINPR_ASSERT(queue);
WINPR_ASSERT(thread);
if (!MessageQueue_Post(queue, nullptr, 123, nullptr, nullptr))
return false;
if (!MessageQueue_Post(queue, nullptr, 456, nullptr, nullptr))
return false;
if (!MessageQueue_Post(queue, nullptr, 789, nullptr, nullptr))
return false;
if (!MessageQueue_PostQuit(queue, 0))
return false;
const DWORD status = WaitForSingleObject(thread, INFINITE);
return (status == WAIT_OBJECT_0);
}
static bool test_operation(wMessageQueue* queue)
{
WINPR_ASSERT(queue);
HANDLE thread = CreateThread(nullptr, 0, message_queue_consumer_thread, queue, 0, nullptr);
if (!thread)
{
printf("failed to create thread\n");
return false;
}
const bool rc = test_operation_run(queue, thread);
if (!CloseHandle(thread))
return false;
return rc;
}
int TestMessageQueue(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char* argv[])
{
if (!wrap_test(test_growth_big_move))
return -1;
if (!wrap_test(test_growth_small_move))
return -2;
if (!wrap_test(test_operation))
return -3;
return 0;
}

View File

@@ -0,0 +1,402 @@
#include <math.h>
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/print.h>
/**
* C Programming/C Reference/stdio.h/printf:
* http://en.wikibooks.org/wiki/C_Programming/C_Reference/stdio.h/printf
*
* C Programming/Procedures and functions/printf:
* http://en.wikibooks.org/wiki/C_Programming/Procedures_and_functions/printf
*
* C Tutorial printf, Format Specifiers, Format Conversions and Formatted Output:
* http://www.codingunit.com/printf-format-specifiers-format-conversions-and-formatted-output
*/
#define _printf printf // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
static BOOL test_bin_tohex_string(void)
{
BOOL rc = FALSE;
{
const BYTE binbuffer[33] = WINPR_C_ARRAY_INIT;
const char empty[33] = WINPR_C_ARRAY_INIT;
char strbuffer[33] = WINPR_C_ARRAY_INIT;
size_t len = winpr_BinToHexStringBuffer(nullptr, sizeof(binbuffer), strbuffer,
sizeof(strbuffer), TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer, 0, strbuffer, sizeof(strbuffer), TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer, sizeof(binbuffer), nullptr, sizeof(strbuffer),
TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer, sizeof(binbuffer), strbuffer, 0, TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer, 0, strbuffer, 0, TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer, sizeof(binbuffer), nullptr, 0, TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(nullptr, sizeof(binbuffer), strbuffer, 0, TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer, 0, nullptr, 0, TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(nullptr, 0, nullptr, 0, TRUE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer, 0, nullptr, 0, FALSE);
if (len != 0)
goto fail;
if (memcmp(strbuffer, empty, sizeof(strbuffer)) != 0)
goto fail;
}
{
const BYTE binbuffer1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
const char strbuffer1[] = "0102030405060708090A0B0C0D0E0F1011";
const char strbuffer1_space[] = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11";
char buffer[1024] = WINPR_C_ARRAY_INIT;
size_t len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer,
sizeof(buffer), FALSE);
if (len != strnlen(strbuffer1, sizeof(strbuffer1)))
goto fail;
if (memcmp(strbuffer1, buffer, sizeof(strbuffer1)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer, sizeof(buffer),
TRUE);
if (len != strnlen(strbuffer1_space, sizeof(strbuffer1_space)))
goto fail;
if (memcmp(strbuffer1_space, buffer, sizeof(strbuffer1_space)) != 0)
goto fail;
}
{
const BYTE binbuffer1[] = { 0xF1, 0xe2, 0xD3, 0xc4, 0xB5, 0xA6, 0x97, 0x88, 0x79,
0x6A, 0x5b, 0x4C, 0x3d, 0x2E, 0x1f, 0x00, 0xfF };
const char strbuffer1[] = "F1E2D3C4B5A69788796A5B4C3D2E1F00FF";
const char strbuffer1_space[] = "F1 E2 D3 C4 B5 A6 97 88 79 6A 5B 4C 3D 2E 1F 00 FF";
char buffer[1024] = WINPR_C_ARRAY_INIT;
size_t len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer,
sizeof(buffer), FALSE);
if (len != strnlen(strbuffer1, sizeof(strbuffer1)))
goto fail;
if (memcmp(strbuffer1, buffer, sizeof(strbuffer1)) != 0)
goto fail;
len = winpr_BinToHexStringBuffer(binbuffer1, sizeof(binbuffer1), buffer, sizeof(buffer),
TRUE);
if (len != strnlen(strbuffer1_space, sizeof(strbuffer1_space)))
goto fail;
if (memcmp(strbuffer1_space, buffer, sizeof(strbuffer1_space)) != 0)
goto fail;
}
{
}
rc = TRUE;
fail:
return rc;
}
static BOOL test_bin_tohex_string_alloc(void)
{
BOOL rc = FALSE;
char* str = nullptr;
{
const BYTE binbuffer[33] = WINPR_C_ARRAY_INIT;
str = winpr_BinToHexString(nullptr, sizeof(binbuffer), TRUE);
if (str)
goto fail;
str = winpr_BinToHexString(binbuffer, 0, TRUE);
if (str)
goto fail;
str = winpr_BinToHexString(binbuffer, 0, FALSE);
if (str)
goto fail;
str = winpr_BinToHexString(nullptr, sizeof(binbuffer), FALSE);
if (str)
goto fail;
}
{
const BYTE binbuffer1[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
const char strbuffer1[] = "0102030405060708090A0B0C0D0E0F1011";
const char strbuffer1_space[] = "01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11";
str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), FALSE);
if (!str)
goto fail;
if (memcmp(strbuffer1, str, sizeof(strbuffer1)) != 0)
goto fail;
free(str);
str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), TRUE);
if (!str)
goto fail;
if (memcmp(strbuffer1_space, str, sizeof(strbuffer1_space)) != 0)
goto fail;
free(str);
str = nullptr;
}
{
const BYTE binbuffer1[] = { 0xF1, 0xe2, 0xD3, 0xc4, 0xB5, 0xA6, 0x97, 0x88, 0x79,
0x6A, 0x5b, 0x4C, 0x3d, 0x2E, 0x1f, 0x00, 0xfF };
const char strbuffer1[] = "F1E2D3C4B5A69788796A5B4C3D2E1F00FF";
const char strbuffer1_space[] = "F1 E2 D3 C4 B5 A6 97 88 79 6A 5B 4C 3D 2E 1F 00 FF";
str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), FALSE);
if (!str)
goto fail;
if (memcmp(strbuffer1, str, sizeof(strbuffer1)) != 0)
goto fail;
free(str);
str = winpr_BinToHexString(binbuffer1, sizeof(binbuffer1), TRUE);
if (!str)
goto fail;
if (memcmp(strbuffer1_space, str, sizeof(strbuffer1_space)) != 0)
goto fail;
free(str);
str = nullptr;
}
rc = TRUE;
fail:
free(str);
return rc;
}
static BOOL test_hex_string_to_bin(void)
{
BOOL rc = FALSE;
{
const char stringbuffer[] = "123456789ABCDEFabcdef";
const BYTE empty[1024] = WINPR_C_ARRAY_INIT;
BYTE buffer[1024] = WINPR_C_ARRAY_INIT;
size_t len = winpr_HexStringToBinBuffer(nullptr, 0, nullptr, 0);
if (len != 0)
goto fail;
if (memcmp(buffer, empty, sizeof(buffer)) != 0)
goto fail;
len = winpr_HexStringToBinBuffer(nullptr, sizeof(stringbuffer), buffer, sizeof(buffer));
if (len != 0)
goto fail;
if (memcmp(buffer, empty, sizeof(buffer)) != 0)
goto fail;
len = winpr_HexStringToBinBuffer(stringbuffer, 0, buffer, sizeof(buffer));
if (len != 0)
goto fail;
if (memcmp(buffer, empty, sizeof(buffer)) != 0)
goto fail;
len =
winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), nullptr, sizeof(buffer));
if (len != 0)
goto fail;
if (memcmp(buffer, empty, sizeof(buffer)) != 0)
goto fail;
len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, 0);
if (len != 0)
goto fail;
if (memcmp(buffer, empty, sizeof(buffer)) != 0)
goto fail;
}
{
const char stringbuffer[] = "123456789ABCDEF1abcdef";
const BYTE expected[] = {
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1, 0xab, 0xcd, 0xef
};
BYTE buffer[32] = WINPR_C_ARRAY_INIT;
size_t len =
winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer));
if (len != sizeof(expected))
goto fail;
if (memcmp(buffer, expected, sizeof(expected)) != 0)
goto fail;
len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer,
sizeof(expected) / 2);
if (len != sizeof(expected) / 2)
goto fail;
if (memcmp(buffer, expected, sizeof(expected) / 2) != 0)
goto fail;
}
{
const char stringbuffer[] = "12 34 56 78 9A BC DE F1 ab cd ef";
const BYTE expected[] = {
0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf1, 0xab, 0xcd, 0xef
};
BYTE buffer[1024] = WINPR_C_ARRAY_INIT;
size_t len =
winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer));
if (len != sizeof(expected))
goto fail;
if (memcmp(buffer, expected, sizeof(expected)) != 0)
goto fail;
len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer,
sizeof(expected) / 2);
if (len != sizeof(expected) / 2)
goto fail;
if (memcmp(buffer, expected, sizeof(expected) / 2) != 0)
goto fail;
}
{
const char stringbuffer[] = "123456789ABCDEF1abcdef9";
const BYTE expected[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
0xde, 0xf1, 0xab, 0xcd, 0xef, 0x09 };
BYTE buffer[1024] = WINPR_C_ARRAY_INIT;
size_t len =
winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer));
if (len != sizeof(expected))
goto fail;
if (memcmp(buffer, expected, sizeof(expected)) != 0)
goto fail;
len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer,
sizeof(expected) / 2);
if (len != sizeof(expected) / 2)
goto fail;
if (memcmp(buffer, expected, sizeof(expected) / 2) != 0)
goto fail;
}
{
const char stringbuffer[] = "12 34 56 78 9A BC DE F1 ab cd ef 9";
const BYTE expected[] = { 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc,
0xde, 0xf1, 0xab, 0xcd, 0xef, 0x09 };
BYTE buffer[1024] = WINPR_C_ARRAY_INIT;
size_t len =
winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer, sizeof(buffer));
if (len != sizeof(expected))
goto fail;
if (memcmp(buffer, expected, sizeof(expected)) != 0)
goto fail;
len = winpr_HexStringToBinBuffer(stringbuffer, sizeof(stringbuffer), buffer,
sizeof(expected) / 2);
if (len != sizeof(expected) / 2)
goto fail;
if (memcmp(buffer, expected, sizeof(expected) / 2) != 0)
goto fail;
}
rc = TRUE;
fail:
return rc;
}
int TestPrint(int argc, char* argv[])
{
int a = 0;
int b = 0;
float c = NAN;
float d = NAN;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
/**
* 7
* 7
* 007
* 5.10
*/
a = 15;
b = a / 2;
_printf("%d\n", b);
_printf("%3d\n", b);
_printf("%03d\n", b);
c = 15.3f;
d = c / 3.0f;
_printf("%3.2f\n", d);
/**
* 0 -17.778
* 20 -6.667
* 40 04.444
* 60 15.556
* 80 26.667
* 100 37.778
* 120 48.889
* 140 60.000
* 160 71.111
* 180 82.222
* 200 93.333
* 220 104.444
* 240 115.556
* 260 126.667
* 280 137.778
* 300 148.889
*/
for (int a = 0; a <= 300; a = a + 20)
_printf("%3d %06.3f\n", a, (5.0 / 9.0) * (a - 32));
/**
* The color: blue
* First number: 12345
* Second number: 0025
* Third number: 1234
* Float number: 3.14
* Hexadecimal: ff
* Octal: 377
* Unsigned value: 150
* Just print the percentage sign %
*/
_printf("The color: %s\n", "blue");
_printf("First number: %d\n", 12345);
_printf("Second number: %04d\n", 25);
_printf("Third number: %i\n", 1234);
_printf("Float number: %3.2f\n", 3.14159);
_printf("Hexadecimal: %x/%X\n", 255, 255);
_printf("Octal: %o\n", 255);
_printf("Unsigned value: %u\n", 150);
_printf("Just print the percentage sign %%\n");
/**
* :Hello, world!:
* : Hello, world!:
* :Hello, wor:
* :Hello, world!:
* :Hello, world! :
* :Hello, world!:
* : Hello, wor:
* :Hello, wor :
*/
_printf(":%s:\n", "Hello, world!");
_printf(":%15s:\n", "Hello, world!");
_printf(":%.10s:\n", "Hello, world!");
_printf(":%-10s:\n", "Hello, world!");
_printf(":%-15s:\n", "Hello, world!");
_printf(":%.15s:\n", "Hello, world!");
_printf(":%15.10s:\n", "Hello, world!");
_printf(":%-15.10s:\n", "Hello, world!");
if (!test_bin_tohex_string())
return -1;
if (!test_bin_tohex_string_alloc())
return -1;
if (!test_hex_string_to_bin())
return -1;
return 0;
}

View File

@@ -0,0 +1,75 @@
#include <winpr/crt.h>
#include <winpr/thread.h>
#include <winpr/collections.h>
DEFINE_EVENT_BEGIN(MouseMotion)
int x;
int y;
DEFINE_EVENT_END(MouseMotion)
DEFINE_EVENT_BEGIN(MouseButton)
int x;
int y;
int flags;
int button;
DEFINE_EVENT_END(MouseButton)
static void MouseMotionEventHandler(void* context, const MouseMotionEventArgs* e)
{
printf("MouseMotionEvent: x: %d y: %d\n", e->x, e->y);
}
static void MouseButtonEventHandler(void* context, const MouseButtonEventArgs* e)
{
printf("MouseButtonEvent: x: %d y: %d flags: %d button: %d\n", e->x, e->y, e->flags, e->button);
}
static wEventType Node_Events[] = { DEFINE_EVENT_ENTRY(MouseMotion),
DEFINE_EVENT_ENTRY(MouseButton) };
#define NODE_EVENT_COUNT (sizeof(Node_Events) / sizeof(wEventType))
int TestPubSub(int argc, char* argv[])
{
wPubSub* node = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
node = PubSub_New(TRUE);
if (!node)
return -1;
PubSub_AddEventTypes(node, Node_Events, NODE_EVENT_COUNT);
if (PubSub_SubscribeMouseMotion(node, MouseMotionEventHandler) < 0)
return -1;
if (PubSub_SubscribeMouseButton(node, MouseButtonEventHandler) < 0)
return -1;
/* Call Event Handler */
{
MouseMotionEventArgs e;
e.x = 64;
e.y = 128;
PubSub_OnMouseMotion(node, nullptr, &e);
}
{
MouseButtonEventArgs e;
e.x = 23;
e.y = 56;
e.flags = 7;
e.button = 1;
PubSub_OnMouseButton(node, nullptr, &e);
}
PubSub_Free(node);
return 0;
}

View File

@@ -0,0 +1,225 @@
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/collections.h>
static bool wrap_test(bool (*fkt)(wQueue* queue))
{
wQueue* queue = Queue_New(TRUE, -1, -1);
if (!queue)
return false;
WINPR_ASSERT(fkt);
const bool rc = fkt(queue);
Queue_Free(queue);
return rc;
}
static bool check(const void* ptr, size_t pos)
{
if (!ptr)
return false;
if (ptr != (void*)(pos + 23))
return false;
return true;
}
static bool append(wQueue* queue, size_t pos)
{
void* ptr = (void*)(pos + 23);
return Queue_Enqueue(queue, ptr);
}
static bool fill_capcity(wQueue* queue, size_t* pos)
{
WINPR_ASSERT(pos);
size_t cpos = *pos;
const size_t capacity = Queue_Capacity(queue);
while (Queue_Count(queue) < capacity)
{
if (!append(queue, cpos++))
return false;
}
*pos = cpos;
return true;
}
static bool drain(wQueue* queue, size_t expect)
{
void* ptr = Queue_Dequeue(queue);
return check(ptr, expect);
}
static bool drain_capcity(wQueue* queue, size_t remain, size_t* pos)
{
WINPR_ASSERT(pos);
size_t cpos = *pos;
while (Queue_Count(queue) > remain)
{
if (!drain(queue, cpos++))
return false;
}
*pos = cpos;
return true;
}
static bool test_growth_move(wQueue* queue, bool big)
{
WINPR_ASSERT(queue);
const size_t cap = Queue_Capacity(queue);
if (cap < 4)
return false;
size_t wpos = 0;
size_t rpos = 0;
if (!fill_capcity(queue, &wpos))
return false;
/* Ensure the (base) capacity is larger than the allocation step
* so a full copy of tail will not be possible */
if (big)
{
if (!append(queue, wpos++))
return false;
}
if (!drain_capcity(queue, 3, &rpos))
return false;
if (!fill_capcity(queue, &wpos))
return false;
if (!append(queue, wpos++))
return false;
return drain_capcity(queue, 0, &rpos);
}
static bool test_growth_big_move(wQueue* queue)
{
return test_growth_move(queue, true);
}
static bool test_growth_small_move(wQueue* queue)
{
return test_growth_move(queue, false);
}
static bool check_size(wQueue* queue, size_t expected)
{
WINPR_ASSERT(queue);
const size_t count = Queue_Count(queue);
printf("queue count: %" PRIuz "\n", count);
return (count == expected);
}
static bool enqueue(wQueue* queue, size_t val)
{
WINPR_ASSERT(queue);
void* ptr = (void*)(23 + val);
return Queue_Enqueue(queue, ptr);
}
static bool dequeue(wQueue* queue, size_t expected)
{
WINPR_ASSERT(queue);
const void* pexpect = (void*)(23 + expected);
void* ptr = Queue_Dequeue(queue);
return (pexpect == ptr);
}
static bool legacy_test(wQueue* queue)
{
WINPR_ASSERT(queue);
for (size_t index = 1; index <= 10; index++)
{
if (!enqueue(queue, index))
return false;
}
if (!check_size(queue, 10))
return false;
for (size_t index = 1; index <= 10; index++)
{
if (!dequeue(queue, index))
return false;
}
if (!check_size(queue, 0))
return false;
if (!enqueue(queue, 1))
return false;
if (!enqueue(queue, 2))
return false;
if (!enqueue(queue, 3))
return false;
if (!check_size(queue, 3))
return false;
if (!dequeue(queue, 1))
return false;
if (!dequeue(queue, 2))
return false;
if (!check_size(queue, 1))
return false;
if (!enqueue(queue, 4))
return false;
if (!enqueue(queue, 5))
return false;
if (!enqueue(queue, 6))
return false;
if (!check_size(queue, 4))
return false;
if (!dequeue(queue, 3))
return false;
if (!dequeue(queue, 4))
return false;
if (!dequeue(queue, 5))
return false;
if (!dequeue(queue, 6))
return false;
if (!check_size(queue, 0))
return false;
Queue_Clear(queue);
if (!check_size(queue, 0))
return false;
for (size_t x = 0; x < 32; x++)
{
void* ptr = (void*)(23 + x);
if (!Queue_Enqueue(queue, ptr))
return false;
}
if (!check_size(queue, 32))
return false;
Queue_Clear(queue);
return check_size(queue, 0);
}
int TestQueue(WINPR_ATTR_UNUSED int argc, WINPR_ATTR_UNUSED char* argv[])
{
if (!wrap_test(test_growth_big_move))
return -1;
if (!wrap_test(test_growth_small_move))
return -2;
if (!wrap_test(legacy_test))
return -3;
return 0;
}

View File

@@ -0,0 +1,859 @@
#include <winpr/crt.h>
#include <winpr/print.h>
#include <winpr/crypto.h>
#include <winpr/stream.h>
static BOOL TestStream_Verify(wStream* s, size_t mincap, size_t len, size_t pos)
{
if (Stream_Buffer(s) == nullptr)
{
printf("stream buffer is null\n");
return FALSE;
}
if (Stream_ConstPointer(s) == nullptr)
{
printf("stream pointer is null\n");
return FALSE;
}
if (Stream_PointerAs(s, BYTE) < Stream_Buffer(s))
{
printf("stream pointer (%p) or buffer (%p) is invalid\n", Stream_ConstPointer(s),
(void*)Stream_Buffer(s));
return FALSE;
}
if (Stream_Capacity(s) < mincap)
{
printf("stream capacity is %" PRIuz " but minimum expected value is %" PRIuz "\n",
Stream_Capacity(s), mincap);
return FALSE;
}
if (Stream_Length(s) != len)
{
printf("stream has unexpected length (%" PRIuz " instead of %" PRIuz ")\n",
Stream_Length(s), len);
return FALSE;
}
if (Stream_GetPosition(s) != pos)
{
printf("stream has unexpected position (%" PRIuz " instead of %" PRIuz ")\n",
Stream_GetPosition(s), pos);
return FALSE;
}
if (Stream_GetPosition(s) > Stream_Length(s))
{
printf("stream position (%" PRIuz ") exceeds length (%" PRIuz ")\n", Stream_GetPosition(s),
Stream_Length(s));
return FALSE;
}
if (Stream_GetPosition(s) > Stream_Capacity(s))
{
printf("stream position (%" PRIuz ") exceeds capacity (%" PRIuz ")\n",
Stream_GetPosition(s), Stream_Capacity(s));
return FALSE;
}
if (Stream_Length(s) > Stream_Capacity(s))
{
printf("stream length (%" PRIuz ") exceeds capacity (%" PRIuz ")\n", Stream_Length(s),
Stream_Capacity(s));
return FALSE;
}
if (Stream_GetRemainingLength(s) != len - pos)
{
printf("stream remaining length (%" PRIuz " instead of %" PRIuz ")\n",
Stream_GetRemainingLength(s), len - pos);
return FALSE;
}
return TRUE;
}
static BOOL TestStream_New(void)
{
/* Test creation of a 0-size stream with no buffer */
wStream* s = Stream_New(nullptr, 0);
if (s)
return FALSE;
Stream_Free(s, TRUE);
return TRUE;
}
static BOOL TestStream_Static(void)
{
BYTE buffer[20] = WINPR_C_ARRAY_INIT;
wStream staticStream = WINPR_C_ARRAY_INIT;
wStream* s = &staticStream;
UINT16 v = 0;
/* Test creation of a static stream */
Stream_StaticInit(s, buffer, sizeof(buffer));
Stream_Write_UINT16(s, 0xcab1);
Stream_ResetPosition(s);
Stream_Read_UINT16(s, v);
if (v != 0xcab1)
return FALSE;
Stream_ResetPosition(s);
Stream_Write_UINT16(s, 1);
if (!Stream_EnsureRemainingCapacity(s, 10)) /* we can ask for 10 bytes */
return FALSE;
/* 30 is bigger than the buffer, it will be reallocated on the heap */
if (!Stream_EnsureRemainingCapacity(s, 30) || !s->isOwner)
return FALSE;
Stream_Write_UINT16(s, 2);
Stream_ResetPosition(s);
Stream_Read_UINT16(s, v);
if (v != 1)
return FALSE;
Stream_Read_UINT16(s, v);
if (v != 2)
return FALSE;
// Intentional warning as the stream is not allocated.
// Still, Stream_Free should not release such memory, therefore this statement
// is required to test that.
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
Stream_Free(s, TRUE);
WINPR_PRAGMA_DIAG_POP
return TRUE;
}
static BOOL TestStream_Create(size_t count, BOOL selfAlloc)
{
size_t len = 0;
size_t cap = 0;
wStream* s = nullptr;
void* buffer = nullptr;
for (size_t i = 0; i < count; i++)
{
len = cap = i + 1;
if (selfAlloc)
{
if (!(buffer = malloc(cap)))
{
printf("%s: failed to allocate buffer of size %" PRIuz "\n", __func__, cap);
goto fail;
}
}
if (!(s = Stream_New(selfAlloc ? buffer : nullptr, len)))
{
printf("%s: Stream_New failed for stream #%" PRIuz "\n", __func__, i);
goto fail;
}
if (!TestStream_Verify(s, cap, len, 0))
{
goto fail;
}
for (size_t pos = 0; pos < len; pos++)
{
Stream_SetPosition(s, pos);
Stream_SealLength(s);
if (!TestStream_Verify(s, cap, pos, pos))
{
goto fail;
}
}
if (selfAlloc)
{
memset(buffer, (BYTE)(i % 256), cap);
if (memcmp(buffer, Stream_Buffer(s), cap) != 0)
{
printf("%s: buffer memory corruption\n", __func__);
goto fail;
}
}
Stream_Free(s, buffer == nullptr);
free(buffer);
}
return TRUE;
fail:
free(buffer);
if (s)
{
Stream_Free(s, buffer == nullptr);
}
return FALSE;
}
static BOOL TestStream_Extent(UINT32 maxSize)
{
wStream* s = nullptr;
BOOL result = FALSE;
if (!(s = Stream_New(nullptr, 1)))
{
printf("%s: Stream_New failed\n", __func__);
return FALSE;
}
for (UINT32 i = 1; i < maxSize; i++)
{
if (i % 2)
{
if (!Stream_EnsureRemainingCapacity(s, i))
goto fail;
}
else
{
if (!Stream_EnsureCapacity(s, i))
goto fail;
}
Stream_SetPosition(s, i);
Stream_SealLength(s);
if (!TestStream_Verify(s, i, i, i))
{
printf("%s: failed to verify stream in iteration %" PRIu32 "\n", __func__, i);
goto fail;
}
}
result = TRUE;
fail:
if (s)
{
Stream_Free(s, TRUE);
}
return result;
}
#define Stream_Peek_UINT8_BE Stream_Peek_UINT8
#define Stream_Read_UINT8_BE Stream_Read_UINT8
#define Stream_Peek_Get_UINT8_BE Stream_Peek_Get_UINT8
#define Stream_Get_UINT8_BE Stream_Get_UINT8
#define Stream_Peek_INT8_BE Stream_Peek_INT8
#define Stream_Peek_Get_INT8_BE Stream_Peek_Get_INT8
#define Stream_Read_INT8_BE Stream_Read_INT8
#define Stream_Get_INT8_BE Stream_Get_INT8
#define TestStream_PeekAndRead(_s, _r, _t) \
do \
{ \
_t _a = 0; \
_t _b = 0; \
BYTE* _p = Stream_Buffer(_s); \
Stream_ResetPosition(_s); \
Stream_Peek_##_t(_s, _a); \
Stream_Read_##_t(_s, _b); \
if (_a != _b) \
{ \
printf("%s: test1 " #_t "_LE failed\n", __func__); \
(_r) = FALSE; \
} \
Stream_Rewind(_s, sizeof(_t)); \
const _t _d = Stream_Peek_Get_##_t(_s); \
const _t _c = Stream_Get_##_t(_s); \
if (_c != _d) \
{ \
printf("%s: test1 " #_t "_LE failed\n", __func__); \
(_r) = FALSE; \
} \
for (size_t _i = 0; _i < sizeof(_t); _i++) \
{ \
if (((_a >> (_i * 8)) & 0xFF) != _p[_i]) \
{ \
printf("%s: test2 " #_t "_LE failed\n", __func__); \
(_r) = FALSE; \
break; \
} \
} \
/* printf("a: 0x%016llX\n", a); */ \
Stream_ResetPosition(_s); \
Stream_Peek_##_t##_BE(_s, _a); \
Stream_Read_##_t##_BE(_s, _b); \
if (_a != _b) \
{ \
printf("%s: test1 " #_t "_BE failed\n", __func__); \
(_r) = FALSE; \
} \
Stream_Rewind(_s, sizeof(_t)); \
const _t _e = Stream_Peek_Get_##_t##_BE(_s); \
const _t _f = Stream_Get_##_t##_BE(_s); \
if (_e != _f) \
{ \
printf("%s: test1 " #_t "_BE failed\n", __func__); \
(_r) = FALSE; \
} \
for (size_t _i = 0; _i < sizeof(_t); _i++) \
{ \
if (((_a >> (_i * 8)) & 0xFF) != _p[sizeof(_t) - _i - 1]) \
{ \
printf("%s: test2 " #_t "_BE failed\n", __func__); \
(_r) = FALSE; \
break; \
} \
} \
/* printf("a: 0x%016llX\n", a); */ \
} while (0)
static BOOL TestStream_WriteAndRead(UINT64 value)
{
union
{
UINT8 u8;
UINT16 u16;
UINT32 u32;
UINT64 u64;
INT8 i8;
INT16 i16;
INT32 i32;
INT64 i64;
} val;
val.u64 = value;
wStream* s = Stream_New(nullptr, 1024);
if (!s)
return FALSE;
BOOL rc = FALSE;
{
Stream_Write_UINT8(s, val.u8);
Stream_Rewind_UINT8(s);
const UINT8 ru8 = Stream_Get_UINT8(s);
Stream_Rewind_UINT8(s);
if (val.u8 != ru8)
goto fail;
}
{
Stream_Write_UINT16(s, val.u16);
Stream_Rewind_UINT16(s);
const UINT16 ru = Stream_Get_UINT16(s);
Stream_Rewind_UINT16(s);
if (val.u16 != ru)
goto fail;
}
{
Stream_Write_UINT16_BE(s, val.u16);
Stream_Rewind_UINT16(s);
const UINT16 ru = Stream_Get_UINT16_BE(s);
Stream_Rewind_UINT16(s);
if (val.u16 != ru)
goto fail;
}
{
Stream_Write_UINT32(s, val.u32);
Stream_Rewind_UINT32(s);
const UINT32 ru = Stream_Get_UINT32(s);
Stream_Rewind_UINT32(s);
if (val.u32 != ru)
goto fail;
}
{
Stream_Write_UINT32_BE(s, val.u32);
Stream_Rewind_UINT32(s);
const UINT32 ru = Stream_Get_UINT32_BE(s);
Stream_Rewind_UINT32(s);
if (val.u32 != ru)
goto fail;
}
{
Stream_Write_UINT64(s, val.u64);
Stream_Rewind_UINT64(s);
const UINT64 ru = Stream_Get_UINT64(s);
Stream_Rewind_UINT64(s);
if (val.u64 != ru)
goto fail;
}
{
Stream_Write_UINT64_BE(s, val.u64);
Stream_Rewind_UINT64(s);
const UINT64 ru = Stream_Get_UINT64_BE(s);
Stream_Rewind_UINT64(s);
if (val.u64 != ru)
goto fail;
}
{
Stream_Write_INT8(s, val.i8);
Stream_Rewind(s, 1);
const INT8 ru8 = Stream_Get_INT8(s);
Stream_Rewind(s, 1);
if (val.i8 != ru8)
goto fail;
}
{
Stream_Write_INT16(s, val.i16);
Stream_Rewind(s, 2);
const INT16 ru = Stream_Get_INT16(s);
Stream_Rewind(s, 2);
if (val.i16 != ru)
goto fail;
}
{
Stream_Write_INT16_BE(s, val.i16);
Stream_Rewind(s, 2);
const INT16 ru = Stream_Get_INT16_BE(s);
Stream_Rewind(s, 2);
if (val.i16 != ru)
goto fail;
}
{
Stream_Write_INT32(s, val.i32);
Stream_Rewind(s, 4);
const INT32 ru = Stream_Get_INT32(s);
Stream_Rewind(s, 4);
if (val.i32 != ru)
goto fail;
}
{
Stream_Write_INT32_BE(s, val.i32);
Stream_Rewind(s, 4);
const INT32 ru = Stream_Get_INT32_BE(s);
Stream_Rewind(s, 4);
if (val.i32 != ru)
goto fail;
}
{
Stream_Write_INT64(s, val.i64);
Stream_Rewind(s, 8);
const INT64 ru = Stream_Get_INT64(s);
Stream_Rewind(s, 8);
if (val.i64 != ru)
goto fail;
}
{
Stream_Write_INT64_BE(s, val.i64);
Stream_Rewind(s, 8);
const INT64 ru = Stream_Get_INT64_BE(s);
Stream_Rewind(s, 8);
if (val.i64 != ru)
goto fail;
}
rc = TRUE;
fail:
Stream_Free(s, TRUE);
return rc;
}
static BOOL TestStream_Reading(void)
{
BYTE src[] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 };
wStream* s = nullptr;
BOOL result = TRUE;
if (!(s = Stream_New(src, sizeof(src))))
{
printf("%s: Stream_New failed\n", __func__);
return FALSE;
}
TestStream_PeekAndRead(s, result, UINT8);
TestStream_PeekAndRead(s, result, INT8);
TestStream_PeekAndRead(s, result, UINT16);
TestStream_PeekAndRead(s, result, INT16);
TestStream_PeekAndRead(s, result, UINT32);
TestStream_PeekAndRead(s, result, INT32);
TestStream_PeekAndRead(s, result, UINT64);
TestStream_PeekAndRead(s, result, INT64);
Stream_Free(s, FALSE);
return result;
}
static BOOL TestStream_Write(void)
{
BOOL rc = FALSE;
UINT8 u8 = 0;
UINT16 u16 = 0;
UINT32 u32 = 0;
UINT64 u64 = 0;
const BYTE data[] = "someteststreamdata";
wStream* s = Stream_New(nullptr, 100);
if (!s)
goto out;
if (s->pointer != s->buffer)
goto out;
Stream_Write(s, data, sizeof(data));
if (memcmp(Stream_Buffer(s), data, sizeof(data)) == 0)
rc = TRUE;
if (s->pointer != s->buffer + sizeof(data))
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Write_UINT8(s, 42);
if (s->pointer != s->buffer + 1)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Peek_UINT8(s, u8);
if (u8 != 42)
goto out;
Stream_Write_UINT16(s, 0x1234);
if (s->pointer != s->buffer + 2)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Peek_UINT16(s, u16);
if (u16 != 0x1234)
goto out;
Stream_Write_UINT32(s, 0x12345678UL);
if (s->pointer != s->buffer + 4)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Peek_UINT32(s, u32);
if (u32 != 0x12345678UL)
goto out;
Stream_Write_UINT64(s, 0x1234567890ABCDEFULL);
if (s->pointer != s->buffer + 8)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Peek_UINT64(s, u64);
if (u64 != 0x1234567890ABCDEFULL)
goto out;
out:
Stream_Free(s, TRUE);
return rc;
}
static BOOL TestStream_Seek(void)
{
BOOL rc = FALSE;
wStream* s = Stream_New(nullptr, 100);
if (!s)
goto out;
if (s->pointer != s->buffer)
goto out;
Stream_Seek(s, 5);
if (s->pointer != s->buffer + 5)
goto out;
Stream_Seek_UINT8(s);
if (s->pointer != s->buffer + 6)
goto out;
Stream_Seek_UINT16(s);
if (s->pointer != s->buffer + 8)
goto out;
Stream_Seek_UINT32(s);
if (s->pointer != s->buffer + 12)
goto out;
Stream_Seek_UINT64(s);
if (s->pointer != s->buffer + 20)
goto out;
rc = TRUE;
out:
Stream_Free(s, TRUE);
return rc;
}
static BOOL TestStream_Rewind(void)
{
BOOL rc = FALSE;
wStream* s = Stream_New(nullptr, 100);
if (!s)
goto out;
if (s->pointer != s->buffer)
goto out;
Stream_Seek(s, 100);
if (s->pointer != s->buffer + 100)
goto out;
Stream_Rewind(s, 10);
if (s->pointer != s->buffer + 90)
goto out;
Stream_Rewind_UINT8(s);
if (s->pointer != s->buffer + 89)
goto out;
Stream_Rewind_UINT16(s);
if (s->pointer != s->buffer + 87)
goto out;
Stream_Rewind_UINT32(s);
if (s->pointer != s->buffer + 83)
goto out;
Stream_Rewind_UINT64(s);
if (s->pointer != s->buffer + 75)
goto out;
rc = TRUE;
out:
Stream_Free(s, TRUE);
return rc;
}
static BOOL TestStream_Zero(void)
{
BOOL rc = FALSE;
const BYTE data[] = "someteststreamdata";
wStream* s = Stream_New(nullptr, sizeof(data));
if (!s)
goto out;
Stream_Write(s, data, sizeof(data));
if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Zero(s, 5);
if (s->pointer != s->buffer + 5)
goto out;
if (memcmp(Stream_ConstPointer(s), data + 5, sizeof(data) - 5) != 0)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
for (UINT32 x = 0; x < 5; x++)
{
UINT8 val = 0;
Stream_Read_UINT8(s, val);
if (val != 0)
goto out;
}
rc = TRUE;
out:
Stream_Free(s, TRUE);
return rc;
}
static BOOL TestStream_Fill(void)
{
BOOL rc = FALSE;
const BYTE fill[7] = "XXXXXXX";
const BYTE data[] = "someteststreamdata";
wStream* s = Stream_New(nullptr, sizeof(data));
if (!s)
goto out;
Stream_Write(s, data, sizeof(data));
if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Fill(s, fill[0], sizeof(fill));
if (s->pointer != s->buffer + sizeof(fill))
goto out;
if (memcmp(Stream_ConstPointer(s), data + sizeof(fill), sizeof(data) - sizeof(fill)) != 0)
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
if (memcmp(Stream_ConstPointer(s), fill, sizeof(fill)) != 0)
goto out;
rc = TRUE;
out:
Stream_Free(s, TRUE);
return rc;
}
static BOOL TestStream_Copy(void)
{
BOOL rc = FALSE;
const BYTE data[] = "someteststreamdata";
wStream* s = Stream_New(nullptr, sizeof(data));
wStream* d = Stream_New(nullptr, sizeof(data));
if (!s || !d)
goto out;
if (s->pointer != s->buffer)
goto out;
Stream_Write(s, data, sizeof(data));
if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0)
goto out;
if (s->pointer != s->buffer + sizeof(data))
goto out;
Stream_ResetPosition(s);
if (s->pointer != s->buffer)
goto out;
Stream_Copy(s, d, sizeof(data));
if (s->pointer != s->buffer + sizeof(data))
goto out;
if (d->pointer != d->buffer + sizeof(data))
goto out;
if (Stream_GetPosition(s) != Stream_GetPosition(d))
goto out;
if (memcmp(Stream_Buffer(s), data, sizeof(data)) != 0)
goto out;
if (memcmp(Stream_Buffer(d), data, sizeof(data)) != 0)
goto out;
rc = TRUE;
out:
Stream_Free(s, TRUE);
Stream_Free(d, TRUE);
return rc;
}
int TestStream(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!TestStream_Create(200, FALSE))
return 1;
if (!TestStream_Create(200, TRUE))
return 2;
if (!TestStream_Extent(4096))
return 3;
if (!TestStream_Reading())
return 4;
if (!TestStream_New())
return 5;
if (!TestStream_Write())
return 6;
if (!TestStream_Seek())
return 7;
if (!TestStream_Rewind())
return 8;
if (!TestStream_Zero())
return 9;
if (!TestStream_Fill())
return 10;
if (!TestStream_Copy())
return 11;
if (!TestStream_Static())
return 12;
if (!TestStream_WriteAndRead(0x1234567890abcdef))
return 13;
for (size_t x = 0; x < 10; x++)
{
UINT64 val = 0;
if (winpr_RAND(&val, sizeof(val)) < 0)
return -1;
if (!TestStream_WriteAndRead(val))
return 14;
}
return 0;
}

View File

@@ -0,0 +1,82 @@
#include <winpr/crt.h>
#include <winpr/stream.h>
#include <winpr/collections.h>
#define BUFFER_SIZE 16384
int TestStreamPool(int argc, char* argv[])
{
wStream* s[5] = WINPR_C_ARRAY_INIT;
char buffer[8192] = WINPR_C_ARRAY_INIT;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
wStreamPool* pool = StreamPool_New(TRUE, BUFFER_SIZE);
s[0] = StreamPool_Take(pool, 0);
s[1] = StreamPool_Take(pool, 0);
s[2] = StreamPool_Take(pool, 0);
printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer)));
Stream_Release(s[0]);
Stream_Release(s[1]);
Stream_Release(s[2]);
printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer)));
s[3] = StreamPool_Take(pool, 0);
s[4] = StreamPool_Take(pool, 0);
printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer)));
Stream_Release(s[3]);
Stream_Release(s[4]);
printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer)));
s[2] = StreamPool_Take(pool, 0);
s[3] = StreamPool_Take(pool, 0);
s[4] = StreamPool_Take(pool, 0);
printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer)));
Stream_AddRef(s[2]);
Stream_AddRef(s[3]);
Stream_AddRef(s[3]);
Stream_AddRef(s[4]);
Stream_AddRef(s[4]);
Stream_AddRef(s[4]);
Stream_Release(s[2]);
Stream_Release(s[2]);
Stream_Release(s[3]);
Stream_Release(s[3]);
Stream_Release(s[3]);
Stream_Release(s[4]);
Stream_Release(s[4]);
Stream_Release(s[4]);
Stream_Release(s[4]);
printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer)));
s[2] = StreamPool_Take(pool, 0);
s[3] = StreamPool_Take(pool, 0);
s[4] = StreamPool_Take(pool, 0);
printf("%s\n", StreamPool_GetStatistics(pool, buffer, sizeof(buffer)));
Stream_Release(s[2]);
Stream_Release(s[3]);
Stream_Release(s[4]);
StreamPool_Free(pool);
return 0;
}

View File

@@ -0,0 +1,47 @@
#include <winpr/crt.h>
#include <winpr/version.h>
#include <winpr/winpr.h>
int TestVersion(int argc, char* argv[])
{
const char* version = nullptr;
const char* git = nullptr;
const char* build = nullptr;
int major = 0;
int minor = 0;
int revision = 0;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
winpr_get_version(&major, &minor, &revision);
if (major != WINPR_VERSION_MAJOR)
return -1;
if (minor != WINPR_VERSION_MINOR)
return -1;
if (revision != WINPR_VERSION_REVISION)
return -1;
version = winpr_get_version_string();
if (!version)
return -1;
git = winpr_get_build_revision();
if (!git)
return -1;
if (strncmp(git, WINPR_GIT_REVISION, sizeof(WINPR_GIT_REVISION)) != 0)
return -1;
build = winpr_get_build_config();
if (!build)
return -1;
return 0;
}

View File

@@ -0,0 +1,69 @@
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/path.h>
#include <winpr/file.h>
#include <winpr/wlog.h>
int TestWLog(int argc, char* argv[])
{
wLog* root = nullptr;
wLog* logA = nullptr;
wLog* logB = nullptr;
wLogLayout* layout = nullptr;
wLogAppender* appender = nullptr;
char* tmp_path = nullptr;
char* wlog_file = nullptr;
int result = 1;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
if (!(tmp_path = GetKnownPath(KNOWN_PATH_TEMP)))
{
(void)fprintf(stderr, "Failed to get temporary directory!\n");
goto out;
}
root = WLog_GetRoot();
WLog_SetLogAppenderType(root, WLOG_APPENDER_BINARY);
appender = WLog_GetLogAppender(root);
if (!WLog_ConfigureAppender(appender, "outputfilename", "test_w.log"))
goto out;
if (!WLog_ConfigureAppender(appender, "outputfilepath", tmp_path))
goto out;
layout = WLog_GetLogLayout(root);
WLog_Layout_SetPrefixFormat(root, layout, "[%lv:%mn] [%fl|%fn|%ln] - ");
WLog_OpenAppender(root);
logA = WLog_Get("com.test.ChannelA");
logB = WLog_Get("com.test.ChannelB");
WLog_SetLogLevel(logA, WLOG_INFO);
WLog_SetLogLevel(logB, WLOG_ERROR);
WLog_Print(logA, WLOG_INFO, "this is a test");
WLog_Print(logA, WLOG_WARN, "this is a %dnd %s", 2, "test");
WLog_Print(logA, WLOG_ERROR, "this is an error");
WLog_Print(logA, WLOG_TRACE, "this is a trace output");
WLog_Print(logB, WLOG_INFO, "just some info");
WLog_Print(logB, WLOG_WARN, "we're warning a %dnd %s", 2, "time");
WLog_Print(logB, WLOG_ERROR, "we've got an error");
WLog_Print(logB, WLOG_TRACE, "leaving a trace behind");
WLog_CloseAppender(root);
if ((wlog_file = GetCombinedPath(tmp_path, "test_w.log")))
winpr_DeleteFile(wlog_file);
result = 0;
out:
free(wlog_file);
free(tmp_path);
return result;
}

View File

@@ -0,0 +1,128 @@
#include <winpr/crt.h>
#include <winpr/tchar.h>
#include <winpr/path.h>
#include <winpr/wlog.h>
typedef struct
{
UINT32 level;
char* msg;
char* channel;
} test_t;
static const char* function = nullptr;
static const char* channels[] = { "com.test.channelA", "com.test.channelB" };
static const test_t messages[] = { { WLOG_INFO, "this is a test", "com.test.channelA" },
{ WLOG_INFO, "Just some info", "com.test.channelB" },
{ WLOG_WARN, "this is a %dnd %s", "com.test.channelA" },
{ WLOG_WARN, "we're warning a %dnd %s", "com.test.channelB" },
{ WLOG_ERROR, "this is an error", "com.test.channelA" },
{ WLOG_ERROR, "we've got an error", "com.test.channelB" },
{ WLOG_TRACE, "this is a trace output", "com.test.channelA" },
{ WLOG_TRACE, "leaving a trace behind", "com.test.channelB" } };
static BOOL success = TRUE;
static int pos = 0;
static BOOL check(const wLogMessage* msg)
{
BOOL rc = TRUE;
if (!msg)
rc = FALSE;
else if (strcmp(msg->FileName, __FILE__) != 0)
rc = FALSE;
else if (strcmp(msg->FunctionName, function) != 0)
rc = FALSE;
else if (strcmp(msg->PrefixString, messages[pos].channel) != 0)
rc = FALSE;
else if (msg->Level != messages[pos].level)
rc = FALSE;
else if (strcmp(msg->FormatString, messages[pos].msg) != 0)
rc = FALSE;
pos++;
if (!rc)
{
(void)fprintf(stderr, "Test failed!\n");
success = FALSE;
}
return rc;
}
static BOOL CallbackAppenderMessage(const wLogMessage* msg)
{
check(msg);
return TRUE;
}
static BOOL CallbackAppenderData(const wLogMessage* msg)
{
(void)fprintf(stdout, "%s\n", __func__);
return TRUE;
}
static BOOL CallbackAppenderImage(const wLogMessage* msg)
{
(void)fprintf(stdout, "%s\n", __func__);
return TRUE;
}
static BOOL CallbackAppenderPackage(const wLogMessage* msg)
{
(void)fprintf(stdout, "%s\n", __func__);
return TRUE;
}
int TestWLogCallback(int argc, char* argv[])
{
wLog* root = nullptr;
wLog* logA = nullptr;
wLog* logB = nullptr;
wLogLayout* layout = nullptr;
wLogAppender* appender = nullptr;
wLogCallbacks callbacks;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
function = __func__;
root = WLog_GetRoot();
WLog_SetLogAppenderType(root, WLOG_APPENDER_CALLBACK);
appender = WLog_GetLogAppender(root);
callbacks.data = CallbackAppenderData;
callbacks.image = CallbackAppenderImage;
callbacks.message = CallbackAppenderMessage;
callbacks.package = CallbackAppenderPackage;
if (!WLog_ConfigureAppender(appender, "callbacks", (void*)&callbacks))
return -1;
layout = WLog_GetLogLayout(root);
WLog_Layout_SetPrefixFormat(root, layout, "%mn");
WLog_OpenAppender(root);
logA = WLog_Get(channels[0]);
logB = WLog_Get(channels[1]);
WLog_SetLogLevel(logA, WLOG_TRACE);
WLog_SetLogLevel(logB, WLOG_TRACE);
WLog_Print(logA, messages[0].level, messages[0].msg);
WLog_Print(logB, messages[1].level, messages[1].msg);
WLog_Print(logA, messages[2].level, messages[2].msg, 2, "test");
WLog_Print(logB, messages[3].level, messages[3].msg, 2, "time");
WLog_Print(logA, messages[4].level, messages[4].msg);
WLog_Print(logB, messages[5].level, messages[5].msg);
WLog_Print(logA, messages[6].level, messages[6].msg);
WLog_Print(logB, messages[7].level, messages[7].msg);
WLog_CloseAppender(root);
return success ? 0 : -1;
}

View File

@@ -0,0 +1,105 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <winpr/image.h>
static const int formats[] = { WINPR_IMAGE_BITMAP, WINPR_IMAGE_PNG, WINPR_IMAGE_JPEG,
WINPR_IMAGE_WEBP };
static void usage(int argc, char* argv[])
{
const char* prg = "INVALID";
if (argc > 0)
prg = argv[0];
(void)fprintf(stdout, "%s <src> <dst>\n", prg);
(void)fprintf(stdout, "\tConvert image <src> to <dst>\n");
(void)fprintf(stdout, "\tSupported formats (for this build):\n");
for (size_t x = 0; x < ARRAYSIZE(formats); x++)
{
const int format = formats[x];
const char* ext = winpr_image_format_extension(format);
const char* mime = winpr_image_format_mime(format);
const BOOL supported = winpr_image_format_is_supported(format);
if (supported)
{
(void)fprintf(stdout, "\t\t%s [.%s]\n", mime, ext);
}
}
}
static int detect_format(const char* name)
{
const char* dot = strrchr(name, '.');
if (!dot)
{
(void)fprintf(stderr, "'%s' does not have a file extension\n", name);
return -1;
}
for (size_t x = 0; x < ARRAYSIZE(formats); x++)
{
const int format = formats[x];
const char* ext = winpr_image_format_extension(format);
const char* mime = winpr_image_format_mime(format);
const BOOL supported = winpr_image_format_is_supported(format);
if (strcmp(&dot[1], ext) == 0)
{
(void)fprintf(stdout, "'%s' is of format %s [supported:%s]\n", name, mime,
supported ? "true" : "false");
if (!supported)
return -2;
return format;
}
}
(void)fprintf(stderr, "'%s' is a unsupported format\n", name);
return -3;
}
int main(int argc, char* argv[])
{
int rc = -4;
if (argc != 3)
{
usage(argc, argv);
return -1;
}
const char* src = argv[1];
const char* dst = argv[2];
const int sfmt = detect_format(src);
const int dfmt = detect_format(dst);
if ((sfmt < 0) || (dfmt < 0))
{
usage(argc, argv);
return -2;
}
wImage* img = winpr_image_new();
if (!img)
{
return -3;
}
const int rrc = winpr_image_read(img, src);
if (rrc <= 0)
{
(void)fprintf(stderr, "Failed to read image '%s': %d\n", src, rrc);
goto fail;
}
const int wrc = winpr_image_write(img, dst);
if (wrc <= 0)
{
(void)fprintf(stderr, "Failed to write image '%s': %d\n", dst, wrc);
goto fail;
}
(void)fprintf(stdout, "Successfully converted '%s' to '%s'\n", src, dst);
rc = 0;
fail:
winpr_image_free(img, TRUE);
return rc;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -0,0 +1,206 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Debugging helpers
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* 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 _GNU_SOURCE
#define _GNU_SOURCE // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
#endif
#include <assert.h>
#include <stdlib.h>
#include <unwind.h>
#include <winpr/string.h>
#include "debug.h"
#include <winpr/wlog.h>
#include "../log.h"
#include <dlfcn.h>
#define TAG WINPR_TAG("utils.unwind")
#define UNWIND_MAX_LINE_SIZE 1024ULL
typedef struct
{
union
{
uintptr_t uw;
void* pv;
} pc;
union
{
uintptr_t uptr;
void* pv;
} langSpecificData;
} unwind_info_t;
typedef struct
{
size_t pos;
size_t size;
unwind_info_t* info;
} unwind_context_t;
static const char* unwind_reason_str(_Unwind_Reason_Code code)
{
switch (code)
{
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__ARM_DWARF_EH__) && \
!defined(__SEH__)
case _URC_OK:
return "_URC_OK";
#else
case _URC_NO_REASON:
return "_URC_NO_REASON";
case _URC_FATAL_PHASE2_ERROR:
return "_URC_FATAL_PHASE2_ERROR";
case _URC_FATAL_PHASE1_ERROR:
return "_URC_FATAL_PHASE1_ERROR";
case _URC_NORMAL_STOP:
return "_URC_NORMAL_STOP";
#endif
case _URC_FOREIGN_EXCEPTION_CAUGHT:
return "_URC_FOREIGN_EXCEPTION_CAUGHT";
case _URC_END_OF_STACK:
return "_URC_END_OF_STACK";
case _URC_HANDLER_FOUND:
return "_URC_HANDLER_FOUND";
case _URC_INSTALL_CONTEXT:
return "_URC_INSTALL_CONTEXT";
case _URC_CONTINUE_UNWIND:
return "_URC_CONTINUE_UNWIND";
#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__ARM_DWARF_EH__) && \
!defined(__SEH__)
case _URC_FAILURE:
return "_URC_FAILURE";
#endif
default:
return "_URC_UNKNOWN";
}
}
static const char* unwind_reason_str_buffer(_Unwind_Reason_Code code, char* buffer, size_t size)
{
const char* str = unwind_reason_str(code);
(void)_snprintf(buffer, size, "%s [0x%02x]", str, code);
return buffer;
}
static _Unwind_Reason_Code unwind_backtrace_callback(struct _Unwind_Context* context, void* arg)
{
unwind_context_t* ctx = arg;
assert(ctx);
if (ctx->pos < ctx->size)
{
unwind_info_t* info = &ctx->info[ctx->pos++];
info->pc.uw = _Unwind_GetIP(context);
/* _Unwind_GetLanguageSpecificData has various return value definitions,
* cast to the type we expect and disable linter warnings
*/
// NOLINTNEXTLINE(google-readability-casting,readability-redundant-casting)
info->langSpecificData.pv = (void*)_Unwind_GetLanguageSpecificData(context);
}
return _URC_NO_REASON;
}
void* winpr_unwind_backtrace(DWORD size)
{
_Unwind_Reason_Code rc = _URC_FOREIGN_EXCEPTION_CAUGHT;
unwind_context_t* ctx = calloc(1, sizeof(unwind_context_t));
if (!ctx)
goto fail;
ctx->size = size;
ctx->info = calloc(size, sizeof(unwind_info_t));
if (!ctx->info)
goto fail;
rc = _Unwind_Backtrace(unwind_backtrace_callback, ctx);
if (rc != _URC_END_OF_STACK)
{
/* https://github.com/FreeRDP/FreeRDP/issues/11490
*
* there seems to be no consensus on what to return from this function.
* so we just warn about unexpected return codes and return the context regardless.
*/
char buffer[64] = WINPR_C_ARRAY_INIT;
WLog_WARN(TAG, "_Unwind_Backtrace failed with %s",
unwind_reason_str_buffer(rc, buffer, sizeof(buffer)));
}
return ctx;
fail:
winpr_unwind_backtrace_free(ctx);
return nullptr;
}
void winpr_unwind_backtrace_free(void* buffer)
{
unwind_context_t* ctx = buffer;
if (!ctx)
return;
free(ctx->info);
free(ctx);
}
char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used)
{
union
{
void* pv;
char* cp;
char** cpp;
} cnv;
unwind_context_t* ctx = buffer;
cnv.cpp = nullptr;
if (!ctx)
return nullptr;
cnv.pv = calloc(ctx->pos * (sizeof(char*) + UNWIND_MAX_LINE_SIZE), sizeof(char*));
if (!cnv.pv)
return nullptr;
if (used)
*used = ctx->pos;
for (size_t x = 0; x < ctx->pos; x++)
{
char* msg = cnv.cp + ctx->pos * sizeof(char*) + x * UNWIND_MAX_LINE_SIZE;
const unwind_info_t* info = &ctx->info[x];
Dl_info dlinfo = WINPR_C_ARRAY_INIT;
int rc = dladdr(info->pc.pv, &dlinfo);
cnv.cpp[x] = msg;
if (rc == 0)
(void)_snprintf(msg, UNWIND_MAX_LINE_SIZE, "unresolvable, address=%p", info->pc.pv);
else
(void)_snprintf(msg, UNWIND_MAX_LINE_SIZE, "dli_fname=%s [%p], dli_sname=%s [%p]",
dlinfo.dli_fname, dlinfo.dli_fbase, dlinfo.dli_sname, dlinfo.dli_saddr);
}
// NOLINTNEXTLINE(clang-analyzer-unix.Malloc): function is an allocator
return cnv.cpp;
}

View File

@@ -0,0 +1,47 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Debugging helpers
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* 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 WINPR_DEBUG_UNWIND_H
#define WINPR_DEBUG_UNWIND_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <winpr/wtypes.h>
#include <winpr/winpr.h>
#include <winpr/wlog.h>
void winpr_unwind_backtrace_free(void* buffer);
WINPR_ATTR_MALLOC(winpr_unwind_backtrace_free, 1)
WINPR_ATTR_NODISCARD
void* winpr_unwind_backtrace(DWORD size);
WINPR_ATTR_MALLOC(free, 1)
WINPR_ATTR_NODISCARD
char** winpr_unwind_backtrace_symbols(void* buffer, size_t* used);
#ifdef __cplusplus
}
#endif
#endif /* WINPR_DEBUG_UNWIND_H */

View File

@@ -0,0 +1,169 @@
/**
* WinPR: Windows Portable Runtime
* Debugging Utils
*
* Copyright 2014 Armin Novak <armin.novak@thincast.com>
* Copyright 2014 Thincast Technologies GmbH
*
* 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.
*/
#include <stdio.h>
#include <fcntl.h>
#include <io.h>
#include <windows.h>
#include <dbghelp.h>
#include "debug.h"
#ifndef MIN
#define MIN(a, b) (a) < (b) ? (a) : (b)
#endif
typedef struct
{
PVOID* stack;
ULONG used;
ULONG max;
} t_win_stack;
void winpr_win_backtrace_free(void* buffer)
{
t_win_stack* data = (t_win_stack*)buffer;
if (!data)
return;
free(data->stack);
free(data);
}
void* winpr_win_backtrace(DWORD size)
{
HANDLE process = GetCurrentProcess();
t_win_stack* data = calloc(1, sizeof(t_win_stack));
if (!data)
return nullptr;
data->max = size;
data->stack = calloc(data->max, sizeof(PVOID));
if (!data->stack)
{
free(data);
return nullptr;
}
SymInitialize(process, nullptr, TRUE);
data->used = RtlCaptureStackBackTrace(2, size, data->stack, nullptr);
return data;
}
char** winpr_win_backtrace_symbols(void* buffer, size_t* used)
{
if (used)
*used = 0;
if (!buffer)
return nullptr;
{
size_t line_len = 1024;
HANDLE process = GetCurrentProcess();
t_win_stack* data = (t_win_stack*)buffer;
size_t array_size = data->used * sizeof(char*);
size_t lines_size = data->used * line_len;
char** vlines = calloc(1, array_size + lines_size);
SYMBOL_INFO* symbol = calloc(1, sizeof(SYMBOL_INFO) + line_len * sizeof(char));
IMAGEHLP_LINE64* line = (IMAGEHLP_LINE64*)calloc(1, sizeof(IMAGEHLP_LINE64));
if (!vlines || !symbol || !line)
{
free(vlines);
free(symbol);
free(line);
return nullptr;
}
line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
symbol->MaxNameLen = (ULONG)line_len;
symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
/* Set the pointers in the allocated buffer's initial array section */
for (size_t i = 0; i < data->used; i++)
vlines[i] = (char*)vlines + array_size + i * line_len;
for (size_t i = 0; i < data->used; i++)
{
DWORD64 address = (DWORD64)(data->stack[i]);
DWORD displacement;
SymFromAddr(process, address, 0, symbol);
if (SymGetLineFromAddr64(process, address, &displacement, line))
{
sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s in %s:%" PRIu32, symbol->Address,
symbol->Name, line->FileName, line->LineNumber);
}
else
sprintf_s(vlines[i], line_len, "%016" PRIx64 ": %s", symbol->Address, symbol->Name);
}
if (used)
*used = data->used;
free(symbol);
free(line);
return vlines;
}
}
char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size)
{
DWORD nSize = 0;
DWORD dwFlags = 0;
LPTSTR msg = nullptr;
BOOL alloc = FALSE;
dwFlags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER
alloc = TRUE;
dwFlags |= FORMAT_MESSAGE_ALLOCATE_BUFFER;
#else
nSize = (DWORD)(size * sizeof(WCHAR));
msg = (LPTSTR)calloc(nSize, sizeof(WCHAR));
#endif
const DWORD rc =
FormatMessage(dwFlags, nullptr, dw, 0, alloc ? (LPTSTR)&msg : msg, nSize, nullptr);
if (rc > 0)
{
#if defined(UNICODE)
(void)WideCharToMultiByte(CP_ACP, 0, msg, (int)rc, dmsg, (int)(MIN(size - 1, INT_MAX)),
nullptr, nullptr);
#else /* defined(UNICODE) */
memcpy(dmsg, msg, MIN(rc, size - 1));
#endif /* defined(UNICODE) */
dmsg[MIN(rc, size - 1)] = 0;
#ifdef FORMAT_MESSAGE_ALLOCATE_BUFFER
LocalFree(msg);
#else
free(msg);
#endif
}
else
{
_snprintf(dmsg, size, "FAILURE: 0x%08" PRIX32 "", GetLastError());
}
return dmsg;
}

View File

@@ -0,0 +1,40 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Debugging helpers
*
* Copyright 2022 Armin Novak <armin.novak@thincast.com>
* Copyright 2022 Thincast Technologies GmbH
*
* 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 WINPR_DEBUG_WIN_H
#define WINPR_DEBUG_WIN_H
#ifdef __cplusplus
extern "C"
{
#endif
#include <winpr/wtypes.h>
void* winpr_win_backtrace(DWORD size);
void winpr_win_backtrace_free(void* buffer);
char** winpr_win_backtrace_symbols(void* buffer, size_t* used);
char* winpr_win_strerror(DWORD dw, char* dmsg, size_t size);
#ifdef __cplusplus
}
#endif
#endif /* WINPR_DEBUG_WIN_H */

View File

@@ -0,0 +1,185 @@
/**
* WinPR: Windows Portable Runtime
* Debugging Utils
*
* Copyright 2015 Armin Novak <armin.novak@thincast.com>
* Copyright 2015 Thincast Technologies GmbH
*
* 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.
*/
#include <winpr/config.h>
#include <winpr/buildflags.h>
#include <stdlib.h>
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/string.h>
#include <winpr/winpr.h>
#include <winpr/version.h>
#include <winpr/wlog.h>
#include <winpr/file.h>
#include <winpr/build-config.h>
#include "../utils.h"
#if !defined(WIN32)
#include <pthread.h>
#endif
static INIT_ONCE s_winpr_app_details_once = INIT_ONCE_STATIC_INIT;
static char s_winpr_vendor_string[MAX_PATH] = WINPR_C_ARRAY_INIT;
static char s_winpr_product_string[MAX_PATH] = WINPR_C_ARRAY_INIT;
static SSIZE_T s_winpr_version = -1;
static BOOL s_winpr_app_details_are_custom = FALSE;
static BOOL CALLBACK init_app_details(WINPR_ATTR_UNUSED PINIT_ONCE once,
WINPR_ATTR_UNUSED PVOID param,
WINPR_ATTR_UNUSED PVOID* context)
{
const size_t vlen = sizeof(WINPR_VENDOR_STRING);
const size_t plen = sizeof(WINPR_PRODUCT_STRING);
if (!strncpy(s_winpr_vendor_string, WINPR_VENDOR_STRING, vlen))
return FALSE;
if (!strncpy(s_winpr_product_string, WINPR_PRODUCT_STRING, plen))
return FALSE;
#if defined(WITH_RESOURCE_VERSIONING)
s_winpr_version = WINPR_VERSION_MAJOR;
#else
s_winpr_version = -1;
#endif
return TRUE;
}
WINPR_ATTR_NODISCARD
static BOOL initializeApplicationDetails(void)
{
return InitOnceExecuteOnce(&s_winpr_app_details_once, init_app_details, nullptr, nullptr);
}
BOOL winpr_setApplicationDetails(const char* vendor, const char* product, SSIZE_T version)
{
if (!initializeApplicationDetails())
return -1;
if (!vendor || !product)
return FALSE;
const size_t vlen = strnlen(vendor, MAX_PATH);
const size_t plen = strnlen(product, MAX_PATH);
if ((vlen == MAX_PATH) || (plen == MAX_PATH))
return FALSE;
if (!strncpy(s_winpr_vendor_string, vendor, vlen + 1))
return FALSE;
if (!strncpy(s_winpr_product_string, product, plen + 1))
return FALSE;
s_winpr_version = version;
s_winpr_app_details_are_custom = TRUE;
return TRUE;
}
const char* winpr_getApplicationDetailsVendor(void)
{
if (!initializeApplicationDetails())
return nullptr;
return s_winpr_vendor_string;
}
const char* winpr_getApplicationDetailsProduct(void)
{
if (!initializeApplicationDetails())
return nullptr;
return s_winpr_product_string;
}
char* winpr_getApplicatonDetailsRegKey(const char* fmt)
{
char* val = winpr_getApplicatonDetailsCombined('\\');
if (!val)
return nullptr;
char* str = nullptr;
size_t slen = 0;
(void)winpr_asprintf(&str, &slen, fmt, val);
free(val);
return str;
}
char* winpr_getApplicatonDetailsCombined(char separator)
{
const SSIZE_T version = winpr_getApplicationDetailsVersion();
const char* vendor = winpr_getApplicationDetailsVendor();
const char* product = winpr_getApplicationDetailsProduct();
size_t slen = 0;
char* str = nullptr;
if (version < 0)
{
(void)winpr_asprintf(&str, &slen, "%s%c%s", vendor, separator, product);
}
else
{
(void)winpr_asprintf(&str, &slen, "%s%c%s%" PRIdz, vendor, separator, product, version);
}
return str;
}
SSIZE_T winpr_getApplicationDetailsVersion(void)
{
if (!initializeApplicationDetails())
return -1;
return s_winpr_version;
}
BOOL winpr_areApplicationDetailsCustomized(void)
{
return s_winpr_app_details_are_custom;
}
void winpr_get_version(int* major, int* minor, int* revision)
{
if (major)
*major = WINPR_VERSION_MAJOR;
if (minor)
*minor = WINPR_VERSION_MINOR;
if (revision)
*revision = WINPR_VERSION_REVISION;
}
const char* winpr_get_version_string(void)
{
return WINPR_VERSION_FULL;
}
const char* winpr_get_build_revision(void)
{
return WINPR_GIT_REVISION;
}
const char* winpr_get_build_config(void)
{
static const char build_config[] =
"Build configuration: " WINPR_BUILD_CONFIG "\n"
"Build type: " WINPR_BUILD_TYPE "\n"
"CFLAGS: " WINPR_CFLAGS "\n"
"Compiler: " WINPR_COMPILER_ID ", " WINPR_COMPILER_VERSION "\n"
"Target architecture: " WINPR_TARGET_ARCH "\n";
return build_config;
}

View File

@@ -0,0 +1,181 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include "Appender.h"
void WLog_Appender_Free(wLog* log, wLogAppender* appender)
{
if (!appender)
return;
if (appender->Layout)
{
WLog_Layout_Free(log, appender->Layout);
appender->Layout = nullptr;
}
DeleteCriticalSection(&appender->lock);
appender->Free(appender);
}
wLogAppender* WLog_GetLogAppender(wLog* log)
{
if (!log)
return nullptr;
if (!log->Appender)
return WLog_GetLogAppender(log->Parent);
return log->Appender;
}
BOOL WLog_OpenAppender(wLog* log)
{
int status = 0;
wLogAppender* appender = nullptr;
appender = WLog_GetLogAppender(log);
if (!appender)
return FALSE;
if (!appender->Open)
return TRUE;
if (!appender->active)
{
status = appender->Open(log, appender);
appender->active = TRUE;
}
return status;
}
BOOL WLog_CloseAppender(wLog* log)
{
int status = 0;
wLogAppender* appender = nullptr;
appender = WLog_GetLogAppender(log);
if (!appender)
return FALSE;
if (!appender->Close)
return TRUE;
if (appender->active)
{
status = appender->Close(log, appender);
appender->active = FALSE;
}
return status;
}
static wLogAppender* WLog_Appender_New(wLog* log, DWORD logAppenderType)
{
wLogAppender* appender = nullptr;
if (!log)
return nullptr;
switch (logAppenderType)
{
case WLOG_APPENDER_CONSOLE:
appender = WLog_ConsoleAppender_New(log);
break;
case WLOG_APPENDER_FILE:
appender = WLog_FileAppender_New(log);
break;
case WLOG_APPENDER_BINARY:
appender = WLog_BinaryAppender_New(log);
break;
case WLOG_APPENDER_CALLBACK:
appender = WLog_CallbackAppender_New(log);
break;
#ifdef WINPR_HAVE_SYSLOG_H
case WLOG_APPENDER_SYSLOG:
appender = WLog_SyslogAppender_New(log);
break;
#endif
#ifdef WINPR_HAVE_JOURNALD_H
case WLOG_APPENDER_JOURNALD:
appender = WLog_JournaldAppender_New(log);
break;
#endif
case WLOG_APPENDER_UDP:
appender = WLog_UdpAppender_New(log);
break;
default:
(void)fprintf(stderr, "%s: unknown handler type %" PRIu32 "\n", __func__,
logAppenderType);
appender = nullptr;
break;
}
if (!appender)
appender = WLog_ConsoleAppender_New(log);
if (!appender)
return nullptr;
if (!(appender->Layout = WLog_Layout_New(log)))
{
WLog_Appender_Free(log, appender);
return nullptr;
}
if (!InitializeCriticalSectionAndSpinCount(&appender->lock, 4000))
{
WLog_Appender_Free(log, appender);
return nullptr;
}
return appender;
}
BOOL WLog_SetLogAppenderType(wLog* log, DWORD logAppenderType)
{
if (!log)
return FALSE;
if (log->Appender)
{
WLog_Appender_Free(log, log->Appender);
log->Appender = nullptr;
}
log->Appender = WLog_Appender_New(log, logAppenderType);
return log->Appender != nullptr;
}
BOOL WLog_ConfigureAppender(wLogAppender* appender, const char* setting, void* value)
{
/* Just check the settings string is not empty */
if (!appender || !setting || (strnlen(setting, 2) == 0))
return FALSE;
if (appender->Set)
return appender->Set(appender, setting, value);
else
return FALSE;
}

View File

@@ -0,0 +1,39 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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 WINPR_WLOG_APPENDER_PRIVATE_H
#define WINPR_WLOG_APPENDER_PRIVATE_H
#include "wlog.h"
WINPR_LOCAL void WLog_Appender_Free(wLog* log, wLogAppender* appender);
#include "FileAppender.h"
#include "ConsoleAppender.h"
#include "BinaryAppender.h"
#include "CallbackAppender.h"
#ifdef WINPR_HAVE_JOURNALD_H
#include "JournaldAppender.h"
#endif
#ifdef WINPR_HAVE_SYSLOG_H
#include "SyslogAppender.h"
#endif
#include "UdpAppender.h"
#endif /* WINPR_WLOG_APPENDER_PRIVATE_H */

View File

@@ -0,0 +1,236 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2015 Thincast Technologies GmbH
* Copyright 2015 DI (FH) Martin Haimberger <martin.haimberger@thincast.com>
*
* 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.
*/
#include <winpr/config.h>
#include "BinaryAppender.h"
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/file.h>
#include <winpr/path.h>
#include <winpr/stream.h>
typedef struct
{
wLogAppender common;
char* FileName;
char* FilePath;
char* FullFileName;
FILE* FileDescriptor;
} wLogBinaryAppender;
static BOOL WLog_BinaryAppender_Open(wLog* log, wLogAppender* appender)
{
wLogBinaryAppender* binaryAppender = nullptr;
if (!log || !appender)
return FALSE;
binaryAppender = (wLogBinaryAppender*)appender;
if (!binaryAppender->FileName)
{
binaryAppender->FileName = (char*)malloc(MAX_PATH);
if (!binaryAppender->FileName)
return FALSE;
(void)sprintf_s(binaryAppender->FileName, MAX_PATH, "%" PRIu32 ".wlog",
GetCurrentProcessId());
}
if (!binaryAppender->FilePath)
{
binaryAppender->FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog");
if (!binaryAppender->FilePath)
return FALSE;
}
if (!binaryAppender->FullFileName)
{
binaryAppender->FullFileName =
GetCombinedPath(binaryAppender->FilePath, binaryAppender->FileName);
if (!binaryAppender->FullFileName)
return FALSE;
}
if (!winpr_PathFileExists(binaryAppender->FilePath))
{
if (!winpr_PathMakePath(binaryAppender->FilePath, nullptr))
return FALSE;
UnixChangeFileMode(binaryAppender->FilePath, 0xFFFF);
}
binaryAppender->FileDescriptor = winpr_fopen(binaryAppender->FullFileName, "a+");
return binaryAppender->FileDescriptor != nullptr;
}
static BOOL WLog_BinaryAppender_Close(WINPR_ATTR_UNUSED wLog* log, wLogAppender* appender)
{
wLogBinaryAppender* binaryAppender = nullptr;
if (!appender)
return FALSE;
binaryAppender = (wLogBinaryAppender*)appender;
if (!binaryAppender->FileDescriptor)
return TRUE;
if (binaryAppender->FileDescriptor)
(void)fclose(binaryAppender->FileDescriptor);
binaryAppender->FileDescriptor = nullptr;
return TRUE;
}
static BOOL WLog_BinaryAppender_WriteMessage(wLog* log, wLogAppender* appender,
const wLogMessage* message)
{
FILE* fp = nullptr;
wStream* s = nullptr;
size_t MessageLength = 0;
size_t FileNameLength = 0;
size_t FunctionNameLength = 0;
size_t TextStringLength = 0;
BOOL ret = TRUE;
wLogBinaryAppender* binaryAppender = nullptr;
if (!log || !appender || !message)
return FALSE;
binaryAppender = (wLogBinaryAppender*)appender;
fp = binaryAppender->FileDescriptor;
if (!fp)
return FALSE;
FileNameLength = strnlen(message->FileName, INT_MAX);
FunctionNameLength = strnlen(message->FunctionName, INT_MAX);
TextStringLength = strnlen(message->TextString, INT_MAX);
MessageLength =
16 + (4 + FileNameLength + 1) + (4 + FunctionNameLength + 1) + (4 + TextStringLength + 1);
if ((MessageLength > UINT32_MAX) || (FileNameLength > UINT32_MAX) ||
(FunctionNameLength > UINT32_MAX) || (TextStringLength > UINT32_MAX))
return FALSE;
s = Stream_New(nullptr, MessageLength);
if (!s)
return FALSE;
Stream_Write_UINT32(s, (UINT32)MessageLength);
Stream_Write_UINT32(s, message->Type);
Stream_Write_UINT32(s, message->Level);
WINPR_ASSERT(message->LineNumber <= UINT32_MAX);
Stream_Write_UINT32(s, (UINT32)message->LineNumber);
Stream_Write_UINT32(s, (UINT32)FileNameLength);
Stream_Write(s, message->FileName, FileNameLength + 1);
Stream_Write_UINT32(s, (UINT32)FunctionNameLength);
Stream_Write(s, message->FunctionName, FunctionNameLength + 1);
Stream_Write_UINT32(s, (UINT32)TextStringLength);
Stream_Write(s, message->TextString, TextStringLength + 1);
Stream_SealLength(s);
if (fwrite(Stream_Buffer(s), MessageLength, 1, fp) != 1)
ret = FALSE;
Stream_Free(s, TRUE);
return ret;
}
static BOOL WLog_BinaryAppender_WriteDataMessage(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender,
WINPR_ATTR_UNUSED const wLogMessage* message)
{
return TRUE;
}
static BOOL WLog_BinaryAppender_WriteImageMessage(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender,
WINPR_ATTR_UNUSED const wLogMessage* message)
{
return TRUE;
}
static BOOL WLog_BinaryAppender_Set(wLogAppender* appender, const char* setting, void* value)
{
wLogBinaryAppender* binaryAppender = (wLogBinaryAppender*)appender;
/* Just check if the value string is longer than 0 */
if (!value || (strnlen(value, 2) == 0))
return FALSE;
if (!strcmp("outputfilename", setting))
{
binaryAppender->FileName = _strdup((const char*)value);
if (!binaryAppender->FileName)
return FALSE;
}
else if (!strcmp("outputfilepath", setting))
{
binaryAppender->FilePath = _strdup((const char*)value);
if (!binaryAppender->FilePath)
return FALSE;
}
else
return FALSE;
return TRUE;
}
static void WLog_BinaryAppender_Free(wLogAppender* appender)
{
wLogBinaryAppender* binaryAppender = nullptr;
if (appender)
{
binaryAppender = (wLogBinaryAppender*)appender;
free(binaryAppender->FileName);
free(binaryAppender->FilePath);
free(binaryAppender->FullFileName);
free(binaryAppender);
}
}
wLogAppender* WLog_BinaryAppender_New(WINPR_ATTR_UNUSED wLog* log)
{
wLogBinaryAppender* BinaryAppender = (wLogBinaryAppender*)calloc(1, sizeof(wLogBinaryAppender));
if (!BinaryAppender)
return nullptr;
BinaryAppender->common.Type = WLOG_APPENDER_BINARY;
BinaryAppender->common.Open = WLog_BinaryAppender_Open;
BinaryAppender->common.Close = WLog_BinaryAppender_Close;
BinaryAppender->common.WriteMessage = WLog_BinaryAppender_WriteMessage;
BinaryAppender->common.WriteDataMessage = WLog_BinaryAppender_WriteDataMessage;
BinaryAppender->common.WriteImageMessage = WLog_BinaryAppender_WriteImageMessage;
BinaryAppender->common.Free = WLog_BinaryAppender_Free;
BinaryAppender->common.Set = WLog_BinaryAppender_Set;
return &BinaryAppender->common;
}

View File

@@ -0,0 +1,29 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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 WINPR_WLOG_BINARY_APPENDER_PRIVATE_H
#define WINPR_WLOG_BINARY_APPENDER_PRIVATE_H
#include "wlog.h"
WINPR_ATTR_MALLOC(WLog_Appender_Free, 2)
WINPR_ATTR_NODISCARD
WINPR_LOCAL wLogAppender* WLog_BinaryAppender_New(wLog* log);
#endif /* WINPR_WLOG_BINARY_APPENDER_PRIVATE_H */

View File

@@ -0,0 +1,175 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2014 Armin Novak <armin.novak@thincast.com>
*
* 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.
*/
#include <winpr/config.h>
#include "CallbackAppender.h"
typedef struct
{
wLogAppender common;
wLogCallbacks* callbacks;
} wLogCallbackAppender;
static BOOL WLog_CallbackAppender_Open(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender)
{
return TRUE;
}
static BOOL WLog_CallbackAppender_Close(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender)
{
return TRUE;
}
static BOOL WLog_CallbackAppender_WriteMessage(wLog* log, wLogAppender* appender,
const wLogMessage* cmessage)
{
WINPR_ASSERT(cmessage);
if (!appender)
return FALSE;
char prefix[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
WLog_Layout_GetMessagePrefix(log, appender->Layout, cmessage, prefix, sizeof(prefix));
wLogCallbackAppender* callbackAppender = (wLogCallbackAppender*)appender;
if (callbackAppender->callbacks && callbackAppender->callbacks->message)
{
wLogMessage message = *cmessage;
message.PrefixString = prefix;
return callbackAppender->callbacks->message(&message);
}
else
return FALSE;
}
static BOOL WLog_CallbackAppender_WriteDataMessage(wLog* log, wLogAppender* appender,
const wLogMessage* cmessage)
{
if (!appender)
return FALSE;
char prefix[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
WLog_Layout_GetMessagePrefix(log, appender->Layout, cmessage, prefix, sizeof(prefix));
wLogCallbackAppender* callbackAppender = (wLogCallbackAppender*)appender;
if (callbackAppender->callbacks && callbackAppender->callbacks->data)
{
wLogMessage message = *cmessage;
message.PrefixString = prefix;
return callbackAppender->callbacks->data(&message);
}
else
return FALSE;
}
static BOOL WLog_CallbackAppender_WriteImageMessage(wLog* log, wLogAppender* appender,
const wLogMessage* cmessage)
{
WINPR_ASSERT(cmessage);
if (!appender)
return FALSE;
char prefix[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
WLog_Layout_GetMessagePrefix(log, appender->Layout, cmessage, prefix, sizeof(prefix));
wLogCallbackAppender* callbackAppender = (wLogCallbackAppender*)appender;
if (callbackAppender->callbacks && callbackAppender->callbacks->image)
{
wLogMessage message = *cmessage;
message.PrefixString = prefix;
return callbackAppender->callbacks->image(&message);
}
else
return FALSE;
}
static BOOL WLog_CallbackAppender_WritePacketMessage(wLog* log, wLogAppender* appender,
const wLogMessage* cmessage)
{
WINPR_ASSERT(cmessage);
if (!appender)
return FALSE;
char prefix[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
WLog_Layout_GetMessagePrefix(log, appender->Layout, cmessage, prefix, sizeof(prefix));
wLogCallbackAppender* callbackAppender = (wLogCallbackAppender*)appender;
if (callbackAppender->callbacks && callbackAppender->callbacks->package)
{
wLogMessage message = *cmessage;
message.PrefixString = prefix;
return callbackAppender->callbacks->package(&message);
}
else
return FALSE;
}
static BOOL WLog_CallbackAppender_Set(wLogAppender* appender, const char* setting, void* value)
{
wLogCallbackAppender* callbackAppender = (wLogCallbackAppender*)appender;
if (!value || (strcmp(setting, "callbacks") != 0))
return FALSE;
if (!(callbackAppender->callbacks = calloc(1, sizeof(wLogCallbacks))))
{
return FALSE;
}
callbackAppender->callbacks = memcpy(callbackAppender->callbacks, value, sizeof(wLogCallbacks));
return TRUE;
}
static void WLog_CallbackAppender_Free(wLogAppender* appender)
{
wLogCallbackAppender* callbackAppender = nullptr;
if (!appender)
{
return;
}
callbackAppender = (wLogCallbackAppender*)appender;
free(callbackAppender->callbacks);
free(appender);
}
wLogAppender* WLog_CallbackAppender_New(WINPR_ATTR_UNUSED wLog* log)
{
wLogCallbackAppender* CallbackAppender =
(wLogCallbackAppender*)calloc(1, sizeof(wLogCallbackAppender));
if (!CallbackAppender)
return nullptr;
CallbackAppender->common.Type = WLOG_APPENDER_CALLBACK;
CallbackAppender->common.Open = WLog_CallbackAppender_Open;
CallbackAppender->common.Close = WLog_CallbackAppender_Close;
CallbackAppender->common.WriteMessage = WLog_CallbackAppender_WriteMessage;
CallbackAppender->common.WriteDataMessage = WLog_CallbackAppender_WriteDataMessage;
CallbackAppender->common.WriteImageMessage = WLog_CallbackAppender_WriteImageMessage;
CallbackAppender->common.WritePacketMessage = WLog_CallbackAppender_WritePacketMessage;
CallbackAppender->common.Free = WLog_CallbackAppender_Free;
CallbackAppender->common.Set = WLog_CallbackAppender_Set;
return &CallbackAppender->common;
}

View File

@@ -0,0 +1,29 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2014 Armin Novak <armin.novak@thincast.com>
*
* 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 WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H
#define WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H
#include "wlog.h"
WINPR_ATTR_MALLOC(WLog_Appender_Free, 2)
WINPR_ATTR_NODISCARD
WINPR_LOCAL wLogAppender* WLog_CallbackAppender_New(wLog* log);
#endif /* WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H */

View File

@@ -0,0 +1,276 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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.
*/
#include <winpr/config.h>
#include "ConsoleAppender.h"
#include "Message.h"
#ifdef ANDROID
#include <android/log.h>
#endif
#define WLOG_CONSOLE_DEFAULT 0
#define WLOG_CONSOLE_STDOUT 1
#define WLOG_CONSOLE_STDERR 2
#define WLOG_CONSOLE_DEBUG 4
typedef struct
{
wLogAppender common;
int outputStream;
} wLogConsoleAppender;
static BOOL WLog_ConsoleAppender_Open(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender)
{
return TRUE;
}
static BOOL WLog_ConsoleAppender_Close(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender)
{
return TRUE;
}
static BOOL WLog_ConsoleAppender_WriteMessage(wLog* log, wLogAppender* appender,
const wLogMessage* cmessage)
{
if (!appender)
return FALSE;
wLogConsoleAppender* consoleAppender = (wLogConsoleAppender*)appender;
char prefix[WLOG_MAX_PREFIX_SIZE] = WINPR_C_ARRAY_INIT;
WLog_Layout_GetMessagePrefix(log, appender->Layout, cmessage, prefix, sizeof(prefix));
#ifdef _WIN32
if (consoleAppender->outputStream == WLOG_CONSOLE_DEBUG)
{
OutputDebugStringA(prefix);
OutputDebugStringA(cmessage->TextString);
OutputDebugStringA("\n");
return TRUE;
}
#endif
#ifdef ANDROID
android_LogPriority level;
switch (cmessage->Level)
{
case WLOG_TRACE:
level = ANDROID_LOG_VERBOSE;
break;
case WLOG_DEBUG:
level = ANDROID_LOG_DEBUG;
break;
case WLOG_INFO:
level = ANDROID_LOG_INFO;
break;
case WLOG_WARN:
level = ANDROID_LOG_WARN;
break;
case WLOG_ERROR:
level = ANDROID_LOG_ERROR;
break;
case WLOG_FATAL:
level = ANDROID_LOG_FATAL;
break;
case WLOG_OFF:
level = ANDROID_LOG_SILENT;
break;
default:
level = ANDROID_LOG_FATAL;
break;
}
if (level != ANDROID_LOG_SILENT)
__android_log_print(level, log->Name, "%s%s", prefix, cmessage->TextString);
#else
FILE* fp = nullptr;
switch (consoleAppender->outputStream)
{
case WLOG_CONSOLE_STDOUT:
fp = stdout;
break;
case WLOG_CONSOLE_STDERR:
fp = stderr;
break;
default:
switch (cmessage->Level)
{
case WLOG_TRACE:
case WLOG_DEBUG:
case WLOG_INFO:
fp = stdout;
break;
default:
fp = stderr;
break;
}
break;
}
if (cmessage->Level != WLOG_OFF)
(void)fprintf(fp, "%s%s\n", prefix, cmessage->TextString);
#endif
return TRUE;
}
static int g_DataId = 0;
static BOOL WLog_ConsoleAppender_WriteDataMessage(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender,
const wLogMessage* message)
{
#if defined(ANDROID)
return FALSE;
#else
int DataId = 0;
char* FullFileName = nullptr;
DataId = g_DataId++;
FullFileName = WLog_Message_GetOutputFileName(DataId, "dat");
WLog_DataMessage_Write(FullFileName, message->Data, message->Length);
free(FullFileName);
return TRUE;
#endif
}
static int g_ImageId = 0;
static BOOL WLog_ConsoleAppender_WriteImageMessage(WINPR_ATTR_UNUSED wLog* log,
WINPR_ATTR_UNUSED wLogAppender* appender,
const wLogMessage* message)
{
#if defined(ANDROID)
return FALSE;
#else
int ImageId = 0;
char* FullFileName = nullptr;
ImageId = g_ImageId++;
FullFileName = WLog_Message_GetOutputFileName(ImageId, "bmp");
WLog_ImageMessage_Write(FullFileName, message->ImageData, message->ImageWidth,
message->ImageHeight, message->ImageBpp);
free(FullFileName);
return TRUE;
#endif
}
static int g_PacketId = 0;
static BOOL WLog_ConsoleAppender_WritePacketMessage(WINPR_ATTR_UNUSED wLog* log,
wLogAppender* appender,
const wLogMessage* message)
{
#if defined(ANDROID)
return FALSE;
#else
char* FullFileName = nullptr;
g_PacketId++;
if (!appender->PacketMessageContext)
{
FullFileName = WLog_Message_GetOutputFileName(-1, "pcap");
appender->PacketMessageContext = (void*)Pcap_Open(FullFileName, TRUE);
free(FullFileName);
}
if (appender->PacketMessageContext)
return WLog_PacketMessage_Write((wPcap*)appender->PacketMessageContext, message->PacketData,
message->PacketLength, message->PacketFlags);
return TRUE;
#endif
}
static BOOL WLog_ConsoleAppender_Set(wLogAppender* appender, const char* setting, void* value)
{
wLogConsoleAppender* consoleAppender = (wLogConsoleAppender*)appender;
/* Just check the value string is not empty */
if (!value || (strnlen(value, 2) == 0))
return FALSE;
if (strcmp("outputstream", setting) != 0)
return FALSE;
if (!strcmp("stdout", value))
consoleAppender->outputStream = WLOG_CONSOLE_STDOUT;
else if (!strcmp("stderr", value))
consoleAppender->outputStream = WLOG_CONSOLE_STDERR;
else if (!strcmp("default", value))
consoleAppender->outputStream = WLOG_CONSOLE_DEFAULT;
else if (!strcmp("debug", value))
consoleAppender->outputStream = WLOG_CONSOLE_DEBUG;
else
return FALSE;
return TRUE;
}
static void WLog_ConsoleAppender_Free(wLogAppender* appender)
{
if (appender)
{
if (appender->PacketMessageContext)
{
Pcap_Close((wPcap*)appender->PacketMessageContext);
}
free(appender);
}
}
wLogAppender* WLog_ConsoleAppender_New(WINPR_ATTR_UNUSED wLog* log)
{
wLogConsoleAppender* ConsoleAppender =
(wLogConsoleAppender*)calloc(1, sizeof(wLogConsoleAppender));
if (!ConsoleAppender)
return nullptr;
ConsoleAppender->common.Type = WLOG_APPENDER_CONSOLE;
ConsoleAppender->common.Open = WLog_ConsoleAppender_Open;
ConsoleAppender->common.Close = WLog_ConsoleAppender_Close;
ConsoleAppender->common.WriteMessage = WLog_ConsoleAppender_WriteMessage;
ConsoleAppender->common.WriteDataMessage = WLog_ConsoleAppender_WriteDataMessage;
ConsoleAppender->common.WriteImageMessage = WLog_ConsoleAppender_WriteImageMessage;
ConsoleAppender->common.WritePacketMessage = WLog_ConsoleAppender_WritePacketMessage;
ConsoleAppender->common.Set = WLog_ConsoleAppender_Set;
ConsoleAppender->common.Free = WLog_ConsoleAppender_Free;
ConsoleAppender->outputStream = WLOG_CONSOLE_DEFAULT;
#ifdef _WIN32
if (IsDebuggerPresent())
ConsoleAppender->outputStream = WLOG_CONSOLE_DEBUG;
#endif
return &ConsoleAppender->common;
}

View File

@@ -0,0 +1,29 @@
/**
* WinPR: Windows Portable Runtime
* WinPR Logger
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
*
* 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 WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H
#define WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H
#include "wlog.h"
WINPR_ATTR_MALLOC(WLog_Appender_Free, 2)
WINPR_ATTR_NODISCARD
WINPR_LOCAL wLogAppender* WLog_ConsoleAppender_New(wLog* log);
#endif /* WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H */

Some files were not shown because too many files have changed in this diff Show More