summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/utils/wlog
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /winpr/libwinpr/utils/wlog
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'winpr/libwinpr/utils/wlog')
-rw-r--r--winpr/libwinpr/utils/wlog/Appender.c176
-rw-r--r--winpr/libwinpr/utils/wlog/Appender.h39
-rw-r--r--winpr/libwinpr/utils/wlog/BinaryAppender.c238
-rw-r--r--winpr/libwinpr/utils/wlog/BinaryAppender.h28
-rw-r--r--winpr/libwinpr/utils/wlog/CallbackAppender.c168
-rw-r--r--winpr/libwinpr/utils/wlog/CallbackAppender.h28
-rw-r--r--winpr/libwinpr/utils/wlog/ConsoleAppender.c276
-rw-r--r--winpr/libwinpr/utils/wlog/ConsoleAppender.h28
-rw-r--r--winpr/libwinpr/utils/wlog/DataMessage.c48
-rw-r--r--winpr/libwinpr/utils/wlog/DataMessage.h25
-rw-r--r--winpr/libwinpr/utils/wlog/FileAppender.c287
-rw-r--r--winpr/libwinpr/utils/wlog/FileAppender.h28
-rw-r--r--winpr/libwinpr/utils/wlog/ImageMessage.c37
-rw-r--r--winpr/libwinpr/utils/wlog/ImageMessage.h25
-rw-r--r--winpr/libwinpr/utils/wlog/JournaldAppender.c210
-rw-r--r--winpr/libwinpr/utils/wlog/JournaldAppender.h31
-rw-r--r--winpr/libwinpr/utils/wlog/Layout.c375
-rw-r--r--winpr/libwinpr/utils/wlog/Layout.h41
-rw-r--r--winpr/libwinpr/utils/wlog/Message.c64
-rw-r--r--winpr/libwinpr/utils/wlog/Message.h29
-rw-r--r--winpr/libwinpr/utils/wlog/PacketMessage.c487
-rw-r--r--winpr/libwinpr/utils/wlog/PacketMessage.h111
-rw-r--r--winpr/libwinpr/utils/wlog/SyslogAppender.c137
-rw-r--r--winpr/libwinpr/utils/wlog/SyslogAppender.h32
-rw-r--r--winpr/libwinpr/utils/wlog/UdpAppender.c222
-rw-r--r--winpr/libwinpr/utils/wlog/UdpAppender.h34
-rw-r--r--winpr/libwinpr/utils/wlog/wlog.c1071
-rw-r--r--winpr/libwinpr/utils/wlog/wlog.h92
28 files changed, 4367 insertions, 0 deletions
diff --git a/winpr/libwinpr/utils/wlog/Appender.c b/winpr/libwinpr/utils/wlog/Appender.c
new file mode 100644
index 0000000..a1cbbd6
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/Appender.c
@@ -0,0 +1,176 @@
+/**
+ * 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 = NULL;
+ }
+
+ DeleteCriticalSection(&appender->lock);
+ appender->Free(appender);
+}
+
+wLogAppender* WLog_GetLogAppender(wLog* log)
+{
+ if (!log)
+ return NULL;
+
+ if (!log->Appender)
+ return WLog_GetLogAppender(log->Parent);
+
+ return log->Appender;
+}
+
+BOOL WLog_OpenAppender(wLog* log)
+{
+ int status = 0;
+ wLogAppender* appender = NULL;
+
+ 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 = NULL;
+
+ 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 = NULL;
+
+ if (!log)
+ return NULL;
+
+ 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 = (wLogAppender*)WLog_UdpAppender_New(log);
+ break;
+ default:
+ fprintf(stderr, "%s: unknown handler type %" PRIu32 "\n", __func__, logAppenderType);
+ appender = NULL;
+ break;
+ }
+
+ if (!appender)
+ appender = (wLogAppender*)WLog_ConsoleAppender_New(log);
+
+ if (!appender)
+ return NULL;
+
+ if (!(appender->Layout = WLog_Layout_New(log)))
+ {
+ WLog_Appender_Free(log, appender);
+ return NULL;
+ }
+
+ InitializeCriticalSectionAndSpinCount(&appender->lock, 4000);
+
+ return appender;
+}
+
+BOOL WLog_SetLogAppenderType(wLog* log, DWORD logAppenderType)
+{
+ if (!log)
+ return FALSE;
+
+ if (log->Appender)
+ {
+ WLog_Appender_Free(log, log->Appender);
+ log->Appender = NULL;
+ }
+
+ log->Appender = WLog_Appender_New(log, logAppenderType);
+ return log->Appender != NULL;
+}
+
+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;
+}
diff --git a/winpr/libwinpr/utils/wlog/Appender.h b/winpr/libwinpr/utils/wlog/Appender.h
new file mode 100644
index 0000000..00f8119
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/Appender.h
@@ -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"
+
+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 */
diff --git a/winpr/libwinpr/utils/wlog/BinaryAppender.c b/winpr/libwinpr/utils/wlog/BinaryAppender.c
new file mode 100644
index 0000000..e9a440a
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/BinaryAppender.c
@@ -0,0 +1,238 @@
+/**
+ * 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
+{
+ WLOG_APPENDER_COMMON();
+
+ char* FileName;
+ char* FilePath;
+ char* FullFileName;
+ FILE* FileDescriptor;
+} wLogBinaryAppender;
+
+static BOOL WLog_BinaryAppender_Open(wLog* log, wLogAppender* appender)
+{
+ wLogBinaryAppender* binaryAppender = NULL;
+ if (!log || !appender)
+ return FALSE;
+
+ binaryAppender = (wLogBinaryAppender*)appender;
+ if (!binaryAppender->FileName)
+ {
+ binaryAppender->FileName = (char*)malloc(MAX_PATH);
+ if (!binaryAppender->FileName)
+ return FALSE;
+ 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, 0))
+ return FALSE;
+ UnixChangeFileMode(binaryAppender->FilePath, 0xFFFF);
+ }
+
+ binaryAppender->FileDescriptor = winpr_fopen(binaryAppender->FullFileName, "a+");
+
+ if (!binaryAppender->FileDescriptor)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_BinaryAppender_Close(wLog* log, wLogAppender* appender)
+{
+ wLogBinaryAppender* binaryAppender = NULL;
+
+ if (!appender)
+ return FALSE;
+
+ binaryAppender = (wLogBinaryAppender*)appender;
+ if (!binaryAppender->FileDescriptor)
+ return TRUE;
+
+ if (binaryAppender->FileDescriptor)
+ fclose(binaryAppender->FileDescriptor);
+
+ binaryAppender->FileDescriptor = NULL;
+
+ return TRUE;
+}
+
+static BOOL WLog_BinaryAppender_WriteMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ FILE* fp = NULL;
+ wStream* s = NULL;
+ size_t MessageLength = 0;
+ size_t FileNameLength = 0;
+ size_t FunctionNameLength = 0;
+ size_t TextStringLength = 0;
+ BOOL ret = TRUE;
+ wLogBinaryAppender* binaryAppender = NULL;
+
+ 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(NULL, 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(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ return TRUE;
+}
+
+static BOOL WLog_BinaryAppender_WriteImageMessage(wLog* log, wLogAppender* appender,
+ 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 = NULL;
+ if (appender)
+ {
+ binaryAppender = (wLogBinaryAppender*)appender;
+ free(binaryAppender->FileName);
+ free(binaryAppender->FilePath);
+ free(binaryAppender->FullFileName);
+ free(binaryAppender);
+ }
+}
+
+wLogAppender* WLog_BinaryAppender_New(wLog* log)
+{
+ wLogBinaryAppender* BinaryAppender = NULL;
+
+ BinaryAppender = (wLogBinaryAppender*)calloc(1, sizeof(wLogBinaryAppender));
+ if (!BinaryAppender)
+ return NULL;
+
+ BinaryAppender->Type = WLOG_APPENDER_BINARY;
+ BinaryAppender->Open = WLog_BinaryAppender_Open;
+ BinaryAppender->Close = WLog_BinaryAppender_Close;
+ BinaryAppender->WriteMessage = WLog_BinaryAppender_WriteMessage;
+ BinaryAppender->WriteDataMessage = WLog_BinaryAppender_WriteDataMessage;
+ BinaryAppender->WriteImageMessage = WLog_BinaryAppender_WriteImageMessage;
+ BinaryAppender->Free = WLog_BinaryAppender_Free;
+ BinaryAppender->Set = WLog_BinaryAppender_Set;
+
+ return (wLogAppender*)BinaryAppender;
+}
diff --git a/winpr/libwinpr/utils/wlog/BinaryAppender.h b/winpr/libwinpr/utils/wlog/BinaryAppender.h
new file mode 100644
index 0000000..fb65d9f
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/BinaryAppender.h
@@ -0,0 +1,28 @@
+/**
+ * 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)
+wLogAppender* WLog_BinaryAppender_New(wLog* log);
+
+#endif /* WINPR_WLOG_BINARY_APPENDER_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/CallbackAppender.c b/winpr/libwinpr/utils/wlog/CallbackAppender.c
new file mode 100644
index 0000000..f324e7c
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/CallbackAppender.c
@@ -0,0 +1,168 @@
+/**
+ * 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
+{
+ WLOG_APPENDER_COMMON();
+
+ wLogCallbacks* callbacks;
+} wLogCallbackAppender;
+
+static BOOL WLog_CallbackAppender_Open(wLog* log, wLogAppender* appender)
+{
+ return TRUE;
+}
+
+static BOOL WLog_CallbackAppender_Close(wLog* log, wLogAppender* appender)
+{
+ return TRUE;
+}
+
+static BOOL WLog_CallbackAppender_WriteMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ wLogCallbackAppender* callbackAppender = NULL;
+
+ if (!appender)
+ return FALSE;
+
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+
+ callbackAppender = (wLogCallbackAppender*)appender;
+
+ if (callbackAppender->callbacks && callbackAppender->callbacks->message)
+ return callbackAppender->callbacks->message(message);
+ else
+ return FALSE;
+}
+
+static BOOL WLog_CallbackAppender_WriteDataMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ wLogCallbackAppender* callbackAppender = NULL;
+
+ if (!appender)
+ return FALSE;
+
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+
+ callbackAppender = (wLogCallbackAppender*)appender;
+ if (callbackAppender->callbacks && callbackAppender->callbacks->data)
+ return callbackAppender->callbacks->data(message);
+ else
+ return FALSE;
+}
+
+static BOOL WLog_CallbackAppender_WriteImageMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ wLogCallbackAppender* callbackAppender = NULL;
+
+ if (!appender)
+ return FALSE;
+
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+
+ callbackAppender = (wLogCallbackAppender*)appender;
+ if (callbackAppender->callbacks && callbackAppender->callbacks->image)
+ return callbackAppender->callbacks->image(message);
+ else
+ return FALSE;
+}
+
+static BOOL WLog_CallbackAppender_WritePacketMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ wLogCallbackAppender* callbackAppender = NULL;
+
+ if (!appender)
+ return FALSE;
+
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+
+ callbackAppender = (wLogCallbackAppender*)appender;
+ if (callbackAppender->callbacks && callbackAppender->callbacks->package)
+ 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"))
+ 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 = NULL;
+ if (!appender)
+ {
+ return;
+ }
+
+ callbackAppender = (wLogCallbackAppender*)appender;
+
+ free(callbackAppender->callbacks);
+ free(appender);
+}
+
+wLogAppender* WLog_CallbackAppender_New(wLog* log)
+{
+ wLogCallbackAppender* CallbackAppender = NULL;
+
+ CallbackAppender = (wLogCallbackAppender*)calloc(1, sizeof(wLogCallbackAppender));
+ if (!CallbackAppender)
+ return NULL;
+
+ CallbackAppender->Type = WLOG_APPENDER_CALLBACK;
+
+ CallbackAppender->Open = WLog_CallbackAppender_Open;
+ CallbackAppender->Close = WLog_CallbackAppender_Close;
+ CallbackAppender->WriteMessage = WLog_CallbackAppender_WriteMessage;
+ CallbackAppender->WriteDataMessage = WLog_CallbackAppender_WriteDataMessage;
+ CallbackAppender->WriteImageMessage = WLog_CallbackAppender_WriteImageMessage;
+ CallbackAppender->WritePacketMessage = WLog_CallbackAppender_WritePacketMessage;
+ CallbackAppender->Free = WLog_CallbackAppender_Free;
+ CallbackAppender->Set = WLog_CallbackAppender_Set;
+
+ return (wLogAppender*)CallbackAppender;
+}
diff --git a/winpr/libwinpr/utils/wlog/CallbackAppender.h b/winpr/libwinpr/utils/wlog/CallbackAppender.h
new file mode 100644
index 0000000..cd10f7d
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/CallbackAppender.h
@@ -0,0 +1,28 @@
+/**
+ * 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)
+wLogAppender* WLog_CallbackAppender_New(wLog* log);
+
+#endif /* WINPR_WLOG_CALLBACK_APPENDER_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.c b/winpr/libwinpr/utils/wlog/ConsoleAppender.c
new file mode 100644
index 0000000..0a50ef6
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.c
@@ -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
+{
+ WLOG_APPENDER_COMMON();
+
+ int outputStream;
+} wLogConsoleAppender;
+
+static BOOL WLog_ConsoleAppender_Open(wLog* log, wLogAppender* appender)
+{
+ return TRUE;
+}
+
+static BOOL WLog_ConsoleAppender_Close(wLog* log, wLogAppender* appender)
+{
+ return TRUE;
+}
+
+static BOOL WLog_ConsoleAppender_WriteMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ FILE* fp = NULL;
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ wLogConsoleAppender* consoleAppender = NULL;
+ if (!appender)
+ return FALSE;
+
+ consoleAppender = (wLogConsoleAppender*)appender;
+
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+
+#ifdef _WIN32
+ if (consoleAppender->outputStream == WLOG_CONSOLE_DEBUG)
+ {
+ OutputDebugStringA(message->PrefixString);
+ OutputDebugStringA(message->TextString);
+ OutputDebugStringA("\n");
+
+ return TRUE;
+ }
+#endif
+#ifdef ANDROID
+ (void)fp;
+ android_LogPriority level;
+ switch (message->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", message->PrefixString, message->TextString);
+
+#else
+ switch (consoleAppender->outputStream)
+ {
+ case WLOG_CONSOLE_STDOUT:
+ fp = stdout;
+ break;
+ case WLOG_CONSOLE_STDERR:
+ fp = stderr;
+ break;
+ default:
+ switch (message->Level)
+ {
+ case WLOG_TRACE:
+ case WLOG_DEBUG:
+ case WLOG_INFO:
+ fp = stdout;
+ break;
+ default:
+ fp = stderr;
+ break;
+ }
+ break;
+ }
+
+ if (message->Level != WLOG_OFF)
+ fprintf(fp, "%s%s\n", message->PrefixString, message->TextString);
+#endif
+ return TRUE;
+}
+
+static int g_DataId = 0;
+
+static BOOL WLog_ConsoleAppender_WriteDataMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+#if defined(ANDROID)
+ return FALSE;
+#else
+ int DataId = 0;
+ char* FullFileName = NULL;
+
+ 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(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+#if defined(ANDROID)
+ return FALSE;
+#else
+ int ImageId = 0;
+ char* FullFileName = NULL;
+
+ 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(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+#if defined(ANDROID)
+ return FALSE;
+#else
+ char* FullFileName = NULL;
+
+ 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))
+ 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(wLog* log)
+{
+ wLogConsoleAppender* ConsoleAppender = NULL;
+
+ ConsoleAppender = (wLogConsoleAppender*)calloc(1, sizeof(wLogConsoleAppender));
+
+ if (!ConsoleAppender)
+ return NULL;
+
+ ConsoleAppender->Type = WLOG_APPENDER_CONSOLE;
+
+ ConsoleAppender->Open = WLog_ConsoleAppender_Open;
+ ConsoleAppender->Close = WLog_ConsoleAppender_Close;
+ ConsoleAppender->WriteMessage = WLog_ConsoleAppender_WriteMessage;
+ ConsoleAppender->WriteDataMessage = WLog_ConsoleAppender_WriteDataMessage;
+ ConsoleAppender->WriteImageMessage = WLog_ConsoleAppender_WriteImageMessage;
+ ConsoleAppender->WritePacketMessage = WLog_ConsoleAppender_WritePacketMessage;
+ ConsoleAppender->Set = WLog_ConsoleAppender_Set;
+ ConsoleAppender->Free = WLog_ConsoleAppender_Free;
+
+ ConsoleAppender->outputStream = WLOG_CONSOLE_DEFAULT;
+
+#ifdef _WIN32
+ if (IsDebuggerPresent())
+ ConsoleAppender->outputStream = WLOG_CONSOLE_DEBUG;
+#endif
+
+ return (wLogAppender*)ConsoleAppender;
+}
diff --git a/winpr/libwinpr/utils/wlog/ConsoleAppender.h b/winpr/libwinpr/utils/wlog/ConsoleAppender.h
new file mode 100644
index 0000000..f6a1405
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/ConsoleAppender.h
@@ -0,0 +1,28 @@
+/**
+ * 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)
+wLogAppender* WLog_ConsoleAppender_New(wLog* log);
+
+#endif /* WINPR_WLOG_CONSOLE_APPENDER_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/DataMessage.c b/winpr/libwinpr/utils/wlog/DataMessage.c
new file mode 100644
index 0000000..512fddd
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/DataMessage.c
@@ -0,0 +1,48 @@
+/**
+ * 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 "wlog.h"
+
+#include "DataMessage.h"
+
+#include <winpr/file.h>
+
+#include "../../log.h"
+#define TAG WINPR_TAG("utils.wlog")
+
+BOOL WLog_DataMessage_Write(const char* filename, const void* data, size_t length)
+{
+ FILE* fp = NULL;
+ BOOL ret = TRUE;
+
+ fp = winpr_fopen(filename, "w+b");
+
+ if (!fp)
+ {
+ // WLog_ERR(TAG, "failed to open file %s", filename);
+ return FALSE;
+ }
+
+ if (fwrite(data, length, 1, fp) != 1)
+ ret = FALSE;
+ fclose(fp);
+ return ret;
+}
diff --git a/winpr/libwinpr/utils/wlog/DataMessage.h b/winpr/libwinpr/utils/wlog/DataMessage.h
new file mode 100644
index 0000000..db2c09e
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/DataMessage.h
@@ -0,0 +1,25 @@
+/**
+ * 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_DATA_MESSAGE_PRIVATE_H
+#define WINPR_WLOG_DATA_MESSAGE_PRIVATE_H
+
+BOOL WLog_DataMessage_Write(const char* filename, const void* data, size_t length);
+
+#endif /* WINPR_WLOG_DATA_MESSAGE_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/FileAppender.c b/winpr/libwinpr/utils/wlog/FileAppender.c
new file mode 100644
index 0000000..7afc658
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/FileAppender.c
@@ -0,0 +1,287 @@
+/**
+ * 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 "FileAppender.h"
+#include "Message.h"
+
+#include <winpr/crt.h>
+#include <winpr/environment.h>
+#include <winpr/file.h>
+#include <winpr/path.h>
+
+typedef struct
+{
+ WLOG_APPENDER_COMMON();
+
+ char* FileName;
+ char* FilePath;
+ char* FullFileName;
+ FILE* FileDescriptor;
+} wLogFileAppender;
+
+static BOOL WLog_FileAppender_SetOutputFileName(wLogFileAppender* appender, const char* filename)
+{
+ appender->FileName = _strdup(filename);
+
+ if (!appender->FileName)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_FileAppender_SetOutputFilePath(wLogFileAppender* appender, const char* filepath)
+{
+ appender->FilePath = _strdup(filepath);
+
+ if (!appender->FilePath)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_FileAppender_Open(wLog* log, wLogAppender* appender)
+{
+ wLogFileAppender* fileAppender = NULL;
+
+ if (!log || !appender)
+ return FALSE;
+
+ fileAppender = (wLogFileAppender*)appender;
+
+ if (!fileAppender->FilePath)
+ {
+ fileAppender->FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog");
+
+ if (!fileAppender->FilePath)
+ return FALSE;
+ }
+
+ if (!fileAppender->FileName)
+ {
+ fileAppender->FileName = (char*)malloc(MAX_PATH);
+
+ if (!fileAppender->FileName)
+ return FALSE;
+
+ sprintf_s(fileAppender->FileName, MAX_PATH, "%" PRIu32 ".log", GetCurrentProcessId());
+ }
+
+ if (!fileAppender->FullFileName)
+ {
+ fileAppender->FullFileName =
+ GetCombinedPath(fileAppender->FilePath, fileAppender->FileName);
+
+ if (!fileAppender->FullFileName)
+ return FALSE;
+ }
+
+ if (!winpr_PathFileExists(fileAppender->FilePath))
+ {
+ if (!winpr_PathMakePath(fileAppender->FilePath, 0))
+ return FALSE;
+
+ UnixChangeFileMode(fileAppender->FilePath, 0xFFFF);
+ }
+
+ fileAppender->FileDescriptor = winpr_fopen(fileAppender->FullFileName, "a+");
+
+ if (!fileAppender->FileDescriptor)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_FileAppender_Close(wLog* log, wLogAppender* appender)
+{
+ wLogFileAppender* fileAppender = NULL;
+
+ if (!log || !appender)
+ return FALSE;
+
+ fileAppender = (wLogFileAppender*)appender;
+
+ if (!fileAppender->FileDescriptor)
+ return TRUE;
+
+ fclose(fileAppender->FileDescriptor);
+ fileAppender->FileDescriptor = NULL;
+ return TRUE;
+}
+
+static BOOL WLog_FileAppender_WriteMessage(wLog* log, wLogAppender* appender, wLogMessage* message)
+{
+ FILE* fp = NULL;
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ wLogFileAppender* fileAppender = NULL;
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ fileAppender = (wLogFileAppender*)appender;
+ fp = fileAppender->FileDescriptor;
+
+ if (!fp)
+ return FALSE;
+
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+ fprintf(fp, "%s%s\n", message->PrefixString, message->TextString);
+ fflush(fp); /* slow! */
+ return TRUE;
+}
+
+static int g_DataId = 0;
+
+static BOOL WLog_FileAppender_WriteDataMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ int DataId = 0;
+ char* FullFileName = NULL;
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ DataId = g_DataId++;
+ FullFileName = WLog_Message_GetOutputFileName(DataId, "dat");
+ WLog_DataMessage_Write(FullFileName, message->Data, message->Length);
+ free(FullFileName);
+ return TRUE;
+}
+
+static int g_ImageId = 0;
+
+static BOOL WLog_FileAppender_WriteImageMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ int ImageId = 0;
+ char* FullFileName = NULL;
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ 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;
+}
+
+static BOOL WLog_FileAppender_Set(wLogAppender* appender, const char* setting, void* value)
+{
+ wLogFileAppender* fileAppender = (wLogFileAppender*)appender;
+
+ /* Just check the value string is not empty */
+ if (!value || (strnlen(value, 2) == 0))
+ return FALSE;
+
+ if (!strcmp("outputfilename", setting))
+ return WLog_FileAppender_SetOutputFileName(fileAppender, (const char*)value);
+
+ if (!strcmp("outputfilepath", setting))
+ return WLog_FileAppender_SetOutputFilePath(fileAppender, (const char*)value);
+
+ return FALSE;
+}
+
+static void WLog_FileAppender_Free(wLogAppender* appender)
+{
+ wLogFileAppender* fileAppender = NULL;
+
+ if (appender)
+ {
+ fileAppender = (wLogFileAppender*)appender;
+ free(fileAppender->FileName);
+ free(fileAppender->FilePath);
+ free(fileAppender->FullFileName);
+ free(fileAppender);
+ }
+}
+
+wLogAppender* WLog_FileAppender_New(wLog* log)
+{
+ LPSTR env = NULL;
+ LPCSTR name = NULL;
+ DWORD nSize = 0;
+ wLogFileAppender* FileAppender = NULL;
+ FileAppender = (wLogFileAppender*)calloc(1, sizeof(wLogFileAppender));
+
+ if (!FileAppender)
+ return NULL;
+
+ FileAppender->Type = WLOG_APPENDER_FILE;
+ FileAppender->Open = WLog_FileAppender_Open;
+ FileAppender->Close = WLog_FileAppender_Close;
+ FileAppender->WriteMessage = WLog_FileAppender_WriteMessage;
+ FileAppender->WriteDataMessage = WLog_FileAppender_WriteDataMessage;
+ FileAppender->WriteImageMessage = WLog_FileAppender_WriteImageMessage;
+ FileAppender->Free = WLog_FileAppender_Free;
+ FileAppender->Set = WLog_FileAppender_Set;
+ name = "WLOG_FILEAPPENDER_OUTPUT_FILE_PATH";
+ nSize = GetEnvironmentVariableA(name, NULL, 0);
+
+ if (nSize)
+ {
+ BOOL status = 0;
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ goto error_free;
+
+ if (GetEnvironmentVariableA(name, env, nSize) != nSize - 1)
+ {
+ free(env);
+ goto error_free;
+ }
+
+ status = WLog_FileAppender_SetOutputFilePath(FileAppender, env);
+ free(env);
+
+ if (!status)
+ goto error_free;
+ }
+
+ name = "WLOG_FILEAPPENDER_OUTPUT_FILE_NAME";
+ nSize = GetEnvironmentVariableA(name, NULL, 0);
+
+ if (nSize)
+ {
+ BOOL status = FALSE;
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ goto error_output_file_name;
+
+ if (GetEnvironmentVariableA(name, env, nSize) == nSize - 1)
+ status = WLog_FileAppender_SetOutputFileName(FileAppender, env);
+ free(env);
+
+ if (!status)
+ goto error_output_file_name;
+ }
+
+ return (wLogAppender*)FileAppender;
+error_output_file_name:
+ free(FileAppender->FilePath);
+error_free:
+ free(FileAppender);
+ return NULL;
+}
diff --git a/winpr/libwinpr/utils/wlog/FileAppender.h b/winpr/libwinpr/utils/wlog/FileAppender.h
new file mode 100644
index 0000000..8938488
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/FileAppender.h
@@ -0,0 +1,28 @@
+/**
+ * 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_FILE_APPENDER_PRIVATE_H
+#define WINPR_WLOG_FILE_APPENDER_PRIVATE_H
+
+#include "wlog.h"
+
+WINPR_ATTR_MALLOC(WLog_Appender_Free, 2)
+wLogAppender* WLog_FileAppender_New(wLog* log);
+
+#endif /* WINPR_WLOG_FILE_APPENDER_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/ImageMessage.c b/winpr/libwinpr/utils/wlog/ImageMessage.c
new file mode 100644
index 0000000..ce60032
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/ImageMessage.c
@@ -0,0 +1,37 @@
+/**
+ * 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 "wlog.h"
+#include <winpr/image.h>
+
+#include "ImageMessage.h"
+
+BOOL WLog_ImageMessage_Write(char* filename, void* data, size_t width, size_t height, size_t bpp)
+{
+ int status = 0;
+
+ status = winpr_bitmap_write(filename, data, width, height, bpp);
+
+ if (status < 0)
+ return FALSE;
+
+ return TRUE;
+}
diff --git a/winpr/libwinpr/utils/wlog/ImageMessage.h b/winpr/libwinpr/utils/wlog/ImageMessage.h
new file mode 100644
index 0000000..15ed81b
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/ImageMessage.h
@@ -0,0 +1,25 @@
+/**
+ * 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_IMAGE_MESSAGE_PRIVATE_H
+#define WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H
+
+BOOL WLog_ImageMessage_Write(char* filename, void* data, size_t width, size_t height, size_t bpp);
+
+#endif /* WINPR_WLOG_IMAGE_MESSAGE_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/JournaldAppender.c b/winpr/libwinpr/utils/wlog/JournaldAppender.c
new file mode 100644
index 0000000..504108b
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/JournaldAppender.c
@@ -0,0 +1,210 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * WinPR Logger
+ *
+ * Copyright © 2015 Thincast Technologies GmbH
+ * Copyright © 2015 David FORT <contact@hardening-consulting.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 "JournaldAppender.h"
+
+#include <unistd.h>
+#include <syslog.h>
+#include <systemd/sd-journal.h>
+
+#include <winpr/crt.h>
+#include <winpr/environment.h>
+
+typedef struct
+{
+ WLOG_APPENDER_COMMON();
+ char* identifier;
+ FILE* stream;
+} wLogJournaldAppender;
+
+static BOOL WLog_JournaldAppender_Open(wLog* log, wLogAppender* appender)
+{
+ int fd = 0;
+ wLogJournaldAppender* journaldAppender = NULL;
+
+ if (!log || !appender)
+ return FALSE;
+
+ journaldAppender = (wLogJournaldAppender*)appender;
+ if (journaldAppender->stream)
+ return TRUE;
+
+ fd = sd_journal_stream_fd(journaldAppender->identifier, LOG_INFO, 1);
+ if (fd < 0)
+ return FALSE;
+
+ journaldAppender->stream = fdopen(fd, "w");
+ if (!journaldAppender->stream)
+ {
+ close(fd);
+ return FALSE;
+ }
+
+ setbuffer(journaldAppender->stream, NULL, 0);
+ return TRUE;
+}
+
+static BOOL WLog_JournaldAppender_Close(wLog* log, wLogAppender* appender)
+{
+ if (!log || !appender)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_JournaldAppender_WriteMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ char* formatStr = NULL;
+ wLogJournaldAppender* journaldAppender = NULL;
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ journaldAppender = (wLogJournaldAppender*)appender;
+
+ switch (message->Level)
+ {
+ case WLOG_TRACE:
+ case WLOG_DEBUG:
+ formatStr = "<7>%s%s\n";
+ break;
+ case WLOG_INFO:
+ formatStr = "<6>%s%s\n";
+ break;
+ case WLOG_WARN:
+ formatStr = "<4>%s%s\n";
+ break;
+ case WLOG_ERROR:
+ formatStr = "<3>%s%s\n";
+ break;
+ case WLOG_FATAL:
+ formatStr = "<2>%s%s\n";
+ break;
+ case WLOG_OFF:
+ return TRUE;
+ default:
+ fprintf(stderr, "%s: unknown level %" PRIu32 "\n", __func__, message->Level);
+ return FALSE;
+ }
+
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+
+ if (message->Level != WLOG_OFF)
+ fprintf(journaldAppender->stream, formatStr, message->PrefixString, message->TextString);
+ return TRUE;
+}
+
+static BOOL WLog_JournaldAppender_WriteDataMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ if (!log || !appender || !message)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_JournaldAppender_WriteImageMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ if (!log || !appender || !message)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_JournaldAppender_Set(wLogAppender* appender, const char* setting, void* value)
+{
+ wLogJournaldAppender* journaldAppender = (wLogJournaldAppender*)appender;
+
+ /* Just check the value string is not empty */
+ if (!value || (strnlen(value, 2) == 0))
+ return FALSE;
+
+ if (strcmp("identifier", setting))
+ return FALSE;
+
+ /* If the stream is already open the identifier can't be changed */
+ if (journaldAppender->stream)
+ return FALSE;
+
+ if (journaldAppender->identifier)
+ free(journaldAppender->identifier);
+
+ return ((journaldAppender->identifier = _strdup((const char*)value)) != NULL);
+}
+
+static void WLog_JournaldAppender_Free(wLogAppender* appender)
+{
+ wLogJournaldAppender* journaldAppender = NULL;
+ if (appender)
+ {
+ journaldAppender = (wLogJournaldAppender*)appender;
+ if (journaldAppender->stream)
+ fclose(journaldAppender->stream);
+ free(journaldAppender->identifier);
+ free(journaldAppender);
+ }
+}
+
+wLogAppender* WLog_JournaldAppender_New(wLog* log)
+{
+ wLogJournaldAppender* appender = NULL;
+ DWORD nSize = 0;
+ LPCSTR name = "WLOG_JOURNALD_ID";
+
+ appender = (wLogJournaldAppender*)calloc(1, sizeof(wLogJournaldAppender));
+ if (!appender)
+ return NULL;
+
+ appender->Type = WLOG_APPENDER_JOURNALD;
+ appender->Open = WLog_JournaldAppender_Open;
+ appender->Close = WLog_JournaldAppender_Close;
+ appender->WriteMessage = WLog_JournaldAppender_WriteMessage;
+ appender->WriteDataMessage = WLog_JournaldAppender_WriteDataMessage;
+ appender->WriteImageMessage = WLog_JournaldAppender_WriteImageMessage;
+ appender->Set = WLog_JournaldAppender_Set;
+ appender->Free = WLog_JournaldAppender_Free;
+
+ nSize = GetEnvironmentVariableA(name, NULL, 0);
+ if (nSize)
+ {
+ appender->identifier = (LPSTR)malloc(nSize);
+ if (!appender->identifier)
+ goto error_open;
+
+ if (GetEnvironmentVariableA(name, appender->identifier, nSize) != nSize - 1)
+ goto error_open;
+
+ if (!WLog_JournaldAppender_Open(log, (wLogAppender*)appender))
+ goto error_open;
+ }
+
+ return (wLogAppender*)appender;
+
+error_open:
+ free(appender->identifier);
+ free(appender);
+ return NULL;
+}
diff --git a/winpr/libwinpr/utils/wlog/JournaldAppender.h b/winpr/libwinpr/utils/wlog/JournaldAppender.h
new file mode 100644
index 0000000..49223c5
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/JournaldAppender.h
@@ -0,0 +1,31 @@
+/**
+ * Copyright © 2015 Thincast Technologies GmbH
+ * Copyright © 2015 David FORT <contact@hardening-consulting.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_
+#define WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_
+
+#include "wlog.h"
+
+wLogAppender* WLog_JournaldAppender_New(wLog* log);
+
+#endif /* WINPR_LIBWINPR_UTILS_WLOG_JOURNALDAPPENDER_H_ */
diff --git a/winpr/libwinpr/utils/wlog/Layout.c b/winpr/libwinpr/utils/wlog/Layout.c
new file mode 100644
index 0000000..188c15b
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/Layout.c
@@ -0,0 +1,375 @@
+/**
+ * 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 <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/print.h>
+#include <winpr/sysinfo.h>
+#include <winpr/environment.h>
+
+#include "wlog.h"
+
+#include "Layout.h"
+
+#if defined __linux__ && !defined ANDROID
+#include <unistd.h>
+#include <sys/syscall.h>
+#endif
+
+#ifndef MIN
+#define MIN(x, y) (((x) < (y)) ? (x) : (y))
+#endif
+
+struct format_option_recurse;
+
+struct format_option
+{
+ const char* fmt;
+ size_t fmtlen;
+ const char* replace;
+ size_t replacelen;
+ const char* (*fkt)(void*);
+ void* arg;
+ const char* (*ext)(const struct format_option* opt, const char* str, size_t* preplacelen,
+ size_t* pskiplen);
+ struct format_option_recurse* recurse;
+};
+
+struct format_option_recurse
+{
+ struct format_option* options;
+ size_t nroptions;
+ wLog* log;
+ wLogLayout* layout;
+ wLogMessage* message;
+ char buffer[WLOG_MAX_PREFIX_SIZE];
+};
+
+/**
+ * Log Layout
+ */
+WINPR_ATTR_FORMAT_ARG(3, 0)
+static void WLog_PrintMessagePrefixVA(wLog* log, wLogMessage* message,
+ WINPR_FORMAT_ARG const char* format, va_list args)
+{
+ WINPR_ASSERT(message);
+ vsnprintf(message->PrefixString, WLOG_MAX_PREFIX_SIZE - 1, format, args);
+}
+
+WINPR_ATTR_FORMAT_ARG(3, 4)
+static void WLog_PrintMessagePrefix(wLog* log, wLogMessage* message,
+ WINPR_FORMAT_ARG const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ WLog_PrintMessagePrefixVA(log, message, format, args);
+ va_end(args);
+}
+
+static const char* get_tid(void* arg)
+{
+ char* str = arg;
+ size_t tid = 0;
+#if defined __linux__ && !defined ANDROID
+ /* On Linux we prefer to see the LWP id */
+ tid = (size_t)syscall(SYS_gettid);
+#else
+ tid = (size_t)GetCurrentThreadId();
+#endif
+ sprintf(str, "%08" PRIxz, tid);
+ return str;
+}
+
+static BOOL log_invalid_fmt(const char* what)
+{
+ fprintf(stderr, "Invalid format string '%s'\n", what);
+ return FALSE;
+}
+
+static BOOL check_and_log_format_size(char* format, size_t size, size_t index, size_t add)
+{
+ /* format string must be '\0' terminated, so abort at size - 1 */
+ if (index + add + 1 >= size)
+ {
+ fprintf(stderr,
+ "Format string too long ['%s', max %" PRIuz ", used %" PRIuz ", adding %" PRIuz
+ "]\n",
+ format, size, index, add);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int opt_compare_fn(const void* a, const void* b)
+{
+ const char* what = a;
+ const struct format_option* opt = b;
+ if (!opt)
+ return -1;
+ return strncmp(what, opt->fmt, opt->fmtlen);
+}
+
+static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
+ char* format, size_t formatlen);
+
+static const char* skip_if_null(const struct format_option* opt, const char* fmt,
+ size_t* preplacelen, size_t* pskiplen)
+{
+ WINPR_ASSERT(opt);
+ WINPR_ASSERT(fmt);
+ WINPR_ASSERT(preplacelen);
+ WINPR_ASSERT(pskiplen);
+
+ *preplacelen = 0;
+ *pskiplen = 0;
+
+ const char* str = &fmt[opt->fmtlen]; /* Skip first %{ from string */
+ const char* end = strstr(str, opt->replace);
+ if (!end)
+ return NULL;
+ *pskiplen = end - fmt + opt->replacelen;
+
+ if (!opt->arg)
+ return NULL;
+
+ const size_t replacelen = end - str;
+
+ char buffer[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ memcpy(buffer, str, MIN(replacelen, ARRAYSIZE(buffer) - 1));
+
+ if (!replace_format_string(buffer, opt->recurse, opt->recurse->buffer,
+ ARRAYSIZE(opt->recurse->buffer)))
+ return NULL;
+
+ *preplacelen = strnlen(opt->recurse->buffer, ARRAYSIZE(opt->recurse->buffer));
+ return opt->recurse->buffer;
+}
+
+static BOOL replace_format_string(const char* FormatString, struct format_option_recurse* recurse,
+ char* format, size_t formatlen)
+{
+ WINPR_ASSERT(FormatString);
+ WINPR_ASSERT(recurse);
+
+ size_t index = 0;
+
+ while (*FormatString)
+ {
+ const struct format_option* opt =
+ bsearch(FormatString, recurse->options, recurse->nroptions,
+ sizeof(struct format_option), opt_compare_fn);
+ if (opt)
+ {
+ size_t replacelen = opt->replacelen;
+ size_t fmtlen = opt->fmtlen;
+ const char* replace = opt->replace;
+ const void* arg = opt->arg;
+
+ if (opt->ext)
+ replace = opt->ext(opt, FormatString, &replacelen, &fmtlen);
+ if (opt->fkt)
+ arg = opt->fkt(opt->arg);
+
+ if (replace && (replacelen > 0))
+ {
+ const int rc = _snprintf(&format[index], formatlen - index, replace, arg);
+ if (rc < 0)
+ return FALSE;
+ if (!check_and_log_format_size(format, formatlen, index, rc))
+ return FALSE;
+ index += rc;
+ }
+ FormatString += fmtlen;
+ }
+ else
+ {
+ /* Unknown format string */
+ if (*FormatString == '%')
+ return log_invalid_fmt(FormatString);
+
+ if (!check_and_log_format_size(format, formatlen, index, 1))
+ return FALSE;
+ format[index++] = *FormatString++;
+ }
+ }
+
+ if (!check_and_log_format_size(format, formatlen, index, 0))
+ return FALSE;
+ return TRUE;
+}
+
+BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message)
+{
+ char format[WLOG_MAX_PREFIX_SIZE] = { 0 };
+
+ WINPR_ASSERT(layout);
+ WINPR_ASSERT(message);
+
+ char tid[32] = { 0 };
+ SYSTEMTIME localTime = { 0 };
+ GetLocalTime(&localTime);
+
+ struct format_option_recurse recurse = {
+ .options = NULL, .nroptions = 0, .log = log, .layout = layout, .message = message
+ };
+
+#define ENTRY(x) x, sizeof(x) - 1
+ struct format_option options[] = {
+ { ENTRY("%ctx"), ENTRY("%s"), log->custom, log->context, NULL, &recurse }, /* log context */
+ { ENTRY("%dw"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDayOfWeek, NULL,
+ &recurse }, /* day of week */
+ { ENTRY("%dy"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wDay, NULL,
+ &recurse }, /* day of year */
+ { ENTRY("%fl"), ENTRY("%s"), NULL, (void*)message->FileName, NULL, &recurse }, /* file */
+ { ENTRY("%fn"), ENTRY("%s"), NULL, (void*)message->FunctionName, NULL,
+ &recurse }, /* function */
+ { ENTRY("%hr"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wHour, NULL,
+ &recurse }, /* hours */
+ { ENTRY("%ln"), ENTRY("%" PRIuz), NULL, (void*)(size_t)message->LineNumber, NULL,
+ &recurse }, /* line number */
+ { ENTRY("%lv"), ENTRY("%s"), NULL, (void*)WLOG_LEVELS[message->Level], NULL,
+ &recurse }, /* log level */
+ { ENTRY("%mi"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMinute, NULL,
+ &recurse }, /* minutes */
+ { ENTRY("%ml"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wMilliseconds, NULL,
+ &recurse }, /* milliseconds */
+ { ENTRY("%mn"), ENTRY("%s"), NULL, log->Name, NULL, &recurse }, /* module name */
+ { ENTRY("%mo"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wMonth, NULL,
+ &recurse }, /* month */
+ { ENTRY("%pid"), ENTRY("%u"), NULL, (void*)(size_t)GetCurrentProcessId(), NULL,
+ &recurse }, /* process id */
+ { ENTRY("%se"), ENTRY("%02u"), NULL, (void*)(size_t)localTime.wSecond, NULL,
+ &recurse }, /* seconds */
+ { ENTRY("%tid"), ENTRY("%s"), get_tid, tid, NULL, &recurse }, /* thread id */
+ { ENTRY("%yr"), ENTRY("%u"), NULL, (void*)(size_t)localTime.wYear, NULL,
+ &recurse }, /* year */
+ { ENTRY("%{"), ENTRY("%}"), NULL, log->context, skip_if_null,
+ &recurse }, /* skip if no context */
+ };
+
+ recurse.options = options;
+ recurse.nroptions = ARRAYSIZE(options);
+
+ if (!replace_format_string(layout->FormatString, &recurse, format, ARRAYSIZE(format)))
+ return FALSE;
+
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_FORMAT_SECURITY
+
+ WLog_PrintMessagePrefix(log, message, format);
+
+ WINPR_PRAGMA_DIAG_POP
+
+ return TRUE;
+}
+
+wLogLayout* WLog_GetLogLayout(wLog* log)
+{
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+ return appender->Layout;
+}
+
+BOOL WLog_Layout_SetPrefixFormat(wLog* log, wLogLayout* layout, const char* format)
+{
+ free(layout->FormatString);
+ layout->FormatString = NULL;
+
+ if (format)
+ {
+ layout->FormatString = _strdup(format);
+
+ if (!layout->FormatString)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+wLogLayout* WLog_Layout_New(wLog* log)
+{
+ LPCSTR prefix = "WLOG_PREFIX";
+ DWORD nSize = 0;
+ char* env = NULL;
+ wLogLayout* layout = NULL;
+ layout = (wLogLayout*)calloc(1, sizeof(wLogLayout));
+
+ if (!layout)
+ return NULL;
+
+ nSize = GetEnvironmentVariableA(prefix, NULL, 0);
+
+ if (nSize)
+ {
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ {
+ free(layout);
+ return NULL;
+ }
+
+ if (GetEnvironmentVariableA(prefix, env, nSize) != nSize - 1)
+ {
+ free(env);
+ free(layout);
+ return NULL;
+ }
+ }
+
+ if (env)
+ layout->FormatString = env;
+ else
+ {
+#ifdef ANDROID
+ layout->FormatString = _strdup("[pid=%pid:tid=%tid] - [%fn]%{[%ctx]%}: ");
+#else
+ layout->FormatString =
+ _strdup("[%hr:%mi:%se:%ml] [%pid:%tid] [%lv][%mn] - [%fn]%{[%ctx]%}: ");
+#endif
+
+ if (!layout->FormatString)
+ {
+ free(layout);
+ return NULL;
+ }
+ }
+
+ return layout;
+}
+
+void WLog_Layout_Free(wLog* log, wLogLayout* layout)
+{
+ if (layout)
+ {
+ if (layout->FormatString)
+ {
+ free(layout->FormatString);
+ layout->FormatString = NULL;
+ }
+
+ free(layout);
+ }
+}
diff --git a/winpr/libwinpr/utils/wlog/Layout.h b/winpr/libwinpr/utils/wlog/Layout.h
new file mode 100644
index 0000000..8698077
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/Layout.h
@@ -0,0 +1,41 @@
+/**
+ * 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_LAYOUT_PRIVATE_H
+#define WINPR_WLOG_LAYOUT_PRIVATE_H
+
+#include "wlog.h"
+
+/**
+ * Log Layout
+ */
+
+struct s_wLogLayout
+{
+ DWORD Type;
+
+ LPSTR FormatString;
+};
+
+void WLog_Layout_Free(wLog* log, wLogLayout* layout);
+
+WINPR_ATTR_MALLOC(WLog_Layout_Free, 2)
+wLogLayout* WLog_Layout_New(wLog* log);
+
+#endif /* WINPR_WLOG_LAYOUT_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/Message.c b/winpr/libwinpr/utils/wlog/Message.c
new file mode 100644
index 0000000..bedb5ed
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/Message.c
@@ -0,0 +1,64 @@
+/**
+ * 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 <winpr/crt.h>
+#include <winpr/path.h>
+#include <winpr/file.h>
+
+#include "wlog.h"
+
+#include "Message.h"
+
+char* WLog_Message_GetOutputFileName(int id, const char* ext)
+{
+ DWORD ProcessId = 0;
+ char* FilePath = NULL;
+ char* FileName = NULL;
+ char* FullFileName = NULL;
+
+ if (!(FileName = (char*)malloc(256)))
+ return NULL;
+
+ FilePath = GetKnownSubPath(KNOWN_PATH_TEMP, "wlog");
+
+ if (!winpr_PathFileExists(FilePath))
+ {
+ if (!winpr_PathMakePath(FilePath, NULL))
+ {
+ free(FileName);
+ free(FilePath);
+ return NULL;
+ }
+ }
+
+ ProcessId = GetCurrentProcessId();
+ if (id >= 0)
+ sprintf_s(FileName, 256, "%" PRIu32 "-%d.%s", ProcessId, id, ext);
+ else
+ sprintf_s(FileName, 256, "%" PRIu32 ".%s", ProcessId, ext);
+
+ FullFileName = GetCombinedPath(FilePath, FileName);
+
+ free(FileName);
+ free(FilePath);
+
+ return FullFileName;
+}
diff --git a/winpr/libwinpr/utils/wlog/Message.h b/winpr/libwinpr/utils/wlog/Message.h
new file mode 100644
index 0000000..c65b33c
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/Message.h
@@ -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_MESSAGE_PRIVATE_H
+#define WINPR_WLOG_MESSAGE_PRIVATE_H
+
+#include "DataMessage.h"
+#include "ImageMessage.h"
+#include "PacketMessage.h"
+
+char* WLog_Message_GetOutputFileName(int id, const char* ext);
+
+#endif /* WINPR_WLOG_MESSAGE_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/PacketMessage.c b/winpr/libwinpr/utils/wlog/PacketMessage.c
new file mode 100644
index 0000000..cc1c812
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/PacketMessage.c
@@ -0,0 +1,487 @@
+/**
+ * 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 "wlog.h"
+
+#include "PacketMessage.h"
+
+#include <winpr/wtypes.h>
+#include <winpr/crt.h>
+#include <winpr/file.h>
+#include <winpr/stream.h>
+
+#include "../../log.h"
+#define TAG WINPR_TAG("utils.wlog")
+
+#ifndef _WIN32
+#include <sys/time.h>
+#else
+#include <time.h>
+#include <sys/timeb.h>
+#include <winpr/windows.h>
+
+static int gettimeofday(struct timeval* tp, void* tz)
+{
+ struct _timeb timebuffer;
+ _ftime(&timebuffer);
+ tp->tv_sec = (long)timebuffer.time;
+ tp->tv_usec = timebuffer.millitm * 1000;
+ return 0;
+}
+#endif
+
+static BOOL Pcap_Read_Header(wPcap* pcap, wPcapHeader* header)
+{
+ if (pcap && pcap->fp && fread((void*)header, sizeof(wPcapHeader), 1, pcap->fp) == 1)
+ return TRUE;
+ return FALSE;
+}
+
+/* currently unused code */
+#if 0
+static BOOL Pcap_Read_RecordHeader(wPcap* pcap, wPcapRecordHeader* record)
+{
+ if (pcap && pcap->fp && (fread((void*) record, sizeof(wPcapRecordHeader), 1, pcap->fp) == 1))
+ return TRUE;
+ return FALSE;
+}
+
+static BOOL Pcap_Read_Record(wPcap* pcap, wPcapRecord* record)
+{
+ if (pcap && pcap->fp)
+ {
+ if (!Pcap_Read_RecordHeader(pcap, &record->header))
+ return FALSE;
+ record->length = record->header.incl_len;
+ record->data = malloc(record->length);
+ if (!record->data)
+ return FALSE;
+ if (fread(record->data, record->length, 1, pcap->fp) != 1)
+ {
+ free(record->data);
+ record->length = 0;
+ record->data = NULL;
+ return FALSE;
+ }
+ }
+ return TRUE;
+}
+
+static BOOL Pcap_Add_Record(wPcap* pcap, void* data, UINT32 length)
+{
+ wPcapRecord* record;
+ struct timeval tp;
+
+ if (!pcap->tail)
+ {
+ pcap->tail = (wPcapRecord*) calloc(1, sizeof(wPcapRecord));
+ if (!pcap->tail)
+ return FALSE;
+ pcap->head = pcap->tail;
+ pcap->record = pcap->head;
+ record = pcap->tail;
+ }
+ else
+ {
+ record = (wPcapRecord*) calloc(1, sizeof(wPcapRecord));
+ if (!record)
+ return FALSE;
+ pcap->tail->next = record;
+ pcap->tail = record;
+ }
+
+ if (!pcap->record)
+ pcap->record = record;
+
+ record->data = data;
+ record->length = length;
+ record->header.incl_len = length;
+ record->header.orig_len = length;
+ gettimeofday(&tp, 0);
+ record->header.ts_sec = tp.tv_sec;
+ record->header.ts_usec = tp.tv_usec;
+ return TRUE;
+}
+
+static BOOL Pcap_HasNext_Record(wPcap* pcap)
+{
+ if (pcap->file_size - (_ftelli64(pcap->fp)) <= 16)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL Pcap_GetNext_RecordHeader(wPcap* pcap, wPcapRecord* record)
+{
+ if (!Pcap_HasNext_Record(pcap) || !Pcap_Read_RecordHeader(pcap, &record->header))
+ return FALSE;
+
+ record->length = record->header.incl_len;
+ return TRUE;
+}
+
+static BOOL Pcap_GetNext_RecordContent(wPcap* pcap, wPcapRecord* record)
+{
+ if (pcap && pcap->fp && fread(record->data, record->length, 1, pcap->fp) == 1)
+ return TRUE;
+
+ return FALSE;
+}
+
+static BOOL Pcap_GetNext_Record(wPcap* pcap, wPcapRecord* record)
+{
+ if (!Pcap_HasNext_Record(pcap))
+ return FALSE;
+
+ return Pcap_Read_Record(pcap, record);
+}
+#endif
+
+static BOOL Pcap_Write_Header(wPcap* pcap, wPcapHeader* header)
+{
+ if (pcap && pcap->fp && fwrite((void*)header, sizeof(wPcapHeader), 1, pcap->fp) == 1)
+ return TRUE;
+ return FALSE;
+}
+
+static BOOL Pcap_Write_RecordHeader(wPcap* pcap, wPcapRecordHeader* record)
+{
+ if (pcap && pcap->fp && fwrite((void*)record, sizeof(wPcapRecordHeader), 1, pcap->fp) == 1)
+ return TRUE;
+ return FALSE;
+}
+
+static BOOL Pcap_Write_RecordContent(wPcap* pcap, wPcapRecord* record)
+{
+ if (pcap && pcap->fp && fwrite(record->data, record->length, 1, pcap->fp) == 1)
+ return TRUE;
+ return FALSE;
+}
+
+static BOOL Pcap_Write_Record(wPcap* pcap, wPcapRecord* record)
+{
+ return Pcap_Write_RecordHeader(pcap, &record->header) && Pcap_Write_RecordContent(pcap, record);
+}
+
+wPcap* Pcap_Open(char* name, BOOL write)
+{
+ wPcap* pcap = NULL;
+ FILE* pcap_fp = winpr_fopen(name, write ? "w+b" : "rb");
+
+ if (!pcap_fp)
+ {
+ WLog_ERR(TAG, "opening pcap file");
+ return NULL;
+ }
+
+ pcap = (wPcap*)calloc(1, sizeof(wPcap));
+
+ if (!pcap)
+ goto out_fail;
+
+ pcap->name = name;
+ pcap->write = write;
+ pcap->record_count = 0;
+ pcap->fp = pcap_fp;
+
+ if (write)
+ {
+ pcap->header.magic_number = PCAP_MAGIC_NUMBER;
+ pcap->header.version_major = 2;
+ pcap->header.version_minor = 4;
+ pcap->header.thiszone = 0;
+ pcap->header.sigfigs = 0;
+ pcap->header.snaplen = 0xFFFFFFFF;
+ pcap->header.network = 1; /* ethernet */
+ if (!Pcap_Write_Header(pcap, &pcap->header))
+ goto out_fail;
+ }
+ else
+ {
+ if (_fseeki64(pcap->fp, 0, SEEK_END) < 0)
+ goto out_fail;
+ pcap->file_size = (SSIZE_T)_ftelli64(pcap->fp);
+ if (pcap->file_size < 0)
+ goto out_fail;
+ if (_fseeki64(pcap->fp, 0, SEEK_SET) < 0)
+ goto out_fail;
+ if (!Pcap_Read_Header(pcap, &pcap->header))
+ goto out_fail;
+ }
+
+ return pcap;
+
+out_fail:
+ if (pcap_fp)
+ fclose(pcap_fp);
+ free(pcap);
+ return NULL;
+}
+
+void Pcap_Flush(wPcap* pcap)
+{
+ if (!pcap || !pcap->fp)
+ return;
+
+ while (pcap->record)
+ {
+ if (!Pcap_Write_Record(pcap, pcap->record))
+ return;
+ pcap->record = pcap->record->next;
+ }
+
+ fflush(pcap->fp);
+ return;
+}
+
+void Pcap_Close(wPcap* pcap)
+{
+ if (!pcap || !pcap->fp)
+ return;
+
+ Pcap_Flush(pcap);
+ fclose(pcap->fp);
+ free(pcap);
+}
+
+static BOOL WLog_PacketMessage_Write_EthernetHeader(wPcap* pcap, wEthernetHeader* ethernet)
+{
+ wStream* s = NULL;
+ wStream sbuffer = { 0 };
+ BYTE buffer[14] = { 0 };
+ BOOL ret = TRUE;
+
+ if (!pcap || !pcap->fp || !ethernet)
+ return FALSE;
+
+ s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
+ if (!s)
+ return FALSE;
+ Stream_Write(s, ethernet->Destination, 6);
+ Stream_Write(s, ethernet->Source, 6);
+ Stream_Write_UINT16_BE(s, ethernet->Type);
+ if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1)
+ ret = FALSE;
+
+ return ret;
+}
+
+static UINT16 IPv4Checksum(BYTE* ipv4, int length)
+{
+ UINT16 tmp16 = 0;
+ long checksum = 0;
+
+ while (length > 1)
+ {
+ tmp16 = *((UINT16*)ipv4);
+ checksum += tmp16;
+ length -= 2;
+ ipv4 += 2;
+ }
+
+ if (length > 0)
+ checksum += *ipv4;
+
+ while (checksum >> 16)
+ checksum = (checksum & 0xFFFF) + (checksum >> 16);
+
+ return (UINT16)(~checksum);
+}
+
+static BOOL WLog_PacketMessage_Write_IPv4Header(wPcap* pcap, wIPv4Header* ipv4)
+{
+ wStream* s = NULL;
+ wStream sbuffer = { 0 };
+ BYTE buffer[20] = { 0 };
+ int ret = TRUE;
+
+ if (!pcap || !pcap->fp || !ipv4)
+ return FALSE;
+
+ s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
+ if (!s)
+ return FALSE;
+ Stream_Write_UINT8(s, (ipv4->Version << 4) | ipv4->InternetHeaderLength);
+ Stream_Write_UINT8(s, ipv4->TypeOfService);
+ Stream_Write_UINT16_BE(s, ipv4->TotalLength);
+ Stream_Write_UINT16_BE(s, ipv4->Identification);
+ Stream_Write_UINT16_BE(s, (ipv4->InternetProtocolFlags << 13) | ipv4->FragmentOffset);
+ Stream_Write_UINT8(s, ipv4->TimeToLive);
+ Stream_Write_UINT8(s, ipv4->Protocol);
+ Stream_Write_UINT16(s, ipv4->HeaderChecksum);
+ Stream_Write_UINT32_BE(s, ipv4->SourceAddress);
+ Stream_Write_UINT32_BE(s, ipv4->DestinationAddress);
+ ipv4->HeaderChecksum = IPv4Checksum((BYTE*)buffer, 20);
+ Stream_Rewind(s, 10);
+ Stream_Write_UINT16(s, ipv4->HeaderChecksum);
+
+ if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1)
+ ret = FALSE;
+
+ return ret;
+}
+
+static BOOL WLog_PacketMessage_Write_TcpHeader(wPcap* pcap, wTcpHeader* tcp)
+{
+ wStream* s = NULL;
+ wStream sbuffer = { 0 };
+ BYTE buffer[20] = { 0 };
+ BOOL ret = TRUE;
+
+ if (!pcap || !pcap->fp || !tcp)
+ return FALSE;
+
+ s = Stream_StaticInit(&sbuffer, buffer, sizeof(buffer));
+ if (!s)
+ return FALSE;
+ Stream_Write_UINT16_BE(s, tcp->SourcePort);
+ Stream_Write_UINT16_BE(s, tcp->DestinationPort);
+ Stream_Write_UINT32_BE(s, tcp->SequenceNumber);
+ Stream_Write_UINT32_BE(s, tcp->AcknowledgementNumber);
+ Stream_Write_UINT8(s, (tcp->Offset << 4) | tcp->Reserved);
+ Stream_Write_UINT8(s, tcp->TcpFlags);
+ Stream_Write_UINT16_BE(s, tcp->Window);
+ Stream_Write_UINT16_BE(s, tcp->Checksum);
+ Stream_Write_UINT16_BE(s, tcp->UrgentPointer);
+
+ if (pcap->fp)
+ {
+ if (fwrite(buffer, sizeof(buffer), 1, pcap->fp) != 1)
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+static UINT32 g_InboundSequenceNumber = 0;
+static UINT32 g_OutboundSequenceNumber = 0;
+
+BOOL WLog_PacketMessage_Write(wPcap* pcap, void* data, size_t length, DWORD flags)
+{
+ wTcpHeader tcp;
+ wIPv4Header ipv4;
+ struct timeval tp;
+ wPcapRecord record;
+ wEthernetHeader ethernet;
+ ethernet.Type = 0x0800;
+
+ if (!pcap || !pcap->fp)
+ return FALSE;
+
+ if (flags & WLOG_PACKET_OUTBOUND)
+ {
+ /* 00:15:5D:01:64:04 */
+ ethernet.Source[0] = 0x00;
+ ethernet.Source[1] = 0x15;
+ ethernet.Source[2] = 0x5D;
+ ethernet.Source[3] = 0x01;
+ ethernet.Source[4] = 0x64;
+ ethernet.Source[5] = 0x04;
+ /* 00:15:5D:01:64:01 */
+ ethernet.Destination[0] = 0x00;
+ ethernet.Destination[1] = 0x15;
+ ethernet.Destination[2] = 0x5D;
+ ethernet.Destination[3] = 0x01;
+ ethernet.Destination[4] = 0x64;
+ ethernet.Destination[5] = 0x01;
+ }
+ else
+ {
+ /* 00:15:5D:01:64:01 */
+ ethernet.Source[0] = 0x00;
+ ethernet.Source[1] = 0x15;
+ ethernet.Source[2] = 0x5D;
+ ethernet.Source[3] = 0x01;
+ ethernet.Source[4] = 0x64;
+ ethernet.Source[5] = 0x01;
+ /* 00:15:5D:01:64:04 */
+ ethernet.Destination[0] = 0x00;
+ ethernet.Destination[1] = 0x15;
+ ethernet.Destination[2] = 0x5D;
+ ethernet.Destination[3] = 0x01;
+ ethernet.Destination[4] = 0x64;
+ ethernet.Destination[5] = 0x04;
+ }
+
+ ipv4.Version = 4;
+ ipv4.InternetHeaderLength = 5;
+ ipv4.TypeOfService = 0;
+ ipv4.TotalLength = (UINT16)(length + 20 + 20);
+ ipv4.Identification = 0;
+ ipv4.InternetProtocolFlags = 0x02;
+ ipv4.FragmentOffset = 0;
+ ipv4.TimeToLive = 128;
+ ipv4.Protocol = 6; /* TCP */
+ ipv4.HeaderChecksum = 0;
+
+ if (flags & WLOG_PACKET_OUTBOUND)
+ {
+ ipv4.SourceAddress = 0xC0A80196; /* 192.168.1.150 */
+ ipv4.DestinationAddress = 0x4A7D64C8; /* 74.125.100.200 */
+ }
+ else
+ {
+ ipv4.SourceAddress = 0x4A7D64C8; /* 74.125.100.200 */
+ ipv4.DestinationAddress = 0xC0A80196; /* 192.168.1.150 */
+ }
+
+ tcp.SourcePort = 3389;
+ tcp.DestinationPort = 3389;
+
+ if (flags & WLOG_PACKET_OUTBOUND)
+ {
+ tcp.SequenceNumber = g_OutboundSequenceNumber;
+ tcp.AcknowledgementNumber = g_InboundSequenceNumber;
+ g_OutboundSequenceNumber += length;
+ }
+ else
+ {
+ tcp.SequenceNumber = g_InboundSequenceNumber;
+ tcp.AcknowledgementNumber = g_OutboundSequenceNumber;
+ g_InboundSequenceNumber += length;
+ }
+
+ tcp.Offset = 5;
+ tcp.Reserved = 0;
+ tcp.TcpFlags = 0x0018;
+ tcp.Window = 0x7FFF;
+ tcp.Checksum = 0;
+ tcp.UrgentPointer = 0;
+ record.data = data;
+ record.length = length;
+ const size_t offset = 14 + 20 + 20;
+ WINPR_ASSERT(record.length <= UINT32_MAX - offset);
+ record.header.incl_len = (UINT32)record.length + offset;
+ record.header.orig_len = (UINT32)record.length + offset;
+ record.next = NULL;
+ gettimeofday(&tp, 0);
+ record.header.ts_sec = tp.tv_sec;
+ record.header.ts_usec = tp.tv_usec;
+ if (!Pcap_Write_RecordHeader(pcap, &record.header) ||
+ !WLog_PacketMessage_Write_EthernetHeader(pcap, &ethernet) ||
+ !WLog_PacketMessage_Write_IPv4Header(pcap, &ipv4) ||
+ !WLog_PacketMessage_Write_TcpHeader(pcap, &tcp) || !Pcap_Write_RecordContent(pcap, &record))
+ return FALSE;
+ fflush(pcap->fp);
+ return TRUE;
+}
diff --git a/winpr/libwinpr/utils/wlog/PacketMessage.h b/winpr/libwinpr/utils/wlog/PacketMessage.h
new file mode 100644
index 0000000..088ee8f
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/PacketMessage.h
@@ -0,0 +1,111 @@
+/**
+ * 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_PACKET_MESSAGE_PRIVATE_H
+#define WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H
+
+#include "wlog.h"
+
+#define PCAP_MAGIC_NUMBER 0xA1B2C3D4
+
+typedef struct
+{
+ UINT32 magic_number; /* magic number */
+ UINT16 version_major; /* major version number */
+ UINT16 version_minor; /* minor version number */
+ INT32 thiszone; /* GMT to local correction */
+ UINT32 sigfigs; /* accuracy of timestamps */
+ UINT32 snaplen; /* max length of captured packets, in octets */
+ UINT32 network; /* data link type */
+} wPcapHeader;
+
+typedef struct
+{
+ UINT32 ts_sec; /* timestamp seconds */
+ UINT32 ts_usec; /* timestamp microseconds */
+ UINT32 incl_len; /* number of octets of packet saved in file */
+ UINT32 orig_len; /* actual length of packet */
+} wPcapRecordHeader;
+
+typedef struct s_wPcapRecort
+{
+ wPcapRecordHeader header;
+ void* data;
+ size_t length;
+ struct s_wPcapRecort* next;
+} wPcapRecord;
+
+typedef struct
+{
+ FILE* fp;
+ char* name;
+ BOOL write;
+ SSIZE_T file_size;
+ size_t record_count;
+ wPcapHeader header;
+ wPcapRecord* head;
+ wPcapRecord* tail;
+ wPcapRecord* record;
+} wPcap;
+
+wPcap* Pcap_Open(char* name, BOOL write);
+void Pcap_Close(wPcap* pcap);
+
+void Pcap_Flush(wPcap* pcap);
+
+typedef struct
+{
+ BYTE Destination[6];
+ BYTE Source[6];
+ UINT16 Type;
+} wEthernetHeader;
+
+typedef struct
+{
+ BYTE Version;
+ BYTE InternetHeaderLength;
+ BYTE TypeOfService;
+ UINT16 TotalLength;
+ UINT16 Identification;
+ BYTE InternetProtocolFlags;
+ UINT16 FragmentOffset;
+ BYTE TimeToLive;
+ BYTE Protocol;
+ UINT16 HeaderChecksum;
+ UINT32 SourceAddress;
+ UINT32 DestinationAddress;
+} wIPv4Header;
+
+typedef struct
+{
+ UINT16 SourcePort;
+ UINT16 DestinationPort;
+ UINT32 SequenceNumber;
+ UINT32 AcknowledgementNumber;
+ BYTE Offset;
+ BYTE Reserved;
+ BYTE TcpFlags;
+ UINT16 Window;
+ UINT16 Checksum;
+ UINT16 UrgentPointer;
+} wTcpHeader;
+
+BOOL WLog_PacketMessage_Write(wPcap* pcap, void* data, size_t length, DWORD flags);
+
+#endif /* WINPR_WLOG_PACKET_MESSAGE_PRIVATE_H */
diff --git a/winpr/libwinpr/utils/wlog/SyslogAppender.c b/winpr/libwinpr/utils/wlog/SyslogAppender.c
new file mode 100644
index 0000000..73baade
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/SyslogAppender.c
@@ -0,0 +1,137 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * WinPR Logger
+ *
+ * Copyright © 2015 Thincast Technologies GmbH
+ * Copyright © 2015 David FORT <contact@hardening-consulting.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 "SyslogAppender.h"
+#include <syslog.h>
+
+typedef struct
+{
+ WLOG_APPENDER_COMMON();
+} wLogSyslogAppender;
+
+static int getSyslogLevel(DWORD level)
+{
+ switch (level)
+ {
+ case WLOG_TRACE:
+ case WLOG_DEBUG:
+ return LOG_DEBUG;
+ case WLOG_INFO:
+ return LOG_INFO;
+ case WLOG_WARN:
+ return LOG_WARNING;
+ case WLOG_ERROR:
+ return LOG_ERR;
+ case WLOG_FATAL:
+ return LOG_CRIT;
+ case WLOG_OFF:
+ default:
+ return -1;
+ }
+}
+
+static BOOL WLog_SyslogAppender_Open(wLog* log, wLogAppender* appender)
+{
+ if (!log || !appender)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_SyslogAppender_Close(wLog* log, wLogAppender* appender)
+{
+ if (!log || !appender)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_SyslogAppender_WriteMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ int syslogLevel = 0;
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ syslogLevel = getSyslogLevel(message->Level);
+ if (syslogLevel >= 0)
+ syslog(syslogLevel, "%s", message->TextString);
+
+ return TRUE;
+}
+
+static BOOL WLog_SyslogAppender_WriteDataMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ int syslogLevel = 0;
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ syslogLevel = getSyslogLevel(message->Level);
+ if (syslogLevel >= 0)
+ syslog(syslogLevel, "skipped data message of %" PRIuz " bytes", message->Length);
+
+ return TRUE;
+}
+
+static BOOL WLog_SyslogAppender_WriteImageMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ int syslogLevel = 0;
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ syslogLevel = getSyslogLevel(message->Level);
+ if (syslogLevel >= 0)
+ syslog(syslogLevel, "skipped image (%" PRIuz "x%" PRIuz "x%" PRIuz ")", message->ImageWidth,
+ message->ImageHeight, message->ImageBpp);
+
+ return TRUE;
+}
+
+static void WLog_SyslogAppender_Free(wLogAppender* appender)
+{
+ free(appender);
+}
+
+wLogAppender* WLog_SyslogAppender_New(wLog* log)
+{
+ wLogSyslogAppender* appender = NULL;
+
+ appender = (wLogSyslogAppender*)calloc(1, sizeof(wLogSyslogAppender));
+ if (!appender)
+ return NULL;
+
+ appender->Type = WLOG_APPENDER_SYSLOG;
+
+ appender->Open = WLog_SyslogAppender_Open;
+ appender->Close = WLog_SyslogAppender_Close;
+ appender->WriteMessage = WLog_SyslogAppender_WriteMessage;
+ appender->WriteDataMessage = WLog_SyslogAppender_WriteDataMessage;
+ appender->WriteImageMessage = WLog_SyslogAppender_WriteImageMessage;
+ appender->Free = WLog_SyslogAppender_Free;
+
+ return (wLogAppender*)appender;
+}
diff --git a/winpr/libwinpr/utils/wlog/SyslogAppender.h b/winpr/libwinpr/utils/wlog/SyslogAppender.h
new file mode 100644
index 0000000..bbb30d5
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/SyslogAppender.h
@@ -0,0 +1,32 @@
+/**
+ * Copyright © 2015 Thincast Technologies GmbH
+ * Copyright © 2015 David FORT <contact@hardening-consulting.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_
+#define WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_
+
+#include "wlog.h"
+
+WINPR_ATTR_MALLOC(WLog_Appender_Free, 2)
+wLogAppender* WLog_SyslogAppender_New(wLog* log);
+
+#endif /* WINPR_LIBWINPR_UTILS_WLOG_SYSLOGAPPENDER_H_ */
diff --git a/winpr/libwinpr/utils/wlog/UdpAppender.c b/winpr/libwinpr/utils/wlog/UdpAppender.c
new file mode 100644
index 0000000..19501dc
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/UdpAppender.c
@@ -0,0 +1,222 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * WinPR Logger
+ *
+ * Copyright © 2015 Thincast Technologies GmbH
+ * Copyright © 2015 David FORT <contact@hardening-consulting.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/environment.h>
+#include <winpr/winsock.h>
+
+#include "wlog.h"
+
+typedef struct
+{
+ WLOG_APPENDER_COMMON();
+ char* host;
+ struct sockaddr targetAddr;
+ int targetAddrLen;
+ SOCKET sock;
+} wLogUdpAppender;
+
+static BOOL WLog_UdpAppender_Open(wLog* log, wLogAppender* appender)
+{
+ wLogUdpAppender* udpAppender = NULL;
+ char addressString[256] = { 0 };
+ struct addrinfo hints = { 0 };
+ struct addrinfo* result = { 0 };
+ int status = 0;
+ size_t addrLen = 0;
+ char* colonPos = NULL;
+
+ if (!appender)
+ return FALSE;
+
+ udpAppender = (wLogUdpAppender*)appender;
+
+ if (udpAppender->targetAddrLen) /* already opened */
+ return TRUE;
+
+ colonPos = strchr(udpAppender->host, ':');
+
+ if (!colonPos)
+ return FALSE;
+
+ addrLen = (colonPos - udpAppender->host);
+ memcpy(addressString, udpAppender->host, addrLen);
+ addressString[addrLen] = '\0';
+ hints.ai_family = AF_INET;
+ hints.ai_socktype = SOCK_DGRAM;
+ status = getaddrinfo(addressString, colonPos + 1, &hints, &result);
+
+ if (status != 0)
+ return FALSE;
+
+ if (result->ai_addrlen > sizeof(udpAppender->targetAddr))
+ {
+ freeaddrinfo(result);
+ return FALSE;
+ }
+
+ memcpy(&udpAppender->targetAddr, result->ai_addr, result->ai_addrlen);
+ udpAppender->targetAddrLen = (int)result->ai_addrlen;
+ freeaddrinfo(result);
+ return TRUE;
+}
+
+static BOOL WLog_UdpAppender_Close(wLog* log, wLogAppender* appender)
+{
+ if (!log || !appender)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_UdpAppender_WriteMessage(wLog* log, wLogAppender* appender, wLogMessage* message)
+{
+ char prefix[WLOG_MAX_PREFIX_SIZE] = { 0 };
+ wLogUdpAppender* udpAppender = NULL;
+
+ if (!log || !appender || !message)
+ return FALSE;
+
+ udpAppender = (wLogUdpAppender*)appender;
+ message->PrefixString = prefix;
+ WLog_Layout_GetMessagePrefix(log, appender->Layout, message);
+ _sendto(udpAppender->sock, message->PrefixString, (int)strnlen(message->PrefixString, INT_MAX),
+ 0, &udpAppender->targetAddr, udpAppender->targetAddrLen);
+ _sendto(udpAppender->sock, message->TextString, (int)strnlen(message->TextString, INT_MAX), 0,
+ &udpAppender->targetAddr, udpAppender->targetAddrLen);
+ _sendto(udpAppender->sock, "\n", 1, 0, &udpAppender->targetAddr, udpAppender->targetAddrLen);
+ return TRUE;
+}
+
+static BOOL WLog_UdpAppender_WriteDataMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ if (!log || !appender || !message)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_UdpAppender_WriteImageMessage(wLog* log, wLogAppender* appender,
+ wLogMessage* message)
+{
+ if (!log || !appender || !message)
+ return FALSE;
+
+ return TRUE;
+}
+
+static BOOL WLog_UdpAppender_Set(wLogAppender* appender, const char* setting, void* value)
+{
+ const char target[] = "target";
+ wLogUdpAppender* udpAppender = (wLogUdpAppender*)appender;
+
+ /* Just check the value string is not empty */
+ if (!value || (strnlen(value, 2) == 0))
+ return FALSE;
+
+ if (strncmp(target, setting, sizeof(target)))
+ return FALSE;
+
+ udpAppender->targetAddrLen = 0;
+
+ if (udpAppender->host)
+ free(udpAppender->host);
+
+ udpAppender->host = _strdup((const char*)value);
+ return (udpAppender->host != NULL) && WLog_UdpAppender_Open(NULL, appender);
+}
+
+static void WLog_UdpAppender_Free(wLogAppender* appender)
+{
+ wLogUdpAppender* udpAppender = NULL;
+
+ if (appender)
+ {
+ udpAppender = (wLogUdpAppender*)appender;
+
+ if (udpAppender->sock != INVALID_SOCKET)
+ {
+ closesocket(udpAppender->sock);
+ udpAppender->sock = INVALID_SOCKET;
+ }
+
+ free(udpAppender->host);
+ free(udpAppender);
+ }
+}
+
+wLogAppender* WLog_UdpAppender_New(wLog* log)
+{
+ wLogUdpAppender* appender = NULL;
+ DWORD nSize = 0;
+ LPCSTR name = NULL;
+ appender = (wLogUdpAppender*)calloc(1, sizeof(wLogUdpAppender));
+
+ if (!appender)
+ return NULL;
+
+ appender->Type = WLOG_APPENDER_UDP;
+ appender->Open = WLog_UdpAppender_Open;
+ appender->Close = WLog_UdpAppender_Close;
+ appender->WriteMessage = WLog_UdpAppender_WriteMessage;
+ appender->WriteDataMessage = WLog_UdpAppender_WriteDataMessage;
+ appender->WriteImageMessage = WLog_UdpAppender_WriteImageMessage;
+ appender->Free = WLog_UdpAppender_Free;
+ appender->Set = WLog_UdpAppender_Set;
+ appender->sock = _socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+
+ if (appender->sock == INVALID_SOCKET)
+ goto error_sock;
+
+ name = "WLOG_UDP_TARGET";
+ nSize = GetEnvironmentVariableA(name, NULL, 0);
+
+ if (nSize)
+ {
+ appender->host = (LPSTR)malloc(nSize);
+
+ if (!appender->host)
+ goto error_open;
+
+ if (GetEnvironmentVariableA(name, appender->host, nSize) != nSize - 1)
+ goto error_open;
+
+ if (!WLog_UdpAppender_Open(log, (wLogAppender*)appender))
+ goto error_open;
+ }
+ else
+ {
+ appender->host = _strdup("127.0.0.1:20000");
+
+ if (!appender->host)
+ goto error_open;
+ }
+
+ return (wLogAppender*)appender;
+error_open:
+ free(appender->host);
+ closesocket(appender->sock);
+error_sock:
+ free(appender);
+ return NULL;
+}
diff --git a/winpr/libwinpr/utils/wlog/UdpAppender.h b/winpr/libwinpr/utils/wlog/UdpAppender.h
new file mode 100644
index 0000000..eda98b9
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/UdpAppender.h
@@ -0,0 +1,34 @@
+/**
+ * Copyright © 2015 Thincast Technologies GmbH
+ * Copyright © 2015 David FORT <contact@hardening-consulting.com>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and
+ * its documentation for any purpose is hereby granted without fee, provided
+ * that the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of the copyright holders not be used in
+ * advertising or publicity pertaining to distribution of the software
+ * without specific, written prior permission. The copyright holders make
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied warranty.
+ *
+ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_
+#define WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_
+
+#include <winpr/wlog.h>
+
+#include "wlog.h"
+
+WINPR_ATTR_MALLOC(WLog_Appender_Free, 2)
+wLogAppender* WLog_UdpAppender_New(wLog* log);
+
+#endif /* WINPR_LIBWINPR_UTILS_WLOG_UDPAPPENDER_H_ */
diff --git a/winpr/libwinpr/utils/wlog/wlog.c b/winpr/libwinpr/utils/wlog/wlog.c
new file mode 100644
index 0000000..4f064ff
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/wlog.c
@@ -0,0 +1,1071 @@
+/**
+ * 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 <stdio.h>
+#include <stdarg.h>
+#include <string.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/print.h>
+#include <winpr/debug.h>
+#include <winpr/environment.h>
+#include <winpr/wlog.h>
+
+#if defined(ANDROID)
+#include <android/log.h>
+#include "../log.h"
+#endif
+
+#include "wlog.h"
+
+typedef struct
+{
+ DWORD Level;
+ LPSTR* Names;
+ size_t NameCount;
+} wLogFilter;
+
+#define WLOG_FILTER_NOT_FILTERED -1
+#define WLOG_FILTER_NOT_INITIALIZED -2
+/**
+ * References for general logging concepts:
+ *
+ * Short introduction to log4j:
+ * http://logging.apache.org/log4j/1.2/manual.html
+ *
+ * logging - Logging facility for Python:
+ * http://docs.python.org/2/library/logging.html
+ */
+
+LPCSTR WLOG_LEVELS[7] = { "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", "OFF" };
+
+static INIT_ONCE _WLogInitialized = INIT_ONCE_STATIC_INIT;
+static DWORD g_FilterCount = 0;
+static wLogFilter* g_Filters = NULL;
+static wLog* g_RootLog = NULL;
+
+static wLog* WLog_New(LPCSTR name, wLog* rootLogger);
+static void WLog_Free(wLog* log);
+static LONG WLog_GetFilterLogLevel(wLog* log);
+static int WLog_ParseLogLevel(LPCSTR level);
+static BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name);
+static BOOL WLog_ParseFilters(wLog* root);
+static wLog* WLog_Get_int(wLog* root, LPCSTR name);
+
+#if !defined(_WIN32)
+static void WLog_Uninit_(void) __attribute__((destructor));
+#endif
+
+static void WLog_Uninit_(void)
+{
+ wLog* child = NULL;
+ wLog* root = g_RootLog;
+
+ if (!root)
+ return;
+
+ for (DWORD index = 0; index < root->ChildrenCount; index++)
+ {
+ child = root->Children[index];
+ WLog_Free(child);
+ }
+
+ WLog_Free(root);
+ g_RootLog = NULL;
+}
+
+static void WLog_Lock(wLog* log)
+{
+ WINPR_ASSERT(log);
+ EnterCriticalSection(&log->lock);
+}
+
+static void WLog_Unlock(wLog* log)
+{
+ WINPR_ASSERT(log);
+ LeaveCriticalSection(&log->lock);
+}
+
+static BOOL CALLBACK WLog_InitializeRoot(PINIT_ONCE InitOnce, PVOID Parameter, PVOID* Context)
+{
+ char* env = NULL;
+ DWORD nSize = 0;
+ DWORD logAppenderType = 0;
+ LPCSTR appender = "WLOG_APPENDER";
+
+ WINPR_UNUSED(InitOnce);
+ WINPR_UNUSED(Parameter);
+ WINPR_UNUSED(Context);
+
+ if (!(g_RootLog = WLog_New("", NULL)))
+ return FALSE;
+
+ g_RootLog->IsRoot = TRUE;
+ logAppenderType = WLOG_APPENDER_CONSOLE;
+ nSize = GetEnvironmentVariableA(appender, NULL, 0);
+
+ if (nSize)
+ {
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ goto fail;
+
+ if (GetEnvironmentVariableA(appender, env, nSize) != nSize - 1)
+ {
+ fprintf(stderr, "%s environment variable modified in my back", appender);
+ free(env);
+ goto fail;
+ }
+
+ if (_stricmp(env, "CONSOLE") == 0)
+ logAppenderType = WLOG_APPENDER_CONSOLE;
+ else if (_stricmp(env, "FILE") == 0)
+ logAppenderType = WLOG_APPENDER_FILE;
+ else if (_stricmp(env, "BINARY") == 0)
+ logAppenderType = WLOG_APPENDER_BINARY;
+
+#ifdef WINPR_HAVE_SYSLOG_H
+ else if (_stricmp(env, "SYSLOG") == 0)
+ logAppenderType = WLOG_APPENDER_SYSLOG;
+
+#endif /* WINPR_HAVE_SYSLOG_H */
+#ifdef WINPR_HAVE_JOURNALD_H
+ else if (_stricmp(env, "JOURNALD") == 0)
+ logAppenderType = WLOG_APPENDER_JOURNALD;
+
+#endif
+ else if (_stricmp(env, "UDP") == 0)
+ logAppenderType = WLOG_APPENDER_UDP;
+
+ free(env);
+ }
+
+ if (!WLog_SetLogAppenderType(g_RootLog, logAppenderType))
+ goto fail;
+
+ if (!WLog_ParseFilters(g_RootLog))
+ goto fail;
+
+ atexit(WLog_Uninit_);
+
+ return TRUE;
+fail:
+ WLog_Uninit_();
+ return FALSE;
+}
+
+static BOOL log_recursion(LPCSTR file, LPCSTR fkt, size_t line)
+{
+ BOOL status = FALSE;
+ char** msg = NULL;
+ size_t used = 0;
+ void* bt = winpr_backtrace(20);
+#if defined(ANDROID)
+ LPCSTR tag = WINPR_TAG("utils.wlog");
+#endif
+
+ if (!bt)
+ return FALSE;
+
+ msg = winpr_backtrace_symbols(bt, &used);
+
+ if (!msg)
+ goto out;
+
+#if defined(ANDROID)
+
+ if (__android_log_print(ANDROID_LOG_FATAL, tag, "Recursion detected!!!") < 0)
+ goto out;
+
+ if (__android_log_print(ANDROID_LOG_FATAL, tag, "Check %s [%s:%zu]", fkt, file, line) < 0)
+ goto out;
+
+ for (size_t i = 0; i < used; i++)
+ if (__android_log_print(ANDROID_LOG_FATAL, tag, "%zu: %s", i, msg[i]) < 0)
+ goto out;
+
+#else
+
+ if (fprintf(stderr, "[%s]: Recursion detected!\n", fkt) < 0)
+ goto out;
+
+ if (fprintf(stderr, "[%s]: Check %s:%" PRIuz "\n", fkt, file, line) < 0)
+ goto out;
+
+ for (size_t i = 0; i < used; i++)
+ if (fprintf(stderr, "%s: %" PRIuz ": %s\n", fkt, i, msg[i]) < 0)
+ goto out;
+
+#endif
+ status = TRUE;
+out:
+ free(msg);
+ winpr_backtrace_free(bt);
+ return status;
+}
+
+static BOOL WLog_Write(wLog* log, wLogMessage* message)
+{
+ BOOL status = FALSE;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->WriteMessage)
+ {
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WriteMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+static BOOL WLog_WriteData(wLog* log, wLogMessage* message)
+{
+ BOOL status = 0;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ if (!appender->WriteDataMessage)
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WriteDataMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+static BOOL WLog_WriteImage(wLog* log, wLogMessage* message)
+{
+ BOOL status = 0;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ if (!appender->WriteImageMessage)
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WriteImageMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+static BOOL WLog_WritePacket(wLog* log, wLogMessage* message)
+{
+ BOOL status = 0;
+ wLogAppender* appender = NULL;
+ appender = WLog_GetLogAppender(log);
+
+ if (!appender)
+ return FALSE;
+
+ if (!appender->active)
+ if (!WLog_OpenAppender(log))
+ return FALSE;
+
+ if (!appender->WritePacketMessage)
+ return FALSE;
+
+ EnterCriticalSection(&appender->lock);
+
+ if (appender->recursive)
+ status = log_recursion(message->FileName, message->FunctionName, message->LineNumber);
+ else
+ {
+ appender->recursive = TRUE;
+ status = appender->WritePacketMessage(log, appender, message);
+ appender->recursive = FALSE;
+ }
+
+ LeaveCriticalSection(&appender->lock);
+ return status;
+}
+
+BOOL WLog_PrintMessageVA(wLog* log, DWORD type, DWORD level, size_t line, const char* file,
+ const char* function, va_list args)
+{
+ BOOL status = FALSE;
+ wLogMessage message = { 0 };
+ message.Type = type;
+ message.Level = level;
+ message.LineNumber = line;
+ message.FileName = file;
+ message.FunctionName = function;
+
+ switch (type)
+ {
+ case WLOG_MESSAGE_TEXT:
+ message.FormatString = va_arg(args, const char*);
+
+ if (!strchr(message.FormatString, '%'))
+ {
+ message.TextString = message.FormatString;
+ status = WLog_Write(log, &message);
+ }
+ else
+ {
+ char formattedLogMessage[WLOG_MAX_STRING_SIZE] = { 0 };
+
+ if (vsnprintf(formattedLogMessage, WLOG_MAX_STRING_SIZE - 1, message.FormatString,
+ args) < 0)
+ return FALSE;
+
+ message.TextString = formattedLogMessage;
+ status = WLog_Write(log, &message);
+ }
+
+ break;
+
+ case WLOG_MESSAGE_DATA:
+ message.Data = va_arg(args, void*);
+ message.Length = va_arg(args, size_t);
+ status = WLog_WriteData(log, &message);
+ break;
+
+ case WLOG_MESSAGE_IMAGE:
+ message.ImageData = va_arg(args, void*);
+ message.ImageWidth = va_arg(args, size_t);
+ message.ImageHeight = va_arg(args, size_t);
+ message.ImageBpp = va_arg(args, size_t);
+ status = WLog_WriteImage(log, &message);
+ break;
+
+ case WLOG_MESSAGE_PACKET:
+ message.PacketData = va_arg(args, void*);
+ message.PacketLength = va_arg(args, size_t);
+ message.PacketFlags = va_arg(args, unsigned);
+ status = WLog_WritePacket(log, &message);
+ break;
+
+ default:
+ break;
+ }
+
+ return status;
+}
+
+BOOL WLog_PrintMessage(wLog* log, DWORD type, DWORD level, size_t line, const char* file,
+ const char* function, ...)
+{
+ BOOL status = 0;
+ va_list args;
+ va_start(args, function);
+ status = WLog_PrintMessageVA(log, type, level, line, file, function, args);
+ va_end(args);
+ return status;
+}
+
+DWORD WLog_GetLogLevel(wLog* log)
+{
+ if (!log)
+ return WLOG_OFF;
+
+ if (log->FilterLevel <= WLOG_FILTER_NOT_INITIALIZED)
+ log->FilterLevel = WLog_GetFilterLogLevel(log);
+
+ if (log->FilterLevel > WLOG_FILTER_NOT_FILTERED)
+ return (DWORD)log->FilterLevel;
+ else if (log->Level == WLOG_LEVEL_INHERIT)
+ log->Level = WLog_GetLogLevel(log->Parent);
+
+ return log->Level;
+}
+
+BOOL WLog_IsLevelActive(wLog* _log, DWORD _log_level)
+{
+ DWORD level = 0;
+
+ if (!_log)
+ return FALSE;
+
+ level = WLog_GetLogLevel(_log);
+
+ if (level == WLOG_OFF)
+ return FALSE;
+
+ return _log_level >= level;
+}
+
+BOOL WLog_SetStringLogLevel(wLog* log, LPCSTR level)
+{
+ int lvl = 0;
+
+ if (!log || !level)
+ return FALSE;
+
+ lvl = WLog_ParseLogLevel(level);
+
+ if (lvl < 0)
+ return FALSE;
+
+ return WLog_SetLogLevel(log, (DWORD)lvl);
+}
+
+static BOOL WLog_reset_log_filters(wLog* log)
+{
+ if (!log)
+ return FALSE;
+
+ log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
+
+ for (DWORD x = 0; x < log->ChildrenCount; x++)
+ {
+ wLog* child = log->Children[x];
+
+ if (!WLog_reset_log_filters(child))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL WLog_AddStringLogFilters_int(wLog* root, LPCSTR filter)
+{
+ LPSTR p = NULL;
+ LPCSTR filterStr = NULL;
+
+ if (!filter)
+ return FALSE;
+
+ DWORD count = 1;
+ LPCSTR cpp = filter;
+
+ while ((cpp = strchr(cpp, ',')) != NULL)
+ {
+ count++;
+ cpp++;
+ }
+
+ DWORD pos = g_FilterCount;
+ DWORD size = g_FilterCount + count;
+ wLogFilter* tmp = (wLogFilter*)realloc(g_Filters, size * sizeof(wLogFilter));
+
+ if (!tmp)
+ return FALSE;
+
+ g_Filters = tmp;
+ LPSTR cp = (LPSTR)_strdup(filter);
+
+ if (!cp)
+ return FALSE;
+
+ p = cp;
+ filterStr = cp;
+
+ do
+ {
+ p = strchr(p, ',');
+
+ if (p)
+ *p = '\0';
+
+ if (pos < size)
+ {
+ if (!WLog_ParseFilter(root, &g_Filters[pos++], filterStr))
+ {
+ free(cp);
+ return FALSE;
+ }
+ }
+ else
+ break;
+
+ if (p)
+ {
+ filterStr = p + 1;
+ p++;
+ }
+ } while (p != NULL);
+
+ g_FilterCount = size;
+ free(cp);
+ return WLog_reset_log_filters(root);
+}
+
+BOOL WLog_AddStringLogFilters(LPCSTR filter)
+{
+ /* Ensure logger is initialized */
+ wLog* root = WLog_GetRoot();
+ return WLog_AddStringLogFilters_int(root, filter);
+}
+
+static BOOL WLog_UpdateInheritLevel(wLog* log, DWORD logLevel)
+{
+ if (!log)
+ return FALSE;
+
+ if (log->inherit)
+ {
+ log->Level = logLevel;
+
+ for (DWORD x = 0; x < log->ChildrenCount; x++)
+ {
+ wLog* child = log->Children[x];
+
+ if (!WLog_UpdateInheritLevel(child, logLevel))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL WLog_SetLogLevel(wLog* log, DWORD logLevel)
+{
+ if (!log)
+ return FALSE;
+
+ if ((logLevel > WLOG_OFF) && (logLevel != WLOG_LEVEL_INHERIT))
+ logLevel = WLOG_OFF;
+
+ log->Level = logLevel;
+ log->inherit = (logLevel == WLOG_LEVEL_INHERIT) ? TRUE : FALSE;
+
+ for (DWORD x = 0; x < log->ChildrenCount; x++)
+ {
+ wLog* child = log->Children[x];
+
+ if (!WLog_UpdateInheritLevel(child, logLevel))
+ return FALSE;
+ }
+
+ return WLog_reset_log_filters(log);
+}
+
+int WLog_ParseLogLevel(LPCSTR level)
+{
+ int iLevel = -1;
+
+ if (!level)
+ return -1;
+
+ if (_stricmp(level, "TRACE") == 0)
+ iLevel = WLOG_TRACE;
+ else if (_stricmp(level, "DEBUG") == 0)
+ iLevel = WLOG_DEBUG;
+ else if (_stricmp(level, "INFO") == 0)
+ iLevel = WLOG_INFO;
+ else if (_stricmp(level, "WARN") == 0)
+ iLevel = WLOG_WARN;
+ else if (_stricmp(level, "ERROR") == 0)
+ iLevel = WLOG_ERROR;
+ else if (_stricmp(level, "FATAL") == 0)
+ iLevel = WLOG_FATAL;
+ else if (_stricmp(level, "OFF") == 0)
+ iLevel = WLOG_OFF;
+
+ return iLevel;
+}
+
+BOOL WLog_ParseFilter(wLog* root, wLogFilter* filter, LPCSTR name)
+{
+ const char* pc = NULL;
+ char* p = NULL;
+ char* q = NULL;
+ size_t count = 0;
+ LPSTR names = NULL;
+ int iLevel = 0;
+ count = 1;
+
+ WINPR_UNUSED(root);
+
+ if (!name)
+ return FALSE;
+
+ pc = name;
+
+ if (pc)
+ {
+ while ((pc = strchr(pc, '.')) != NULL)
+ {
+ count++;
+ pc++;
+ }
+ }
+
+ names = _strdup(name);
+
+ if (!names)
+ return FALSE;
+
+ filter->NameCount = count;
+ filter->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR));
+
+ if (!filter->Names)
+ {
+ free(names);
+ filter->NameCount = 0;
+ return FALSE;
+ }
+
+ filter->Names[count] = NULL;
+ count = 0;
+ p = (char*)names;
+ filter->Names[count++] = p;
+ q = strrchr(p, ':');
+
+ if (!q)
+ {
+ free(names);
+ free(filter->Names);
+ filter->Names = NULL;
+ filter->NameCount = 0;
+ return FALSE;
+ }
+
+ *q = '\0';
+ q++;
+ iLevel = WLog_ParseLogLevel(q);
+
+ if (iLevel < 0)
+ {
+ free(names);
+ free(filter->Names);
+ filter->Names = NULL;
+ filter->NameCount = 0;
+ return FALSE;
+ }
+
+ filter->Level = (DWORD)iLevel;
+
+ while ((p = strchr(p, '.')) != NULL)
+ {
+ if (count < filter->NameCount)
+ filter->Names[count++] = p + 1;
+
+ *p = '\0';
+ p++;
+ }
+
+ return TRUE;
+}
+
+BOOL WLog_ParseFilters(wLog* root)
+{
+ LPCSTR filter = "WLOG_FILTER";
+ BOOL res = FALSE;
+ char* env = NULL;
+ DWORD nSize = 0;
+ free(g_Filters);
+ g_Filters = NULL;
+ g_FilterCount = 0;
+ nSize = GetEnvironmentVariableA(filter, NULL, 0);
+
+ if (nSize < 1)
+ return TRUE;
+
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ return FALSE;
+
+ if (GetEnvironmentVariableA(filter, env, nSize) == nSize - 1)
+ res = WLog_AddStringLogFilters_int(root, env);
+
+ free(env);
+ return res;
+}
+
+LONG WLog_GetFilterLogLevel(wLog* log)
+{
+ BOOL match = FALSE;
+
+ if (log->FilterLevel >= 0)
+ return log->FilterLevel;
+
+ log->FilterLevel = WLOG_FILTER_NOT_FILTERED;
+ for (DWORD i = 0; i < g_FilterCount; i++)
+ {
+ const wLogFilter* filter = &g_Filters[i];
+ for (DWORD j = 0; j < filter->NameCount; j++)
+ {
+ if (j >= log->NameCount)
+ break;
+
+ if (_stricmp(filter->Names[j], "*") == 0)
+ {
+ match = TRUE;
+ log->FilterLevel = filter->Level;
+ break;
+ }
+
+ if (_stricmp(filter->Names[j], log->Names[j]) != 0)
+ break;
+
+ if (j == (log->NameCount - 1))
+ {
+ match = log->NameCount == filter->NameCount;
+ if (match)
+ log->FilterLevel = filter->Level;
+ break;
+ }
+ }
+
+ if (match)
+ break;
+ }
+
+ return log->FilterLevel;
+}
+
+static BOOL WLog_ParseName(wLog* log, LPCSTR name)
+{
+ const char* cp = name;
+ char* p = NULL;
+ size_t count = 1;
+ LPSTR names = NULL;
+
+ while ((cp = strchr(cp, '.')) != NULL)
+ {
+ count++;
+ cp++;
+ }
+
+ names = _strdup(name);
+
+ if (!names)
+ return FALSE;
+
+ log->NameCount = count;
+ log->Names = (LPSTR*)calloc((count + 1UL), sizeof(LPSTR));
+
+ if (!log->Names)
+ {
+ free(names);
+ return FALSE;
+ }
+
+ log->Names[count] = NULL;
+ count = 0;
+ p = (char*)names;
+ log->Names[count++] = p;
+
+ while ((p = strchr(p, '.')) != NULL)
+ {
+ if (count < log->NameCount)
+ log->Names[count++] = p + 1;
+
+ *p = '\0';
+ p++;
+ }
+
+ return TRUE;
+}
+
+wLog* WLog_New(LPCSTR name, wLog* rootLogger)
+{
+ wLog* log = NULL;
+ char* env = NULL;
+ DWORD nSize = 0;
+ int iLevel = 0;
+ log = (wLog*)calloc(1, sizeof(wLog));
+
+ if (!log)
+ return NULL;
+
+ log->Name = _strdup(name);
+
+ if (!log->Name)
+ goto out_fail;
+
+ if (!WLog_ParseName(log, name))
+ goto out_fail;
+
+ log->Parent = rootLogger;
+ log->ChildrenCount = 0;
+ log->ChildrenSize = 16;
+ log->FilterLevel = WLOG_FILTER_NOT_INITIALIZED;
+
+ if (!(log->Children = (wLog**)calloc(log->ChildrenSize, sizeof(wLog*))))
+ goto out_fail;
+
+ log->Appender = NULL;
+
+ if (rootLogger)
+ {
+ log->Level = WLOG_LEVEL_INHERIT;
+ log->inherit = TRUE;
+ }
+ else
+ {
+ LPCSTR level = "WLOG_LEVEL";
+ log->Level = WLOG_INFO;
+ nSize = GetEnvironmentVariableA(level, NULL, 0);
+
+ if (nSize)
+ {
+ env = (LPSTR)malloc(nSize);
+
+ if (!env)
+ goto out_fail;
+
+ if (GetEnvironmentVariableA(level, env, nSize) != nSize - 1)
+ {
+ fprintf(stderr, "%s environment variable changed in my back !\n", level);
+ free(env);
+ goto out_fail;
+ }
+
+ iLevel = WLog_ParseLogLevel(env);
+ free(env);
+
+ if (iLevel >= 0)
+ {
+ if (!WLog_SetLogLevel(log, (DWORD)iLevel))
+ goto out_fail;
+ }
+ }
+ }
+
+ iLevel = WLog_GetFilterLogLevel(log);
+
+ if (iLevel >= 0)
+ {
+ if (!WLog_SetLogLevel(log, (DWORD)iLevel))
+ goto out_fail;
+ }
+
+ InitializeCriticalSectionAndSpinCount(&log->lock, 4000);
+
+ return log;
+out_fail:
+ free(log->Children);
+ free(log->Name);
+ free(log);
+ return NULL;
+}
+
+void WLog_Free(wLog* log)
+{
+ if (log)
+ {
+ if (log->Appender)
+ {
+ WLog_Appender_Free(log, log->Appender);
+ log->Appender = NULL;
+ }
+
+ free(log->Name);
+ free(log->Names[0]);
+ free(log->Names);
+ free(log->Children);
+ DeleteCriticalSection(&log->lock);
+ free(log);
+ }
+}
+
+wLog* WLog_GetRoot(void)
+{
+ if (!InitOnceExecuteOnce(&_WLogInitialized, WLog_InitializeRoot, NULL, NULL))
+ return NULL;
+
+ return g_RootLog;
+}
+
+static BOOL WLog_AddChild(wLog* parent, wLog* child)
+{
+ BOOL status = FALSE;
+
+ WLog_Lock(parent);
+
+ if (parent->ChildrenCount >= parent->ChildrenSize)
+ {
+ wLog** tmp = NULL;
+ parent->ChildrenSize *= 2;
+
+ if (!parent->ChildrenSize)
+ {
+ if (parent->Children)
+ free(parent->Children);
+
+ parent->Children = NULL;
+ }
+ else
+ {
+ tmp = (wLog**)realloc(parent->Children, sizeof(wLog*) * parent->ChildrenSize);
+
+ if (!tmp)
+ {
+ if (parent->Children)
+ free(parent->Children);
+
+ parent->Children = NULL;
+ goto exit;
+ }
+
+ parent->Children = tmp;
+ }
+ }
+
+ if (!parent->Children)
+ goto exit;
+
+ parent->Children[parent->ChildrenCount++] = child;
+ child->Parent = parent;
+
+ WLog_Unlock(parent);
+
+ status = TRUE;
+exit:
+ return status;
+}
+
+static wLog* WLog_FindChild(wLog* root, LPCSTR name)
+{
+ wLog* child = NULL;
+ BOOL found = FALSE;
+
+ if (!root)
+ return NULL;
+
+ WLog_Lock(root);
+
+ for (DWORD index = 0; index < root->ChildrenCount; index++)
+ {
+ child = root->Children[index];
+
+ if (strcmp(child->Name, name) == 0)
+ {
+ found = TRUE;
+ break;
+ }
+ }
+
+ WLog_Unlock(root);
+
+ return (found) ? child : NULL;
+}
+
+static wLog* WLog_Get_int(wLog* root, LPCSTR name)
+{
+ wLog* log = NULL;
+
+ if (!(log = WLog_FindChild(root, name)))
+ {
+ if (!root)
+ return NULL;
+
+ if (!(log = WLog_New(name, root)))
+ return NULL;
+
+ if (!WLog_AddChild(root, log))
+ {
+ WLog_Free(log);
+ return NULL;
+ }
+ }
+
+ return log;
+}
+
+wLog* WLog_Get(LPCSTR name)
+{
+ wLog* root = WLog_GetRoot();
+ return WLog_Get_int(root, name);
+}
+
+#if defined(WITH_WINPR_DEPRECATED)
+BOOL WLog_Init(void)
+{
+ return WLog_GetRoot() != NULL;
+}
+
+BOOL WLog_Uninit(void)
+{
+ wLog* root = g_RootLog;
+
+ if (!root)
+ return FALSE;
+
+ WLog_Lock(root);
+
+ for (DWORD index = 0; index < root->ChildrenCount; index++)
+ {
+ wLog* child = root->Children[index];
+ WLog_Free(child);
+ }
+
+ WLog_Unlock(root);
+
+ WLog_Free(root);
+ g_RootLog = NULL;
+
+ return TRUE;
+}
+#endif
+
+BOOL WLog_SetContext(wLog* log, const char* (*fkt)(void*), void* context)
+{
+ WINPR_ASSERT(log);
+
+ log->custom = fkt;
+ log->context = context;
+ return TRUE;
+}
diff --git a/winpr/libwinpr/utils/wlog/wlog.h b/winpr/libwinpr/utils/wlog/wlog.h
new file mode 100644
index 0000000..2d4a41e
--- /dev/null
+++ b/winpr/libwinpr/utils/wlog/wlog.h
@@ -0,0 +1,92 @@
+/**
+ * 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_PRIVATE_H
+#define WINPR_WLOG_PRIVATE_H
+
+#include <winpr/wlog.h>
+
+#define WLOG_MAX_PREFIX_SIZE 512
+#define WLOG_MAX_STRING_SIZE 8192
+
+typedef BOOL (*WLOG_APPENDER_OPEN_FN)(wLog* log, wLogAppender* appender);
+typedef BOOL (*WLOG_APPENDER_CLOSE_FN)(wLog* log, wLogAppender* appender);
+typedef BOOL (*WLOG_APPENDER_WRITE_MESSAGE_FN)(wLog* log, wLogAppender* appender,
+ wLogMessage* message);
+typedef BOOL (*WLOG_APPENDER_WRITE_DATA_MESSAGE_FN)(wLog* log, wLogAppender* appender,
+ wLogMessage* message);
+typedef BOOL (*WLOG_APPENDER_WRITE_IMAGE_MESSAGE_FN)(wLog* log, wLogAppender* appender,
+ wLogMessage* message);
+typedef BOOL (*WLOG_APPENDER_WRITE_PACKET_MESSAGE_FN)(wLog* log, wLogAppender* appender,
+ wLogMessage* message);
+typedef BOOL (*WLOG_APPENDER_SET)(wLogAppender* appender, const char* setting, void* value);
+typedef void (*WLOG_APPENDER_FREE)(wLogAppender* appender);
+
+#define WLOG_APPENDER_COMMON() \
+ DWORD Type; \
+ BOOL active; \
+ wLogLayout* Layout; \
+ CRITICAL_SECTION lock; \
+ BOOL recursive; \
+ void* TextMessageContext; \
+ void* DataMessageContext; \
+ void* ImageMessageContext; \
+ void* PacketMessageContext; \
+ WLOG_APPENDER_OPEN_FN Open; \
+ WLOG_APPENDER_CLOSE_FN Close; \
+ WLOG_APPENDER_WRITE_MESSAGE_FN WriteMessage; \
+ WLOG_APPENDER_WRITE_DATA_MESSAGE_FN WriteDataMessage; \
+ WLOG_APPENDER_WRITE_IMAGE_MESSAGE_FN WriteImageMessage; \
+ WLOG_APPENDER_WRITE_PACKET_MESSAGE_FN WritePacketMessage; \
+ WLOG_APPENDER_FREE Free; \
+ WLOG_APPENDER_SET Set
+
+struct s_wLogAppender
+{
+ WLOG_APPENDER_COMMON();
+};
+
+struct s_wLog
+{
+ LPSTR Name;
+ LONG FilterLevel;
+ DWORD Level;
+
+ BOOL IsRoot;
+ BOOL inherit;
+ LPSTR* Names;
+ size_t NameCount;
+ wLogAppender* Appender;
+
+ wLog* Parent;
+ wLog** Children;
+ DWORD ChildrenCount;
+ DWORD ChildrenSize;
+ CRITICAL_SECTION lock;
+ const char* (*custom)(void*);
+ void* context;
+};
+
+extern const char* WLOG_LEVELS[7];
+BOOL WLog_Layout_GetMessagePrefix(wLog* log, wLogLayout* layout, wLogMessage* message);
+
+#include "Layout.h"
+#include "Appender.h"
+
+#endif /* WINPR_WLOG_PRIVATE_H */